• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     test->ofile = open(outfile, O_CREAT|O_RDWR|CURL_O_BINARY, 0777);
458     if(test->ofile == -1) {
459       logmsg("Couldn't create and/or open file %s for upload!", outfile);
460       return -1; /* failure! */
461     }
462   }
463   else if(test->ofile <= 0) {
464     return -1; /* failure! */
465   }
466 
467   count = b->counter;             /* remember byte count */
468   b->counter = BF_FREE;           /* reset flag */
469   dp = &b->buf.hdr;
470   nextone = !nextone;             /* incr for next time */
471   writebuf = dp->th_data;
472 
473   if(count <= 0)
474     return -1;                    /* nak logic? */
475 
476   if(convert == 0)
477     return write(test->ofile, writebuf, count);
478 
479   p = writebuf;
480   ct = count;
481   while(ct--) {                   /* loop over the buffer */
482     c = *p++;                     /* pick up a character */
483     if(prevchar == '\r') {        /* if prev char was cr */
484       if(c == '\n')               /* if have cr,lf then just */
485         lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */
486       else
487         if(c == '\0')             /* if have cr,nul then */
488           goto skipit;            /* just skip over the putc */
489       /* else just fall through and allow it */
490     }
491     /* formerly
492        putc(c, file); */
493     if(1 != write(test->ofile, &c, 1))
494       break;
495 skipit:
496     prevchar = c;
497   }
498   return count;
499 }
500 
501 /* When an error has occurred, it is possible that the two sides are out of
502  * synch.  Ie: that what I think is the other side's response to packet N is
503  * really their response to packet N-1.
504  *
505  * So, to try to prevent that, we flush all the input queued up for us on the
506  * network connection on our host.
507  *
508  * We return the number of packets we flushed (mostly for reporting when trace
509  * is active).
510  */
511 
synchnet(curl_socket_t f)512 static int synchnet(curl_socket_t f /* socket to flush */)
513 {
514   int j = 0;
515   char rbuf[PKTSIZE];
516   srvr_sockaddr_union_t fromaddr;
517   curl_socklen_t fromaddrlen;
518 
519   for(;;) {
520 #if defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
521     long i;
522     (void) IoctlSocket(f, FIONBIO, &i);
523 #elif defined(HAVE_IOCTLSOCKET)
524     unsigned long i;
525     (void) ioctlsocket(f, FIONREAD, &i);
526 #else
527     int i;
528     (void) ioctl(f, FIONREAD, &i);
529 #endif
530     if(i) {
531       j++;
532 #ifdef USE_IPV6
533       if(!use_ipv6)
534 #endif
535         fromaddrlen = sizeof(fromaddr.sa4);
536 #ifdef USE_IPV6
537       else
538         fromaddrlen = sizeof(fromaddr.sa6);
539 #endif
540       (void) recvfrom(f, rbuf, sizeof(rbuf), 0,
541                       &fromaddr.sa, &fromaddrlen);
542     }
543     else
544       break;
545   }
546   return j;
547 }
548 
main(int argc,char ** argv)549 int main(int argc, char **argv)
550 {
551   srvr_sockaddr_union_t me;
552   struct tftphdr *tp;
553   ssize_t n = 0;
554   int arg = 1;
555   unsigned short port = DEFAULT_PORT;
556   curl_socket_t sock = CURL_SOCKET_BAD;
557   int flag;
558   int rc;
559   int error;
560   struct testcase test;
561   int result = 0;
562 
563   memset(&test, 0, sizeof(test));
564 
565   while(argc > arg) {
566     if(!strcmp("--version", argv[arg])) {
567       printf("tftpd IPv4%s\n",
568 #ifdef USE_IPV6
569              "/IPv6"
570 #else
571              ""
572 #endif
573              );
574       return 0;
575     }
576     else if(!strcmp("--pidfile", argv[arg])) {
577       arg++;
578       if(argc > arg)
579         pidname = argv[arg++];
580     }
581     else if(!strcmp("--portfile", argv[arg])) {
582       arg++;
583       if(argc > arg)
584         portname = argv[arg++];
585     }
586     else if(!strcmp("--logfile", argv[arg])) {
587       arg++;
588       if(argc > arg)
589         serverlogfile = argv[arg++];
590     }
591     else if(!strcmp("--logdir", argv[arg])) {
592       arg++;
593       if(argc > arg)
594         logdir = argv[arg++];
595     }
596     else if(!strcmp("--ipv4", argv[arg])) {
597 #ifdef USE_IPV6
598       ipv_inuse = "IPv4";
599       use_ipv6 = FALSE;
600 #endif
601       arg++;
602     }
603     else if(!strcmp("--ipv6", argv[arg])) {
604 #ifdef USE_IPV6
605       ipv_inuse = "IPv6";
606       use_ipv6 = TRUE;
607 #endif
608       arg++;
609     }
610     else if(!strcmp("--port", argv[arg])) {
611       arg++;
612       if(argc > arg) {
613         char *endptr;
614         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
615         port = curlx_ultous(ulnum);
616         arg++;
617       }
618     }
619     else if(!strcmp("--srcdir", argv[arg])) {
620       arg++;
621       if(argc > arg) {
622         path = argv[arg];
623         arg++;
624       }
625     }
626     else {
627       puts("Usage: tftpd [option]\n"
628            " --version\n"
629            " --logfile [file]\n"
630            " --logdir [directory]\n"
631            " --pidfile [file]\n"
632            " --portfile [file]\n"
633            " --ipv4\n"
634            " --ipv6\n"
635            " --port [port]\n"
636            " --srcdir [path]");
637       return 0;
638     }
639   }
640 
641   msnprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock",
642             logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
643 
644 #ifdef _WIN32
645   win32_init();
646   atexit(win32_cleanup);
647 #endif
648 
649   install_signal_handlers(true);
650 
651 #ifdef USE_IPV6
652   if(!use_ipv6)
653 #endif
654     sock = socket(AF_INET, SOCK_DGRAM, 0);
655 #ifdef USE_IPV6
656   else
657     sock = socket(AF_INET6, SOCK_DGRAM, 0);
658 #endif
659 
660   if(CURL_SOCKET_BAD == sock) {
661     error = SOCKERRNO;
662     logmsg("Error creating socket: (%d) %s", error, sstrerror(error));
663     result = 1;
664     goto tftpd_cleanup;
665   }
666 
667   flag = 1;
668   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
669             (void *)&flag, sizeof(flag))) {
670     error = SOCKERRNO;
671     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
672            error, sstrerror(error));
673     result = 1;
674     goto tftpd_cleanup;
675   }
676 
677 #ifdef USE_IPV6
678   if(!use_ipv6) {
679 #endif
680     memset(&me.sa4, 0, sizeof(me.sa4));
681     me.sa4.sin_family = AF_INET;
682     me.sa4.sin_addr.s_addr = INADDR_ANY;
683     me.sa4.sin_port = htons(port);
684     rc = bind(sock, &me.sa, sizeof(me.sa4));
685 #ifdef USE_IPV6
686   }
687   else {
688     memset(&me.sa6, 0, sizeof(me.sa6));
689     me.sa6.sin6_family = AF_INET6;
690     me.sa6.sin6_addr = in6addr_any;
691     me.sa6.sin6_port = htons(port);
692     rc = bind(sock, &me.sa, sizeof(me.sa6));
693   }
694 #endif /* USE_IPV6 */
695   if(0 != rc) {
696     error = SOCKERRNO;
697     logmsg("Error binding socket on port %hu: (%d) %s", port, error,
698            sstrerror(error));
699     result = 1;
700     goto tftpd_cleanup;
701   }
702 
703   if(!port) {
704     /* The system was supposed to choose a port number, figure out which
705        port we actually got and update the listener port value with it. */
706     curl_socklen_t la_size;
707     srvr_sockaddr_union_t localaddr;
708 #ifdef USE_IPV6
709     if(!use_ipv6)
710 #endif
711       la_size = sizeof(localaddr.sa4);
712 #ifdef USE_IPV6
713     else
714       la_size = sizeof(localaddr.sa6);
715 #endif
716     memset(&localaddr.sa, 0, (size_t)la_size);
717     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
718       error = SOCKERRNO;
719       logmsg("getsockname() failed with error: (%d) %s",
720              error, sstrerror(error));
721       sclose(sock);
722       goto tftpd_cleanup;
723     }
724     switch(localaddr.sa.sa_family) {
725     case AF_INET:
726       port = ntohs(localaddr.sa4.sin_port);
727       break;
728 #ifdef USE_IPV6
729     case AF_INET6:
730       port = ntohs(localaddr.sa6.sin6_port);
731       break;
732 #endif
733     default:
734       break;
735     }
736     if(!port) {
737       /* Real failure, listener port shall not be zero beyond this point. */
738       logmsg("Apparently getsockname() succeeded, with listener port zero.");
739       logmsg("A valid reason for this failure is a binary built without");
740       logmsg("proper network library linkage. This might not be the only");
741       logmsg("reason, but double check it before anything else.");
742       result = 2;
743       goto tftpd_cleanup;
744     }
745   }
746 
747   wrotepidfile = write_pidfile(pidname);
748   if(!wrotepidfile) {
749     result = 1;
750     goto tftpd_cleanup;
751   }
752 
753   if(portname) {
754     wroteportfile = write_portfile(portname, port);
755     if(!wroteportfile) {
756       result = 1;
757       goto tftpd_cleanup;
758     }
759   }
760 
761   logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
762 
763   for(;;) {
764     fromlen = sizeof(from);
765 #ifdef USE_IPV6
766     if(!use_ipv6)
767 #endif
768       fromlen = sizeof(from.sa4);
769 #ifdef USE_IPV6
770     else
771       fromlen = sizeof(from.sa6);
772 #endif
773     n = (ssize_t)recvfrom(sock, &buf.storage[0], sizeof(buf.storage), 0,
774                           &from.sa, &fromlen);
775     if(got_exit_signal)
776       break;
777     if(n < 0) {
778       logmsg("recvfrom");
779       result = 3;
780       break;
781     }
782 
783     set_advisor_read_lock(loglockfile);
784     serverlogslocked = 1;
785 
786 #ifdef USE_IPV6
787     if(!use_ipv6) {
788 #endif
789       from.sa4.sin_family = AF_INET;
790       peer = socket(AF_INET, SOCK_DGRAM, 0);
791       if(CURL_SOCKET_BAD == peer) {
792         logmsg("socket");
793         result = 2;
794         break;
795       }
796       if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) {
797         logmsg("connect: fail");
798         result = 1;
799         break;
800       }
801 #ifdef USE_IPV6
802     }
803     else {
804       from.sa6.sin6_family = AF_INET6;
805       peer = socket(AF_INET6, SOCK_DGRAM, 0);
806       if(CURL_SOCKET_BAD == peer) {
807         logmsg("socket");
808         result = 2;
809         break;
810       }
811       if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) {
812         logmsg("connect: fail");
813         result = 1;
814         break;
815       }
816     }
817 #endif
818 
819     maxtimeout = 5*TIMEOUT;
820 
821     tp = &buf.hdr;
822     tp->th_opcode = ntohs(tp->th_opcode);
823     if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
824       memset(&test, 0, sizeof(test));
825       if(do_tftp(&test, tp, n) < 0)
826         break;
827       free(test.buffer);
828     }
829     sclose(peer);
830     peer = CURL_SOCKET_BAD;
831 
832     if(got_exit_signal)
833       break;
834 
835     if(serverlogslocked) {
836       serverlogslocked = 0;
837       clear_advisor_read_lock(loglockfile);
838     }
839 
840     logmsg("end of one transfer");
841 
842   }
843 
844 tftpd_cleanup:
845 
846   if(test.ofile > 0)
847     close(test.ofile);
848 
849   if((peer != sock) && (peer != CURL_SOCKET_BAD))
850     sclose(peer);
851 
852   if(sock != CURL_SOCKET_BAD)
853     sclose(sock);
854 
855   if(got_exit_signal)
856     logmsg("signalled to die");
857 
858   if(wrotepidfile)
859     unlink(pidname);
860   if(wroteportfile)
861     unlink(portname);
862 
863   if(serverlogslocked) {
864     serverlogslocked = 0;
865     clear_advisor_read_lock(loglockfile);
866   }
867 
868   restore_signal_handlers(true);
869 
870   if(got_exit_signal) {
871     logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
872            ipv_inuse, (int)port, (long)Curl_getpid(), exit_signal);
873     /*
874      * To properly set the return status of the process we
875      * must raise the same signal SIGINT or SIGTERM that we
876      * caught and let the old handler take care of it.
877      */
878     raise(exit_signal);
879   }
880 
881   logmsg("========> tftpd quits");
882   return result;
883 }
884 
885 /*
886  * Handle initial connection protocol.
887  */
do_tftp(struct testcase * test,struct tftphdr * tp,ssize_t size)888 static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
889 {
890   char *cp;
891   int first = 1, ecode;
892   const struct formats *pf;
893   char *filename, *mode = NULL;
894 #ifdef USE_WINSOCK
895   DWORD recvtimeout, recvtimeoutbak;
896 #endif
897   const char *option = "mode"; /* mode is implicit */
898   int toggle = 1;
899   FILE *server;
900   char dumpfile[256];
901 
902   msnprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, REQUEST_DUMP);
903 
904   /* Open request dump file. */
905   server = fopen(dumpfile, "ab");
906   if(!server) {
907     int error = errno;
908     logmsg("fopen() failed with error: %d %s", error, strerror(error));
909     logmsg("Error opening file: %s", dumpfile);
910     return -1;
911   }
912 
913   /* store input protocol */
914   fprintf(server, "opcode = %x\n", tp->th_opcode);
915 
916   cp = (char *)&tp->th_stuff;
917   filename = cp;
918   do {
919     bool endofit = true;
920     while(cp < &buf.storage[size]) {
921       if(*cp == '\0') {
922         endofit = false;
923         break;
924       }
925       cp++;
926     }
927     if(endofit)
928       /* no more options */
929       break;
930 
931     /* before increasing pointer, make sure it is still within the legal
932        space */
933     if((cp + 1) < &buf.storage[size]) {
934       ++cp;
935       if(first) {
936         /* store the mode since we need it later */
937         mode = cp;
938         first = 0;
939       }
940       if(toggle)
941         /* name/value pair: */
942         fprintf(server, "%s = %s\n", option, cp);
943       else {
944         /* store the name pointer */
945         option = cp;
946       }
947       toggle ^= 1;
948     }
949     else
950       /* No more options */
951       break;
952   } while(1);
953 
954   if(*cp || !mode) {
955     nak(EBADOP);
956     fclose(server);
957     return 3;
958   }
959 
960   /* store input protocol */
961   fprintf(server, "filename = %s\n", filename);
962 
963   for(cp = mode; cp && *cp; cp++)
964     if(ISUPPER(*cp))
965       *cp = (char)tolower((int)*cp);
966 
967   /* store input protocol */
968   fclose(server);
969 
970   for(pf = formata; pf->f_mode; pf++)
971     if(strcmp(pf->f_mode, mode) == 0)
972       break;
973   if(!pf->f_mode) {
974     nak(EBADOP);
975     return 2;
976   }
977   ecode = validate_access(test, filename, tp->th_opcode);
978   if(ecode) {
979     nak(ecode);
980     return 1;
981   }
982 
983 #ifdef USE_WINSOCK
984   recvtimeout = sizeof(recvtimeoutbak);
985   getsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
986              (char *)&recvtimeoutbak, (int *)&recvtimeout);
987   recvtimeout = TIMEOUT*1000;
988   setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
989              (const char *)&recvtimeout, sizeof(recvtimeout));
990 #endif
991 
992   if(tp->th_opcode == opcode_WRQ)
993     recvtftp(test, pf);
994   else
995     sendtftp(test, pf);
996 
997 #ifdef USE_WINSOCK
998   recvtimeout = recvtimeoutbak;
999   setsockopt(peer, SOL_SOCKET, SO_RCVTIMEO,
1000              (const char *)&recvtimeout, sizeof(recvtimeout));
1001 #endif
1002 
1003   return 0;
1004 }
1005 
1006 /* Based on the testno, parse the correct server commands. */
parse_servercmd(struct testcase * req)1007 static int parse_servercmd(struct testcase *req)
1008 {
1009   FILE *stream;
1010   int error;
1011 
1012   stream = test2fopen(req->testno, logdir);
1013   if(!stream) {
1014     error = errno;
1015     logmsg("fopen() failed with error: %d %s", error, strerror(error));
1016     logmsg("  Couldn't open test file %ld", req->testno);
1017     return 1; /* done */
1018   }
1019   else {
1020     char *orgcmd = NULL;
1021     char *cmd = NULL;
1022     size_t cmdsize = 0;
1023     int num = 0;
1024 
1025     /* get the custom server control "commands" */
1026     error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
1027     fclose(stream);
1028     if(error) {
1029       logmsg("getpart() failed with error: %d", error);
1030       return 1; /* done */
1031     }
1032 
1033     cmd = orgcmd;
1034     while(cmd && cmdsize) {
1035       char *check;
1036       if(1 == sscanf(cmd, "writedelay: %d", &num)) {
1037         logmsg("instructed to delay %d secs between packets", num);
1038         req->writedelay = num;
1039       }
1040       else {
1041         logmsg("Unknown <servercmd> instruction found: %s", cmd);
1042       }
1043       /* try to deal with CRLF or just LF */
1044       check = strchr(cmd, '\r');
1045       if(!check)
1046         check = strchr(cmd, '\n');
1047 
1048       if(check) {
1049         /* get to the letter following the newline */
1050         while((*check == '\r') || (*check == '\n'))
1051           check++;
1052 
1053         if(!*check)
1054           /* if we reached a zero, get out */
1055           break;
1056         cmd = check;
1057       }
1058       else
1059         break;
1060     }
1061     free(orgcmd);
1062   }
1063 
1064   return 0; /* OK! */
1065 }
1066 
1067 
1068 /*
1069  * Validate file access.
1070  */
validate_access(struct testcase * test,const char * filename,unsigned short mode)1071 static int validate_access(struct testcase *test,
1072                            const char *filename, unsigned short mode)
1073 {
1074   char *ptr;
1075 
1076   logmsg("trying to get file: %s mode %x", filename, mode);
1077 
1078   if(!strncmp("verifiedserver", filename, 14)) {
1079     char weare[128];
1080     size_t count = msnprintf(weare, sizeof(weare), "WE ROOLZ: %"
1081                              CURL_FORMAT_CURL_OFF_T "\r\n", our_getpid());
1082 
1083     logmsg("Are-we-friendly question received");
1084     test->buffer = strdup(weare);
1085     test->rptr = test->buffer; /* set read pointer */
1086     test->bufsize = count;    /* set total count */
1087     test->rcount = count;     /* set data left to read */
1088     return 0; /* fine */
1089   }
1090 
1091   /* find the last slash */
1092   ptr = strrchr(filename, '/');
1093 
1094   if(ptr) {
1095     char partbuf[80]="data";
1096     long partno;
1097     long testno;
1098     FILE *stream;
1099 
1100     ptr++; /* skip the slash */
1101 
1102     /* skip all non-numericals following the slash */
1103     while(*ptr && !ISDIGIT(*ptr))
1104       ptr++;
1105 
1106     /* get the number */
1107     testno = strtol(ptr, &ptr, 10);
1108 
1109     if(testno > 10000) {
1110       partno = testno % 10000;
1111       testno /= 10000;
1112     }
1113     else
1114       partno = 0;
1115 
1116 
1117     logmsg("requested test number %ld part %ld", testno, partno);
1118 
1119     test->testno = testno;
1120 
1121     (void)parse_servercmd(test);
1122 
1123     stream = test2fopen(testno, logdir);
1124 
1125     if(0 != partno)
1126       msnprintf(partbuf, sizeof(partbuf), "data%ld", partno);
1127 
1128     if(!stream) {
1129       int error = errno;
1130       logmsg("fopen() failed with error: %d %s", error, strerror(error));
1131       logmsg("Couldn't open test file for test: %ld", testno);
1132       return EACCESS;
1133     }
1134     else {
1135       size_t count;
1136       int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
1137       fclose(stream);
1138       if(error) {
1139         logmsg("getpart() failed with error: %d", error);
1140         return EACCESS;
1141       }
1142       if(test->buffer) {
1143         test->rptr = test->buffer; /* set read pointer */
1144         test->bufsize = count;    /* set total count */
1145         test->rcount = count;     /* set data left to read */
1146       }
1147       else
1148         return EACCESS;
1149     }
1150   }
1151   else {
1152     logmsg("no slash found in path");
1153     return EACCESS; /* failure */
1154   }
1155 
1156   logmsg("file opened and all is good");
1157   return 0;
1158 }
1159 
1160 /*
1161  * Send the requested file.
1162  */
sendtftp(struct testcase * test,const struct formats * pf)1163 static void sendtftp(struct testcase *test, const struct formats *pf)
1164 {
1165   int size;
1166   ssize_t n;
1167   /* These are volatile to live through a siglongjmp */
1168   volatile unsigned short sendblock; /* block count */
1169   struct tftphdr * volatile sdp = r_init(); /* data buffer */
1170   struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */
1171 
1172   sendblock = 1;
1173 #if defined(HAVE_ALARM) && defined(SIGALRM)
1174   mysignal(SIGALRM, timer);
1175 #endif
1176   do {
1177     size = readit(test, (struct tftphdr **)&sdp, pf->f_convert);
1178     if(size < 0) {
1179       nak(errno + 100);
1180       return;
1181     }
1182     sdp->th_opcode = htons(opcode_DATA);
1183     sdp->th_block = htons(sendblock);
1184     timeout = 0;
1185 #ifdef HAVE_SIGSETJMP
1186     (void) sigsetjmp(timeoutbuf, 1);
1187 #endif
1188     if(test->writedelay) {
1189       logmsg("Pausing %d seconds before %d bytes", test->writedelay,
1190              size);
1191       wait_ms(1000*test->writedelay);
1192     }
1193 
1194 send_data:
1195     logmsg("write");
1196     if(swrite(peer, sdp, size + 4) != size + 4) {
1197       logmsg("write: fail");
1198       return;
1199     }
1200     read_ahead(test, pf->f_convert);
1201     for(;;) {
1202 #ifdef HAVE_ALARM
1203       alarm(rexmtval);        /* read the ack */
1204 #endif
1205       logmsg("read");
1206       n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage));
1207       logmsg("read: %zd", n);
1208 #ifdef HAVE_ALARM
1209       alarm(0);
1210 #endif
1211       if(got_exit_signal)
1212         return;
1213       if(n < 0) {
1214         logmsg("read: fail");
1215         return;
1216       }
1217       sap->th_opcode = ntohs(sap->th_opcode);
1218       sap->th_block = ntohs(sap->th_block);
1219 
1220       if(sap->th_opcode == opcode_ERROR) {
1221         logmsg("got ERROR");
1222         return;
1223       }
1224 
1225       if(sap->th_opcode == opcode_ACK) {
1226         if(sap->th_block == sendblock) {
1227           break;
1228         }
1229         /* Re-synchronize with the other side */
1230         (void) synchnet(peer);
1231         if(sap->th_block == (sendblock-1)) {
1232           goto send_data;
1233         }
1234       }
1235 
1236     }
1237     sendblock++;
1238   } while(size == SEGSIZE);
1239 }
1240 
1241 /*
1242  * Receive a file.
1243  */
recvtftp(struct testcase * test,const struct formats * pf)1244 static void recvtftp(struct testcase *test, const struct formats *pf)
1245 {
1246   ssize_t n, size;
1247   /* These are volatile to live through a siglongjmp */
1248   volatile unsigned short recvblock; /* block count */
1249   struct tftphdr * volatile rdp;     /* data buffer */
1250   struct tftphdr *rap;      /* ack buffer */
1251 
1252   recvblock = 0;
1253   rdp = w_init();
1254 #if defined(HAVE_ALARM) && defined(SIGALRM)
1255   mysignal(SIGALRM, timer);
1256 #endif
1257   rap = &ackbuf.hdr;
1258   do {
1259     timeout = 0;
1260     rap->th_opcode = htons(opcode_ACK);
1261     rap->th_block = htons(recvblock);
1262     recvblock++;
1263 #ifdef HAVE_SIGSETJMP
1264     (void) sigsetjmp(timeoutbuf, 1);
1265 #endif
1266 send_ack:
1267     logmsg("write");
1268     if(swrite(peer, &ackbuf.storage[0], 4) != 4) {
1269       logmsg("write: fail");
1270       goto abort;
1271     }
1272     write_behind(test, pf->f_convert);
1273     for(;;) {
1274 #ifdef HAVE_ALARM
1275       alarm(rexmtval);
1276 #endif
1277       logmsg("read");
1278       n = sread(peer, rdp, PKTSIZE);
1279       logmsg("read: %zd", n);
1280 #ifdef HAVE_ALARM
1281       alarm(0);
1282 #endif
1283       if(got_exit_signal)
1284         goto abort;
1285       if(n < 0) {                       /* really? */
1286         logmsg("read: fail");
1287         goto abort;
1288       }
1289       rdp->th_opcode = ntohs(rdp->th_opcode);
1290       rdp->th_block = ntohs(rdp->th_block);
1291       if(rdp->th_opcode == opcode_ERROR)
1292         goto abort;
1293       if(rdp->th_opcode == opcode_DATA) {
1294         if(rdp->th_block == recvblock) {
1295           break;                         /* normal */
1296         }
1297         /* Re-synchronize with the other side */
1298         (void) synchnet(peer);
1299         if(rdp->th_block == (recvblock-1))
1300           goto send_ack;                 /* rexmit */
1301       }
1302     }
1303 
1304     size = writeit(test, &rdp, (int)(n - 4), pf->f_convert);
1305     if(size != (n-4)) {                 /* ahem */
1306       if(size < 0)
1307         nak(errno + 100);
1308       else
1309         nak(ENOSPACE);
1310       goto abort;
1311     }
1312   } while(size == SEGSIZE);
1313   write_behind(test, pf->f_convert);
1314   /* close the output file as early as possible after upload completion */
1315   if(test->ofile > 0) {
1316     close(test->ofile);
1317     test->ofile = 0;
1318   }
1319 
1320   rap->th_opcode = htons(opcode_ACK);  /* send the "final" ack */
1321   rap->th_block = htons(recvblock);
1322   (void) swrite(peer, &ackbuf.storage[0], 4);
1323 #if defined(HAVE_ALARM) && defined(SIGALRM)
1324   mysignal(SIGALRM, justtimeout);        /* just abort read on timeout */
1325   alarm(rexmtval);
1326 #endif
1327   /* normally times out and quits */
1328   n = sread(peer, &buf.storage[0], sizeof(buf.storage));
1329 #ifdef HAVE_ALARM
1330   alarm(0);
1331 #endif
1332   if(got_exit_signal)
1333     goto abort;
1334   if(n >= 4 &&                               /* if read some data */
1335      rdp->th_opcode == opcode_DATA &&        /* and got a data block */
1336      recvblock == rdp->th_block) {           /* then my last ack was lost */
1337     (void) swrite(peer, &ackbuf.storage[0], 4);  /* resend final ack */
1338   }
1339 abort:
1340   /* make sure the output file is closed in case of abort */
1341   if(test->ofile > 0) {
1342     close(test->ofile);
1343     test->ofile = 0;
1344   }
1345   return;
1346 }
1347 
1348 /*
1349  * Send a nak packet (error message).  Error code passed in is one of the
1350  * standard TFTP codes, or a Unix errno offset by 100.
1351  */
nak(int error)1352 static void nak(int error)
1353 {
1354   struct tftphdr *tp;
1355   int length;
1356   struct errmsg *pe;
1357 
1358   tp = &buf.hdr;
1359   tp->th_opcode = htons(opcode_ERROR);
1360   tp->th_code = htons((unsigned short)error);
1361   for(pe = errmsgs; pe->e_code >= 0; pe++)
1362     if(pe->e_code == error)
1363       break;
1364   if(pe->e_code < 0) {
1365     pe->e_msg = strerror(error - 100);
1366     tp->th_code = EUNDEF;   /* set 'undef' errorcode */
1367   }
1368   length = (int)strlen(pe->e_msg);
1369 
1370   /* we use memcpy() instead of strcpy() in order to avoid buffer overflow
1371    * report from glibc with FORTIFY_SOURCE */
1372   memcpy(tp->th_msg, pe->e_msg, length + 1);
1373   length += 5;
1374   if(swrite(peer, &buf.storage[0], length) != length)
1375     logmsg("nak: fail\n");
1376 }
1377