Return to tftpd.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / server |
1.1 ! misho 1: /*************************************************************************** ! 2: * _ _ ____ _ ! 3: * Project ___| | | | _ \| | ! 4: * / __| | | | |_) | | ! 5: * | (__| |_| | _ <| |___ ! 6: * \___|\___/|_| \_\_____| ! 7: * ! 8: * ! 9: * Trivial file transfer protocol server. ! 10: * ! 11: * This code includes many modifications by Jim Guyton <guyton@rand-unix> ! 12: * ! 13: * This source file was started based on netkit-tftpd 0.17 ! 14: * Heavily modified for curl's test suite ! 15: */ ! 16: ! 17: /* ! 18: * Copyright (C) 2005 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. ! 19: * Copyright (c) 1983, Regents of the University of California. ! 20: * All rights reserved. ! 21: * ! 22: * Redistribution and use in source and binary forms, with or without ! 23: * modification, are permitted provided that the following conditions ! 24: * are met: ! 25: * 1. Redistributions of source code must retain the above copyright ! 26: * notice, this list of conditions and the following disclaimer. ! 27: * 2. Redistributions in binary form must reproduce the above copyright ! 28: * notice, this list of conditions and the following disclaimer in the ! 29: * documentation and/or other materials provided with the distribution. ! 30: * 3. All advertising materials mentioning features or use of this software ! 31: * must display the following acknowledgement: ! 32: * This product includes software developed by the University of ! 33: * California, Berkeley and its contributors. ! 34: * 4. Neither the name of the University nor the names of its contributors ! 35: * may be used to endorse or promote products derived from this software ! 36: * without specific prior written permission. ! 37: * ! 38: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 39: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 40: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 41: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 42: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 43: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 44: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 45: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 46: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 47: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 48: * SUCH DAMAGE. ! 49: */ ! 50: ! 51: #include "server_setup.h" ! 52: ! 53: #ifdef HAVE_SYS_IOCTL_H ! 54: #include <sys/ioctl.h> ! 55: #endif ! 56: #ifdef HAVE_SIGNAL_H ! 57: #include <signal.h> ! 58: #endif ! 59: #ifdef HAVE_FCNTL_H ! 60: #include <fcntl.h> ! 61: #endif ! 62: #ifdef HAVE_NETINET_IN_H ! 63: #include <netinet/in.h> ! 64: #endif ! 65: #ifdef HAVE_ARPA_INET_H ! 66: #include <arpa/inet.h> ! 67: #endif ! 68: #ifdef HAVE_ARPA_TFTP_H ! 69: #include <arpa/tftp.h> ! 70: #else ! 71: #include "tftp.h" ! 72: #endif ! 73: #ifdef HAVE_NETDB_H ! 74: #include <netdb.h> ! 75: #endif ! 76: #ifdef HAVE_SYS_FILIO_H ! 77: /* FIONREAD on Solaris 7 */ ! 78: #include <sys/filio.h> ! 79: #endif ! 80: ! 81: #include <setjmp.h> ! 82: ! 83: #ifdef HAVE_PWD_H ! 84: #include <pwd.h> ! 85: #endif ! 86: ! 87: #define ENABLE_CURLX_PRINTF ! 88: /* make the curlx header define all printf() functions to use the curlx_* ! 89: versions instead */ ! 90: #include "curlx.h" /* from the private lib dir */ ! 91: #include "getpart.h" ! 92: #include "util.h" ! 93: #include "server_sockaddr.h" ! 94: ! 95: /* include memdebug.h last */ ! 96: #include "memdebug.h" ! 97: ! 98: /***************************************************************************** ! 99: * STRUCT DECLARATIONS AND DEFINES * ! 100: *****************************************************************************/ ! 101: ! 102: #ifndef PKTSIZE ! 103: #define PKTSIZE (SEGSIZE + 4) /* SEGSIZE defined in arpa/tftp.h */ ! 104: #endif ! 105: ! 106: struct testcase { ! 107: char *buffer; /* holds the file data to send to the client */ ! 108: size_t bufsize; /* size of the data in buffer */ ! 109: char *rptr; /* read pointer into the buffer */ ! 110: size_t rcount; /* amount of data left to read of the file */ ! 111: long testno; /* test case number */ ! 112: int ofile; /* file descriptor for output file when uploading to us */ ! 113: ! 114: int writedelay; /* number of seconds between each packet */ ! 115: }; ! 116: ! 117: struct formats { ! 118: const char *f_mode; ! 119: int f_convert; ! 120: }; ! 121: ! 122: struct errmsg { ! 123: int e_code; ! 124: const char *e_msg; ! 125: }; ! 126: ! 127: typedef union { ! 128: struct tftphdr hdr; ! 129: char storage[PKTSIZE]; ! 130: } tftphdr_storage_t; ! 131: ! 132: /* ! 133: * bf.counter values in range [-1 .. SEGSIZE] represents size of data in the ! 134: * bf.buf buffer. Additionally it can also hold flags BF_ALLOC or BF_FREE. ! 135: */ ! 136: ! 137: struct bf { ! 138: int counter; /* size of data in buffer, or flag */ ! 139: tftphdr_storage_t buf; /* room for data packet */ ! 140: }; ! 141: ! 142: #define BF_ALLOC -3 /* alloc'd but not yet filled */ ! 143: #define BF_FREE -2 /* free */ ! 144: ! 145: #define opcode_RRQ 1 ! 146: #define opcode_WRQ 2 ! 147: #define opcode_DATA 3 ! 148: #define opcode_ACK 4 ! 149: #define opcode_ERROR 5 ! 150: ! 151: #define TIMEOUT 5 ! 152: ! 153: #undef MIN ! 154: #define MIN(x,y) ((x)<(y)?(x):(y)) ! 155: ! 156: #ifndef DEFAULT_LOGFILE ! 157: #define DEFAULT_LOGFILE "log/tftpd.log" ! 158: #endif ! 159: ! 160: #define REQUEST_DUMP "log/server.input" ! 161: ! 162: #define DEFAULT_PORT 8999 /* UDP */ ! 163: ! 164: /***************************************************************************** ! 165: * GLOBAL VARIABLES * ! 166: *****************************************************************************/ ! 167: ! 168: static struct errmsg errmsgs[] = { ! 169: { EUNDEF, "Undefined error code" }, ! 170: { ENOTFOUND, "File not found" }, ! 171: { EACCESS, "Access violation" }, ! 172: { ENOSPACE, "Disk full or allocation exceeded" }, ! 173: { EBADOP, "Illegal TFTP operation" }, ! 174: { EBADID, "Unknown transfer ID" }, ! 175: { EEXISTS, "File already exists" }, ! 176: { ENOUSER, "No such user" }, ! 177: { -1, 0 } ! 178: }; ! 179: ! 180: static struct formats formata[] = { ! 181: { "netascii", 1 }, ! 182: { "octet", 0 }, ! 183: { NULL, 0 } ! 184: }; ! 185: ! 186: static struct bf bfs[2]; ! 187: ! 188: static int nextone; /* index of next buffer to use */ ! 189: static int current; /* index of buffer in use */ ! 190: ! 191: /* control flags for crlf conversions */ ! 192: static int newline = 0; /* fillbuf: in middle of newline expansion */ ! 193: static int prevchar = -1; /* putbuf: previous char (cr check) */ ! 194: ! 195: static tftphdr_storage_t buf; ! 196: static tftphdr_storage_t ackbuf; ! 197: ! 198: static srvr_sockaddr_union_t from; ! 199: static curl_socklen_t fromlen; ! 200: ! 201: static curl_socket_t peer = CURL_SOCKET_BAD; ! 202: ! 203: static unsigned int timeout; ! 204: static unsigned int maxtimeout = 5 * TIMEOUT; ! 205: ! 206: #ifdef ENABLE_IPV6 ! 207: static bool use_ipv6 = FALSE; ! 208: #endif ! 209: static const char *ipv_inuse = "IPv4"; ! 210: ! 211: const char *serverlogfile = DEFAULT_LOGFILE; ! 212: static const char *pidname = ".tftpd.pid"; ! 213: static const char *portfile = NULL; ! 214: static int serverlogslocked = 0; ! 215: static int wrotepidfile = 0; ! 216: ! 217: #ifdef HAVE_SIGSETJMP ! 218: static sigjmp_buf timeoutbuf; ! 219: #endif ! 220: ! 221: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 222: static const unsigned int rexmtval = TIMEOUT; ! 223: #endif ! 224: ! 225: /***************************************************************************** ! 226: * FUNCTION PROTOTYPES * ! 227: *****************************************************************************/ ! 228: ! 229: static struct tftphdr *rw_init(int); ! 230: ! 231: static struct tftphdr *w_init(void); ! 232: ! 233: static struct tftphdr *r_init(void); ! 234: ! 235: static void read_ahead(struct testcase *test, int convert); ! 236: ! 237: static ssize_t write_behind(struct testcase *test, int convert); ! 238: ! 239: static int synchnet(curl_socket_t); ! 240: ! 241: static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size); ! 242: ! 243: static int validate_access(struct testcase *test, const char *fname, int mode); ! 244: ! 245: static void sendtftp(struct testcase *test, struct formats *pf); ! 246: ! 247: static void recvtftp(struct testcase *test, struct formats *pf); ! 248: ! 249: static void nak(int error); ! 250: ! 251: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 252: ! 253: static void mysignal(int sig, void (*handler)(int)); ! 254: ! 255: static void timer(int signum); ! 256: ! 257: static void justtimeout(int signum); ! 258: ! 259: #endif /* HAVE_ALARM && SIGALRM */ ! 260: ! 261: /***************************************************************************** ! 262: * FUNCTION IMPLEMENTATIONS * ! 263: *****************************************************************************/ ! 264: ! 265: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 266: ! 267: /* ! 268: * Like signal(), but with well-defined semantics. ! 269: */ ! 270: static void mysignal(int sig, void (*handler)(int)) ! 271: { ! 272: struct sigaction sa; ! 273: memset(&sa, 0, sizeof(sa)); ! 274: sa.sa_handler = handler; ! 275: sigaction(sig, &sa, NULL); ! 276: } ! 277: ! 278: static void timer(int signum) ! 279: { ! 280: (void)signum; ! 281: ! 282: logmsg("alarm!"); ! 283: ! 284: timeout += rexmtval; ! 285: if(timeout >= maxtimeout) { ! 286: if(wrotepidfile) { ! 287: wrotepidfile = 0; ! 288: unlink(pidname); ! 289: } ! 290: if(serverlogslocked) { ! 291: serverlogslocked = 0; ! 292: clear_advisor_read_lock(SERVERLOGS_LOCK); ! 293: } ! 294: exit(1); ! 295: } ! 296: #ifdef HAVE_SIGSETJMP ! 297: siglongjmp(timeoutbuf, 1); ! 298: #endif ! 299: } ! 300: ! 301: static void justtimeout(int signum) ! 302: { ! 303: (void)signum; ! 304: } ! 305: ! 306: #endif /* HAVE_ALARM && SIGALRM */ ! 307: ! 308: /* ! 309: * init for either read-ahead or write-behind. ! 310: * zero for write-behind, one for read-head. ! 311: */ ! 312: static struct tftphdr *rw_init(int x) ! 313: { ! 314: newline = 0; /* init crlf flag */ ! 315: prevchar = -1; ! 316: bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ ! 317: current = 0; ! 318: bfs[1].counter = BF_FREE; ! 319: nextone = x; /* ahead or behind? */ ! 320: return &bfs[0].buf.hdr; ! 321: } ! 322: ! 323: static struct tftphdr *w_init(void) ! 324: { ! 325: return rw_init(0); /* write-behind */ ! 326: } ! 327: ! 328: static struct tftphdr *r_init(void) ! 329: { ! 330: return rw_init(1); /* read-ahead */ ! 331: } ! 332: ! 333: /* Have emptied current buffer by sending to net and getting ack. ! 334: Free it and return next buffer filled with data. ! 335: */ ! 336: static int readit(struct testcase *test, struct tftphdr **dpp, ! 337: int convert /* if true, convert to ascii */) ! 338: { ! 339: struct bf *b; ! 340: ! 341: bfs[current].counter = BF_FREE; /* free old one */ ! 342: current = !current; /* "incr" current */ ! 343: ! 344: b = &bfs[current]; /* look at new buffer */ ! 345: if(b->counter == BF_FREE) /* if it's empty */ ! 346: read_ahead(test, convert); /* fill it */ ! 347: ! 348: *dpp = &b->buf.hdr; /* set caller's ptr */ ! 349: return b->counter; ! 350: } ! 351: ! 352: /* ! 353: * fill the input buffer, doing ascii conversions if requested ! 354: * conversions are lf -> cr, lf and cr -> cr, nul ! 355: */ ! 356: static void read_ahead(struct testcase *test, ! 357: int convert /* if true, convert to ascii */) ! 358: { ! 359: int i; ! 360: char *p; ! 361: int c; ! 362: struct bf *b; ! 363: struct tftphdr *dp; ! 364: ! 365: b = &bfs[nextone]; /* look at "next" buffer */ ! 366: if(b->counter != BF_FREE) /* nop if not free */ ! 367: return; ! 368: nextone = !nextone; /* "incr" next buffer ptr */ ! 369: ! 370: dp = &b->buf.hdr; ! 371: ! 372: if(convert == 0) { ! 373: /* The former file reading code did this: ! 374: b->counter = read(fileno(file), dp->th_data, SEGSIZE); */ ! 375: size_t copy_n = MIN(SEGSIZE, test->rcount); ! 376: memcpy(dp->th_data, test->rptr, copy_n); ! 377: ! 378: /* decrease amount, advance pointer */ ! 379: test->rcount -= copy_n; ! 380: test->rptr += copy_n; ! 381: b->counter = (int)copy_n; ! 382: return; ! 383: } ! 384: ! 385: p = dp->th_data; ! 386: for(i = 0 ; i < SEGSIZE; i++) { ! 387: if(newline) { ! 388: if(prevchar == '\n') ! 389: c = '\n'; /* lf to cr,lf */ ! 390: else ! 391: c = '\0'; /* cr to cr,nul */ ! 392: newline = 0; ! 393: } ! 394: else { ! 395: if(test->rcount) { ! 396: c = test->rptr[0]; ! 397: test->rptr++; ! 398: test->rcount--; ! 399: } ! 400: else ! 401: break; ! 402: if(c == '\n' || c == '\r') { ! 403: prevchar = c; ! 404: c = '\r'; ! 405: newline = 1; ! 406: } ! 407: } ! 408: *p++ = (char)c; ! 409: } ! 410: b->counter = (int)(p - dp->th_data); ! 411: } ! 412: ! 413: /* Update count associated with the buffer, get new buffer from the queue. ! 414: Calls write_behind only if next buffer not available. ! 415: */ ! 416: static int writeit(struct testcase *test, struct tftphdr * volatile *dpp, ! 417: int ct, int convert) ! 418: { ! 419: bfs[current].counter = ct; /* set size of data to write */ ! 420: current = !current; /* switch to other buffer */ ! 421: if(bfs[current].counter != BF_FREE) /* if not free */ ! 422: write_behind(test, convert); /* flush it */ ! 423: bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ ! 424: *dpp = &bfs[current].buf.hdr; ! 425: return ct; /* this is a lie of course */ ! 426: } ! 427: ! 428: /* ! 429: * Output a buffer to a file, converting from netascii if requested. ! 430: * CR, NUL -> CR and CR, LF => LF. ! 431: * Note spec is undefined if we get CR as last byte of file or a ! 432: * CR followed by anything else. In this case we leave it alone. ! 433: */ ! 434: static ssize_t write_behind(struct testcase *test, int convert) ! 435: { ! 436: char *writebuf; ! 437: int count; ! 438: int ct; ! 439: char *p; ! 440: int c; /* current character */ ! 441: struct bf *b; ! 442: struct tftphdr *dp; ! 443: ! 444: b = &bfs[nextone]; ! 445: if(b->counter < -1) /* anything to flush? */ ! 446: return 0; /* just nop if nothing to do */ ! 447: ! 448: if(!test->ofile) { ! 449: char outfile[256]; ! 450: msnprintf(outfile, sizeof(outfile), "log/upload.%ld", test->testno); ! 451: #ifdef WIN32 ! 452: test->ofile = open(outfile, O_CREAT|O_RDWR|O_BINARY, 0777); ! 453: #else ! 454: test->ofile = open(outfile, O_CREAT|O_RDWR, 0777); ! 455: #endif ! 456: if(test->ofile == -1) { ! 457: logmsg("Couldn't create and/or open file %s for upload!", outfile); ! 458: return -1; /* failure! */ ! 459: } ! 460: } ! 461: ! 462: count = b->counter; /* remember byte count */ ! 463: b->counter = BF_FREE; /* reset flag */ ! 464: dp = &b->buf.hdr; ! 465: nextone = !nextone; /* incr for next time */ ! 466: writebuf = dp->th_data; ! 467: ! 468: if(count <= 0) ! 469: return -1; /* nak logic? */ ! 470: ! 471: if(convert == 0) ! 472: return write(test->ofile, writebuf, count); ! 473: ! 474: p = writebuf; ! 475: ct = count; ! 476: while(ct--) { /* loop over the buffer */ ! 477: c = *p++; /* pick up a character */ ! 478: if(prevchar == '\r') { /* if prev char was cr */ ! 479: if(c == '\n') /* if have cr,lf then just */ ! 480: lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */ ! 481: else ! 482: if(c == '\0') /* if have cr,nul then */ ! 483: goto skipit; /* just skip over the putc */ ! 484: /* else just fall through and allow it */ ! 485: } ! 486: /* formerly ! 487: putc(c, file); */ ! 488: if(1 != write(test->ofile, &c, 1)) ! 489: break; ! 490: skipit: ! 491: prevchar = c; ! 492: } ! 493: return count; ! 494: } ! 495: ! 496: /* When an error has occurred, it is possible that the two sides are out of ! 497: * synch. Ie: that what I think is the other side's response to packet N is ! 498: * really their response to packet N-1. ! 499: * ! 500: * So, to try to prevent that, we flush all the input queued up for us on the ! 501: * network connection on our host. ! 502: * ! 503: * We return the number of packets we flushed (mostly for reporting when trace ! 504: * is active). ! 505: */ ! 506: ! 507: static int synchnet(curl_socket_t f /* socket to flush */) ! 508: { ! 509: ! 510: #if defined(HAVE_IOCTLSOCKET) ! 511: unsigned long i; ! 512: #else ! 513: int i; ! 514: #endif ! 515: int j = 0; ! 516: char rbuf[PKTSIZE]; ! 517: srvr_sockaddr_union_t fromaddr; ! 518: curl_socklen_t fromaddrlen; ! 519: ! 520: for(;;) { ! 521: #if defined(HAVE_IOCTLSOCKET) ! 522: (void) ioctlsocket(f, FIONREAD, &i); ! 523: #else ! 524: (void) ioctl(f, FIONREAD, &i); ! 525: #endif ! 526: if(i) { ! 527: j++; ! 528: #ifdef ENABLE_IPV6 ! 529: if(!use_ipv6) ! 530: #endif ! 531: fromaddrlen = sizeof(fromaddr.sa4); ! 532: #ifdef ENABLE_IPV6 ! 533: else ! 534: fromaddrlen = sizeof(fromaddr.sa6); ! 535: #endif ! 536: (void) recvfrom(f, rbuf, sizeof(rbuf), 0, ! 537: &fromaddr.sa, &fromaddrlen); ! 538: } ! 539: else ! 540: break; ! 541: } ! 542: return j; ! 543: } ! 544: ! 545: int main(int argc, char **argv) ! 546: { ! 547: srvr_sockaddr_union_t me; ! 548: struct tftphdr *tp; ! 549: ssize_t n = 0; ! 550: int arg = 1; ! 551: unsigned short port = DEFAULT_PORT; ! 552: curl_socket_t sock = CURL_SOCKET_BAD; ! 553: int flag; ! 554: int rc; ! 555: int error; ! 556: long pid; ! 557: struct testcase test; ! 558: int result = 0; ! 559: ! 560: memset(&test, 0, sizeof(test)); ! 561: ! 562: while(argc>arg) { ! 563: if(!strcmp("--version", argv[arg])) { ! 564: printf("tftpd IPv4%s\n", ! 565: #ifdef ENABLE_IPV6 ! 566: "/IPv6" ! 567: #else ! 568: "" ! 569: #endif ! 570: ); ! 571: return 0; ! 572: } ! 573: else if(!strcmp("--pidfile", argv[arg])) { ! 574: arg++; ! 575: if(argc>arg) ! 576: pidname = argv[arg++]; ! 577: } ! 578: else if(!strcmp("--portfile", argv[arg])) { ! 579: arg++; ! 580: if(argc>arg) ! 581: portfile = argv[arg++]; ! 582: } ! 583: else if(!strcmp("--logfile", argv[arg])) { ! 584: arg++; ! 585: if(argc>arg) ! 586: serverlogfile = argv[arg++]; ! 587: } ! 588: else if(!strcmp("--ipv4", argv[arg])) { ! 589: #ifdef ENABLE_IPV6 ! 590: ipv_inuse = "IPv4"; ! 591: use_ipv6 = FALSE; ! 592: #endif ! 593: arg++; ! 594: } ! 595: else if(!strcmp("--ipv6", argv[arg])) { ! 596: #ifdef ENABLE_IPV6 ! 597: ipv_inuse = "IPv6"; ! 598: use_ipv6 = TRUE; ! 599: #endif ! 600: arg++; ! 601: } ! 602: else if(!strcmp("--port", argv[arg])) { ! 603: arg++; ! 604: if(argc>arg) { ! 605: char *endptr; ! 606: unsigned long ulnum = strtoul(argv[arg], &endptr, 10); ! 607: port = curlx_ultous(ulnum); ! 608: arg++; ! 609: } ! 610: } ! 611: else if(!strcmp("--srcdir", argv[arg])) { ! 612: arg++; ! 613: if(argc>arg) { ! 614: path = argv[arg]; ! 615: arg++; ! 616: } ! 617: } ! 618: else { ! 619: puts("Usage: tftpd [option]\n" ! 620: " --version\n" ! 621: " --logfile [file]\n" ! 622: " --pidfile [file]\n" ! 623: " --ipv4\n" ! 624: " --ipv6\n" ! 625: " --port [port]\n" ! 626: " --srcdir [path]"); ! 627: return 0; ! 628: } ! 629: } ! 630: ! 631: #ifdef WIN32 ! 632: win32_init(); ! 633: atexit(win32_cleanup); ! 634: #endif ! 635: ! 636: install_signal_handlers(true); ! 637: ! 638: pid = (long)getpid(); ! 639: ! 640: #ifdef ENABLE_IPV6 ! 641: if(!use_ipv6) ! 642: #endif ! 643: sock = socket(AF_INET, SOCK_DGRAM, 0); ! 644: #ifdef ENABLE_IPV6 ! 645: else ! 646: sock = socket(AF_INET6, SOCK_DGRAM, 0); ! 647: #endif ! 648: ! 649: if(CURL_SOCKET_BAD == sock) { ! 650: error = SOCKERRNO; ! 651: logmsg("Error creating socket: (%d) %s", ! 652: error, strerror(error)); ! 653: result = 1; ! 654: goto tftpd_cleanup; ! 655: } ! 656: ! 657: flag = 1; ! 658: if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, ! 659: (void *)&flag, sizeof(flag))) { ! 660: error = SOCKERRNO; ! 661: logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", ! 662: error, strerror(error)); ! 663: result = 1; ! 664: goto tftpd_cleanup; ! 665: } ! 666: ! 667: #ifdef ENABLE_IPV6 ! 668: if(!use_ipv6) { ! 669: #endif ! 670: memset(&me.sa4, 0, sizeof(me.sa4)); ! 671: me.sa4.sin_family = AF_INET; ! 672: me.sa4.sin_addr.s_addr = INADDR_ANY; ! 673: me.sa4.sin_port = htons(port); ! 674: rc = bind(sock, &me.sa, sizeof(me.sa4)); ! 675: #ifdef ENABLE_IPV6 ! 676: } ! 677: else { ! 678: memset(&me.sa6, 0, sizeof(me.sa6)); ! 679: me.sa6.sin6_family = AF_INET6; ! 680: me.sa6.sin6_addr = in6addr_any; ! 681: me.sa6.sin6_port = htons(port); ! 682: rc = bind(sock, &me.sa, sizeof(me.sa6)); ! 683: } ! 684: #endif /* ENABLE_IPV6 */ ! 685: if(0 != rc) { ! 686: error = SOCKERRNO; ! 687: logmsg("Error binding socket on port %hu: (%d) %s", ! 688: port, error, strerror(error)); ! 689: result = 1; ! 690: goto tftpd_cleanup; ! 691: } ! 692: ! 693: if(!port) { ! 694: /* The system was supposed to choose a port number, figure out which ! 695: port we actually got and update the listener port value with it. */ ! 696: curl_socklen_t la_size; ! 697: srvr_sockaddr_union_t localaddr; ! 698: #ifdef ENABLE_IPV6 ! 699: if(!use_ipv6) ! 700: #endif ! 701: la_size = sizeof(localaddr.sa4); ! 702: #ifdef ENABLE_IPV6 ! 703: else ! 704: la_size = sizeof(localaddr.sa6); ! 705: #endif ! 706: memset(&localaddr.sa, 0, (size_t)la_size); ! 707: if(getsockname(sock, &localaddr.sa, &la_size) < 0) { ! 708: error = SOCKERRNO; ! 709: logmsg("getsockname() failed with error: (%d) %s", ! 710: error, strerror(error)); ! 711: sclose(sock); ! 712: goto tftpd_cleanup; ! 713: } ! 714: switch(localaddr.sa.sa_family) { ! 715: case AF_INET: ! 716: port = ntohs(localaddr.sa4.sin_port); ! 717: break; ! 718: #ifdef ENABLE_IPV6 ! 719: case AF_INET6: ! 720: port = ntohs(localaddr.sa6.sin6_port); ! 721: break; ! 722: #endif ! 723: default: ! 724: break; ! 725: } ! 726: if(!port) { ! 727: /* Real failure, listener port shall not be zero beyond this point. */ ! 728: logmsg("Apparently getsockname() succeeded, with listener port zero."); ! 729: logmsg("A valid reason for this failure is a binary built without"); ! 730: logmsg("proper network library linkage. This might not be the only"); ! 731: logmsg("reason, but double check it before anything else."); ! 732: result = 2; ! 733: goto tftpd_cleanup; ! 734: } ! 735: } ! 736: ! 737: wrotepidfile = write_pidfile(pidname); ! 738: if(!wrotepidfile) { ! 739: result = 1; ! 740: goto tftpd_cleanup; ! 741: } ! 742: ! 743: if(portfile) { ! 744: wrotepidfile = write_portfile(portfile, port); ! 745: if(!wrotepidfile) { ! 746: result = 1; ! 747: goto tftpd_cleanup; ! 748: } ! 749: } ! 750: ! 751: logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port); ! 752: ! 753: for(;;) { ! 754: fromlen = sizeof(from); ! 755: #ifdef ENABLE_IPV6 ! 756: if(!use_ipv6) ! 757: #endif ! 758: fromlen = sizeof(from.sa4); ! 759: #ifdef ENABLE_IPV6 ! 760: else ! 761: fromlen = sizeof(from.sa6); ! 762: #endif ! 763: n = (ssize_t)recvfrom(sock, &buf.storage[0], sizeof(buf.storage), 0, ! 764: &from.sa, &fromlen); ! 765: if(got_exit_signal) ! 766: break; ! 767: if(n < 0) { ! 768: logmsg("recvfrom"); ! 769: result = 3; ! 770: break; ! 771: } ! 772: ! 773: set_advisor_read_lock(SERVERLOGS_LOCK); ! 774: serverlogslocked = 1; ! 775: ! 776: #ifdef ENABLE_IPV6 ! 777: if(!use_ipv6) { ! 778: #endif ! 779: from.sa4.sin_family = AF_INET; ! 780: peer = socket(AF_INET, SOCK_DGRAM, 0); ! 781: if(CURL_SOCKET_BAD == peer) { ! 782: logmsg("socket"); ! 783: result = 2; ! 784: break; ! 785: } ! 786: if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) { ! 787: logmsg("connect: fail"); ! 788: result = 1; ! 789: break; ! 790: } ! 791: #ifdef ENABLE_IPV6 ! 792: } ! 793: else { ! 794: from.sa6.sin6_family = AF_INET6; ! 795: peer = socket(AF_INET6, SOCK_DGRAM, 0); ! 796: if(CURL_SOCKET_BAD == peer) { ! 797: logmsg("socket"); ! 798: result = 2; ! 799: break; ! 800: } ! 801: if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) { ! 802: logmsg("connect: fail"); ! 803: result = 1; ! 804: break; ! 805: } ! 806: } ! 807: #endif ! 808: ! 809: maxtimeout = 5*TIMEOUT; ! 810: ! 811: tp = &buf.hdr; ! 812: tp->th_opcode = ntohs(tp->th_opcode); ! 813: if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) { ! 814: memset(&test, 0, sizeof(test)); ! 815: if(do_tftp(&test, tp, n) < 0) ! 816: break; ! 817: free(test.buffer); ! 818: } ! 819: sclose(peer); ! 820: peer = CURL_SOCKET_BAD; ! 821: ! 822: if(test.ofile > 0) { ! 823: close(test.ofile); ! 824: test.ofile = 0; ! 825: } ! 826: ! 827: if(got_exit_signal) ! 828: break; ! 829: ! 830: if(serverlogslocked) { ! 831: serverlogslocked = 0; ! 832: clear_advisor_read_lock(SERVERLOGS_LOCK); ! 833: } ! 834: ! 835: logmsg("end of one transfer"); ! 836: ! 837: } ! 838: ! 839: tftpd_cleanup: ! 840: ! 841: if(test.ofile > 0) ! 842: close(test.ofile); ! 843: ! 844: if((peer != sock) && (peer != CURL_SOCKET_BAD)) ! 845: sclose(peer); ! 846: ! 847: if(sock != CURL_SOCKET_BAD) ! 848: sclose(sock); ! 849: ! 850: if(got_exit_signal) ! 851: logmsg("signalled to die"); ! 852: ! 853: if(wrotepidfile) ! 854: unlink(pidname); ! 855: if(portfile) ! 856: unlink(portfile); ! 857: ! 858: if(serverlogslocked) { ! 859: serverlogslocked = 0; ! 860: clear_advisor_read_lock(SERVERLOGS_LOCK); ! 861: } ! 862: ! 863: restore_signal_handlers(true); ! 864: ! 865: if(got_exit_signal) { ! 866: logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", ! 867: ipv_inuse, (int)port, pid, exit_signal); ! 868: /* ! 869: * To properly set the return status of the process we ! 870: * must raise the same signal SIGINT or SIGTERM that we ! 871: * caught and let the old handler take care of it. ! 872: */ ! 873: raise(exit_signal); ! 874: } ! 875: ! 876: logmsg("========> tftpd quits"); ! 877: return result; ! 878: } ! 879: ! 880: /* ! 881: * Handle initial connection protocol. ! 882: */ ! 883: static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) ! 884: { ! 885: char *cp; ! 886: int first = 1, ecode; ! 887: struct formats *pf; ! 888: char *filename, *mode = NULL; ! 889: #ifdef USE_WINSOCK ! 890: DWORD recvtimeout, recvtimeoutbak; ! 891: #endif ! 892: const char *option = "mode"; /* mode is implicit */ ! 893: int toggle = 1; ! 894: ! 895: /* Open request dump file. */ ! 896: FILE *server = fopen(REQUEST_DUMP, "ab"); ! 897: if(!server) { ! 898: int error = errno; ! 899: logmsg("fopen() failed with error: %d %s", error, strerror(error)); ! 900: logmsg("Error opening file: %s", REQUEST_DUMP); ! 901: return -1; ! 902: } ! 903: ! 904: /* store input protocol */ ! 905: fprintf(server, "opcode: %x\n", tp->th_opcode); ! 906: ! 907: cp = (char *)&tp->th_stuff; ! 908: filename = cp; ! 909: do { ! 910: bool endofit = true; ! 911: while(cp < &buf.storage[size]) { ! 912: if(*cp == '\0') { ! 913: endofit = false; ! 914: break; ! 915: } ! 916: cp++; ! 917: } ! 918: if(endofit) ! 919: /* no more options */ ! 920: break; ! 921: ! 922: /* before increasing pointer, make sure it is still within the legal ! 923: space */ ! 924: if((cp + 1) < &buf.storage[size]) { ! 925: ++cp; ! 926: if(first) { ! 927: /* store the mode since we need it later */ ! 928: mode = cp; ! 929: first = 0; ! 930: } ! 931: if(toggle) ! 932: /* name/value pair: */ ! 933: fprintf(server, "%s: %s\n", option, cp); ! 934: else { ! 935: /* store the name pointer */ ! 936: option = cp; ! 937: } ! 938: toggle ^= 1; ! 939: } ! 940: else ! 941: /* No more options */ ! 942: break; ! 943: } while(1); ! 944: ! 945: if(*cp) { ! 946: nak(EBADOP); ! 947: fclose(server); ! 948: return 3; ! 949: } ! 950: ! 951: /* store input protocol */ ! 952: fprintf(server, "filename: %s\n", filename); ! 953: ! 954: for(cp = mode; cp && *cp; cp++) ! 955: if(ISUPPER(*cp)) ! 956: *cp = (char)tolower((int)*cp); ! 957: ! 958: /* store input protocol */ ! 959: fclose(server); ! 960: ! 961: for(pf = formata; pf->f_mode; pf++) ! 962: if(strcmp(pf->f_mode, mode) == 0) ! 963: break; ! 964: if(!pf->f_mode) { ! 965: nak(EBADOP); ! 966: return 2; ! 967: } ! 968: ecode = validate_access(test, filename, tp->th_opcode); ! 969: if(ecode) { ! 970: nak(ecode); ! 971: return 1; ! 972: } ! 973: ! 974: #ifdef USE_WINSOCK ! 975: recvtimeout = sizeof(recvtimeoutbak); ! 976: getsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, ! 977: (char *)&recvtimeoutbak, (int *)&recvtimeout); ! 978: recvtimeout = TIMEOUT*1000; ! 979: setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, ! 980: (const char *)&recvtimeout, sizeof(recvtimeout)); ! 981: #endif ! 982: ! 983: if(tp->th_opcode == opcode_WRQ) ! 984: recvtftp(test, pf); ! 985: else ! 986: sendtftp(test, pf); ! 987: ! 988: #ifdef USE_WINSOCK ! 989: recvtimeout = recvtimeoutbak; ! 990: setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO, ! 991: (const char *)&recvtimeout, sizeof(recvtimeout)); ! 992: #endif ! 993: ! 994: return 0; ! 995: } ! 996: ! 997: /* Based on the testno, parse the correct server commands. */ ! 998: static int parse_servercmd(struct testcase *req) ! 999: { ! 1000: FILE *stream; ! 1001: int error; ! 1002: ! 1003: stream = test2fopen(req->testno); ! 1004: if(!stream) { ! 1005: error = errno; ! 1006: logmsg("fopen() failed with error: %d %s", error, strerror(error)); ! 1007: logmsg(" Couldn't open test file %ld", req->testno); ! 1008: return 1; /* done */ ! 1009: } ! 1010: else { ! 1011: char *orgcmd = NULL; ! 1012: char *cmd = NULL; ! 1013: size_t cmdsize = 0; ! 1014: int num = 0; ! 1015: ! 1016: /* get the custom server control "commands" */ ! 1017: error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream); ! 1018: fclose(stream); ! 1019: if(error) { ! 1020: logmsg("getpart() failed with error: %d", error); ! 1021: return 1; /* done */ ! 1022: } ! 1023: ! 1024: cmd = orgcmd; ! 1025: while(cmd && cmdsize) { ! 1026: char *check; ! 1027: if(1 == sscanf(cmd, "writedelay: %d", &num)) { ! 1028: logmsg("instructed to delay %d secs between packets", num); ! 1029: req->writedelay = num; ! 1030: } ! 1031: else { ! 1032: logmsg("Unknown <servercmd> instruction found: %s", cmd); ! 1033: } ! 1034: /* try to deal with CRLF or just LF */ ! 1035: check = strchr(cmd, '\r'); ! 1036: if(!check) ! 1037: check = strchr(cmd, '\n'); ! 1038: ! 1039: if(check) { ! 1040: /* get to the letter following the newline */ ! 1041: while((*check == '\r') || (*check == '\n')) ! 1042: check++; ! 1043: ! 1044: if(!*check) ! 1045: /* if we reached a zero, get out */ ! 1046: break; ! 1047: cmd = check; ! 1048: } ! 1049: else ! 1050: break; ! 1051: } ! 1052: free(orgcmd); ! 1053: } ! 1054: ! 1055: return 0; /* OK! */ ! 1056: } ! 1057: ! 1058: ! 1059: /* ! 1060: * Validate file access. ! 1061: */ ! 1062: static int validate_access(struct testcase *test, ! 1063: const char *filename, int mode) ! 1064: { ! 1065: char *ptr; ! 1066: ! 1067: logmsg("trying to get file: %s mode %x", filename, mode); ! 1068: ! 1069: if(!strncmp("verifiedserver", filename, 14)) { ! 1070: char weare[128]; ! 1071: size_t count = msnprintf(weare, sizeof(weare), ! 1072: "WE ROOLZ: %ld\r\n", (long)getpid()); ! 1073: ! 1074: logmsg("Are-we-friendly question received"); ! 1075: test->buffer = strdup(weare); ! 1076: test->rptr = test->buffer; /* set read pointer */ ! 1077: test->bufsize = count; /* set total count */ ! 1078: test->rcount = count; /* set data left to read */ ! 1079: return 0; /* fine */ ! 1080: } ! 1081: ! 1082: /* find the last slash */ ! 1083: ptr = strrchr(filename, '/'); ! 1084: ! 1085: if(ptr) { ! 1086: char partbuf[80]="data"; ! 1087: long partno; ! 1088: long testno; ! 1089: FILE *stream; ! 1090: ! 1091: ptr++; /* skip the slash */ ! 1092: ! 1093: /* skip all non-numericals following the slash */ ! 1094: while(*ptr && !ISDIGIT(*ptr)) ! 1095: ptr++; ! 1096: ! 1097: /* get the number */ ! 1098: testno = strtol(ptr, &ptr, 10); ! 1099: ! 1100: if(testno > 10000) { ! 1101: partno = testno % 10000; ! 1102: testno /= 10000; ! 1103: } ! 1104: else ! 1105: partno = 0; ! 1106: ! 1107: ! 1108: logmsg("requested test number %ld part %ld", testno, partno); ! 1109: ! 1110: test->testno = testno; ! 1111: ! 1112: (void)parse_servercmd(test); ! 1113: ! 1114: stream = test2fopen(testno); ! 1115: ! 1116: if(0 != partno) ! 1117: msnprintf(partbuf, sizeof(partbuf), "data%ld", partno); ! 1118: ! 1119: if(!stream) { ! 1120: int error = errno; ! 1121: logmsg("fopen() failed with error: %d %s", error, strerror(error)); ! 1122: logmsg("Couldn't open test file for test : %d", testno); ! 1123: return EACCESS; ! 1124: } ! 1125: else { ! 1126: size_t count; ! 1127: int error = getpart(&test->buffer, &count, "reply", partbuf, stream); ! 1128: fclose(stream); ! 1129: if(error) { ! 1130: logmsg("getpart() failed with error: %d", error); ! 1131: return EACCESS; ! 1132: } ! 1133: if(test->buffer) { ! 1134: test->rptr = test->buffer; /* set read pointer */ ! 1135: test->bufsize = count; /* set total count */ ! 1136: test->rcount = count; /* set data left to read */ ! 1137: } ! 1138: else ! 1139: return EACCESS; ! 1140: } ! 1141: } ! 1142: else { ! 1143: logmsg("no slash found in path"); ! 1144: return EACCESS; /* failure */ ! 1145: } ! 1146: ! 1147: logmsg("file opened and all is good"); ! 1148: return 0; ! 1149: } ! 1150: ! 1151: /* ! 1152: * Send the requested file. ! 1153: */ ! 1154: static void sendtftp(struct testcase *test, struct formats *pf) ! 1155: { ! 1156: int size; ! 1157: ssize_t n; ! 1158: /* These are volatile to live through a siglongjmp */ ! 1159: volatile unsigned short sendblock; /* block count */ ! 1160: struct tftphdr * volatile sdp = r_init(); /* data buffer */ ! 1161: struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */ ! 1162: ! 1163: sendblock = 1; ! 1164: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 1165: mysignal(SIGALRM, timer); ! 1166: #endif ! 1167: do { ! 1168: size = readit(test, (struct tftphdr **)&sdp, pf->f_convert); ! 1169: if(size < 0) { ! 1170: nak(errno + 100); ! 1171: return; ! 1172: } ! 1173: sdp->th_opcode = htons((unsigned short)opcode_DATA); ! 1174: sdp->th_block = htons(sendblock); ! 1175: timeout = 0; ! 1176: #ifdef HAVE_SIGSETJMP ! 1177: (void) sigsetjmp(timeoutbuf, 1); ! 1178: #endif ! 1179: if(test->writedelay) { ! 1180: logmsg("Pausing %d seconds before %d bytes", test->writedelay, ! 1181: size); ! 1182: wait_ms(1000*test->writedelay); ! 1183: } ! 1184: ! 1185: send_data: ! 1186: if(swrite(peer, sdp, size + 4) != size + 4) { ! 1187: logmsg("write"); ! 1188: return; ! 1189: } ! 1190: read_ahead(test, pf->f_convert); ! 1191: for(;;) { ! 1192: #ifdef HAVE_ALARM ! 1193: alarm(rexmtval); /* read the ack */ ! 1194: #endif ! 1195: n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage)); ! 1196: #ifdef HAVE_ALARM ! 1197: alarm(0); ! 1198: #endif ! 1199: if(got_exit_signal) ! 1200: return; ! 1201: if(n < 0) { ! 1202: logmsg("read: fail"); ! 1203: return; ! 1204: } ! 1205: sap->th_opcode = ntohs((unsigned short)sap->th_opcode); ! 1206: sap->th_block = ntohs(sap->th_block); ! 1207: ! 1208: if(sap->th_opcode == opcode_ERROR) { ! 1209: logmsg("got ERROR"); ! 1210: return; ! 1211: } ! 1212: ! 1213: if(sap->th_opcode == opcode_ACK) { ! 1214: if(sap->th_block == sendblock) { ! 1215: break; ! 1216: } ! 1217: /* Re-synchronize with the other side */ ! 1218: (void) synchnet(peer); ! 1219: if(sap->th_block == (sendblock-1)) { ! 1220: goto send_data; ! 1221: } ! 1222: } ! 1223: ! 1224: } ! 1225: sendblock++; ! 1226: } while(size == SEGSIZE); ! 1227: } ! 1228: ! 1229: /* ! 1230: * Receive a file. ! 1231: */ ! 1232: static void recvtftp(struct testcase *test, struct formats *pf) ! 1233: { ! 1234: ssize_t n, size; ! 1235: /* These are volatile to live through a siglongjmp */ ! 1236: volatile unsigned short recvblock; /* block count */ ! 1237: struct tftphdr * volatile rdp; /* data buffer */ ! 1238: struct tftphdr *rap; /* ack buffer */ ! 1239: ! 1240: recvblock = 0; ! 1241: rdp = w_init(); ! 1242: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 1243: mysignal(SIGALRM, timer); ! 1244: #endif ! 1245: rap = &ackbuf.hdr; ! 1246: do { ! 1247: timeout = 0; ! 1248: rap->th_opcode = htons((unsigned short)opcode_ACK); ! 1249: rap->th_block = htons(recvblock); ! 1250: recvblock++; ! 1251: #ifdef HAVE_SIGSETJMP ! 1252: (void) sigsetjmp(timeoutbuf, 1); ! 1253: #endif ! 1254: send_ack: ! 1255: if(swrite(peer, &ackbuf.storage[0], 4) != 4) { ! 1256: logmsg("write: fail\n"); ! 1257: goto abort; ! 1258: } ! 1259: write_behind(test, pf->f_convert); ! 1260: for(;;) { ! 1261: #ifdef HAVE_ALARM ! 1262: alarm(rexmtval); ! 1263: #endif ! 1264: n = sread(peer, rdp, PKTSIZE); ! 1265: #ifdef HAVE_ALARM ! 1266: alarm(0); ! 1267: #endif ! 1268: if(got_exit_signal) ! 1269: goto abort; ! 1270: if(n < 0) { /* really? */ ! 1271: logmsg("read: fail\n"); ! 1272: goto abort; ! 1273: } ! 1274: rdp->th_opcode = ntohs((unsigned short)rdp->th_opcode); ! 1275: rdp->th_block = ntohs(rdp->th_block); ! 1276: if(rdp->th_opcode == opcode_ERROR) ! 1277: goto abort; ! 1278: if(rdp->th_opcode == opcode_DATA) { ! 1279: if(rdp->th_block == recvblock) { ! 1280: break; /* normal */ ! 1281: } ! 1282: /* Re-synchronize with the other side */ ! 1283: (void) synchnet(peer); ! 1284: if(rdp->th_block == (recvblock-1)) ! 1285: goto send_ack; /* rexmit */ ! 1286: } ! 1287: } ! 1288: ! 1289: size = writeit(test, &rdp, (int)(n - 4), pf->f_convert); ! 1290: if(size != (n-4)) { /* ahem */ ! 1291: if(size < 0) ! 1292: nak(errno + 100); ! 1293: else ! 1294: nak(ENOSPACE); ! 1295: goto abort; ! 1296: } ! 1297: } while(size == SEGSIZE); ! 1298: write_behind(test, pf->f_convert); ! 1299: ! 1300: rap->th_opcode = htons((unsigned short)opcode_ACK); /* send the "final" ! 1301: ack */ ! 1302: rap->th_block = htons(recvblock); ! 1303: (void) swrite(peer, &ackbuf.storage[0], 4); ! 1304: #if defined(HAVE_ALARM) && defined(SIGALRM) ! 1305: mysignal(SIGALRM, justtimeout); /* just abort read on timeout */ ! 1306: alarm(rexmtval); ! 1307: #endif ! 1308: /* normally times out and quits */ ! 1309: n = sread(peer, &buf.storage[0], sizeof(buf.storage)); ! 1310: #ifdef HAVE_ALARM ! 1311: alarm(0); ! 1312: #endif ! 1313: if(got_exit_signal) ! 1314: goto abort; ! 1315: if(n >= 4 && /* if read some data */ ! 1316: rdp->th_opcode == opcode_DATA && /* and got a data block */ ! 1317: recvblock == rdp->th_block) { /* then my last ack was lost */ ! 1318: (void) swrite(peer, &ackbuf.storage[0], 4); /* resend final ack */ ! 1319: } ! 1320: abort: ! 1321: return; ! 1322: } ! 1323: ! 1324: /* ! 1325: * Send a nak packet (error message). Error code passed in is one of the ! 1326: * standard TFTP codes, or a Unix errno offset by 100. ! 1327: */ ! 1328: static void nak(int error) ! 1329: { ! 1330: struct tftphdr *tp; ! 1331: int length; ! 1332: struct errmsg *pe; ! 1333: ! 1334: tp = &buf.hdr; ! 1335: tp->th_opcode = htons((unsigned short)opcode_ERROR); ! 1336: tp->th_code = htons((unsigned short)error); ! 1337: for(pe = errmsgs; pe->e_code >= 0; pe++) ! 1338: if(pe->e_code == error) ! 1339: break; ! 1340: if(pe->e_code < 0) { ! 1341: pe->e_msg = strerror(error - 100); ! 1342: tp->th_code = EUNDEF; /* set 'undef' errorcode */ ! 1343: } ! 1344: length = (int)strlen(pe->e_msg); ! 1345: ! 1346: /* we use memcpy() instead of strcpy() in order to avoid buffer overflow ! 1347: * report from glibc with FORTIFY_SOURCE */ ! 1348: memcpy(tp->th_msg, pe->e_msg, length + 1); ! 1349: length += 5; ! 1350: if(swrite(peer, &buf.storage[0], length) != length) ! 1351: logmsg("nak: fail\n"); ! 1352: }