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