diff options
Diffstat (limited to 'contrib/tftp/tftpd.c')
-rw-r--r-- | contrib/tftp/tftpd.c | 742 |
1 files changed, 0 insertions, 742 deletions
diff --git a/contrib/tftp/tftpd.c b/contrib/tftp/tftpd.c deleted file mode 100644 index 325a7134c..000000000 --- a/contrib/tftp/tftpd.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1983 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tftpd.c 5.8 (Berkeley) 6/18/88"; -#endif /* not lint */ - -/* - * Trivial file transfer protocol server. - * - * This version includes many modifications by Jim Guyton <guyton@rand-unix> - * - * Further modifications by Markus Gutschke <gutschk@math.uni-muenster.de> - * - RFC1782 option parsing - * - RFC1783 extended blocksize - * - "-c" option for changing the root directory - * - "-d" option for debugging output - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/stat.h> - -#include <netinet/in.h> - -#include <arpa/tftp.h> - -#include <alloca.h> -#include <string.h> -#include <signal.h> -#include <stdio.h> -#include <errno.h> -#include <ctype.h> -#include <netdb.h> -#include <setjmp.h> -#include <syslog.h> - -#define TIMEOUT 5 - -#ifndef OACK -#define OACK 06 -#endif - -#ifndef EOPTNEG -#define EOPTNEG 8 -#endif - -extern int errno; -struct sockaddr_in sin = { AF_INET }; -int peer; -int rexmtval = TIMEOUT; -int maxtimeout = 5*TIMEOUT; - -#define PKTSIZE (1432+4) /* SEGSIZE+4 */ -int segsize = SEGSIZE; -char buf[PKTSIZE]; -char ackbuf[PKTSIZE]; -struct sockaddr_in from; -int fromlen; - -char *rootdir = NULL; -int debug = 0; - -struct filters { - struct filters *next; - char *fname; -} *filters = NULL; -int isfilter = 0; - -main(argc, argv) - char *argv[]; -{ - register struct tftphdr *tp; - register int n; - int on = 1; - extern int optind; - extern char *optarg; - - openlog(argv[0], LOG_PID, LOG_DAEMON); - - while ((n = getopt(argc, argv, "c:dr:")) >= 0) { - switch (n) { - case 'c': - if (rootdir) - goto usage; - rootdir = optarg; - break; - case 'd': - debug++; - break; - case 'r': { - struct filters *fp = (void *) - malloc(sizeof(struct filters) + - strlen(optarg) + 1); - fp->next = filters; - fp->fname = (char *)(fp + 1); - strcpy(fp->fname, optarg); - filters = fp; - break; } - default: - usage: - syslog(LOG_ERR, "Usage: %s [-c chroot] " - "[-r readfilter] [-d]\n", - argv[0]); - exit(1); - } - } - if (argc-optind != 0) - goto usage; - - ioctl(0, FIONBIO, &on); -/* if (ioctl(0, FIONBIO, &on) < 0) { - syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); - exit(1); - } -*/ - fromlen = sizeof (from); - n = recvfrom(0, buf, segsize+4, 0, - (struct sockaddr *)&from, &fromlen); - if (n < 0) { - syslog(LOG_ERR, "recvfrom: %m\n"); - exit(1); - } - /* - * Now that we have read the message out of the UDP - * socket, we fork and exit. Thus, inetd will go back - * to listening to the tftp port, and the next request - * to come in will start up a new instance of tftpd. - * - * We do this so that inetd can run tftpd in "wait" mode. - * The problem with tftpd running in "nowait" mode is that - * inetd may get one or more successful "selects" on the - * tftp port before we do our receive, so more than one - * instance of tftpd may be started up. Worse, if tftpd - * break before doing the above "recvfrom", inetd would - * spawn endless instances, clogging the system. - */ - { - int pid; - int i, j; - - for (i = 1; i < 20; i++) { - pid = fork(); - if (pid < 0) { - sleep(i); - /* - * flush out to most recently sent request. - * - * This may drop some request, but those - * will be resent by the clients when - * they timeout. The positive effect of - * this flush is to (try to) prevent more - * than one tftpd being started up to service - * a single request from a single client. - */ - j = sizeof from; - i = recvfrom(0, buf, segsize+4, 0, - (struct sockaddr *)&from, &j); - if (i > 0) { - n = i; - fromlen = j; - } - } else { - break; - } - } - if (pid < 0) { - syslog(LOG_ERR, "fork: %m\n"); - exit(1); - } else if (pid != 0) { - exit(0); - } - } - from.sin_family = AF_INET; - alarm(0); - close(0); - close(1); - peer = socket(AF_INET, SOCK_DGRAM, 0); - if (peer < 0) { - syslog(LOG_ERR, "socket: %m\n"); - exit(1); - } - if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { - syslog(LOG_ERR, "bind: %m\n"); - exit(1); - } - if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { - syslog(LOG_ERR, "connect: %m\n"); - exit(1); - } - tp = (struct tftphdr *)buf; - tp->th_opcode = ntohs(tp->th_opcode); - if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) - tftp(tp, n); - exit(1); -} - -int validate_access(); -int sendfile(), recvfile(); - -struct formats { - char *f_mode; - int (*f_validate)(); - int (*f_send)(); - int (*f_recv)(); - int f_convert; -} formats[] = { - { "netascii", validate_access, sendfile, recvfile, 1 }, - { "octet", validate_access, sendfile, recvfile, 0 }, -#ifdef notdef - { "mail", validate_user, sendmail, recvmail, 1 }, -#endif - { 0 } -}; - -int set_blksize(); - -struct options { - char *o_opt; - int (*o_fnc)(); -} options[] = { - { "blksize", set_blksize }, - { 0 } -}; - -/* - * Set a non-standard block size (c.f. RFC1783) - */ - -set_blksize(val, ret) - char *val; - char **ret; -{ - static char b_ret[5]; - int sz = atoi(val); - - if (sz < 8) { - if (debug) - syslog(LOG_ERR, "Requested packetsize %d < 8\n", sz); - return(0); - } else if (sz > PKTSIZE-4) { - if (debug) - syslog(LOG_INFO, "Requested packetsize %d > %d\n", - sz, PKTSIZE-4); - sz = PKTSIZE-4; - } else if (debug) - syslog(LOG_INFO, "Adjusted packetsize to %d octets\n", sz); - - segsize = sz; - sprintf(*ret = b_ret, "%d", sz); - return(1); -} - -/* - * Parse RFC1782 style options - */ - -do_opt(opt, val, ap) - char *opt; - char *val; - char **ap; -{ - struct options *po; - char *ret; - - for (po = options; po->o_opt; po++) - if (strcasecmp(po->o_opt, opt) == 0) { - if (po->o_fnc(val, &ret)) { - if (*ap + strlen(opt) + strlen(ret) + 2 >= - ackbuf + sizeof(ackbuf)) { - if (debug) - syslog(LOG_ERR, - "Ackbuf overflow\n"); - nak(ENOSPACE); - exit(1); - } - *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt), - '\000')+1, val), - '\000')+1; - } else { - nak(EOPTNEG); - exit(1); - } - break; - } - if (debug && !po->o_opt) - syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val); - return; -} - -/* - * Handle initial connection protocol. - */ -tftp(tp, size) - struct tftphdr *tp; - int size; -{ - register char *cp; - int argn = 0, ecode; - register struct formats *pf; - char *filename, *mode; - char *val, *opt; - char *ap = ackbuf+2; - int isopts; - - ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK); - filename = cp = tp->th_stuff; -again: - while (cp < buf + size) { - if (*cp == '\0') - break; - cp++; - } - if (*cp != '\0') { - if (debug) - syslog(LOG_WARNING, "Received illegal request\n"); - nak(EBADOP); - exit(1); - } - if (!argn++) { - mode = ++cp; - goto again; - } else { - if (debug && argn == 3) - syslog(LOG_INFO, "Found RFC1782 style options\n"); - *(argn & 1 ? &val : &opt) = ++cp; - if (argn & 1) - do_opt(opt, val, &ap); - if (cp < buf + size && *cp != '\000') - goto again; - } - - for (cp = mode; *cp; cp++) - if (isupper(*cp)) - *cp = tolower(*cp); - for (pf = formats; pf->f_mode; pf++) - if (strcmp(pf->f_mode, mode) == 0) - break; - if (pf->f_mode == 0) { - if (debug) - syslog(LOG_WARNING, "Unknown data format: %s\n", mode); - nak(EBADOP); - exit(1); - } - - if (rootdir) { - cp = alloca(strlen(rootdir) + strlen(filename) + 1); - if (cp == NULL) { - nak(100+ENOMEM); - exit(1); - } - if (*filename != '/') { - if (debug) - syslog(LOG_ERR, - "Filename has to be absolute: %s\n", - filename); - nak(EACCESS); - exit(1); - } - filename = strcat(strcpy(cp, rootdir), filename); - } - - ecode = (*pf->f_validate)(filename, tp->th_opcode); - if (ecode) { - nak(ecode, ERROR); - exit(1); - } - isopts = ap != (ackbuf+2); - (tp->th_opcode == WRQ ? *pf->f_recv : *pf->f_send) - (pf, isopts ? ackbuf : NULL, isopts ? ap-ackbuf : 0); - exit(0); -} - - -FILE *file; - -/* - * Validate file access. Since we - * have no uid or gid, for now require - * file to exist and be publicly - * readable/writable. - * Note also, full path name must be - * given as we have no login directory. - */ -validate_access(filename, mode) - char *filename; - int mode; -{ - struct stat stbuf; - int fd; - char *cp; - - isfilter = 0; - if (mode == RRQ) { - struct filters *fp = filters; - for (; fp; fp = fp->next) { - if (!strcmp(fp->fname, - filename + - (rootdir ? strlen(rootdir) : 0))) { - if (debug) - syslog(LOG_INFO, "Opening input " - "filter: %s\n", filename); - if ((file = popen(filename, "r")) == NULL) { - syslog(LOG_ERR, "Failed to open input " - "filter\n"); - return (EACCESS); } - fd = fileno(file); - isfilter = 1; - return (0); - } - } - } - - if (*filename != '/') { - if (debug) - syslog(LOG_ERR, "Filename has to be absolute: %s\n", - filename); - return (EACCESS); - } - for (cp = filename; *cp; cp++) - if (*cp == '~' || *cp == '$' || - (*cp == '/' && cp[1] == '.' && cp[2] == '.')) { - if (debug) - syslog(LOG_ERR, "Illegal filename: %s\n", - filename); - return (EACCESS); - } - if (debug) - syslog(LOG_INFO, "Validating \"%s\" for %sing\n", - filename, mode == RRQ ? "read" : "writ"); - if (stat(filename, &stbuf) < 0) - return (errno == ENOENT ? ENOTFOUND : EACCESS); - if (mode == RRQ) { - if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) - return (EACCESS); - } else { - if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) - return (EACCESS); - } - fd = open(filename, mode == RRQ ? 0 : 1); - if (fd < 0) - return (errno + 100); - file = fdopen(fd, (mode == RRQ)? "r":"w"); - if (file == NULL) { - return errno+100; - } - return (0); -} - -int timeout; -jmp_buf timeoutbuf; - -void timer(int sig) -{ - - timeout += rexmtval; - if (timeout >= maxtimeout) { - if (debug) - syslog(LOG_WARNING, "Timeout!\n"); - exit(1); - } - longjmp(timeoutbuf, 1); -} - -/* - * Send the requested file. - */ -sendfile(pf, oap, oacklen) - struct formats *pf; - struct tftphdr *oap; - int oacklen; -{ - struct tftphdr *dp, *r_init(); - register struct tftphdr *ap; /* ack packet */ - register int size, n; - u_short block = 1; - - signal(SIGALRM, timer); - - ap = (struct tftphdr *)ackbuf; - - if (oap) { - timeout = 0; - (void) setjmp(timeoutbuf); - oack: - if (send(peer, oap, oacklen, 0) != oacklen) { - syslog(LOG_ERR, "tftpd: write: %m\n"); - goto abort; - } - for ( ; ; ) { - alarm(rexmtval); - n = recv(peer, ackbuf, sizeof (ackbuf), 0); - alarm(0); - if (n < 0) { - syslog(LOG_ERR, "tftpd: read: %m\n"); - goto abort; - } - ap->th_opcode = ntohs((u_short)ap->th_opcode); - ap->th_block = ntohs(ap->th_block); - - if (ap->th_opcode == ERROR) { - if (debug) - syslog(LOG_ERR, "Client does not " - "accept options\n"); - goto abort; } - - if (ap->th_opcode == ACK) { - if (ap->th_block == 0) { - if (debug) - syslog(LOG_DEBUG, - "RFC1782 option " - "negotiation " - "succeeded\n"); - break; - } - /* Re-synchronize with the other side */ - (void) synchnet(peer); - goto oack; - } - } - } - - dp = r_init(); - do { - size = readit(file, &dp, pf->f_convert); - if (size < 0) { - nak(errno + 100); - goto abort; - } - dp->th_opcode = htons((u_short)DATA); - dp->th_block = htons(block); - timeout = 0; - (void) setjmp(timeoutbuf); - -send_data: - if (send(peer, dp, size + 4, 0) != size + 4) { - syslog(LOG_ERR, "tftpd: write: %m\n"); - goto abort; - } - read_ahead(file, pf->f_convert); - for ( ; ; ) { - alarm(rexmtval); /* read the ack */ - n = recv(peer, ackbuf, sizeof (ackbuf), 0); - alarm(0); - if (n < 0) { - syslog(LOG_ERR, "tftpd: read: %m\n"); - goto abort; - } - ap->th_opcode = ntohs((u_short)ap->th_opcode); - ap->th_block = ntohs(ap->th_block); - - if (ap->th_opcode == ERROR) - goto abort; - - if (ap->th_opcode == ACK) { - if (ap->th_block == block) { - break; - } - /* Re-synchronize with the other side */ - (void) synchnet(peer); - if (ap->th_block == (block -1)) { - goto send_data; - } - } - - } - block++; - } while (size == segsize); -abort: - if (isfilter) - pclose(file); - else - (void) fclose(file); - isfilter = 0; -} - -void justquit(int sig) -{ - exit(0); -} - - -/* - * Receive a file. - */ -recvfile(pf, oap, oacklen) - struct formats *pf; - struct tftphdr *oap; - int oacklen; -{ - struct tftphdr *dp, *w_init(); - register struct tftphdr *ap; /* ack buffer */ - register int acksize, n, size; - u_short block = 0; - - signal(SIGALRM, timer); - dp = w_init(); - do { - timeout = 0; - - if (!block++ && oap) { - ap = (struct tftphdr *)oap; - acksize = oacklen; - } else { - ap = (struct tftphdr *)ackbuf; - ap->th_opcode = htons((u_short)ACK); - ap->th_block = htons(block-1); - acksize = 4; - } - (void) setjmp(timeoutbuf); -send_ack: - if (send(peer, (char *)ap, acksize, 0) != acksize) { - syslog(LOG_ERR, "tftpd: write: %m\n"); - goto abort; - } - write_behind(file, pf->f_convert); - for ( ; ; ) { - alarm(rexmtval); - n = recv(peer, dp, segsize+4, 0); - alarm(0); - if (n < 0) { /* really? */ - syslog(LOG_ERR, "tftpd: read: %m\n"); - goto abort; - } - dp->th_opcode = ntohs((u_short)dp->th_opcode); - dp->th_block = ntohs(dp->th_block); - if (dp->th_opcode == ERROR) - goto abort; - if (dp->th_opcode == DATA) { - if (dp->th_block == block) { - break; /* normal */ - } - /* Re-synchronize with the other side */ - (void) synchnet(peer); - if (dp->th_block == (block-1)) - goto send_ack; /* rexmit */ - } - } - /* size = write(file, dp->th_data, n - 4); */ - size = writeit(file, &dp, n - 4, pf->f_convert); - if (size != (n-4)) { /* ahem */ - if (size < 0) nak(errno + 100); - else nak(ENOSPACE); - goto abort; - } - } while (size == segsize); - write_behind(file, pf->f_convert); - if (isfilter) - pclose(file); - else - (void) fclose(file); /* close data file */ - isfilter = 0; - - ap = (struct tftphdr *)ackbuf; - ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ - ap->th_block = htons(block); - (void) send(peer, ackbuf, 4, 0); - - signal(SIGALRM, justquit); /* just quit on timeout */ - alarm(rexmtval); - n = recv(peer, buf, segsize, 0); /* normally times out and quits */ - alarm(0); - if (n >= 4 && /* if read some data */ - dp->th_opcode == DATA && /* and got a data block */ - block == dp->th_block) { /* then my last ack was lost */ - (void) send(peer, ackbuf, 4, 0); /* resend final ack */ - } -abort: - return; -} - -struct errmsg { - int e_code; - const char *e_msg; -} errmsgs[] = { - { EUNDEF, "Undefined error code" }, - { ENOTFOUND, "File not found" }, - { EACCESS, "Access violation" }, - { ENOSPACE, "Disk full or allocation exceeded" }, - { EBADOP, "Illegal TFTP operation" }, - { EBADID, "Unknown transfer ID" }, - { EEXISTS, "File already exists" }, - { ENOUSER, "No such user" }, - { EOPTNEG, "Failure to negotiate RFC1782 options" }, - { -1, 0 } -}; - -/* - * Send a nak packet (error message). - * Error code passed in is one of the - * standard TFTP codes, or a UNIX errno - * offset by 100. - */ -nak(error) - int error; -{ - register struct tftphdr *tp; - int length; - register struct errmsg *pe; -/* extern char *sys_errlist[]; */ - - tp = (struct tftphdr *)buf; - tp->th_opcode = htons((u_short)ERROR); - tp->th_code = htons((u_short)error); - for (pe = errmsgs; pe->e_code >= 0; pe++) - if (pe->e_code == error) - break; - if (pe->e_code < 0) { - pe->e_msg = sys_errlist[error -100]; - tp->th_code = EUNDEF; /* set 'undef' errorcode */ - } - strcpy(tp->th_msg, pe->e_msg); - length = strlen(pe->e_msg); - tp->th_msg[length] = '\0'; - length += 5; - if (debug) - syslog(LOG_ERR, "Negative acknowledge: %s\n", tp->th_msg); - if (send(peer, buf, length, 0) != length) - syslog(LOG_ERR, "nak: %m\n"); -} |