|
|
1.1 ! root 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: (1) source distributions retain this entire copyright ! 7: * notice and comment, and (2) distributions including binaries display ! 8: * the following acknowledgement: ``This product includes software ! 9: * developed by the University of California, Berkeley and its contributors'' ! 10: * in the documentation or other materials provided with the distribution ! 11: * and in all advertising materials mentioning features or use of this ! 12: * software. Neither the name of the University nor the names of its ! 13: * contributors may be used to endorse or promote products derived ! 14: * from this software without specific prior written permission. ! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 16: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 17: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 18: */ ! 19: ! 20: #ifndef lint ! 21: char copyright[] = ! 22: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ ! 23: All rights reserved.\n"; ! 24: #endif /* not lint */ ! 25: ! 26: #ifndef lint ! 27: static char sccsid[] = "@(#)tftpd.c 5.12 (Berkeley) 6/1/90"; ! 28: #endif /* not lint */ ! 29: ! 30: /* ! 31: * Trivial file transfer protocol server. ! 32: * ! 33: * This version includes many modifications by Jim Guyton <guyton@rand-unix> ! 34: */ ! 35: ! 36: #include <sys/types.h> ! 37: #include <sys/socket.h> ! 38: #include <sys/ioctl.h> ! 39: #include <sys/wait.h> ! 40: #include <sys/stat.h> ! 41: #include <sys/signal.h> ! 42: ! 43: #include <netinet/in.h> ! 44: ! 45: #include <arpa/tftp.h> ! 46: ! 47: #include <netdb.h> ! 48: #include <setjmp.h> ! 49: #include <stdio.h> ! 50: #include <errno.h> ! 51: #include <ctype.h> ! 52: #include <syslog.h> ! 53: #include <string.h> ! 54: ! 55: #define TIMEOUT 5 ! 56: ! 57: extern int errno; ! 58: struct sockaddr_in sin = { AF_INET }; ! 59: int peer; ! 60: int rexmtval = TIMEOUT; ! 61: int maxtimeout = 5*TIMEOUT; ! 62: ! 63: #define PKTSIZE SEGSIZE+4 ! 64: char buf[PKTSIZE]; ! 65: char ackbuf[PKTSIZE]; ! 66: struct sockaddr_in from; ! 67: int fromlen; ! 68: ! 69: #define MAXARG 4 ! 70: char *dirs[MAXARG+1]; ! 71: ! 72: main(ac, av) ! 73: char **av; ! 74: { ! 75: register struct tftphdr *tp; ! 76: register int n = 0; ! 77: int on = 1; ! 78: ! 79: ac--; av++; ! 80: while (ac-- > 0 && n < MAXARG) ! 81: dirs[n++] = *av++; ! 82: openlog("tftpd", LOG_PID, LOG_DAEMON); ! 83: if (ioctl(0, FIONBIO, &on) < 0) { ! 84: syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); ! 85: exit(1); ! 86: } ! 87: fromlen = sizeof (from); ! 88: n = recvfrom(0, buf, sizeof (buf), 0, ! 89: (caddr_t)&from, &fromlen); ! 90: if (n < 0) { ! 91: syslog(LOG_ERR, "recvfrom: %m\n"); ! 92: exit(1); ! 93: } ! 94: /* ! 95: * Now that we have read the message out of the UDP ! 96: * socket, we fork and exit. Thus, inetd will go back ! 97: * to listening to the tftp port, and the next request ! 98: * to come in will start up a new instance of tftpd. ! 99: * ! 100: * We do this so that inetd can run tftpd in "wait" mode. ! 101: * The problem with tftpd running in "nowait" mode is that ! 102: * inetd may get one or more successful "selects" on the ! 103: * tftp port before we do our receive, so more than one ! 104: * instance of tftpd may be started up. Worse, if tftpd ! 105: * break before doing the above "recvfrom", inetd would ! 106: * spawn endless instances, clogging the system. ! 107: */ ! 108: { ! 109: int pid; ! 110: int i, j; ! 111: ! 112: for (i = 1; i < 20; i++) { ! 113: pid = fork(); ! 114: if (pid < 0) { ! 115: sleep(i); ! 116: /* ! 117: * flush out to most recently sent request. ! 118: * ! 119: * This may drop some request, but those ! 120: * will be resent by the clients when ! 121: * they timeout. The positive effect of ! 122: * this flush is to (try to) prevent more ! 123: * than one tftpd being started up to service ! 124: * a single request from a single client. ! 125: */ ! 126: j = sizeof from; ! 127: i = recvfrom(0, buf, sizeof (buf), 0, ! 128: (caddr_t)&from, &j); ! 129: if (i > 0) { ! 130: n = i; ! 131: fromlen = j; ! 132: } ! 133: } else { ! 134: break; ! 135: } ! 136: } ! 137: if (pid < 0) { ! 138: syslog(LOG_ERR, "fork: %m\n"); ! 139: exit(1); ! 140: } else if (pid != 0) { ! 141: exit(0); ! 142: } ! 143: } ! 144: from.sin_family = AF_INET; ! 145: alarm(0); ! 146: close(0); ! 147: close(1); ! 148: peer = socket(AF_INET, SOCK_DGRAM, 0); ! 149: if (peer < 0) { ! 150: syslog(LOG_ERR, "socket: %m\n"); ! 151: exit(1); ! 152: } ! 153: if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) { ! 154: syslog(LOG_ERR, "bind: %m\n"); ! 155: exit(1); ! 156: } ! 157: if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) { ! 158: syslog(LOG_ERR, "connect: %m\n"); ! 159: exit(1); ! 160: } ! 161: tp = (struct tftphdr *)buf; ! 162: tp->th_opcode = ntohs(tp->th_opcode); ! 163: if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) ! 164: tftp(tp, n); ! 165: exit(1); ! 166: } ! 167: ! 168: int validate_access(); ! 169: int sendfile(), recvfile(); ! 170: ! 171: struct formats { ! 172: char *f_mode; ! 173: int (*f_validate)(); ! 174: int (*f_send)(); ! 175: int (*f_recv)(); ! 176: int f_convert; ! 177: } formats[] = { ! 178: { "netascii", validate_access, sendfile, recvfile, 1 }, ! 179: { "octet", validate_access, sendfile, recvfile, 0 }, ! 180: #ifdef notdef ! 181: { "mail", validate_user, sendmail, recvmail, 1 }, ! 182: #endif ! 183: { 0 } ! 184: }; ! 185: ! 186: /* ! 187: * Handle initial connection protocol. ! 188: */ ! 189: tftp(tp, size) ! 190: struct tftphdr *tp; ! 191: int size; ! 192: { ! 193: register char *cp; ! 194: int first = 1, ecode; ! 195: register struct formats *pf; ! 196: char *filename, *mode; ! 197: ! 198: filename = cp = tp->th_stuff; ! 199: again: ! 200: while (cp < buf + size) { ! 201: if (*cp == '\0') ! 202: break; ! 203: cp++; ! 204: } ! 205: if (*cp != '\0') { ! 206: nak(EBADOP); ! 207: exit(1); ! 208: } ! 209: if (first) { ! 210: mode = ++cp; ! 211: first = 0; ! 212: goto again; ! 213: } ! 214: for (cp = mode; *cp; cp++) ! 215: if (isupper(*cp)) ! 216: *cp = tolower(*cp); ! 217: for (pf = formats; pf->f_mode; pf++) ! 218: if (strcmp(pf->f_mode, mode) == 0) ! 219: break; ! 220: if (pf->f_mode == 0) { ! 221: nak(EBADOP); ! 222: exit(1); ! 223: } ! 224: ecode = (*pf->f_validate)(filename, tp->th_opcode); ! 225: if (ecode) { ! 226: nak(ecode); ! 227: exit(1); ! 228: } ! 229: if (tp->th_opcode == WRQ) ! 230: (*pf->f_recv)(pf); ! 231: else ! 232: (*pf->f_send)(pf); ! 233: exit(0); ! 234: } ! 235: ! 236: ! 237: FILE *file; ! 238: ! 239: /* ! 240: * Validate file access. Since we ! 241: * have no uid or gid, for now require ! 242: * file to exist and be publicly ! 243: * readable/writable. ! 244: * If we were invoked with arguments ! 245: * from inetd then the file must also be ! 246: * in one of the given directory prefixes. ! 247: * Note also, full path name must be ! 248: * given as we have no login directory. ! 249: */ ! 250: validate_access(filename, mode) ! 251: char *filename; ! 252: int mode; ! 253: { ! 254: struct stat stbuf; ! 255: int fd; ! 256: char *cp, **dirp; ! 257: ! 258: if (*filename != '/') ! 259: return (EACCESS); ! 260: /* ! 261: * prevent tricksters from getting around the directory restrictions ! 262: */ ! 263: for (cp = filename + 1; *cp; cp++) ! 264: if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0) ! 265: return(EACCESS); ! 266: for (dirp = dirs; *dirp; dirp++) ! 267: if (strncmp(filename, *dirp, strlen(*dirp)) == 0) ! 268: break; ! 269: if (*dirp==0 && dirp!=dirs) ! 270: return (EACCESS); ! 271: if (stat(filename, &stbuf) < 0) ! 272: return (errno == ENOENT ? ENOTFOUND : EACCESS); ! 273: if (mode == RRQ) { ! 274: if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) ! 275: return (EACCESS); ! 276: } else { ! 277: if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) ! 278: return (EACCESS); ! 279: } ! 280: fd = open(filename, mode == RRQ ? 0 : 1); ! 281: if (fd < 0) ! 282: return (errno + 100); ! 283: file = fdopen(fd, (mode == RRQ)? "r":"w"); ! 284: if (file == NULL) { ! 285: return errno+100; ! 286: } ! 287: return (0); ! 288: } ! 289: ! 290: int timeout; ! 291: jmp_buf timeoutbuf; ! 292: ! 293: timer() ! 294: { ! 295: ! 296: timeout += rexmtval; ! 297: if (timeout >= maxtimeout) ! 298: exit(1); ! 299: longjmp(timeoutbuf, 1); ! 300: } ! 301: ! 302: /* ! 303: * Send the requested file. ! 304: */ ! 305: sendfile(pf) ! 306: struct formats *pf; ! 307: { ! 308: struct tftphdr *dp, *r_init(); ! 309: register struct tftphdr *ap; /* ack packet */ ! 310: register int block = 1, size, n; ! 311: ! 312: signal(SIGALRM, timer); ! 313: dp = r_init(); ! 314: ap = (struct tftphdr *)ackbuf; ! 315: do { ! 316: size = readit(file, &dp, pf->f_convert); ! 317: if (size < 0) { ! 318: nak(errno + 100); ! 319: goto abort; ! 320: } ! 321: dp->th_opcode = htons((u_short)DATA); ! 322: dp->th_block = htons((u_short)block); ! 323: timeout = 0; ! 324: (void) setjmp(timeoutbuf); ! 325: ! 326: send_data: ! 327: if (send(peer, dp, size + 4, 0) != size + 4) { ! 328: syslog(LOG_ERR, "tftpd: write: %m\n"); ! 329: goto abort; ! 330: } ! 331: read_ahead(file, pf->f_convert); ! 332: for ( ; ; ) { ! 333: alarm(rexmtval); /* read the ack */ ! 334: n = recv(peer, ackbuf, sizeof (ackbuf), 0); ! 335: alarm(0); ! 336: if (n < 0) { ! 337: syslog(LOG_ERR, "tftpd: read: %m\n"); ! 338: goto abort; ! 339: } ! 340: ap->th_opcode = ntohs((u_short)ap->th_opcode); ! 341: ap->th_block = ntohs((u_short)ap->th_block); ! 342: ! 343: if (ap->th_opcode == ERROR) ! 344: goto abort; ! 345: ! 346: if (ap->th_opcode == ACK) { ! 347: if (ap->th_block == block) { ! 348: break; ! 349: } ! 350: /* Re-synchronize with the other side */ ! 351: (void) synchnet(peer); ! 352: if (ap->th_block == (block -1)) { ! 353: goto send_data; ! 354: } ! 355: } ! 356: ! 357: } ! 358: block++; ! 359: } while (size == SEGSIZE); ! 360: abort: ! 361: (void) fclose(file); ! 362: } ! 363: ! 364: justquit() ! 365: { ! 366: exit(0); ! 367: } ! 368: ! 369: ! 370: /* ! 371: * Receive a file. ! 372: */ ! 373: recvfile(pf) ! 374: struct formats *pf; ! 375: { ! 376: struct tftphdr *dp, *w_init(); ! 377: register struct tftphdr *ap; /* ack buffer */ ! 378: register int block = 0, n, size; ! 379: ! 380: signal(SIGALRM, timer); ! 381: dp = w_init(); ! 382: ap = (struct tftphdr *)ackbuf; ! 383: do { ! 384: timeout = 0; ! 385: ap->th_opcode = htons((u_short)ACK); ! 386: ap->th_block = htons((u_short)block); ! 387: block++; ! 388: (void) setjmp(timeoutbuf); ! 389: send_ack: ! 390: if (send(peer, ackbuf, 4, 0) != 4) { ! 391: syslog(LOG_ERR, "tftpd: write: %m\n"); ! 392: goto abort; ! 393: } ! 394: write_behind(file, pf->f_convert); ! 395: for ( ; ; ) { ! 396: alarm(rexmtval); ! 397: n = recv(peer, dp, PKTSIZE, 0); ! 398: alarm(0); ! 399: if (n < 0) { /* really? */ ! 400: syslog(LOG_ERR, "tftpd: read: %m\n"); ! 401: goto abort; ! 402: } ! 403: dp->th_opcode = ntohs((u_short)dp->th_opcode); ! 404: dp->th_block = ntohs((u_short)dp->th_block); ! 405: if (dp->th_opcode == ERROR) ! 406: goto abort; ! 407: if (dp->th_opcode == DATA) { ! 408: if (dp->th_block == block) { ! 409: break; /* normal */ ! 410: } ! 411: /* Re-synchronize with the other side */ ! 412: (void) synchnet(peer); ! 413: if (dp->th_block == (block-1)) ! 414: goto send_ack; /* rexmit */ ! 415: } ! 416: } ! 417: /* size = write(file, dp->th_data, n - 4); */ ! 418: size = writeit(file, &dp, n - 4, pf->f_convert); ! 419: if (size != (n-4)) { /* ahem */ ! 420: if (size < 0) nak(errno + 100); ! 421: else nak(ENOSPACE); ! 422: goto abort; ! 423: } ! 424: } while (size == SEGSIZE); ! 425: write_behind(file, pf->f_convert); ! 426: (void) fclose(file); /* close data file */ ! 427: ! 428: ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ ! 429: ap->th_block = htons((u_short)(block)); ! 430: (void) send(peer, ackbuf, 4, 0); ! 431: ! 432: signal(SIGALRM, justquit); /* just quit on timeout */ ! 433: alarm(rexmtval); ! 434: n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ ! 435: alarm(0); ! 436: if (n >= 4 && /* if read some data */ ! 437: dp->th_opcode == DATA && /* and got a data block */ ! 438: block == dp->th_block) { /* then my last ack was lost */ ! 439: (void) send(peer, ackbuf, 4, 0); /* resend final ack */ ! 440: } ! 441: abort: ! 442: return; ! 443: } ! 444: ! 445: struct errmsg { ! 446: int e_code; ! 447: char *e_msg; ! 448: } errmsgs[] = { ! 449: { EUNDEF, "Undefined error code" }, ! 450: { ENOTFOUND, "File not found" }, ! 451: { EACCESS, "Access violation" }, ! 452: { ENOSPACE, "Disk full or allocation exceeded" }, ! 453: { EBADOP, "Illegal TFTP operation" }, ! 454: { EBADID, "Unknown transfer ID" }, ! 455: { EEXISTS, "File already exists" }, ! 456: { ENOUSER, "No such user" }, ! 457: { -1, 0 } ! 458: }; ! 459: ! 460: /* ! 461: * Send a nak packet (error message). ! 462: * Error code passed in is one of the ! 463: * standard TFTP codes, or a UNIX errno ! 464: * offset by 100. ! 465: */ ! 466: nak(error) ! 467: int error; ! 468: { ! 469: register struct tftphdr *tp; ! 470: int length; ! 471: register struct errmsg *pe; ! 472: ! 473: tp = (struct tftphdr *)buf; ! 474: tp->th_opcode = htons((u_short)ERROR); ! 475: tp->th_code = htons((u_short)error); ! 476: for (pe = errmsgs; pe->e_code >= 0; pe++) ! 477: if (pe->e_code == error) ! 478: break; ! 479: if (pe->e_code < 0) { ! 480: pe->e_msg = strerror(error - 100); ! 481: tp->th_code = EUNDEF; /* set 'undef' errorcode */ ! 482: } ! 483: strcpy(tp->th_msg, pe->e_msg); ! 484: length = strlen(pe->e_msg); ! 485: tp->th_msg[length] = '\0'; ! 486: length += 5; ! 487: if (send(peer, buf, length, 0) != length) ! 488: syslog(LOG_ERR, "nak: %m\n"); ! 489: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.