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