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