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 | |
108 | struct 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 | |
119 | struct formats { |
120 | const char *f_mode; |
121 | int f_convert; |
122 | }; |
123 | |
124 | struct errmsg { |
125 | int e_code; |
126 | const char *e_msg; |
127 | }; |
128 | |
129 | typedef 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 | |
139 | struct 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 | |
170 | static 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 | |
182 | static struct formats formata[] = { |
183 | { "netascii" , 1 }, |
184 | { "octet" , 0 }, |
185 | { NULL, 0 } |
186 | }; |
187 | |
188 | static struct bf bfs[2]; |
189 | |
190 | static int nextone; /* index of next buffer to use */ |
191 | static int current; /* index of buffer in use */ |
192 | |
193 | /* control flags for crlf conversions */ |
194 | static int newline = 0; /* fillbuf: in middle of newline expansion */ |
195 | static int prevchar = -1; /* putbuf: previous char (cr check) */ |
196 | |
197 | static tftphdr_storage_t buf; |
198 | static tftphdr_storage_t ackbuf; |
199 | |
200 | static srvr_sockaddr_union_t from; |
201 | static curl_socklen_t fromlen; |
202 | |
203 | static curl_socket_t peer = CURL_SOCKET_BAD; |
204 | |
205 | static unsigned int timeout; |
206 | static unsigned int maxtimeout = 5 * TIMEOUT; |
207 | |
208 | #ifdef ENABLE_IPV6 |
209 | static bool use_ipv6 = FALSE; |
210 | #endif |
211 | static const char *ipv_inuse = "IPv4" ; |
212 | |
213 | const char *serverlogfile = DEFAULT_LOGFILE; |
214 | static const char *pidname = ".tftpd.pid" ; |
215 | static const char *portname = NULL; /* none by default */ |
216 | static int serverlogslocked = 0; |
217 | static int wrotepidfile = 0; |
218 | static int wroteportfile = 0; |
219 | |
220 | #ifdef HAVE_SIGSETJMP |
221 | static sigjmp_buf timeoutbuf; |
222 | #endif |
223 | |
224 | #if defined(HAVE_ALARM) && defined(SIGALRM) |
225 | static const unsigned int rexmtval = TIMEOUT; |
226 | #endif |
227 | |
228 | /***************************************************************************** |
229 | * FUNCTION PROTOTYPES * |
230 | *****************************************************************************/ |
231 | |
232 | static struct tftphdr *rw_init(int); |
233 | |
234 | static struct tftphdr *w_init(void); |
235 | |
236 | static struct tftphdr *r_init(void); |
237 | |
238 | static void read_ahead(struct testcase *test, int convert); |
239 | |
240 | static ssize_t write_behind(struct testcase *test, int convert); |
241 | |
242 | static int synchnet(curl_socket_t); |
243 | |
244 | static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size); |
245 | |
246 | static int validate_access(struct testcase *test, const char *fname, int mode); |
247 | |
248 | static void sendtftp(struct testcase *test, struct formats *pf); |
249 | |
250 | static void recvtftp(struct testcase *test, struct formats *pf); |
251 | |
252 | static void nak(int error); |
253 | |
254 | #if defined(HAVE_ALARM) && defined(SIGALRM) |
255 | |
256 | static void mysignal(int sig, void (*handler)(int)); |
257 | |
258 | static void timer(int signum); |
259 | |
260 | static 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 | */ |
273 | static 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 | |
281 | static 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 | |
308 | static 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 | */ |
319 | static 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 | |
330 | static struct tftphdr *w_init(void) |
331 | { |
332 | return rw_init(0); /* write-behind */ |
333 | } |
334 | |
335 | static 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 | */ |
343 | static 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 | */ |
363 | static 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 | */ |
423 | static 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 | */ |
441 | static 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 | |
514 | static 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 | |
552 | int 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 | |
839 | tftpd_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 | */ |
883 | static 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. */ |
998 | static 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 | */ |
1062 | static 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 | */ |
1154 | static 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 | */ |
1235 | static 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 |
1257 | send_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 | } |
1331 | abort: |
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 | */ |
1344 | static 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 | |