summaryrefslogtreecommitdiffstats
path: root/contrib/tftp/tftpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tftp/tftpd.c')
-rw-r--r--contrib/tftp/tftpd.c742
1 files changed, 0 insertions, 742 deletions
diff --git a/contrib/tftp/tftpd.c b/contrib/tftp/tftpd.c
deleted file mode 100644
index 325a7134..00000000
--- 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");
-}