tftpd.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  *
00009  * Trivial file transfer protocol server.
00010  *
00011  * This code includes many modifications by Jim Guyton <guyton@rand-unix>
00012  *
00013  * This source file was started based on netkit-tftpd 0.17
00014  * Heavily modified for curl's test suite
00015  */
00016 
00017 /*
00018  * Copyright (c) 1983, 2016 Regents of the University of California.
00019  * All rights reserved.
00020  *
00021  * Redistribution and use in source and binary forms, with or without
00022  * modification, are permitted provided that the following conditions
00023  * are met:
00024  * 1. Redistributions of source code must retain the above copyright
00025  *    notice, this list of conditions and the following disclaimer.
00026  * 2. Redistributions in binary form must reproduce the above copyright
00027  *    notice, this list of conditions and the following disclaimer in the
00028  *    documentation and/or other materials provided with the distribution.
00029  * 3. All advertising materials mentioning features or use of this software
00030  *    must display the following acknowledgement:
00031  *      This product includes software developed by the University of
00032  *      California, Berkeley and its contributors.
00033  * 4. Neither the name of the University nor the names of its contributors
00034  *    may be used to endorse or promote products derived from this software
00035  *    without specific prior written permission.
00036  *
00037  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00038  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00039  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00040  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00041  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00042  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00043  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00044  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00045  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00046  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00047  * SUCH DAMAGE.
00048  */
00049 
00050 #include "server_setup.h"
00051 
00052 #ifdef HAVE_SYS_IOCTL_H
00053 #include <sys/ioctl.h>
00054 #endif
00055 #ifdef HAVE_SIGNAL_H
00056 #include <signal.h>
00057 #endif
00058 #ifdef HAVE_FCNTL_H
00059 #include <fcntl.h>
00060 #endif
00061 #ifdef HAVE_NETINET_IN_H
00062 #include <netinet/in.h>
00063 #endif
00064 #ifdef HAVE_ARPA_INET_H
00065 #include <arpa/inet.h>
00066 #endif
00067 #ifdef HAVE_ARPA_TFTP_H
00068 #include <arpa/tftp.h>
00069 #else
00070 #include "tftp.h"
00071 #endif
00072 #ifdef HAVE_NETDB_H
00073 #include <netdb.h>
00074 #endif
00075 #ifdef HAVE_SYS_FILIO_H
00076 /* FIONREAD on Solaris 7 */
00077 #include <sys/filio.h>
00078 #endif
00079 
00080 #include <setjmp.h>
00081 
00082 #ifdef HAVE_PWD_H
00083 #include <pwd.h>
00084 #endif
00085 
00086 #define ENABLE_CURLX_PRINTF
00087 /* make the curlx header define all printf() functions to use the curlx_*
00088    versions instead */
00089 #include "curlx.h" /* from the private lib dir */
00090 #include "getpart.h"
00091 #include "util.h"
00092 #include "server_sockaddr.h"
00093 
00094 /* include memdebug.h last */
00095 #include "memdebug.h"
00096 
00097 /*****************************************************************************
00098 *                      STRUCT DECLARATIONS AND DEFINES                       *
00099 *****************************************************************************/
00100 
00101 #ifndef PKTSIZE
00102 #define PKTSIZE (SEGSIZE + 4)  /* SEGSIZE defined in arpa/tftp.h */
00103 #endif
00104 
00105 struct testcase {
00106   char *buffer;   /* holds the file data to send to the client */
00107   size_t bufsize; /* size of the data in buffer */
00108   char *rptr;     /* read pointer into the buffer */
00109   size_t rcount;  /* amount of data left to read of the file */
00110   long testno;    /* test case number */
00111   int ofile;      /* file descriptor for output file when uploading to us */
00112 
00113   int writedelay; /* number of seconds between each packet */
00114 };
00115 
00116 struct formats {
00117   const char *f_mode;
00118   int f_convert;
00119 };
00120 
00121 struct errmsg {
00122   int e_code;
00123   const char *e_msg;
00124 };
00125 
00126 typedef union {
00127   struct tftphdr hdr;
00128   char storage[PKTSIZE];
00129 } tftphdr_storage_t;
00130 
00131 /*
00132  * bf.counter values in range [-1 .. SEGSIZE] represents size of data in the
00133  * bf.buf buffer. Additionally it can also hold flags BF_ALLOC or BF_FREE.
00134  */
00135 
00136 struct bf {
00137   int counter;            /* size of data in buffer, or flag */
00138   tftphdr_storage_t buf;  /* room for data packet */
00139 };
00140 
00141 #define BF_ALLOC -3       /* alloc'd but not yet filled */
00142 #define BF_FREE  -2       /* free */
00143 
00144 #define opcode_RRQ   1
00145 #define opcode_WRQ   2
00146 #define opcode_DATA  3
00147 #define opcode_ACK   4
00148 #define opcode_ERROR 5
00149 
00150 #define TIMEOUT      5
00151 
00152 #undef MIN
00153 #define MIN(x,y) ((x)<(y)?(x):(y))
00154 
00155 #ifndef DEFAULT_LOGFILE
00156 #define DEFAULT_LOGFILE "log/tftpd.log"
00157 #endif
00158 
00159 #define REQUEST_DUMP  "log/server.input"
00160 
00161 #define DEFAULT_PORT 8999 /* UDP */
00162 
00163 /*****************************************************************************
00164 *                              GLOBAL VARIABLES                              *
00165 *****************************************************************************/
00166 
00167 static struct errmsg errmsgs[] = {
00168   { EUNDEF,       "Undefined error code" },
00169   { ENOTFOUND,    "File not found" },
00170   { EACCESS,      "Access violation" },
00171   { ENOSPACE,     "Disk full or allocation exceeded" },
00172   { EBADOP,       "Illegal TFTP operation" },
00173   { EBADID,       "Unknown transfer ID" },
00174   { EEXISTS,      "File already exists" },
00175   { ENOUSER,      "No such user" },
00176   { -1,           0 }
00177 };
00178 
00179 static struct formats formata[] = {
00180   { "netascii",   1 },
00181   { "octet",      0 },
00182   { NULL,         0 }
00183 };
00184 
00185 static struct bf bfs[2];
00186 
00187 static int nextone;     /* index of next buffer to use */
00188 static int current;     /* index of buffer in use */
00189 
00190                            /* control flags for crlf conversions */
00191 static int newline = 0;    /* fillbuf: in middle of newline expansion */
00192 static int prevchar = -1;  /* putbuf: previous char (cr check) */
00193 
00194 static tftphdr_storage_t buf;
00195 static tftphdr_storage_t ackbuf;
00196 
00197 static srvr_sockaddr_union_t from;
00198 static curl_socklen_t fromlen;
00199 
00200 static curl_socket_t peer = CURL_SOCKET_BAD;
00201 
00202 static int timeout;
00203 static int maxtimeout = 5 * TIMEOUT;
00204 
00205 #ifdef ENABLE_IPV6
00206 static bool use_ipv6 = FALSE;
00207 #endif
00208 static const char *ipv_inuse = "IPv4";
00209 
00210 const  char *serverlogfile = DEFAULT_LOGFILE;
00211 static char *pidname= (char *)".tftpd.pid";
00212 static int serverlogslocked = 0;
00213 static int wrotepidfile = 0;
00214 
00215 #ifdef HAVE_SIGSETJMP
00216 static sigjmp_buf timeoutbuf;
00217 #endif
00218 
00219 #if defined(HAVE_ALARM) && defined(SIGALRM)
00220 static int rexmtval = TIMEOUT;
00221 #endif
00222 
00223 /* do-nothing macro replacement for systems which lack siginterrupt() */
00224 
00225 #ifndef HAVE_SIGINTERRUPT
00226 #define siginterrupt(x,y) do {} while(0)
00227 #endif
00228 
00229 /* vars used to keep around previous signal handlers */
00230 
00231 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
00232 
00233 #ifdef SIGHUP
00234 static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
00235 #endif
00236 
00237 #ifdef SIGPIPE
00238 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
00239 #endif
00240 
00241 #ifdef SIGINT
00242 static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
00243 #endif
00244 
00245 #ifdef SIGTERM
00246 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
00247 #endif
00248 
00249 #if defined(SIGBREAK) && defined(WIN32)
00250 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
00251 #endif
00252 
00253 /* var which if set indicates that the program should finish execution */
00254 
00255 SIG_ATOMIC_T got_exit_signal = 0;
00256 
00257 /* if next is set indicates the first signal handled in exit_signal_handler */
00258 
00259 static volatile int exit_signal = 0;
00260 
00261 /*****************************************************************************
00262 *                            FUNCTION PROTOTYPES                             *
00263 *****************************************************************************/
00264 
00265 static struct tftphdr *rw_init(int);
00266 
00267 static struct tftphdr *w_init(void);
00268 
00269 static struct tftphdr *r_init(void);
00270 
00271 static void read_ahead(struct testcase *test, int convert);
00272 
00273 static ssize_t write_behind(struct testcase *test, int convert);
00274 
00275 static int synchnet(curl_socket_t);
00276 
00277 static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size);
00278 
00279 static int validate_access(struct testcase *test, const char *fname, int mode);
00280 
00281 static void sendtftp(struct testcase *test, struct formats *pf);
00282 
00283 static void recvtftp(struct testcase *test, struct formats *pf);
00284 
00285 static void nak(int error);
00286 
00287 #if defined(HAVE_ALARM) && defined(SIGALRM)
00288 
00289 static void mysignal(int sig, void (*handler)(int));
00290 
00291 static void timer(int signum);
00292 
00293 static void justtimeout(int signum);
00294 
00295 #endif /* HAVE_ALARM && SIGALRM */
00296 
00297 static RETSIGTYPE exit_signal_handler(int signum);
00298 
00299 static void install_signal_handlers(void);
00300 
00301 static void restore_signal_handlers(void);
00302 
00303 /*****************************************************************************
00304 *                          FUNCTION IMPLEMENTATIONS                          *
00305 *****************************************************************************/
00306 
00307 #if defined(HAVE_ALARM) && defined(SIGALRM)
00308 
00309 /*
00310  * Like signal(), but with well-defined semantics.
00311  */
00312 static void mysignal(int sig, void (*handler)(int))
00313 {
00314   struct sigaction sa;
00315   memset(&sa, 0, sizeof(sa));
00316   sa.sa_handler = handler;
00317   sigaction(sig, &sa, NULL);
00318 }
00319 
00320 static void timer(int signum)
00321 {
00322   (void)signum;
00323 
00324   logmsg("alarm!");
00325 
00326   timeout += rexmtval;
00327   if(timeout >= maxtimeout) {
00328     if(wrotepidfile) {
00329       wrotepidfile = 0;
00330       unlink(pidname);
00331     }
00332     if(serverlogslocked) {
00333       serverlogslocked = 0;
00334       clear_advisor_read_lock(SERVERLOGS_LOCK);
00335     }
00336     exit(1);
00337   }
00338 #ifdef HAVE_SIGSETJMP
00339   siglongjmp(timeoutbuf, 1);
00340 #endif
00341 }
00342 
00343 static void justtimeout(int signum)
00344 {
00345   (void)signum;
00346 }
00347 
00348 #endif /* HAVE_ALARM && SIGALRM */
00349 
00350 /* signal handler that will be triggered to indicate that the program
00351   should finish its execution in a controlled manner as soon as possible.
00352   The first time this is called it will set got_exit_signal to one and
00353   store in exit_signal the signal that triggered its execution. */
00354 
00355 static RETSIGTYPE exit_signal_handler(int signum)
00356 {
00357   int old_errno = errno;
00358   if(got_exit_signal == 0) {
00359     got_exit_signal = 1;
00360     exit_signal = signum;
00361   }
00362   (void)signal(signum, exit_signal_handler);
00363   errno = old_errno;
00364 }
00365 
00366 static void install_signal_handlers(void)
00367 {
00368 #ifdef SIGHUP
00369   /* ignore SIGHUP signal */
00370   old_sighup_handler = signal(SIGHUP, SIG_IGN);
00371   if(old_sighup_handler == SIG_ERR)
00372     logmsg("cannot install SIGHUP handler: %s", strerror(errno));
00373 #endif
00374 #ifdef SIGPIPE
00375   /* ignore SIGPIPE signal */
00376   old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
00377   if(old_sigpipe_handler == SIG_ERR)
00378     logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
00379 #endif
00380 #ifdef SIGINT
00381   /* handle SIGINT signal with our exit_signal_handler */
00382   old_sigint_handler = signal(SIGINT, exit_signal_handler);
00383   if(old_sigint_handler == SIG_ERR)
00384     logmsg("cannot install SIGINT handler: %s", strerror(errno));
00385   else
00386     siginterrupt(SIGINT, 1);
00387 #endif
00388 #ifdef SIGTERM
00389   /* handle SIGTERM signal with our exit_signal_handler */
00390   old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
00391   if(old_sigterm_handler == SIG_ERR)
00392     logmsg("cannot install SIGTERM handler: %s", strerror(errno));
00393   else
00394     siginterrupt(SIGTERM, 1);
00395 #endif
00396 #if defined(SIGBREAK) && defined(WIN32)
00397   /* handle SIGBREAK signal with our exit_signal_handler */
00398   old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
00399   if(old_sigbreak_handler == SIG_ERR)
00400     logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
00401   else
00402     siginterrupt(SIGBREAK, 1);
00403 #endif
00404 }
00405 
00406 static void restore_signal_handlers(void)
00407 {
00408 #ifdef SIGHUP
00409   if(SIG_ERR != old_sighup_handler)
00410     (void)signal(SIGHUP, old_sighup_handler);
00411 #endif
00412 #ifdef SIGPIPE
00413   if(SIG_ERR != old_sigpipe_handler)
00414     (void)signal(SIGPIPE, old_sigpipe_handler);
00415 #endif
00416 #ifdef SIGINT
00417   if(SIG_ERR != old_sigint_handler)
00418     (void)signal(SIGINT, old_sigint_handler);
00419 #endif
00420 #ifdef SIGTERM
00421   if(SIG_ERR != old_sigterm_handler)
00422     (void)signal(SIGTERM, old_sigterm_handler);
00423 #endif
00424 #if defined(SIGBREAK) && defined(WIN32)
00425   if(SIG_ERR != old_sigbreak_handler)
00426     (void)signal(SIGBREAK, old_sigbreak_handler);
00427 #endif
00428 }
00429 
00430 /*
00431  * init for either read-ahead or write-behind.
00432  * zero for write-behind, one for read-head.
00433  */
00434 static struct tftphdr *rw_init(int x)
00435 {
00436   newline = 0;                    /* init crlf flag */
00437   prevchar = -1;
00438   bfs[0].counter =  BF_ALLOC;     /* pass out the first buffer */
00439   current = 0;
00440   bfs[1].counter = BF_FREE;
00441   nextone = x;                    /* ahead or behind? */
00442   return &bfs[0].buf.hdr;
00443 }
00444 
00445 static struct tftphdr *w_init(void)
00446 {
00447   return rw_init(0); /* write-behind */
00448 }
00449 
00450 static struct tftphdr *r_init(void)
00451 {
00452   return rw_init(1); /* read-ahead */
00453 }
00454 
00455 /* Have emptied current buffer by sending to net and getting ack.
00456    Free it and return next buffer filled with data.
00457  */
00458 static int readit(struct testcase *test, struct tftphdr **dpp,
00459                   int convert /* if true, convert to ascii */)
00460 {
00461   struct bf *b;
00462 
00463   bfs[current].counter = BF_FREE; /* free old one */
00464   current = !current;             /* "incr" current */
00465 
00466   b = &bfs[current];              /* look at new buffer */
00467   if(b->counter == BF_FREE)      /* if it's empty */
00468     read_ahead(test, convert);    /* fill it */
00469 
00470   *dpp = &b->buf.hdr;             /* set caller's ptr */
00471   return b->counter;
00472 }
00473 
00474 /*
00475  * fill the input buffer, doing ascii conversions if requested
00476  * conversions are  lf -> cr, lf  and cr -> cr, nul
00477  */
00478 static void read_ahead(struct testcase *test,
00479                        int convert /* if true, convert to ascii */)
00480 {
00481   int i;
00482   char *p;
00483   int c;
00484   struct bf *b;
00485   struct tftphdr *dp;
00486 
00487   b = &bfs[nextone];              /* look at "next" buffer */
00488   if(b->counter != BF_FREE)      /* nop if not free */
00489     return;
00490   nextone = !nextone;             /* "incr" next buffer ptr */
00491 
00492   dp = &b->buf.hdr;
00493 
00494   if(convert == 0) {
00495     /* The former file reading code did this:
00496        b->counter = read(fileno(file), dp->th_data, SEGSIZE); */
00497     size_t copy_n = MIN(SEGSIZE, test->rcount);
00498     memcpy(dp->th_data, test->rptr, copy_n);
00499 
00500     /* decrease amount, advance pointer */
00501     test->rcount -= copy_n;
00502     test->rptr += copy_n;
00503     b->counter = (int)copy_n;
00504     return;
00505   }
00506 
00507   p = dp->th_data;
00508   for(i = 0 ; i < SEGSIZE; i++) {
00509     if(newline) {
00510       if(prevchar == '\n')
00511         c = '\n';       /* lf to cr,lf */
00512       else
00513         c = '\0';       /* cr to cr,nul */
00514       newline = 0;
00515     }
00516     else {
00517       if(test->rcount) {
00518         c=test->rptr[0];
00519         test->rptr++;
00520         test->rcount--;
00521       }
00522       else
00523         break;
00524       if(c == '\n' || c == '\r') {
00525         prevchar = c;
00526         c = '\r';
00527         newline = 1;
00528       }
00529     }
00530     *p++ = (char)c;
00531   }
00532   b->counter = (int)(p - dp->th_data);
00533 }
00534 
00535 /* Update count associated with the buffer, get new buffer from the queue.
00536    Calls write_behind only if next buffer not available.
00537  */
00538 static int writeit(struct testcase *test, struct tftphdr * volatile *dpp,
00539                    int ct, int convert)
00540 {
00541   bfs[current].counter = ct;      /* set size of data to write */
00542   current = !current;             /* switch to other buffer */
00543   if(bfs[current].counter != BF_FREE)     /* if not free */
00544     write_behind(test, convert);     /* flush it */
00545   bfs[current].counter = BF_ALLOC;        /* mark as alloc'd */
00546   *dpp =  &bfs[current].buf.hdr;
00547   return ct;                      /* this is a lie of course */
00548 }
00549 
00550 /*
00551  * Output a buffer to a file, converting from netascii if requested.
00552  * CR, NUL -> CR  and CR, LF => LF.
00553  * Note spec is undefined if we get CR as last byte of file or a
00554  * CR followed by anything else.  In this case we leave it alone.
00555  */
00556 static ssize_t write_behind(struct testcase *test, int convert)
00557 {
00558   char *writebuf;
00559   int count;
00560   int ct;
00561   char *p;
00562   int c;                          /* current character */
00563   struct bf *b;
00564   struct tftphdr *dp;
00565 
00566   b = &bfs[nextone];
00567   if(b->counter < -1)            /* anything to flush? */
00568     return 0;                     /* just nop if nothing to do */
00569 
00570   if(!test->ofile) {
00571     char outfile[256];
00572     snprintf(outfile, sizeof(outfile), "log/upload.%ld", test->testno);
00573 #ifdef WIN32
00574     test->ofile=open(outfile, O_CREAT|O_RDWR|O_BINARY, 0777);
00575 #else
00576     test->ofile=open(outfile, O_CREAT|O_RDWR, 0777);
00577 #endif
00578     if(test->ofile == -1) {
00579       logmsg("Couldn't create and/or open file %s for upload!", outfile);
00580       return -1; /* failure! */
00581     }
00582   }
00583 
00584   count = b->counter;             /* remember byte count */
00585   b->counter = BF_FREE;           /* reset flag */
00586   dp = &b->buf.hdr;
00587   nextone = !nextone;             /* incr for next time */
00588   writebuf = dp->th_data;
00589 
00590   if(count <= 0)
00591     return -1;                    /* nak logic? */
00592 
00593   if(convert == 0)
00594     return write(test->ofile, writebuf, count);
00595 
00596   p = writebuf;
00597   ct = count;
00598   while(ct--) {                   /* loop over the buffer */
00599     c = *p++;                     /* pick up a character */
00600     if(prevchar == '\r') {        /* if prev char was cr */
00601       if(c == '\n')               /* if have cr,lf then just */
00602         lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */
00603       else
00604         if(c == '\0')             /* if have cr,nul then */
00605           goto skipit;            /* just skip over the putc */
00606       /* else just fall through and allow it */
00607     }
00608     /* formerly
00609        putc(c, file); */
00610     if(1 != write(test->ofile, &c, 1))
00611       break;
00612     skipit:
00613     prevchar = c;
00614   }
00615   return count;
00616 }
00617 
00618 /* When an error has occurred, it is possible that the two sides are out of
00619  * synch.  Ie: that what I think is the other side's response to packet N is
00620  * really their response to packet N-1.
00621  *
00622  * So, to try to prevent that, we flush all the input queued up for us on the
00623  * network connection on our host.
00624  *
00625  * We return the number of packets we flushed (mostly for reporting when trace
00626  * is active).
00627  */
00628 
00629 static int synchnet(curl_socket_t f /* socket to flush */)
00630 {
00631 
00632 #if defined(HAVE_IOCTLSOCKET)
00633   unsigned long i;
00634 #else
00635   int i;
00636 #endif
00637   int j = 0;
00638   char rbuf[PKTSIZE];
00639   srvr_sockaddr_union_t fromaddr;
00640   curl_socklen_t fromaddrlen;
00641 
00642   for(;;) {
00643 #if defined(HAVE_IOCTLSOCKET)
00644     (void) ioctlsocket(f, FIONREAD, &i);
00645 #else
00646     (void) ioctl(f, FIONREAD, &i);
00647 #endif
00648     if(i) {
00649       j++;
00650 #ifdef ENABLE_IPV6
00651       if(!use_ipv6)
00652 #endif
00653         fromaddrlen = sizeof(fromaddr.sa4);
00654 #ifdef ENABLE_IPV6
00655       else
00656         fromaddrlen = sizeof(fromaddr.sa6);
00657 #endif
00658       (void) recvfrom(f, rbuf, sizeof(rbuf), 0,
00659                       &fromaddr.sa, &fromaddrlen);
00660     }
00661     else
00662       break;
00663   }
00664   return j;
00665 }
00666 
00667 int main(int argc, char **argv)
00668 {
00669   srvr_sockaddr_union_t me;
00670   struct tftphdr *tp;
00671   ssize_t n = 0;
00672   int arg = 1;
00673   unsigned short port = DEFAULT_PORT;
00674   curl_socket_t sock = CURL_SOCKET_BAD;
00675   int flag;
00676   int rc;
00677   int error;
00678   long pid;
00679   struct testcase test;
00680   int result = 0;
00681 
00682   memset(&test, 0, sizeof(test));
00683 
00684   while(argc>arg) {
00685     if(!strcmp("--version", argv[arg])) {
00686       printf("tftpd IPv4%s\n",
00687 #ifdef ENABLE_IPV6
00688              "/IPv6"
00689 #else
00690              ""
00691 #endif
00692              );
00693       return 0;
00694     }
00695     else if(!strcmp("--pidfile", argv[arg])) {
00696       arg++;
00697       if(argc>arg)
00698         pidname = argv[arg++];
00699     }
00700     else if(!strcmp("--logfile", argv[arg])) {
00701       arg++;
00702       if(argc>arg)
00703         serverlogfile = argv[arg++];
00704     }
00705     else if(!strcmp("--ipv4", argv[arg])) {
00706 #ifdef ENABLE_IPV6
00707       ipv_inuse = "IPv4";
00708       use_ipv6 = FALSE;
00709 #endif
00710       arg++;
00711     }
00712     else if(!strcmp("--ipv6", argv[arg])) {
00713 #ifdef ENABLE_IPV6
00714       ipv_inuse = "IPv6";
00715       use_ipv6 = TRUE;
00716 #endif
00717       arg++;
00718     }
00719     else if(!strcmp("--port", argv[arg])) {
00720       arg++;
00721       if(argc>arg) {
00722         char *endptr;
00723         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
00724         if((endptr != argv[arg] + strlen(argv[arg])) ||
00725            (ulnum < 1025UL) || (ulnum > 65535UL)) {
00726           fprintf(stderr, "tftpd: invalid --port argument (%s)\n",
00727                   argv[arg]);
00728           return 0;
00729         }
00730         port = curlx_ultous(ulnum);
00731         arg++;
00732       }
00733     }
00734     else if(!strcmp("--srcdir", argv[arg])) {
00735       arg++;
00736       if(argc>arg) {
00737         path = argv[arg];
00738         arg++;
00739       }
00740     }
00741     else {
00742       puts("Usage: tftpd [option]\n"
00743            " --version\n"
00744            " --logfile [file]\n"
00745            " --pidfile [file]\n"
00746            " --ipv4\n"
00747            " --ipv6\n"
00748            " --port [port]\n"
00749            " --srcdir [path]");
00750       return 0;
00751     }
00752   }
00753 
00754 #ifdef WIN32
00755   win32_init();
00756   atexit(win32_cleanup);
00757 #endif
00758 
00759   install_signal_handlers();
00760 
00761   pid = (long)getpid();
00762 
00763 #ifdef ENABLE_IPV6
00764   if(!use_ipv6)
00765 #endif
00766     sock = socket(AF_INET, SOCK_DGRAM, 0);
00767 #ifdef ENABLE_IPV6
00768   else
00769     sock = socket(AF_INET6, SOCK_DGRAM, 0);
00770 #endif
00771 
00772   if(CURL_SOCKET_BAD == sock) {
00773     error = SOCKERRNO;
00774     logmsg("Error creating socket: (%d) %s",
00775            error, strerror(error));
00776     result = 1;
00777     goto tftpd_cleanup;
00778   }
00779 
00780   flag = 1;
00781   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
00782             (void *)&flag, sizeof(flag))) {
00783     error = SOCKERRNO;
00784     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
00785            error, strerror(error));
00786     result = 1;
00787     goto tftpd_cleanup;
00788   }
00789 
00790 #ifdef ENABLE_IPV6
00791   if(!use_ipv6) {
00792 #endif
00793     memset(&me.sa4, 0, sizeof(me.sa4));
00794     me.sa4.sin_family = AF_INET;
00795     me.sa4.sin_addr.s_addr = INADDR_ANY;
00796     me.sa4.sin_port = htons(port);
00797     rc = bind(sock, &me.sa, sizeof(me.sa4));
00798 #ifdef ENABLE_IPV6
00799   }
00800   else {
00801     memset(&me.sa6, 0, sizeof(me.sa6));
00802     me.sa6.sin6_family = AF_INET6;
00803     me.sa6.sin6_addr = in6addr_any;
00804     me.sa6.sin6_port = htons(port);
00805     rc = bind(sock, &me.sa, sizeof(me.sa6));
00806   }
00807 #endif /* ENABLE_IPV6 */
00808   if(0 != rc) {
00809     error = SOCKERRNO;
00810     logmsg("Error binding socket on port %hu: (%d) %s",
00811            port, error, strerror(error));
00812     result = 1;
00813     goto tftpd_cleanup;
00814   }
00815 
00816   wrotepidfile = write_pidfile(pidname);
00817   if(!wrotepidfile) {
00818     result = 1;
00819     goto tftpd_cleanup;
00820   }
00821 
00822   logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
00823 
00824   for(;;) {
00825     fromlen = sizeof(from);
00826 #ifdef ENABLE_IPV6
00827     if(!use_ipv6)
00828 #endif
00829       fromlen = sizeof(from.sa4);
00830 #ifdef ENABLE_IPV6
00831     else
00832       fromlen = sizeof(from.sa6);
00833 #endif
00834     n = (ssize_t)recvfrom(sock, &buf.storage[0], sizeof(buf.storage), 0,
00835                           &from.sa, &fromlen);
00836     if(got_exit_signal)
00837       break;
00838     if(n < 0) {
00839       logmsg("recvfrom");
00840       result = 3;
00841       break;
00842     }
00843 
00844     set_advisor_read_lock(SERVERLOGS_LOCK);
00845     serverlogslocked = 1;
00846 
00847 #ifdef ENABLE_IPV6
00848     if(!use_ipv6) {
00849 #endif
00850       from.sa4.sin_family = AF_INET;
00851       peer = socket(AF_INET, SOCK_DGRAM, 0);
00852       if(CURL_SOCKET_BAD == peer) {
00853         logmsg("socket");
00854         result = 2;
00855         break;
00856       }
00857       if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) {
00858         logmsg("connect: fail");
00859         result = 1;
00860         break;
00861       }
00862 #ifdef ENABLE_IPV6
00863     }
00864     else {
00865       from.sa6.sin6_family = AF_INET6;
00866       peer = socket(AF_INET6, SOCK_DGRAM, 0);
00867       if(CURL_SOCKET_BAD == peer) {
00868         logmsg("socket");
00869         result = 2;
00870         break;
00871       }
00872       if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) {
00873         logmsg("connect: fail");
00874         result = 1;
00875         break;
00876       }
00877     }
00878 #endif
00879 
00880     maxtimeout = 5*TIMEOUT;
00881 
00882     tp = &buf.hdr;
00883     tp->th_opcode = ntohs(tp->th_opcode);
00884     if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
00885       memset(&test, 0, sizeof(test));
00886       if(do_tftp(&test, tp, n) < 0)
00887         break;
00888       free(test.buffer);
00889     }
00890     sclose(peer);
00891     peer = CURL_SOCKET_BAD;
00892 
00893     if(test.ofile > 0) {
00894       close(test.ofile);
00895       test.ofile = 0;
00896     }
00897 
00898     if(got_exit_signal)
00899       break;
00900 
00901     if(serverlogslocked) {
00902       serverlogslocked = 0;
00903       clear_advisor_read_lock(SERVERLOGS_LOCK);
00904     }
00905 
00906     logmsg("end of one transfer");
00907 
00908   }
00909 
00910 tftpd_cleanup:
00911 
00912   if(test.ofile > 0)
00913     close(test.ofile);
00914 
00915   if((peer != sock) && (peer != CURL_SOCKET_BAD))
00916     sclose(peer);
00917 
00918   if(sock != CURL_SOCKET_BAD)
00919     sclose(sock);
00920 
00921   if(got_exit_signal)
00922     logmsg("signalled to die");
00923 
00924   if(wrotepidfile)
00925     unlink(pidname);
00926 
00927   if(serverlogslocked) {
00928     serverlogslocked = 0;
00929     clear_advisor_read_lock(SERVERLOGS_LOCK);
00930   }
00931 
00932   restore_signal_handlers();
00933 
00934   if(got_exit_signal) {
00935     logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
00936            ipv_inuse, (int)port, pid, exit_signal);
00937     /*
00938      * To properly set the return status of the process we
00939      * must raise the same signal SIGINT or SIGTERM that we
00940      * caught and let the old handler take care of it.
00941      */
00942     raise(exit_signal);
00943   }
00944 
00945   logmsg("========> tftpd quits");
00946   return result;
00947 }
00948 
00949 /*
00950  * Handle initial connection protocol.
00951  */
00952 static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
00953 {
00954   char *cp;
00955   int first = 1, ecode;
00956   struct formats *pf;
00957   char *filename, *mode = NULL;
00958   int error;
00959   FILE *server;
00960 #ifdef USE_WINSOCK
00961   DWORD recvtimeout, recvtimeoutbak;
00962 #endif
00963   char *option = (char *)"mode"; /* mode is implicit */
00964   int toggle = 1;
00965 
00966   /* Open request dump file. */
00967   server = fopen(REQUEST_DUMP, "ab");
00968   if(!server) {
00969     error = errno;
00970     logmsg("fopen() failed with error: %d %s", error, strerror(error));
00971     logmsg("Error opening file: %s", REQUEST_DUMP);
00972     return -1;
00973   }
00974 
00975   /* store input protocol */
00976   fprintf(server, "opcode: %x\n", tp->th_opcode);
00977 
00978   cp = (char *)&tp->th_stuff;
00979   filename = cp;
00980   do {
00981     bool endofit = true;
00982     while(cp < &buf.storage[size]) {
00983       if(*cp == '\0') {
00984         endofit = false;
00985         break;
00986       }
00987       cp++;
00988     }
00989     if(endofit)
00990       /* no more options */
00991       break;
00992 
00993     /* before increasing pointer, make sure it is still within the legal
00994        space */
00995     if((cp+1) < &buf.storage[size]) {
00996       ++cp;
00997       if(first) {
00998         /* store the mode since we need it later */
00999         mode = cp;
01000         first = 0;
01001       }
01002       if(toggle)
01003         /* name/value pair: */
01004         fprintf(server, "%s: %s\n", option, cp);
01005       else {
01006         /* store the name pointer */
01007         option = cp;
01008       }
01009       toggle ^= 1;
01010     }
01011     else
01012       /* No more options */
01013       break;
01014   } while(1);
01015 
01016   if(*cp) {
01017     nak(EBADOP);
01018     fclose(server);
01019     return 3;
01020   }
01021 
01022   /* store input protocol */
01023   fprintf(server, "filename: %s\n", filename);
01024 
01025   for(cp = mode; cp && *cp; cp++)
01026     if(ISUPPER(*cp))
01027       *cp = (char)tolower((int)*cp);
01028 
01029   /* store input protocol */
01030   fclose(server);
01031 
01032   for(pf = formata; pf->f_mode; pf++)
01033     if(strcmp(pf->f_mode, mode) == 0)
01034       break;
01035   if(!pf->f_mode) {
01036     nak(EBADOP);
01037     return 2;
01038   }
01039   ecode = validate_access(test, filename, tp->th_opcode);
01040   if(ecode) {
01041     nak(ecode);
01042     return 1;
01043   }
01044 
01045 #ifdef USE_WINSOCK
01046   recvtimeout = sizeof(recvtimeoutbak);
01047   getsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
01048              (char *)&recvtimeoutbak, (int *)&recvtimeout);
01049   recvtimeout = TIMEOUT*1000;
01050   setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
01051              (const char *)&recvtimeout, sizeof(recvtimeout));
01052 #endif
01053 
01054   if(tp->th_opcode == opcode_WRQ)
01055     recvtftp(test, pf);
01056   else
01057     sendtftp(test, pf);
01058 
01059 #ifdef USE_WINSOCK
01060   recvtimeout = recvtimeoutbak;
01061   setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
01062              (const char *)&recvtimeout, sizeof(recvtimeout));
01063 #endif
01064 
01065   return 0;
01066 }
01067 
01068 /* Based on the testno, parse the correct server commands. */
01069 static int parse_servercmd(struct testcase *req)
01070 {
01071   FILE *stream;
01072   char *filename;
01073   int error;
01074 
01075   filename = test2file(req->testno);
01076 
01077   stream=fopen(filename, "rb");
01078   if(!stream) {
01079     error = errno;
01080     logmsg("fopen() failed with error: %d %s", error, strerror(error));
01081     logmsg("  [1] Error opening file: %s", filename);
01082     logmsg("  Couldn't open test file %ld", req->testno);
01083     return 1; /* done */
01084   }
01085   else {
01086     char *orgcmd = NULL;
01087     char *cmd = NULL;
01088     size_t cmdsize = 0;
01089     int num=0;
01090 
01091     /* get the custom server control "commands" */
01092     error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
01093     fclose(stream);
01094     if(error) {
01095       logmsg("getpart() failed with error: %d", error);
01096       return 1; /* done */
01097     }
01098 
01099     cmd = orgcmd;
01100     while(cmd && cmdsize) {
01101       char *check;
01102       if(1 == sscanf(cmd, "writedelay: %d", &num)) {
01103         logmsg("instructed to delay %d secs between packets", num);
01104         req->writedelay = num;
01105       }
01106       else {
01107         logmsg("Unknown <servercmd> instruction found: %s", cmd);
01108       }
01109       /* try to deal with CRLF or just LF */
01110       check = strchr(cmd, '\r');
01111       if(!check)
01112         check = strchr(cmd, '\n');
01113 
01114       if(check) {
01115         /* get to the letter following the newline */
01116         while((*check == '\r') || (*check == '\n'))
01117           check++;
01118 
01119         if(!*check)
01120           /* if we reached a zero, get out */
01121           break;
01122         cmd = check;
01123       }
01124       else
01125         break;
01126     }
01127     free(orgcmd);
01128   }
01129 
01130   return 0; /* OK! */
01131 }
01132 
01133 
01134 /*
01135  * Validate file access.
01136  */
01137 static int validate_access(struct testcase *test,
01138                            const char *filename, int mode)
01139 {
01140   char *ptr;
01141   long testno, partno;
01142   int error;
01143   char partbuf[80]="data";
01144 
01145   logmsg("trying to get file: %s mode %x", filename, mode);
01146 
01147   if(!strncmp("verifiedserver", filename, 14)) {
01148     char weare[128];
01149     size_t count = snprintf(weare, sizeof(weare),
01150                             "WE ROOLZ: %ld\r\n", (long)getpid());
01151 
01152     logmsg("Are-we-friendly question received");
01153     test->buffer = strdup(weare);
01154     test->rptr = test->buffer; /* set read pointer */
01155     test->bufsize = count;    /* set total count */
01156     test->rcount = count;     /* set data left to read */
01157     return 0; /* fine */
01158   }
01159 
01160   /* find the last slash */
01161   ptr = strrchr(filename, '/');
01162 
01163   if(ptr) {
01164     char *file;
01165 
01166     ptr++; /* skip the slash */
01167 
01168     /* skip all non-numericals following the slash */
01169     while(*ptr && !ISDIGIT(*ptr))
01170       ptr++;
01171 
01172     /* get the number */
01173     testno = strtol(ptr, &ptr, 10);
01174 
01175     if(testno > 10000) {
01176       partno = testno % 10000;
01177       testno /= 10000;
01178     }
01179     else
01180       partno = 0;
01181 
01182 
01183     logmsg("requested test number %ld part %ld", testno, partno);
01184 
01185     test->testno = testno;
01186 
01187     (void)parse_servercmd(test);
01188 
01189     file = test2file(testno);
01190 
01191     if(0 != partno)
01192       snprintf(partbuf, sizeof(partbuf), "data%ld", partno);
01193 
01194     if(file) {
01195       FILE *stream=fopen(file, "rb");
01196       if(!stream) {
01197         error = errno;
01198         logmsg("fopen() failed with error: %d %s", error, strerror(error));
01199         logmsg("Error opening file: %s", file);
01200         logmsg("Couldn't open test file: %s", file);
01201         return EACCESS;
01202       }
01203       else {
01204         size_t count;
01205         error = getpart(&test->buffer, &count, "reply", partbuf, stream);
01206         fclose(stream);
01207         if(error) {
01208           logmsg("getpart() failed with error: %d", error);
01209           return EACCESS;
01210         }
01211         if(test->buffer) {
01212           test->rptr = test->buffer; /* set read pointer */
01213           test->bufsize = count;    /* set total count */
01214           test->rcount = count;     /* set data left to read */
01215         }
01216         else
01217           return EACCESS;
01218       }
01219 
01220     }
01221     else
01222       return EACCESS;
01223   }
01224   else {
01225     logmsg("no slash found in path");
01226     return EACCESS; /* failure */
01227   }
01228 
01229   logmsg("file opened and all is good");
01230   return 0;
01231 }
01232 
01233 /*
01234  * Send the requested file.
01235  */
01236 static void sendtftp(struct testcase *test, struct formats *pf)
01237 {
01238   int size;
01239   ssize_t n;
01240   /* These are volatile to live through a siglongjmp */
01241   volatile unsigned short sendblock; /* block count */
01242   struct tftphdr * volatile sdp = r_init(); /* data buffer */
01243   struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */
01244 
01245   sendblock = 1;
01246 #if defined(HAVE_ALARM) && defined(SIGALRM)
01247   mysignal(SIGALRM, timer);
01248 #endif
01249   do {
01250     size = readit(test, (struct tftphdr **)&sdp, pf->f_convert);
01251     if(size < 0) {
01252       nak(errno + 100);
01253       return;
01254     }
01255     sdp->th_opcode = htons((unsigned short)opcode_DATA);
01256     sdp->th_block = htons(sendblock);
01257     timeout = 0;
01258 #ifdef HAVE_SIGSETJMP
01259     (void) sigsetjmp(timeoutbuf, 1);
01260 #endif
01261     if(test->writedelay) {
01262       logmsg("Pausing %d seconds before %d bytes", test->writedelay,
01263              size);
01264       wait_ms(1000*test->writedelay);
01265     }
01266 
01267     send_data:
01268     if(swrite(peer, sdp, size + 4) != size + 4) {
01269       logmsg("write");
01270       return;
01271     }
01272     read_ahead(test, pf->f_convert);
01273     for(;;) {
01274 #ifdef HAVE_ALARM
01275       alarm(rexmtval);        /* read the ack */
01276 #endif
01277       n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage));
01278 #ifdef HAVE_ALARM
01279       alarm(0);
01280 #endif
01281       if(got_exit_signal)
01282         return;
01283       if(n < 0) {
01284         logmsg("read: fail");
01285         return;
01286       }
01287       sap->th_opcode = ntohs((unsigned short)sap->th_opcode);
01288       sap->th_block = ntohs(sap->th_block);
01289 
01290       if(sap->th_opcode == opcode_ERROR) {
01291         logmsg("got ERROR");
01292         return;
01293       }
01294 
01295       if(sap->th_opcode == opcode_ACK) {
01296         if(sap->th_block == sendblock) {
01297           break;
01298         }
01299         /* Re-synchronize with the other side */
01300         (void) synchnet(peer);
01301         if(sap->th_block == (sendblock-1)) {
01302           goto send_data;
01303         }
01304       }
01305 
01306     }
01307     sendblock++;
01308   } while(size == SEGSIZE);
01309 }
01310 
01311 /*
01312  * Receive a file.
01313  */
01314 static void recvtftp(struct testcase *test, struct formats *pf)
01315 {
01316   ssize_t n, size;
01317   /* These are volatile to live through a siglongjmp */
01318   volatile unsigned short recvblock; /* block count */
01319   struct tftphdr * volatile rdp;     /* data buffer */
01320   struct tftphdr *rap;      /* ack buffer */
01321 
01322   recvblock = 0;
01323   rdp = w_init();
01324 #if defined(HAVE_ALARM) && defined(SIGALRM)
01325   mysignal(SIGALRM, timer);
01326 #endif
01327   rap = &ackbuf.hdr;
01328   do {
01329     timeout = 0;
01330     rap->th_opcode = htons((unsigned short)opcode_ACK);
01331     rap->th_block = htons(recvblock);
01332     recvblock++;
01333 #ifdef HAVE_SIGSETJMP
01334     (void) sigsetjmp(timeoutbuf, 1);
01335 #endif
01336 send_ack:
01337     if(swrite(peer, &ackbuf.storage[0], 4) != 4) {
01338       logmsg("write: fail\n");
01339       goto abort;
01340     }
01341     write_behind(test, pf->f_convert);
01342     for(;;) {
01343 #ifdef HAVE_ALARM
01344       alarm(rexmtval);
01345 #endif
01346       n = sread(peer, rdp, PKTSIZE);
01347 #ifdef HAVE_ALARM
01348       alarm(0);
01349 #endif
01350       if(got_exit_signal)
01351         goto abort;
01352       if(n < 0) {                       /* really? */
01353         logmsg("read: fail\n");
01354         goto abort;
01355       }
01356       rdp->th_opcode = ntohs((unsigned short)rdp->th_opcode);
01357       rdp->th_block = ntohs(rdp->th_block);
01358       if(rdp->th_opcode == opcode_ERROR)
01359         goto abort;
01360       if(rdp->th_opcode == opcode_DATA) {
01361         if(rdp->th_block == recvblock) {
01362           break;                         /* normal */
01363         }
01364         /* Re-synchronize with the other side */
01365         (void) synchnet(peer);
01366         if(rdp->th_block == (recvblock-1))
01367           goto send_ack;                 /* rexmit */
01368       }
01369     }
01370 
01371     size = writeit(test, &rdp, (int)(n - 4), pf->f_convert);
01372     if(size != (n-4)) {                 /* ahem */
01373       if(size < 0)
01374         nak(errno + 100);
01375       else
01376         nak(ENOSPACE);
01377       goto abort;
01378     }
01379   } while(size == SEGSIZE);
01380   write_behind(test, pf->f_convert);
01381 
01382   rap->th_opcode = htons((unsigned short)opcode_ACK);  /* send the "final"
01383                                                           ack */
01384   rap->th_block = htons(recvblock);
01385   (void) swrite(peer, &ackbuf.storage[0], 4);
01386 #if defined(HAVE_ALARM) && defined(SIGALRM)
01387   mysignal(SIGALRM, justtimeout);        /* just abort read on timeout */
01388   alarm(rexmtval);
01389 #endif
01390   /* normally times out and quits */
01391   n = sread(peer, &buf.storage[0], sizeof(buf.storage));
01392 #ifdef HAVE_ALARM
01393   alarm(0);
01394 #endif
01395   if(got_exit_signal)
01396     goto abort;
01397   if(n >= 4 &&                               /* if read some data */
01398      rdp->th_opcode == opcode_DATA &&        /* and got a data block */
01399      recvblock == rdp->th_block) {           /* then my last ack was lost */
01400     (void) swrite(peer, &ackbuf.storage[0], 4);  /* resend final ack */
01401   }
01402 abort:
01403   return;
01404 }
01405 
01406 /*
01407  * Send a nak packet (error message).  Error code passed in is one of the
01408  * standard TFTP codes, or a Unix errno offset by 100.
01409  */
01410 static void nak(int error)
01411 {
01412   struct tftphdr *tp;
01413   int length;
01414   struct errmsg *pe;
01415 
01416   tp = &buf.hdr;
01417   tp->th_opcode = htons((unsigned short)opcode_ERROR);
01418   tp->th_code = htons((unsigned short)error);
01419   for(pe = errmsgs; pe->e_code >= 0; pe++)
01420     if(pe->e_code == error)
01421       break;
01422   if(pe->e_code < 0) {
01423     pe->e_msg = strerror(error - 100);
01424     tp->th_code = EUNDEF;   /* set 'undef' errorcode */
01425   }
01426   length = (int)strlen(pe->e_msg);
01427 
01428   /* we use memcpy() instead of strcpy() in order to avoid buffer overflow
01429    * report from glibc with FORTIFY_SOURCE */
01430   memcpy(tp->th_msg, pe->e_msg, length + 1);
01431   length += 5;
01432   if(swrite(peer, &buf.storage[0], length) != length)
01433     logmsg("nak: fail\n");
01434 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06