diff options
Diffstat (limited to 'networking/tftp.c')
-rw-r--r-- | networking/tftp.c | 87 |
1 files changed, 48 insertions, 39 deletions
diff --git a/networking/tftp.c b/networking/tftp.c index 04bfe844f..60fdff232 100644 --- a/networking/tftp.c +++ b/networking/tftp.c | |||
@@ -319,7 +319,7 @@ static int tftp_protocol( | |||
319 | uint16_t opcode; | 319 | uint16_t opcode; |
320 | uint16_t block_nr; | 320 | uint16_t block_nr; |
321 | uint16_t recv_blk; | 321 | uint16_t recv_blk; |
322 | int open_mode, local_fd; | 322 | int local_fd = -1; |
323 | int retries, waittime_ms; | 323 | int retries, waittime_ms; |
324 | int io_bufsize = blksize + 4; | 324 | int io_bufsize = blksize + 4; |
325 | char *cp; | 325 | char *cp; |
@@ -354,19 +354,6 @@ static int tftp_protocol( | |||
354 | } | 354 | } |
355 | } | 355 | } |
356 | 356 | ||
357 | /* Prepare open mode */ | ||
358 | if (CMD_PUT(option_mask32)) { | ||
359 | open_mode = O_RDONLY; | ||
360 | } else { | ||
361 | open_mode = O_WRONLY | O_TRUNC | O_CREAT; | ||
362 | #if ENABLE_TFTPD | ||
363 | if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) { | ||
364 | /* tftpd without -c */ | ||
365 | open_mode = O_WRONLY | O_TRUNC; | ||
366 | } | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | /* Examples of network traffic. | 357 | /* Examples of network traffic. |
371 | * Note two cases when ACKs with block# of 0 are sent. | 358 | * Note two cases when ACKs with block# of 0 are sent. |
372 | * | 359 | * |
@@ -400,12 +387,29 @@ static int tftp_protocol( | |||
400 | 387 | ||
401 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ | 388 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ |
402 | /* Open file (must be after changing user) */ | 389 | /* Open file (must be after changing user) */ |
390 | int open_mode = O_RDONLY; | ||
391 | if (CMD_GET(option_mask32)) { | ||
392 | open_mode = O_WRONLY | O_TRUNC | O_CREAT; | ||
393 | if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) { | ||
394 | /* tftpd without -c */ | ||
395 | open_mode = O_WRONLY | O_TRUNC; | ||
396 | } | ||
397 | } | ||
403 | local_fd = open(local_file, open_mode, 0666); | 398 | local_fd = open(local_file, open_mode, 0666); |
404 | if (local_fd < 0) { | 399 | if (local_fd < 0) { |
400 | /* sanitize name, it came from untrusted remote side */ | ||
401 | unsigned char *p = (void *) local_file; | ||
402 | while (*p) { | ||
403 | if (*p < ' ') | ||
404 | *p = '?'; | ||
405 | p++; | ||
406 | } | ||
407 | bb_perror_msg("can't open '%s'", local_file); | ||
405 | G_error_pkt_reason = ERR_NOFILE; | 408 | G_error_pkt_reason = ERR_NOFILE; |
406 | strcpy(G_error_pkt_str, "can't open file"); | 409 | strcpy(G_error_pkt_str, "can't open file"); |
407 | goto send_err_pkt; | 410 | goto send_err_pkt_nomsg; |
408 | } | 411 | } |
412 | |||
409 | /* gcc 4.3.1 would NOT optimize it out as it should! */ | 413 | /* gcc 4.3.1 would NOT optimize it out as it should! */ |
410 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 414 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
411 | if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) { | 415 | if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) { |
@@ -424,10 +428,11 @@ static int tftp_protocol( | |||
424 | block_nr = 0; | 428 | block_nr = 0; |
425 | } | 429 | } |
426 | } else { /* tftp */ | 430 | } else { /* tftp */ |
427 | /* Open file (must be after changing user) */ | 431 | if (CMD_PUT(option_mask32)) { |
428 | local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO; | 432 | local_fd = STDIN_FILENO; |
429 | if (NOT_LONE_DASH(local_file)) | 433 | if (local_file) |
430 | local_fd = xopen(local_file, open_mode); | 434 | local_fd = xopen(local_file, O_RDONLY); |
435 | } | ||
431 | /* Removing #if, or using if() statement instead of #if may lead to | 436 | /* Removing #if, or using if() statement instead of #if may lead to |
432 | * "warning: null argument where non-null required": */ | 437 | * "warning: null argument where non-null required": */ |
433 | #if ENABLE_TFTP | 438 | #if ENABLE_TFTP |
@@ -451,16 +456,14 @@ static int tftp_protocol( | |||
451 | } | 456 | } |
452 | /* add filename and mode */ | 457 | /* add filename and mode */ |
453 | /* fill in packet if the filename fits into xbuf */ | 458 | /* fill in packet if the filename fits into xbuf */ |
454 | len = strlen(remote_file) + 1; | 459 | len = strlen(remote_file); |
455 | if (2 + len + sizeof("octet") >= io_bufsize) { | 460 | if (len + 3 + sizeof("octet") >= io_bufsize) { |
456 | bb_simple_error_msg("remote filename is too long"); | 461 | bb_simple_error_msg("remote filename is too long"); |
457 | goto ret; | 462 | goto ret; |
458 | } | 463 | } |
459 | strcpy(cp, remote_file); | 464 | cp = stpcpy(cp, remote_file) + 1; |
460 | cp += len; | ||
461 | /* add "mode" part of the packet */ | 465 | /* add "mode" part of the packet */ |
462 | strcpy(cp, "octet"); | 466 | cp = stpcpy(cp, "octet") + 1; |
463 | cp += sizeof("octet"); | ||
464 | 467 | ||
465 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE | 468 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
466 | if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size) | 469 | if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size) |
@@ -485,7 +488,7 @@ static int tftp_protocol( | |||
485 | } | 488 | } |
486 | if (want_transfer_size) { | 489 | if (want_transfer_size) { |
487 | /* add "tsize", <nul>, size, <nul> (see RFC2349) */ | 490 | /* add "tsize", <nul>, size, <nul> (see RFC2349) */ |
488 | /* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC) | 491 | /* if tftp and downloading, we send "0" (local_fd is not open yet) |
489 | * and this makes server to send "tsize" option with the size */ | 492 | * and this makes server to send "tsize" option with the size */ |
490 | /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */ | 493 | /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */ |
491 | /* if tftpd and downloading, we are answering to client's request */ | 494 | /* if tftpd and downloading, we are answering to client's request */ |
@@ -494,7 +497,8 @@ static int tftp_protocol( | |||
494 | strcpy(cp, "tsize"); | 497 | strcpy(cp, "tsize"); |
495 | cp += sizeof("tsize"); | 498 | cp += sizeof("tsize"); |
496 | st.st_size = 0; | 499 | st.st_size = 0; |
497 | fstat(local_fd, &st); | 500 | if (local_fd >= 0) |
501 | fstat(local_fd, &st); | ||
498 | cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1; | 502 | cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1; |
499 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR | 503 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
500 | /* Save for progress bar. If 0 (tftp downloading), | 504 | /* Save for progress bar. If 0 (tftp downloading), |
@@ -684,7 +688,13 @@ static int tftp_protocol( | |||
684 | 688 | ||
685 | if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) { | 689 | if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) { |
686 | if (recv_blk == block_nr) { | 690 | if (recv_blk == block_nr) { |
687 | int sz = full_write(local_fd, &rbuf[4], len - 4); | 691 | int sz; |
692 | if (local_fd == -1) { | ||
693 | local_fd = STDOUT_FILENO; | ||
694 | if (local_file) | ||
695 | local_fd = xopen(local_file, O_WRONLY | O_TRUNC | O_CREAT); | ||
696 | } | ||
697 | sz = full_write(local_fd, &rbuf[4], len - 4); | ||
688 | if (sz != len - 4) { | 698 | if (sz != len - 4) { |
689 | strcpy(G_error_pkt_str, bb_msg_write_error); | 699 | strcpy(G_error_pkt_str, bb_msg_write_error); |
690 | G_error_pkt_reason = ERR_WRITE; | 700 | G_error_pkt_reason = ERR_WRITE; |
@@ -721,7 +731,7 @@ static int tftp_protocol( | |||
721 | * must never resend the current DATA packet on receipt | 731 | * must never resend the current DATA packet on receipt |
722 | * of a duplicate ACK". | 732 | * of a duplicate ACK". |
723 | * DATA pkts are resent ONLY on timeout. | 733 | * DATA pkts are resent ONLY on timeout. |
724 | * Thus "goto send_again" will ba a bad mistake above. | 734 | * Thus "goto send_again" will be a bad mistake above. |
725 | * See: | 735 | * See: |
726 | * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome | 736 | * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome |
727 | */ | 737 | */ |
@@ -733,22 +743,27 @@ static int tftp_protocol( | |||
733 | free(xbuf); | 743 | free(xbuf); |
734 | free(rbuf); | 744 | free(rbuf); |
735 | } | 745 | } |
736 | return finished == 0; /* returns 1 on failure */ | 746 | if (!finished) |
747 | goto err; | ||
748 | return EXIT_SUCCESS; | ||
737 | 749 | ||
738 | send_read_err_pkt: | 750 | send_read_err_pkt: |
739 | strcpy(G_error_pkt_str, bb_msg_read_error); | 751 | strcpy(G_error_pkt_str, bb_msg_read_error); |
740 | send_err_pkt: | 752 | send_err_pkt: |
741 | if (G_error_pkt_str[0]) | 753 | if (G_error_pkt_str[0]) |
742 | bb_simple_error_msg(G_error_pkt_str); | 754 | bb_simple_error_msg(G_error_pkt_str); |
755 | send_err_pkt_nomsg: | ||
743 | G.error_pkt[1] = TFTP_ERROR; | 756 | G.error_pkt[1] = TFTP_ERROR; |
744 | xsendto(socket_fd, G.error_pkt, 4 + 1 + strlen(G_error_pkt_str), | 757 | xsendto(socket_fd, G.error_pkt, 4 + 1 + strlen(G_error_pkt_str), |
745 | &peer_lsa->u.sa, peer_lsa->len); | 758 | &peer_lsa->u.sa, peer_lsa->len); |
759 | err: | ||
760 | if (local_fd >= 0 && CMD_GET(option_mask32) && local_file) | ||
761 | unlink(local_file); | ||
746 | return EXIT_FAILURE; | 762 | return EXIT_FAILURE; |
747 | #undef remote_file | 763 | #undef remote_file |
748 | } | 764 | } |
749 | 765 | ||
750 | #if ENABLE_TFTP | 766 | #if ENABLE_TFTP |
751 | |||
752 | int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 767 | int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
753 | int tftp_main(int argc UNUSED_PARAM, char **argv) | 768 | int tftp_main(int argc UNUSED_PARAM, char **argv) |
754 | { | 769 | { |
@@ -761,7 +776,6 @@ int tftp_main(int argc UNUSED_PARAM, char **argv) | |||
761 | # endif | 776 | # endif |
762 | int result; | 777 | int result; |
763 | int port; | 778 | int port; |
764 | IF_GETPUT(int opt;) | ||
765 | 779 | ||
766 | INIT_G(); | 780 | INIT_G(); |
767 | 781 | ||
@@ -802,7 +816,7 @@ int tftp_main(int argc UNUSED_PARAM, char **argv) | |||
802 | } | 816 | } |
803 | } | 817 | } |
804 | 818 | ||
805 | IF_GETPUT(opt =) getopt32(argv, "^" | 819 | getopt32(argv, "^" |
806 | IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p") | 820 | IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p") |
807 | "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:") | 821 | "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:") |
808 | IF_FEATURE_TFTP_HPA_COMPAT("m:") | 822 | IF_FEATURE_TFTP_HPA_COMPAT("m:") |
@@ -853,18 +867,14 @@ int tftp_main(int argc UNUSED_PARAM, char **argv) | |||
853 | # endif | 867 | # endif |
854 | result = tftp_protocol( | 868 | result = tftp_protocol( |
855 | NULL /*our_lsa*/, peer_lsa, | 869 | NULL /*our_lsa*/, peer_lsa, |
856 | local_file, remote_file | 870 | (LONE_DASH(local_file) ? NULL : local_file), remote_file |
857 | IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */) | 871 | IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */) |
858 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) | 872 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) |
859 | ); | 873 | ); |
860 | tftp_progress_done(); | 874 | tftp_progress_done(); |
861 | 875 | ||
862 | if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) { | ||
863 | unlink(local_file); | ||
864 | } | ||
865 | return result; | 876 | return result; |
866 | } | 877 | } |
867 | |||
868 | #endif /* ENABLE_TFTP */ | 878 | #endif /* ENABLE_TFTP */ |
869 | 879 | ||
870 | #if ENABLE_TFTPD | 880 | #if ENABLE_TFTPD |
@@ -1001,7 +1011,6 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1001 | strcpy(G_error_pkt_str, error_msg); | 1011 | strcpy(G_error_pkt_str, error_msg); |
1002 | goto do_proto; | 1012 | goto do_proto; |
1003 | } | 1013 | } |
1004 | |||
1005 | #endif /* ENABLE_TFTPD */ | 1014 | #endif /* ENABLE_TFTPD */ |
1006 | 1015 | ||
1007 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ | 1016 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ |