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>