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