# /* stftp.c */ /* EMACS_MODES: c !fill */ /* This is a simple server tftp for the UNIX net system. It runs * with one connection per process, forking as needed to handle * multiple connections. In the fork, the parent services the * incoming request, while the child becomes the new listener. * This is necessary to avoid lots of zombie children lying around * (it's very inconvenient for the listener to wait for its children). */ #include #include #include #include #include "tftp.h" #include "conn.h" /* Global variables */ #define MAILMODE 0660 /* mail file mode */ #define LOCKMODE 0444 /* lock file mode */ long xfer_size; /* transfer size in bytes */ char *maildir = "/maild/"; /* mail daemon directory */ char *sigmaild = "/bin/sig_maild"; /* program to kick off mail daemon */ char *tftpdir = "/tftpd"; /* tftp directory */ char *lockfile = "/tftpd/lock"; /* the lock file */ long start; /* start time */ long finish; /* finish time */ extern char *tempfile(); /* to get temp file name */ main (argc, argv) int argc; char **argv; { struct conn *cn; /* connection block */ int succ; /* success code */ int lockfd; /* lock file descriptor */ FILE *lockptr; /* lock file pointer */ int pid; /* parent proc. id */ chdir (tftpdir); if ((lockfd = creat (lockfile, LOCKMODE)) < 0) { cn_inform ("Duplicate daemon exiting\n", 0); exit (1); } lockptr = fcons (lockfd, "w"); /* set up lock file */ pid = getpid (); fprintf (lockptr, "%d", pid); fclose (lockptr); time (&start); printf ("\nPid %d %sTFTP Daemon Initialized\n", pid, ctime (&start)); fflush (stdout); init_cmd (); /* set up command file */ if ((cn = cn_lstn ()) == NULL) exit (1); time (&start); log_conn (cn, start); if (cn->dir == READ) succ = tftp_read (cn); /* their read is our write */ else succ = tftp_write (cn); /* and vice versa */ time (&finish); if (succ) { print_stats (xfer_size, finish - start); exit (0); } else exit (1); } log_conn (cn, now) /* Print appropriate logging information to the log file. * * Arguments: */ register struct conn *cn; /* the connection */ long now; /* current time */ { time (&now); printf ("\nPid %d, %s%s req for %s\nfhost %X fsock %o lsock %o\n", getpid (), ctime (&now), (cn->mode == MAIL ? "mail" : (cn->dir == READ ? "read" : "write")), cn->file, cn->fhost, cn->forsock, cn->locsock); fflush (stdout); } tftp_write (cn) /* User asked for a tftp write operation. Do it. Return TRUE on success * and FALSE otherwise. */ struct conn *cn; /* this connection */ { FILE *locfd; /* local file descriptor */ int imfd; /* fd for image mode xfrs */ register caddr_t pdata; /* data pointer */ register struct tftp *ptftp; /* packet being sent */ short len; /* packet length */ int more; /* more data flag */ int cr_seen; /* for netascii */ register int ch; /* next character read */ struct statb stb; /* for file status */ char *tmpnam; /* temp file name */ if (stat (cn->file, &stb) >= 0) { /* already exists */ cn_log ("Local file already exists\n", TEEXIST, 0); cn_err (cn, cn->fhost, cn->forsock, TEEXIST, "file already exists"); cn_close (cn); return (FALSE); } tmpnam = tempfile (cn->file, cn->mode); /* get temp file name */ if (cn->mode == NETASCII || cn->mode == MAIL) { if ((locfd = fopen (tmpnam, "w")) == NULL) { cn_log ("Can't open local file\n", TEACESS, 0); cn_err (cn, cn->fhost, cn->forsock, TEACESS, "unable to open file for write"); cn_close (cn); return (FALSE); } if (cn->mode == MAIL) chmod (tmpnam, MAILMODE); /* change to right mode */ cn_ack (cn); /* ack first packet */ cr_seen = FALSE; more = TRUE; while (more) { if ((ptftp = cn_rcv (cn)) == NULL) { unlink (tmpnam); return (FALSE); } pdata = &ptftp->fp_data.f_data.f_blk[0]; len = cn->cur_len - 4; more = (len == DATALEN); xfer_size += len; while (len-- > 0) { ch = *pdata++; if (cr_seen) { cr_seen = FALSE; if (ch == '\n') putc(ch, locfd); else putc('\r', locfd); } else if (ch == '\r') cr_seen = TRUE; else putc(ch, locfd); } } fclose (locfd); if (cn->mode == MAIL) mail_fin (cn->file, tmpnam); else { /* netascii mode */ link (tmpnam, cn->file); unlink (tmpnam); } cfree (tmpnam); } else { if ((imfd = creat (tmpnam, 0666)) < 0) { cn_log ("Unable to open local file\n", TEACESS, 0); cn_err (cn, cn->fhost, cn->forsock, TEACESS, "unable to open file for write"); cn_close (cn); return (FALSE); } cn_ack (cn); /* ack initial packet */ more = TRUE; while (more) { if ((ptftp = cn_rcv (cn)) == NULL) { unlink (tmpnam); return (FALSE); } pdata = &ptftp->fp_data.f_data.f_blk[0]; len = cn->cur_len - 4; if (len == 0) break; more = (len == DATALEN); xfer_size += len; write (imfd, pdata, len); } close (imfd); link (tmpnam, cn->file); unlink (tmpnam); cfree (tmpnam); } cn_rcvf (cn); return (TRUE); } /* Netascii state defintions for write */ #define NORM 0 /* normal character */ #define NEEDLF 1 /* need a linefeed (for ) */ #define NEEDNUL 2 /* need a null (for ) */ tftp_read (cn) /* User asked for a tftp read transaction. Perform it. * Return TRUE if the transfer completes * successfully and FALSE otherwise. * * Arguments: */ struct conn *cn; /* the connection */ { FILE *locfd; /* local file descriptor */ int imfd; /* file desc. for image writes */ struct tftp *ptftp; /* tftp packet being written */ register caddr_t pdata; /* data pointer */ int len; /* packet length */ register int ch; /* next character */ int more; /* more data to send */ register int state; /* netascii state */ if (cn->mode == NETASCII || cn->mode == MAIL) { if ((locfd = fopen (cn->file, "r")) == NULL) { cn_log ("Can't open local file\n", TEFNF, 0); cn_err (cn, cn->fhost, cn->forsock, TEFNF, "file not found"); cn_close (cn); return (FALSE); } more = TRUE; state = NORM; while (more) { ptftp = cn_mkwrt (cn); /* get write packet */ pdata = &ptftp->fp_data.f_data.f_blk[0]; len = 0; do { if (state == NEEDLF) { *pdata++ = '\n'; state = NORM; } else if (state == NEEDNUL) { *pdata++ = '\0'; state = NORM; } else if ((ch = getc (locfd)) == EOF) { more = FALSE; break; } else if (ch == '\n') { *pdata++ = '\r'; state = NEEDLF; } else if (ch == '\r') { *pdata++ = '\r'; state = NEEDNUL; } else *pdata++ = ch; } while (++len < DATALEN); xfer_size += len; if (!cn_wrt (cn, len)) return (FALSE); } fclose (locfd); } else { /* image mode */ if ((imfd = open (cn->file, 0)) < 0) { cn_log ("Can't open local file\n", TEFNF, 0); cn_err (cn, cn->fhost, cn->forsock, TEFNF, "file not found"); return (FALSE); } more = TRUE; while (more) { ptftp = cn_mkwrt (cn); pdata = &ptftp->fp_data.f_data.f_blk[0]; len = read (imfd, pdata, DATALEN); more = (len == DATALEN); xfer_size += len; if (!cn_wrt (cn, len)) return (FALSE); } close (imfd); } return (cn_wrtf (cn)); } print_stats (size, elapsed) long size; long elapsed; { printf ("\nPid %d Transfer successful\n%D bytes in %D seconds, %D baud\n", getpid (), size, elapsed, (size * 8) / elapsed); } char *tempfile (file, mode) /* Create a temporary file name suitable for a write transfer to the specified * file name with the specified transfer type. If the transfer type is not * MAIL, the temporary file must be in the same directory as the specified * file name. If the mode is MAIL, the name is a user name, and the * temporary file must be in the mail daemon's directory. In either * case the routine returns a pointer to a calloc'ed area containing * the temporary file name. * * Arguments: */ char *file; /* file name specified */ int mode; /* transfer mode specified */ { char *tmp; /* temp pointer to calloc'd area */ int len; /* name length */ char name[NAMSIZ]; /* base name of temp file */ register char *p, *q, *s; /* ptrs. to parse dir spec */ tmpnam (name); /* use stdio temp name routine */ if (mode == MAIL) { /* mail mode? */ file = maildir; /* yes; use daemon's dir */ len = strlen (file); /* len of dir spec */ q = &file[len]; /* q -> end of dir. spec */ } else { /* no; parse off directory spec */ p = q = file; /* q = ptr. after last / in name */ while (*p != NULL) { if (*p++ == '/') q = p; } len = q - file; /* len. of dir spec */ } len += strlen (name) + 1; /* get len to alloc */ tmp = calloc (1, len); /* alloc storage */ for (p = file, s = tmp; p < q;) /* copy in dir spec. */ *s++ = *p++; *s++ = NULL; /* null terminate */ strcat (tmp, name); /* concatenate temp name */ return (tmp); /* that's it */ } mail_fin (user, mailfil) /* Finish receiving a mail transfer and deliver the mail to the mailer * daemon for processing. This requires creating a mail request file * and forking a process to signal the mail daemon. * * Arguments: */ char *user; /* recipient name */ char *mailfil; /* file containing mail */ { char namerec[NAMSIZ]; /* mail file name record */ struct req_rec req; /* request file record */ register char *rqname; /* request file name */ register char *tmp; /* temp request file name */ int rqnlen; /* name length */ register FILE *rqfd; /* request file descriptor */ int pid; /* child process id */ rqnlen = strlen (mailfil) + strlen (RQEXT) + 2; tmp = calloc (1, rqnlen); /* alloc space for filename */ rqname = calloc (1, rqnlen); /* alloc space for filename */ strcpy (tmp, mailfil); /* filename will be "mailfil.req" */ strcat (tmp, "."); /* but for now just "mailfil." */ strcpy (rqname, tmp); strcat (rqname, RQEXT); if ((rqfd = fopen (rqname, "w")) == NULL) { /* can't create, lose */ cn_log ("Unable to create request file %s\n", TEACESS, rqname); return (NULL); } strcpy (namerec, mailfil); /* set up file name rec. */ fwrite (namerec, 1, sizeof (namerec), rqfd); /* write it out */ strcpy (req.rq_name, user); /* set up user name rec */ req.rq_retries = RETRIES; fwrite (&req, 1, sizeof (req), rqfd); /* write it out */ fclose (rqfd); /* close it */ link (tmp, rqname); /* rename to request file name */ unlink (tmp); if ((pid = fork ()) == 0) /* child? */ execl (sigmaild, sigmaild, 0); /* yes, signal mail daemon */ cfree (rqname); /* free storage */ cfree (tmp); cfree (mailfil); }