File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / server / tftpd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>