Initial revision
[people/sha0/gpxe.git] / contrib / tftp / tftpd.c
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23
24 #ifndef lint
25 static char sccsid[] = "@(#)tftpd.c     5.8 (Berkeley) 6/18/88";
26 #endif /* not lint */
27
28 /*
29  * Trivial file transfer protocol server.
30  *
31  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
32  *
33  * Further modifications by Markus Gutschke <gutschk@math.uni-muenster.de>
34  *  - RFC1782 option parsing
35  *  - RFC1783 extended blocksize
36  *  - "-c" option for changing the root directory
37  *  - "-d" option for debugging output
38  */
39
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
43 #include <sys/wait.h>
44 #include <sys/stat.h>
45
46 #include <netinet/in.h>
47
48 #include <arpa/tftp.h>
49
50 #include <alloca.h>
51 #include <string.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <errno.h>
55 #include <ctype.h>
56 #include <netdb.h>
57 #include <setjmp.h>
58 #include <syslog.h>
59
60 #define TIMEOUT         5
61
62 #ifndef OACK
63 #define OACK    06
64 #endif
65
66 #ifndef EOPTNEG
67 #define EOPTNEG 8
68 #endif
69
70 extern  int errno;
71 struct  sockaddr_in sin = { AF_INET };
72 int     peer;
73 int     rexmtval = TIMEOUT;
74 int     maxtimeout = 5*TIMEOUT;
75
76 #define PKTSIZE (1432+4) /* SEGSIZE+4 */
77 int     segsize = SEGSIZE;
78 char    buf[PKTSIZE];
79 char    ackbuf[PKTSIZE];
80 struct  sockaddr_in from;
81 int     fromlen;
82
83 char    *rootdir = NULL;
84 int     debug = 0;
85
86 struct filters {
87         struct filters *next;
88         char           *fname;
89 } *filters = NULL;
90 int     isfilter = 0;
91
92 main(argc, argv)
93         char *argv[];
94 {
95         register struct tftphdr *tp;
96         register int n;
97         int on = 1;
98         extern int optind;
99         extern char *optarg;
100
101         openlog(argv[0], LOG_PID, LOG_DAEMON);
102
103         while ((n = getopt(argc, argv, "c:dr:")) >= 0) {
104                 switch (n) {
105                 case 'c':
106                         if (rootdir)
107                                 goto usage;
108                         rootdir = optarg;
109                         break;
110                 case 'd':
111                         debug++;
112                         break;
113                 case 'r': {
114                         struct filters *fp = (void *)
115                                              malloc(sizeof(struct filters) +
116                                                     strlen(optarg) + 1);
117                         fp->next  = filters;
118                         fp->fname = (char *)(fp + 1);
119                         strcpy(fp->fname, optarg);
120                         filters   = fp;
121                         break; }
122                 default:
123                 usage:
124                         syslog(LOG_ERR, "Usage: %s [-c chroot] "
125                                "[-r readfilter] [-d]\n",
126                                argv[0]);
127                         exit(1);
128                 }
129         }
130         if (argc-optind != 0)
131                 goto usage;
132
133         ioctl(0, FIONBIO, &on);
134 /*      if (ioctl(0, FIONBIO, &on) < 0) {
135                 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
136                 exit(1);
137         }
138 */
139         fromlen = sizeof (from);
140         n = recvfrom(0, buf, segsize+4, 0,
141             (struct sockaddr *)&from, &fromlen);
142         if (n < 0) {
143                 syslog(LOG_ERR, "recvfrom: %m\n");
144                 exit(1);
145         }
146         /*
147          * Now that we have read the message out of the UDP
148          * socket, we fork and exit.  Thus, inetd will go back
149          * to listening to the tftp port, and the next request
150          * to come in will start up a new instance of tftpd.
151          *
152          * We do this so that inetd can run tftpd in "wait" mode.
153          * The problem with tftpd running in "nowait" mode is that
154          * inetd may get one or more successful "selects" on the
155          * tftp port before we do our receive, so more than one
156          * instance of tftpd may be started up.  Worse, if tftpd
157          * break before doing the above "recvfrom", inetd would
158          * spawn endless instances, clogging the system.
159          */
160         {
161                 int pid;
162                 int i, j;
163
164                 for (i = 1; i < 20; i++) {
165                     pid = fork();
166                     if (pid < 0) {
167                                 sleep(i);
168                                 /*
169                                  * flush out to most recently sent request.
170                                  *
171                                  * This may drop some request, but those
172                                  * will be resent by the clients when
173                                  * they timeout.  The positive effect of
174                                  * this flush is to (try to) prevent more
175                                  * than one tftpd being started up to service
176                                  * a single request from a single client.
177                                  */
178                                 j = sizeof from;
179                                 i = recvfrom(0, buf, segsize+4, 0,
180                                     (struct sockaddr *)&from, &j);
181                                 if (i > 0) {
182                                         n = i;
183                                         fromlen = j;
184                                 }
185                     } else {
186                                 break;
187                     }
188                 }
189                 if (pid < 0) {
190                         syslog(LOG_ERR, "fork: %m\n");
191                         exit(1);
192                 } else if (pid != 0) {
193                         exit(0);
194                 }
195         }
196         from.sin_family = AF_INET;
197         alarm(0);
198         close(0);
199         close(1);
200         peer = socket(AF_INET, SOCK_DGRAM, 0);
201         if (peer < 0) {
202                 syslog(LOG_ERR, "socket: %m\n");
203                 exit(1);
204         }
205         if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
206                 syslog(LOG_ERR, "bind: %m\n");
207                 exit(1);
208         }
209         if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
210                 syslog(LOG_ERR, "connect: %m\n");
211                 exit(1);
212         }
213         tp = (struct tftphdr *)buf;
214         tp->th_opcode = ntohs(tp->th_opcode);
215         if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
216                 tftp(tp, n);
217         exit(1);
218 }
219
220 int     validate_access();
221 int     sendfile(), recvfile();
222
223 struct formats {
224         char    *f_mode;
225         int     (*f_validate)();
226         int     (*f_send)();
227         int     (*f_recv)();
228         int     f_convert;
229 } formats[] = {
230         { "netascii",   validate_access,        sendfile,       recvfile, 1 },
231         { "octet",      validate_access,        sendfile,       recvfile, 0 },
232 #ifdef notdef
233         { "mail",       validate_user,          sendmail,       recvmail, 1 },
234 #endif
235         { 0 }
236 };
237
238 int     set_blksize();
239
240 struct options {
241         char    *o_opt;
242         int     (*o_fnc)();
243 } options[] = {
244         { "blksize",    set_blksize },
245         { 0 }
246 };
247
248 /*
249  * Set a non-standard block size (c.f. RFC1783)
250  */
251
252 set_blksize(val, ret)
253         char *val;
254         char **ret;
255 {
256         static char b_ret[5];
257         int sz = atoi(val);
258
259         if (sz < 8) {
260                 if (debug)
261                         syslog(LOG_ERR, "Requested packetsize %d < 8\n", sz);
262                 return(0);
263         } else if (sz > PKTSIZE-4) {
264                 if (debug)
265                         syslog(LOG_INFO, "Requested packetsize %d > %d\n",
266                                sz, PKTSIZE-4);
267                 sz = PKTSIZE-4;
268         } else if (debug)
269                 syslog(LOG_INFO, "Adjusted packetsize to %d octets\n", sz);
270         
271         segsize = sz;
272         sprintf(*ret = b_ret, "%d", sz);
273         return(1);
274 }
275
276 /*
277  * Parse RFC1782 style options
278  */
279
280 do_opt(opt, val, ap)
281         char *opt;
282         char *val;
283         char **ap;
284 {
285         struct options *po;
286         char *ret;
287
288         for (po = options; po->o_opt; po++)
289                 if (strcasecmp(po->o_opt, opt) == 0) {
290                         if (po->o_fnc(val, &ret)) {
291                                 if (*ap + strlen(opt) + strlen(ret) + 2 >=
292                                     ackbuf + sizeof(ackbuf)) {
293                                         if (debug)
294                                                 syslog(LOG_ERR,
295                                                        "Ackbuf overflow\n");
296                                         nak(ENOSPACE);
297                                         exit(1);
298                                 }
299                                 *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),
300                                                              '\000')+1, val),
301                                               '\000')+1;
302                         } else {
303                                 nak(EOPTNEG);
304                                 exit(1);
305                         }
306                         break;
307                 }
308         if (debug && !po->o_opt)
309                 syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val);
310         return;
311 }
312
313 /*
314  * Handle initial connection protocol.
315  */
316 tftp(tp, size)
317         struct tftphdr *tp;
318         int size;
319 {
320         register char *cp;
321         int argn = 0, ecode;
322         register struct formats *pf;
323         char *filename, *mode;
324         char *val, *opt;
325         char *ap = ackbuf+2;
326         int  isopts;
327
328         ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK);
329         filename = cp = tp->th_stuff;
330 again:
331         while (cp < buf + size) {
332                 if (*cp == '\0')
333                         break;
334                 cp++;
335         }
336         if (*cp != '\0') {
337                 if (debug)
338                         syslog(LOG_WARNING, "Received illegal request\n");
339                 nak(EBADOP);
340                 exit(1);
341         }
342         if (!argn++) {
343                 mode = ++cp;
344                 goto again;
345         } else {
346                 if (debug && argn == 3)
347                         syslog(LOG_INFO, "Found RFC1782 style options\n");
348                 *(argn & 1 ? &val : &opt) = ++cp;
349                 if (argn & 1)
350                         do_opt(opt, val, &ap);
351                 if (cp < buf + size && *cp != '\000')
352                         goto again;
353         }
354         
355         for (cp = mode; *cp; cp++)
356                 if (isupper(*cp))
357                         *cp = tolower(*cp);
358         for (pf = formats; pf->f_mode; pf++)
359                 if (strcmp(pf->f_mode, mode) == 0)
360                         break;
361         if (pf->f_mode == 0) {
362                 if (debug)
363                         syslog(LOG_WARNING, "Unknown data format: %s\n", mode);
364                 nak(EBADOP);
365                 exit(1);
366         }
367
368         if (rootdir) {
369                 cp = alloca(strlen(rootdir) + strlen(filename) + 1);
370                 if (cp == NULL) {
371                         nak(100+ENOMEM);
372                         exit(1);
373                 }
374                 if (*filename != '/') {
375                         if (debug)
376                                 syslog(LOG_ERR,
377                                        "Filename has to be absolute: %s\n",
378                                        filename);
379                         nak(EACCESS);
380                         exit(1);
381                 }
382                 filename = strcat(strcpy(cp, rootdir), filename);
383         }
384         
385         ecode = (*pf->f_validate)(filename, tp->th_opcode);
386         if (ecode) {
387                 nak(ecode, ERROR);
388                 exit(1);
389         }
390         isopts = ap != (ackbuf+2);
391         (tp->th_opcode == WRQ ? *pf->f_recv : *pf->f_send)
392                 (pf, isopts ? ackbuf : NULL, isopts ? ap-ackbuf : 0);
393         exit(0);
394 }
395
396
397 FILE *file;
398
399 /*
400  * Validate file access.  Since we
401  * have no uid or gid, for now require
402  * file to exist and be publicly
403  * readable/writable.
404  * Note also, full path name must be
405  * given as we have no login directory.
406  */
407 validate_access(filename, mode)
408         char *filename;
409         int mode;
410 {
411         struct stat stbuf;
412         int     fd;
413         char    *cp;
414
415         isfilter = 0;
416         if (mode == RRQ) {
417                 struct filters *fp = filters;
418                 for (; fp; fp = fp->next) {
419                         if (!strcmp(fp->fname,
420                                     filename +
421                                     (rootdir ? strlen(rootdir) : 0))) {
422                                 if (debug)
423                                         syslog(LOG_INFO, "Opening input "
424                                                "filter: %s\n", filename);
425                                 if ((file = popen(filename, "r")) == NULL) {
426                                         syslog(LOG_ERR, "Failed to open input "
427                                                "filter\n");
428                                         return (EACCESS); }
429                                 fd = fileno(file);
430                                 isfilter = 1;
431                                 return (0);
432                         }
433                 }
434         }
435                                        
436         if (*filename != '/') {
437                 if (debug)
438                         syslog(LOG_ERR, "Filename has to be absolute: %s\n",
439                                filename);
440                 return (EACCESS);
441         }
442         for (cp = filename; *cp; cp++)
443                 if (*cp == '~' || *cp == '$' ||
444                     (*cp == '/' && cp[1] == '.' && cp[2] == '.')) {
445                         if (debug)
446                                 syslog(LOG_ERR, "Illegal filename: %s\n",
447                                        filename);
448                         return (EACCESS);
449                 }
450         if (debug)
451                 syslog(LOG_INFO, "Validating \"%s\" for %sing\n",
452                        filename, mode == RRQ ? "read" : "writ");
453         if (stat(filename, &stbuf) < 0)
454                 return (errno == ENOENT ? ENOTFOUND : EACCESS);
455         if (mode == RRQ) {
456                 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
457                         return (EACCESS);
458         } else {
459                 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
460                         return (EACCESS);
461         }
462         fd = open(filename, mode == RRQ ? 0 : 1);
463         if (fd < 0)
464                 return (errno + 100);
465         file = fdopen(fd, (mode == RRQ)? "r":"w");
466         if (file == NULL) {
467                 return errno+100;
468         }
469         return (0);
470 }
471
472 int     timeout;
473 jmp_buf timeoutbuf;
474
475 void timer(int sig)
476 {
477
478         timeout += rexmtval;
479         if (timeout >= maxtimeout) {
480                 if (debug)
481                         syslog(LOG_WARNING, "Timeout!\n");
482                 exit(1);
483         }
484         longjmp(timeoutbuf, 1);
485 }
486
487 /*
488  * Send the requested file.
489  */
490 sendfile(pf, oap, oacklen)
491         struct formats *pf;
492         struct tftphdr *oap;
493         int oacklen;
494 {
495         struct tftphdr *dp, *r_init();
496         register struct tftphdr *ap;    /* ack packet */
497         register int size, n;
498         u_short block = 1;
499
500         signal(SIGALRM, timer);
501
502         ap = (struct tftphdr *)ackbuf;
503
504         if (oap) {
505                 timeout = 0;
506                 (void) setjmp(timeoutbuf);
507         oack:
508                 if (send(peer, oap, oacklen, 0) != oacklen) {
509                         syslog(LOG_ERR, "tftpd: write: %m\n");
510                         goto abort;
511                 }
512                 for ( ; ; ) {
513                         alarm(rexmtval);
514                         n = recv(peer, ackbuf, sizeof (ackbuf), 0);
515                         alarm(0);
516                         if (n < 0) {
517                                 syslog(LOG_ERR, "tftpd: read: %m\n");
518                                 goto abort;
519                         }
520                         ap->th_opcode = ntohs((u_short)ap->th_opcode);
521                         ap->th_block = ntohs(ap->th_block);
522                         
523                         if (ap->th_opcode == ERROR) {
524                                 if (debug)
525                                         syslog(LOG_ERR, "Client does not "
526                                                "accept options\n");
527                                 goto abort; }
528                         
529                         if (ap->th_opcode == ACK) {
530                                 if (ap->th_block == 0) {
531                                         if (debug)
532                                                 syslog(LOG_DEBUG,
533                                                        "RFC1782 option "
534                                                        "negotiation "
535                                                        "succeeded\n");
536                                         break;
537                                 }
538                                 /* Re-synchronize with the other side */
539                                 (void) synchnet(peer);
540                                 goto oack;
541                         }
542                 }
543         }
544         
545         dp = r_init();
546         do {
547                 size = readit(file, &dp, pf->f_convert);
548                 if (size < 0) {
549                         nak(errno + 100);
550                         goto abort;
551                 }
552                 dp->th_opcode = htons((u_short)DATA);
553                 dp->th_block = htons(block);
554                 timeout = 0;
555                 (void) setjmp(timeoutbuf);
556
557 send_data:
558                 if (send(peer, dp, size + 4, 0) != size + 4) {
559                         syslog(LOG_ERR, "tftpd: write: %m\n");
560                         goto abort;
561                 }
562                 read_ahead(file, pf->f_convert);
563                 for ( ; ; ) {
564                         alarm(rexmtval);        /* read the ack */
565                         n = recv(peer, ackbuf, sizeof (ackbuf), 0);
566                         alarm(0);
567                         if (n < 0) {
568                                 syslog(LOG_ERR, "tftpd: read: %m\n");
569                                 goto abort;
570                         }
571                         ap->th_opcode = ntohs((u_short)ap->th_opcode);
572                         ap->th_block = ntohs(ap->th_block);
573
574                         if (ap->th_opcode == ERROR)
575                                 goto abort;
576                         
577                         if (ap->th_opcode == ACK) {
578                                 if (ap->th_block == block) {
579                                         break;
580                                 }
581                                 /* Re-synchronize with the other side */
582                                 (void) synchnet(peer);
583                                 if (ap->th_block == (block -1)) {
584                                         goto send_data;
585                                 }
586                         }
587
588                 }
589                 block++;
590         } while (size == segsize);
591 abort:
592         if (isfilter)
593                 pclose(file);
594         else
595                 (void) fclose(file);
596         isfilter = 0;
597 }
598
599 void justquit(int sig)
600 {
601         exit(0);
602 }
603
604
605 /*
606  * Receive a file.
607  */
608 recvfile(pf, oap, oacklen)
609         struct formats *pf;
610         struct tftphdr *oap;
611         int oacklen;
612 {
613         struct tftphdr *dp, *w_init();
614         register struct tftphdr *ap;    /* ack buffer */
615         register int acksize, n, size;
616         u_short block = 0;
617
618         signal(SIGALRM, timer);
619         dp = w_init();
620         do {
621                 timeout = 0;
622
623                 if (!block++ && oap) {
624                         ap = (struct tftphdr *)oap;
625                         acksize = oacklen;
626                 } else {
627                         ap = (struct tftphdr *)ackbuf;
628                         ap->th_opcode = htons((u_short)ACK);
629                         ap->th_block = htons(block-1);
630                         acksize = 4;
631                 }
632                 (void) setjmp(timeoutbuf);
633 send_ack:
634                 if (send(peer, (char *)ap, acksize, 0) != acksize) {
635                         syslog(LOG_ERR, "tftpd: write: %m\n");
636                         goto abort;
637                 }
638                 write_behind(file, pf->f_convert);
639                 for ( ; ; ) {
640                         alarm(rexmtval);
641                         n = recv(peer, dp, segsize+4, 0);
642                         alarm(0);
643                         if (n < 0) {            /* really? */
644                                 syslog(LOG_ERR, "tftpd: read: %m\n");
645                                 goto abort;
646                         }
647                         dp->th_opcode = ntohs((u_short)dp->th_opcode);
648                         dp->th_block = ntohs(dp->th_block);
649                         if (dp->th_opcode == ERROR)
650                                 goto abort;
651                         if (dp->th_opcode == DATA) {
652                                 if (dp->th_block == block) {
653                                         break;   /* normal */
654                                 }
655                                 /* Re-synchronize with the other side */
656                                 (void) synchnet(peer);
657                                 if (dp->th_block == (block-1))
658                                         goto send_ack;          /* rexmit */
659                         }
660                 }
661                 /*  size = write(file, dp->th_data, n - 4); */
662                 size = writeit(file, &dp, n - 4, pf->f_convert);
663                 if (size != (n-4)) {                    /* ahem */
664                         if (size < 0) nak(errno + 100);
665                         else nak(ENOSPACE);
666                         goto abort;
667                 }
668         } while (size == segsize);
669         write_behind(file, pf->f_convert);
670         if (isfilter)
671                 pclose(file);
672         else
673                 (void) fclose(file);            /* close data file */
674         isfilter = 0;
675
676         ap = (struct tftphdr *)ackbuf;
677         ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
678         ap->th_block = htons(block);
679         (void) send(peer, ackbuf, 4, 0);
680
681         signal(SIGALRM, justquit);      /* just quit on timeout */
682         alarm(rexmtval);
683         n = recv(peer, buf, segsize, 0); /* normally times out and quits */
684         alarm(0);
685         if (n >= 4 &&                   /* if read some data */
686             dp->th_opcode == DATA &&    /* and got a data block */
687             block == dp->th_block) {    /* then my last ack was lost */
688                 (void) send(peer, ackbuf, 4, 0);     /* resend final ack */
689         }
690 abort:
691         return;
692 }
693
694 struct errmsg {
695         int     e_code;
696         const char      *e_msg;
697 } errmsgs[] = {
698         { EUNDEF,       "Undefined error code" },
699         { ENOTFOUND,    "File not found" },
700         { EACCESS,      "Access violation" },
701         { ENOSPACE,     "Disk full or allocation exceeded" },
702         { EBADOP,       "Illegal TFTP operation" },
703         { EBADID,       "Unknown transfer ID" },
704         { EEXISTS,      "File already exists" },
705         { ENOUSER,      "No such user" },
706         { EOPTNEG,      "Failure to negotiate RFC1782 options" },
707         { -1,           0 }
708 };
709
710 /*
711  * Send a nak packet (error message).
712  * Error code passed in is one of the
713  * standard TFTP codes, or a UNIX errno
714  * offset by 100.
715  */
716 nak(error)
717         int error;
718 {
719         register struct tftphdr *tp;
720         int length;
721         register struct errmsg *pe;
722 /*      extern char *sys_errlist[];     */
723
724         tp = (struct tftphdr *)buf;
725         tp->th_opcode = htons((u_short)ERROR);
726         tp->th_code = htons((u_short)error);
727         for (pe = errmsgs; pe->e_code >= 0; pe++)
728                 if (pe->e_code == error)
729                         break;
730         if (pe->e_code < 0) {
731                 pe->e_msg = sys_errlist[error -100];
732                 tp->th_code = EUNDEF;   /* set 'undef' errorcode */
733         }
734         strcpy(tp->th_msg, pe->e_msg);
735         length = strlen(pe->e_msg);
736         tp->th_msg[length] = '\0';
737         length += 5;
738         if (debug)
739                 syslog(LOG_ERR, "Negative acknowledge: %s\n", tp->th_msg);
740         if (send(peer, buf, length, 0) != length)
741                 syslog(LOG_ERR, "nak: %m\n");
742 }