[ath5k] Update for changes in kernel 2.6.31
[gpxe.git] / contrib / tftp / tftpd.c
blob325a7134cb6a0b6be5900bf1d96a7a591c2b1aaf
1 /*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved.
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.
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 */
24 #ifndef lint
25 static char sccsid[] = "@(#)tftpd.c 5.8 (Berkeley) 6/18/88";
26 #endif /* not lint */
29 * Trivial file transfer protocol server.
31 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
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
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>
46 #include <netinet/in.h>
48 #include <arpa/tftp.h>
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>
60 #define TIMEOUT 5
62 #ifndef OACK
63 #define OACK 06
64 #endif
66 #ifndef EOPTNEG
67 #define EOPTNEG 8
68 #endif
70 extern int errno;
71 struct sockaddr_in sin = { AF_INET };
72 int peer;
73 int rexmtval = TIMEOUT;
74 int maxtimeout = 5*TIMEOUT;
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;
83 char *rootdir = NULL;
84 int debug = 0;
86 struct filters {
87 struct filters *next;
88 char *fname;
89 } *filters = NULL;
90 int isfilter = 0;
92 main(argc, argv)
93 char *argv[];
95 register struct tftphdr *tp;
96 register int n;
97 int on = 1;
98 extern int optind;
99 extern char *optarg;
101 openlog(argv[0], LOG_PID, LOG_DAEMON);
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);
130 if (argc-optind != 0)
131 goto usage;
133 ioctl(0, FIONBIO, &on);
134 /* if (ioctl(0, FIONBIO, &on) < 0) {
135 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
136 exit(1);
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);
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.
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.
161 int pid;
162 int i, j;
164 for (i = 1; i < 20; i++) {
165 pid = fork();
166 if (pid < 0) {
167 sleep(i);
169 * flush out to most recently sent request.
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.
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;
185 } else {
186 break;
189 if (pid < 0) {
190 syslog(LOG_ERR, "fork: %m\n");
191 exit(1);
192 } else if (pid != 0) {
193 exit(0);
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);
205 if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
206 syslog(LOG_ERR, "bind: %m\n");
207 exit(1);
209 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
210 syslog(LOG_ERR, "connect: %m\n");
211 exit(1);
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);
220 int validate_access();
221 int sendfile(), recvfile();
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 }
238 int set_blksize();
240 struct options {
241 char *o_opt;
242 int (*o_fnc)();
243 } options[] = {
244 { "blksize", set_blksize },
245 { 0 }
249 * Set a non-standard block size (c.f. RFC1783)
252 set_blksize(val, ret)
253 char *val;
254 char **ret;
256 static char b_ret[5];
257 int sz = atoi(val);
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);
271 segsize = sz;
272 sprintf(*ret = b_ret, "%d", sz);
273 return(1);
277 * Parse RFC1782 style options
280 do_opt(opt, val, ap)
281 char *opt;
282 char *val;
283 char **ap;
285 struct options *po;
286 char *ret;
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);
299 *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),
300 '\000')+1, val),
301 '\000')+1;
302 } else {
303 nak(EOPTNEG);
304 exit(1);
306 break;
308 if (debug && !po->o_opt)
309 syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val);
310 return;
314 * Handle initial connection protocol.
316 tftp(tp, size)
317 struct tftphdr *tp;
318 int size;
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;
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++;
336 if (*cp != '\0') {
337 if (debug)
338 syslog(LOG_WARNING, "Received illegal request\n");
339 nak(EBADOP);
340 exit(1);
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;
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);
368 if (rootdir) {
369 cp = alloca(strlen(rootdir) + strlen(filename) + 1);
370 if (cp == NULL) {
371 nak(100+ENOMEM);
372 exit(1);
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);
382 filename = strcat(strcpy(cp, rootdir), filename);
385 ecode = (*pf->f_validate)(filename, tp->th_opcode);
386 if (ecode) {
387 nak(ecode, ERROR);
388 exit(1);
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);
397 FILE *file;
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.
407 validate_access(filename, mode)
408 char *filename;
409 int mode;
411 struct stat stbuf;
412 int fd;
413 char *cp;
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);
436 if (*filename != '/') {
437 if (debug)
438 syslog(LOG_ERR, "Filename has to be absolute: %s\n",
439 filename);
440 return (EACCESS);
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);
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);
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;
469 return (0);
472 int timeout;
473 jmp_buf timeoutbuf;
475 void timer(int sig)
478 timeout += rexmtval;
479 if (timeout >= maxtimeout) {
480 if (debug)
481 syslog(LOG_WARNING, "Timeout!\n");
482 exit(1);
484 longjmp(timeoutbuf, 1);
488 * Send the requested file.
490 sendfile(pf, oap, oacklen)
491 struct formats *pf;
492 struct tftphdr *oap;
493 int oacklen;
495 struct tftphdr *dp, *r_init();
496 register struct tftphdr *ap; /* ack packet */
497 register int size, n;
498 u_short block = 1;
500 signal(SIGALRM, timer);
502 ap = (struct tftphdr *)ackbuf;
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;
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;
520 ap->th_opcode = ntohs((u_short)ap->th_opcode);
521 ap->th_block = ntohs(ap->th_block);
523 if (ap->th_opcode == ERROR) {
524 if (debug)
525 syslog(LOG_ERR, "Client does not "
526 "accept options\n");
527 goto abort; }
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;
538 /* Re-synchronize with the other side */
539 (void) synchnet(peer);
540 goto oack;
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;
552 dp->th_opcode = htons((u_short)DATA);
553 dp->th_block = htons(block);
554 timeout = 0;
555 (void) setjmp(timeoutbuf);
557 send_data:
558 if (send(peer, dp, size + 4, 0) != size + 4) {
559 syslog(LOG_ERR, "tftpd: write: %m\n");
560 goto abort;
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;
571 ap->th_opcode = ntohs((u_short)ap->th_opcode);
572 ap->th_block = ntohs(ap->th_block);
574 if (ap->th_opcode == ERROR)
575 goto abort;
577 if (ap->th_opcode == ACK) {
578 if (ap->th_block == block) {
579 break;
581 /* Re-synchronize with the other side */
582 (void) synchnet(peer);
583 if (ap->th_block == (block -1)) {
584 goto send_data;
589 block++;
590 } while (size == segsize);
591 abort:
592 if (isfilter)
593 pclose(file);
594 else
595 (void) fclose(file);
596 isfilter = 0;
599 void justquit(int sig)
601 exit(0);
606 * Receive a file.
608 recvfile(pf, oap, oacklen)
609 struct formats *pf;
610 struct tftphdr *oap;
611 int oacklen;
613 struct tftphdr *dp, *w_init();
614 register struct tftphdr *ap; /* ack buffer */
615 register int acksize, n, size;
616 u_short block = 0;
618 signal(SIGALRM, timer);
619 dp = w_init();
620 do {
621 timeout = 0;
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;
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;
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;
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 */
655 /* Re-synchronize with the other side */
656 (void) synchnet(peer);
657 if (dp->th_block == (block-1))
658 goto send_ack; /* rexmit */
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;
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;
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);
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 */
690 abort:
691 return;
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 }
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.
716 nak(error)
717 int error;
719 register struct tftphdr *tp;
720 int length;
721 register struct errmsg *pe;
722 /* extern char *sys_errlist[]; */
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 */
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");