|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)tftpd.c 4.11 (Berkeley) 7/2/83";
3: #endif
4:
5: /*
6: * Trivial file transfer protocol server.
7: */
8: #include <sys/types.h>
9: #include <sys/socket.h>
10: #include <sys/ioctl.h>
11: #include <sys/wait.h>
12: #include <sys/stat.h>
13:
14: #include <netinet/in.h>
15:
16: #include <arpa/tftp.h>
17:
18: #include <signal.h>
19: #include <stdio.h>
20: #include <errno.h>
21: #include <ctype.h>
22: #include <netdb.h>
23: #include <setjmp.h>
24:
25: #define TIMEOUT 5
26:
27: extern int errno;
28: extern int sys_nerr;
29: extern char *sys_errlist[];
30:
31: struct in_addr my_machine_addr;
32: struct sockaddr_in sin = { AF_INET };
33: int f;
34: int rexmtval = TIMEOUT;
35: int maxtimeout = 5*TIMEOUT;
36: char buf[BUFSIZ];
37: int reapchild();
38:
39: char *inet_ntoa();
40: /*---------------------------------------------------------
41: * Changes
42: *
43: * . change declaration of tftp header for compiler independence.
44: *
45: * . truncate the file after file transfer completes.
46: *
47: * . the daemon keeps its master socket; it creates a new socket
48: * and hands this socket to its children. In the old implementation,
49: * the daemon hands the socket to its child, and creates a new socket;
50: * therefore, requests queued at old socket will be discarded, and
51: * requests arriving before the daemon successfully creates a new
52: * socket are also discarded.
53: *
54: * . change userid and groupid to -2 before verifying the access
55: * rights. Temporary Fix Until An Official Policy Is Established.
56: *
57: * . change perror() to macro PERROR() to ensure that error messages
58: * come to the console.
59: *
60: * . let the system assign address to the child daemon.
61: *--------------------------------------------------------*/
62: #define PERROR(X) { \
63: int cfd; \
64: if ((cfd=open("/dev/console",2)) >=0) { \
65: write(cfd,X,strlen(X)); \
66: write(cfd,": ", 2); \
67: if (errno >=0 && errno <sys_nerr) \
68: write(cfd,sys_errlist[errno], \
69: strlen(sys_errlist[errno]));\
70: write(cfd,"\n",1); \
71: close(cfd); \
72: } \
73: }
74:
75: main(argc, argv)
76: char *argv[];
77: {
78: struct sockaddr_in from;
79: register struct tftphdr *tp;
80: register int n;
81: int bindstat = -1;
82: struct servent *sp;
83:
84: get_myaddrs();
85:
86: sp = getservbyname("tftp", "udp");
87: if (sp == 0) {
88: fprintf(stderr, "tftpd: udp/tftp: unknown service\n");
89: exit(1);
90: }
91: sin.sin_port = sp->s_port;
92: #ifndef DEBUG
93: if (fork())
94: exit(0);
95: for (f = 0; f < 10; f++)
96: (void) close(f);
97: (void) open("/", 0);
98: (void) dup2(0, 1);
99: (void) dup2(0, 2);
100: { int t = open("/dev/tty", 2);
101: if (t >= 0) {
102: ioctl(t, TIOCNOTTY, (char *)0);
103: (void) close(t);
104: }
105: }
106: #endif
107: signal(SIGCHLD, reapchild);
108: do {
109: f = socket(AF_INET, SOCK_DGRAM, 0);
110: if (f < 0) {
111: PERROR("tftpd: socket");
112: sleep(5);
113: }
114: } while (f<0);
115: if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
116: PERROR("tftpd: setsockopt (SO_REUSEADDR)");
117: for (n = 0; n<25; n++) {
118: if ((bindstat = bind(f, (caddr_t)&sin, sizeof (sin), 0))>= 0) {
119: break;
120: }
121: else {
122: PERROR("tftpd: bind");
123: sleep(5);
124: }
125: }
126: if (bindstat <0) {
127: PERROR("tftpd: unable to bind. Terminated.\n");
128: exit(1);
129: }
130:
131: for (;;) {
132: int fromlen;
133:
134: do {
135:
136: fromlen = sizeof (from);
137: from.sin_family = AF_INET;
138: n = recvfrom(f, buf, sizeof (buf), 0,
139: (caddr_t)&from, &fromlen);
140: #ifdef DEBUG
141: fprintf(stderr,"tftpd: got request fr %s %d.\n",
142: inet_ntoa(from.sin_addr), from.sin_port);
143: #endif
144: } while (n <= 0);
145:
146: tp = (struct tftphdr *)buf;
147: tp->th_opcode = ntohs(tp->th_opcode);
148: if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) {
149: #ifdef DEBUG
150: fprintf(stderr,"tftpd: fork a child.\n");
151: #endif
152: if (fork() == 0) {
153: close(f);
154: do {
155: f = socket(AF_INET, SOCK_DGRAM, 0);
156: if (f < 0) {
157: PERROR("tftpd: socket(child)");
158: sleep(5);
159: }
160: } while (f<0);
161:
162: if (setsockopt(f, SOL_SOCKET,
163: SO_REUSEADDR, 0, 0) < 0)
164: PERROR("tftpd: setsockopt(REUSEADDR)");
165:
166: /* let the system assign the address */
167: sin.sin_addr.s_addr = 0;
168: sin.sin_port = 0;
169: while (bind(f,
170: (caddr_t)&sin, sizeof (sin), 0) < 0) {
171: PERROR("tftpd: bind (child)");
172: sleep(5);
173: }
174: tftp(&from, tp, n);
175: }
176: }
177: }
178: }
179:
180: reapchild()
181: {
182: union wait status;
183:
184: while (wait3(&status, WNOHANG, 0) > 0)
185: ;
186: }
187:
188: int validate_access();
189: int sendfile(), recvfile();
190:
191: struct formats {
192: char *f_mode;
193: int (*f_validate)();
194: int (*f_send)();
195: int (*f_recv)();
196: } formats[] = {
197: { "netascii", validate_access, sendfile, recvfile },
198: { "octet", validate_access, sendfile, recvfile },
199: #ifdef notdef
200: { "mail", validate_user, sendmail, recvmail },
201: #endif
202: { 0 }
203: };
204:
205: FILE *fd; /* file being transferred */
206:
207: /*
208: * Handle initial connection protocol.
209: */
210: tftp(client, tp, size)
211: struct sockaddr_in *client;
212: struct tftphdr *tp;
213: int size;
214: {
215: register char *cp;
216: int first = 1, ecode;
217: register struct formats *pf;
218: char *filename, *mode;
219:
220: #ifdef DEBUG
221: fprintf(stderr,"tftpd:sockfd %d, connect to %s %d\n",
222: f, inet_ntoa(client->sin_addr), client->sin_port);
223: #endif
224: if (connect(f, (caddr_t)client, sizeof (*client), 0) < 0) {
225: PERROR("tftpd:connect");
226: exit(1);
227: }
228: #ifdef DEBUG
229: {
230: struct sockaddr_in myname;
231: struct sockaddr_in peername;
232: int t;
233: int err = 0;
234:
235: myname.sin_family = AF_INET;
236: t = sizeof(myname);
237: if (getsockname(f, (caddr_t)&myname, &t) < 0) {
238: PERROR("tftpd:getsockname");
239: err = 1;
240: }
241: if (err!= 1)
242: fprintf(stderr,"tftpd:myname is %s %d\n",
243: inet_ntoa(myname.sin_addr),
244: myname.sin_port);
245:
246: peername.sin_family = AF_INET;
247: t = sizeof(peername);
248: if (getpeername(f, (caddr_t)&peername, &t) < 0) {
249: PERROR("tftpd:getpeername");
250: err = 2;
251: }
252: if (err!=2)
253: fprintf(stderr,"tftpd:peername is %s %d\n",
254: inet_ntoa(peername.sin_addr),
255: peername.sin_port);
256: if (err)
257: exit(1);
258: }
259: #endif
260: filename = cp = tp->th_stuff;
261: again:
262: while (cp < buf + size) {
263: if (*cp == '\0')
264: break;
265: cp++;
266: }
267: if (*cp != '\0') {
268: nak(EBADOP);
269: exit(1);
270: }
271: if (first) {
272: mode = ++cp;
273: first = 0;
274: goto again;
275: }
276: for (cp = mode; *cp; cp++)
277: if (isupper(*cp))
278: *cp = tolower(*cp);
279: for (pf = formats; pf->f_mode; pf++)
280: if (strcmp(pf->f_mode, mode) == 0)
281: break;
282: if (pf->f_mode == 0) {
283: nak(EBADOP);
284: exit(1);
285: }
286: ecode = (*pf->f_validate)(filename, client, tp->th_opcode);
287: if (ecode) {
288: nak(ecode);
289: exit(1);
290: }
291: if (tp->th_opcode == WRQ)
292: (*pf->f_recv)(pf);
293: else
294: (*pf->f_send)(pf);
295: exit(0);
296: }
297:
298: /*
299: * Validate file access. Since we
300: * have no uid or gid, for now require
301: * file to exist and be publicly
302: * readable/writable.
303: * Note also, full path name must be
304: * given as we have no login directory.
305: */
306: validate_access(file, client, mode)
307: char *file;
308: struct sockaddr_in *client;
309: int mode;
310: {
311: struct stat stbuf;
312:
313: if (*file != '/')
314: return (EACCESS);
315:
316: /* temporary fix */
317: if (setuid(-2) <0 ||
318: setgid(-2) <0) {
319: PERROR("tftpd: cannot setuid or setpid");
320: }
321:
322: if (stat(file, &stbuf) < 0)
323: return (errno == ENOENT ? ENOTFOUND : EACCESS);
324: if (mode == RRQ) {
325: if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
326: return (EACCESS);
327: } else {
328: if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
329: return (EACCESS);
330: }
331: fd = fopen(file, mode == RRQ ? "r" : "w");
332: if (!fd)
333: return (errno + 100);
334: if (mode == WRQ) {
335: if ((ftruncate(fileno(fd),0))== -1) {
336: PERROR("tftpd : truncate ");
337: return(errno + 100);
338: }
339: }
340: return (0);
341: }
342:
343: int timeout;
344: jmp_buf timeoutbuf;
345:
346: timer()
347: {
348:
349: timeout += rexmtval;
350: if (timeout >= maxtimeout)
351: exit(1);
352: longjmp(timeoutbuf, 1);
353: }
354:
355: /*
356: * Send the requested file.
357: */
358: sendfile(pf)
359: struct format *pf;
360: {
361: register struct tftphdr *tp;
362: register int block = 1, size, n;
363: int fsize;
364:
365: signal(SIGALRM, timer);
366: tp = (struct tftphdr *)buf;
367: do {
368: size = tftpread(fd, tp->th_data, SEGSIZE, &fsize);
369: if (size < 0) {
370: nak(errno + 100);
371: goto abort;
372: }
373: tp->th_opcode = htons((u_short)DATA);
374: tp->th_block = htons((u_short)block);
375: timeout = 0;
376: (void) setjmp(timeoutbuf);
377: if (write(f, buf, size + 4) != size + 4) {
378: PERROR("tftpd: write");
379: goto abort;
380: }
381: do {
382: alarm(rexmtval);
383: n = read(f, buf, sizeof (buf));
384: alarm(0);
385: if (n < 0) {
386: PERROR("tftpd: read");
387: goto abort;
388: }
389: tp->th_opcode = ntohs((u_short)tp->th_opcode);
390: tp->th_block = ntohs((u_short)tp->th_block);
391: if (tp->th_opcode == ERROR)
392: goto abort;
393: } while (tp->th_opcode != ACK || tp->th_block != block);
394: block++;
395: } while (size == SEGSIZE);
396: abort:
397: (void) fclose(fd);
398: }
399:
400: /*
401: * Receive a file.
402: */
403: recvfile(pf)
404: struct format *pf;
405: {
406: register struct tftphdr *tp;
407: register int block = 0, n, size;
408: int fsize;
409:
410: signal(SIGALRM, timer);
411: tp = (struct tftphdr *)buf;
412: do {
413: timeout = 0;
414: tp->th_opcode = htons((u_short)ACK);
415: tp->th_block = htons((u_short)block);
416: block++;
417: (void) setjmp(timeoutbuf);
418: if (write(f, buf, 4) != 4) {
419: PERROR("tftpd: write");
420: goto abort;
421: }
422: do {
423: alarm(rexmtval);
424: n = read(f, buf, sizeof (buf));
425: alarm(0);
426: if (n < 0) {
427: PERROR("tftpd: read");
428: goto abort;
429: }
430: tp->th_opcode = ntohs((u_short)tp->th_opcode);
431: tp->th_block = ntohs((u_short)tp->th_block);
432: if (tp->th_opcode == ERROR)
433: goto abort;
434: } while (tp->th_opcode != DATA || block != tp->th_block);
435: size = n -4;
436: n = tftpwrite(fd, tp->th_data, size, &fsize);
437: if (n < 0) {
438: nak(errno + 100);
439: goto abort;
440: }
441: } while (size == SEGSIZE);
442: abort:
443: tp->th_opcode = htons((u_short)ACK);
444: tp->th_block = htons((u_short)(block));
445: (void) write(f, buf, 4);
446: (void) fclose(fd);
447: }
448:
449: struct errmsg {
450: int e_code;
451: char *e_msg;
452: } errmsgs[] = {
453: { EUNDEF, "Undefined error code" },
454: { ENOTFOUND, "File not found" },
455: { EACCESS, "Access violation" },
456: { ENOSPACE, "Disk full or allocation exceeded" },
457: { EBADOP, "Illegal TFTP operation" },
458: { EBADID, "Unknown transfer ID" },
459: { EEXISTS, "File already exists" },
460: { ENOUSER, "No such user" },
461: { -1, 0 }
462: };
463:
464: /*
465: * Send a nak packet (error message).
466: * Error code passed in is one of the
467: * standard TFTP codes, or a UNIX errno
468: * offset by 100.
469: */
470: nak(error)
471: int error;
472: {
473: register struct tftphdr *tp;
474: int length;
475: register struct errmsg *pe;
476:
477: tp = (struct tftphdr *)buf;
478: tp->th_opcode = htons((u_short)ERROR);
479: tp->th_code = htons((u_short)error);
480: for (pe = errmsgs; pe->e_code >= 0; pe++)
481: if (pe->e_code == error)
482: break;
483: if (pe->e_code < 0)
484: pe->e_msg = sys_errlist[error - 100];
485: strcpy(tp->th_msg, pe->e_msg);
486: length = strlen(pe->e_msg);
487: tp->th_msg[length] = '\0';
488: length += 5;
489: if (write(f, buf, length) != length)
490: PERROR("tftpd:nak");
491: }
492: get_myaddrs()
493: {
494: struct hostent *hp;
495: char my_machine_name[BUFSIZ];
496:
497: /* look up the address of the local host */
498: if (gethostname(my_machine_name, sizeof(my_machine_name)) <0) {
499: perror("tftpd:cannot get this host name");
500: exit(-1);
501: }
502:
503: hp = gethostbyname(my_machine_name);
504: if (hp == (struct hostent *) 0) {
505: fprintf(stderr,"tftpd:cannot get host address");
506: exit(-2);
507: }
508:
509: if (hp->h_addrtype != AF_INET) {
510: fprintf(stderr,"Protocal mix up with local machine address\n");
511: exit(-1);
512: }
513:
514: bcopy(hp->h_addr, (char *)&my_machine_addr, hp->h_length);
515: }
516:
517: bcopy(fr, to, size)
518: register char *fr;
519: register char *to;
520: register int size;
521: {
522: while (size-->0)
523: *to++ = *fr++;
524: }
525:
526: #include <stdio.h>
527:
528: /*-----------------------------------------
529: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
530: >>>>>>>>>>>> NOTE >>>>>>>>>>>>> Must be identical to those in "tftp"
531: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
532: read from local file and transform lines ending with '\n' to lines
533: ending with '\r' and '\n'; also transform '\r' to '\r' and '\0'
534: ----------------------------------------*/
535: tftpread(fd, buf, bufsize, fsize)
536: FILE *fd;
537: register char *buf;
538: int bufsize;
539: int *fsize; /* count # bytes read from file */
540: {
541:
542: register char *p;
543: int i;
544:
545: /* oldc is to indicate if the transformation is unfinished in the
546: last block */
547: static char oldc;
548:
549: *fsize = 0;
550: for (p=buf; p < buf+bufsize;) {
551: switch(oldc) {
552: case '\n':
553: *p++ = '\n'; /* already output '\r' */
554: oldc = '\0';
555: break;
556: case '\r':
557: *p++ = '\0'; /* already output '\r' */
558: oldc = '\0';
559: break;
560: default:
561: i = fread(&oldc, sizeof(oldc), 1, fd);
562: if (i<0)
563: return(i);
564: if (i==0)
565: goto endit;
566:
567: ++(*fsize);
568: switch(oldc) {
569: case '\n':
570: case '\r':
571: *p++ = '\r';
572: /* will complete the transformation in next
573: ... loop iteration */
574: break;
575: default:
576: *p++ = oldc;
577: break;
578: }
579: }
580:
581: }
582: endit:
583: return(p-buf);
584: }
585: /*-----------------------------------------
586: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
587: >>>>>>>>>>>>>>>> NOTE >>>>>>>>> Must be identical to those "tftp"
588: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
589: write to local file and transform lines ending with '\r''\n' to lines
590: ending with and '\n'; also transform '\r' '\0' to '\r'.
591: ----------------------------------------*/
592: tftpwrite(fd, buf, bufsize, fsize)
593: FILE *fd;
594: register char *buf;
595: int bufsize;
596: int *fsize; /* count # of bytes write to file */
597: {
598: register char *p;
599: char c;
600: int i;
601:
602: /* oldc is to indicate if the transformation is unfinished in the
603: last block */
604: static char oldc;
605:
606: *fsize = 0;
607: for (p=buf; p<buf + bufsize; p++) {
608: c = *p;
609: if (oldc == '\r') {
610: char writethisc;
611:
612: switch(c) {
613: case '\0':
614: writethisc = oldc;
615: break;
616: case '\n':
617: writethisc = '\n';
618: break;
619: default:
620: writethisc = oldc;
621: break;
622: }
623: i = fwrite(&writethisc, sizeof(writethisc), 1, fd);
624: if (i!=1)
625: return(-1);
626: ++(*fsize);
627: if (c=='\0' ||
628: c=='\n') {
629: oldc = '\0';
630: continue;
631: }
632: }
633: if ((oldc = c) == '\r')
634: continue;
635: i = fwrite(&c, sizeof(c), 1, fd);
636: if (i!=1)
637: return(-1);
638: ++(*fsize);
639: }
640: return(*fsize);
641: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.