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