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