Annotation of embedaddon/curl/tests/server/tftpd.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>