|
|
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.