| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: ftp.c,v 1.27 2009/02/22 19:11:48 joerg Exp $ */ | | 1 | /* $NetBSD: ftp.c,v 1.28 2009/08/06 14:02:38 tnn Exp $ */ |
2 | /*- | | 2 | /*- |
3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | | 3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav |
4 | * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org> | | 4 | * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer | | 11 | * notice, this list of conditions and the following disclaimer |
12 | * in this position and unchanged. | | 12 | * in this position and unchanged. |
13 | * 2. Redistributions in binary form must reproduce the above copyright | | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the | | 14 | * notice, this list of conditions and the following disclaimer in the |
| @@ -610,29 +610,32 @@ ftp_setup(conn_t *cconn, conn_t *dconn, | | | @@ -610,29 +610,32 @@ ftp_setup(conn_t *cconn, conn_t *dconn, |
610 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); | | 610 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); |
611 | if (f == NULL) | | 611 | if (f == NULL) |
612 | free(io); | | 612 | free(io); |
613 | return (f); | | 613 | return (f); |
614 | } | | 614 | } |
615 | | | 615 | |
616 | /* | | 616 | /* |
617 | * Transfer file | | 617 | * Transfer file |
618 | */ | | 618 | */ |
619 | static fetchIO * | | 619 | static fetchIO * |
620 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, | | 620 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, |
621 | int mode, off_t offset, const char *flags) | | 621 | int mode, off_t offset, const char *flags) |
622 | { | | 622 | { |
623 | struct sockaddr_storage sa; | | 623 | union anonymous { |
624 | struct sockaddr_in6 *sin6; | | 624 | struct sockaddr_storage ss; |
625 | struct sockaddr_in *sin4; | | 625 | struct sockaddr sa; |
| | | 626 | struct sockaddr_in6 sin6; |
| | | 627 | struct sockaddr_in sin4; |
| | | 628 | } u; |
626 | const char *bindaddr; | | 629 | const char *bindaddr; |
627 | const char *filename; | | 630 | const char *filename; |
628 | int filenamelen, type; | | 631 | int filenamelen, type; |
629 | int low, pasv, verbose; | | 632 | int low, pasv, verbose; |
630 | int e, sd = -1; | | 633 | int e, sd = -1; |
631 | socklen_t l; | | 634 | socklen_t l; |
632 | char *s; | | 635 | char *s; |
633 | fetchIO *df; | | 636 | fetchIO *df; |
634 | | | 637 | |
635 | /* check flags */ | | 638 | /* check flags */ |
636 | low = CHECK_FLAG('l'); | | 639 | low = CHECK_FLAG('l'); |
637 | pasv = !CHECK_FLAG('a'); | | 640 | pasv = !CHECK_FLAG('a'); |
638 | verbose = CHECK_FLAG('v'); | | 641 | verbose = CHECK_FLAG('v'); |
| @@ -640,50 +643,50 @@ ftp_transfer(conn_t *conn, const char *o | | | @@ -640,50 +643,50 @@ ftp_transfer(conn_t *conn, const char *o |
640 | /* passive mode */ | | 643 | /* passive mode */ |
641 | if (!pasv) | | 644 | if (!pasv) |
642 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && | | 645 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && |
643 | strncasecmp(s, "no", 2) != 0); | | 646 | strncasecmp(s, "no", 2) != 0); |
644 | | | 647 | |
645 | /* isolate filename */ | | 648 | /* isolate filename */ |
646 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); | | 649 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); |
647 | | | 650 | |
648 | /* set transfer mode and data type */ | | 651 | /* set transfer mode and data type */ |
649 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) | | 652 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) |
650 | goto ouch; | | 653 | goto ouch; |
651 | | | 654 | |
652 | /* find our own address, bind, and listen */ | | 655 | /* find our own address, bind, and listen */ |
653 | l = sizeof(sa); | | 656 | l = sizeof(u.ss); |
654 | if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) | | 657 | if (getsockname(conn->sd, &u.sa, &l) == -1) |
655 | goto sysouch; | | 658 | goto sysouch; |
656 | if (sa.ss_family == AF_INET6) | | 659 | if (u.ss.ss_family == AF_INET6) |
657 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); | | 660 | unmappedaddr(&u.sin6, &l); |
658 | | | 661 | |
659 | retry_mode: | | 662 | retry_mode: |
660 | | | 663 | |
661 | /* open data socket */ | | 664 | /* open data socket */ |
662 | if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { | | 665 | if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { |
663 | fetch_syserr(); | | 666 | fetch_syserr(); |
664 | return (NULL); | | 667 | return (NULL); |
665 | } | | 668 | } |
666 | | | 669 | |
667 | if (pasv) { | | 670 | if (pasv) { |
668 | unsigned char addr[64]; | | 671 | unsigned char addr[64]; |
669 | char *ln, *p; | | 672 | char *ln, *p; |
670 | unsigned int i; | | 673 | unsigned int i; |
671 | int port; | | 674 | int port; |
672 | | | 675 | |
673 | /* send PASV command */ | | 676 | /* send PASV command */ |
674 | if (verbose) | | 677 | if (verbose) |
675 | fetch_info("setting passive mode"); | | 678 | fetch_info("setting passive mode"); |
676 | switch (sa.ss_family) { | | 679 | switch (u.ss.ss_family) { |
677 | case AF_INET: | | 680 | case AF_INET: |
678 | if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) | | 681 | if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) |
679 | goto ouch; | | 682 | goto ouch; |
680 | break; | | 683 | break; |
681 | case AF_INET6: | | 684 | case AF_INET6: |
682 | if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { | | 685 | if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { |
683 | if (e == -1) | | 686 | if (e == -1) |
684 | goto ouch; | | 687 | goto ouch; |
685 | if ((e = ftp_cmd(conn, "LPSV")) != | | 688 | if ((e = ftp_cmd(conn, "LPSV")) != |
686 | FTP_LPASSIVE_MODE) | | 689 | FTP_LPASSIVE_MODE) |
687 | goto ouch; | | 690 | goto ouch; |
688 | } | | 691 | } |
689 | break; | | 692 | break; |
| @@ -736,151 +739,147 @@ retry_mode: | | | @@ -736,151 +739,147 @@ retry_mode: |
736 | /* Close socket and retry with passive mode. */ | | 739 | /* Close socket and retry with passive mode. */ |
737 | pasv = 0; | | 740 | pasv = 0; |
738 | close(sd); | | 741 | close(sd); |
739 | sd = -1; | | 742 | sd = -1; |
740 | goto retry_mode; | | 743 | goto retry_mode; |
741 | } | | 744 | } |
742 | | | 745 | |
743 | /* seek to required offset */ | | 746 | /* seek to required offset */ |
744 | if (offset) | | 747 | if (offset) |
745 | if (ftp_cmd(conn, "REST %lu", (unsigned long)offset) != FTP_FILE_OK) | | 748 | if (ftp_cmd(conn, "REST %lu", (unsigned long)offset) != FTP_FILE_OK) |
746 | goto sysouch; | | 749 | goto sysouch; |
747 | | | 750 | |
748 | /* construct sockaddr for data socket */ | | 751 | /* construct sockaddr for data socket */ |
749 | l = sizeof(sa); | | 752 | l = sizeof(u.ss); |
750 | if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) | | 753 | if (getpeername(conn->sd, &u.sa, &l) == -1) |
751 | goto sysouch; | | 754 | goto sysouch; |
752 | if (sa.ss_family == AF_INET6) | | 755 | if (u.ss.ss_family == AF_INET6) |
753 | unmappedaddr((struct sockaddr_in6 *)&sa, &l); | | 756 | unmappedaddr(&u.sin6, &l); |
754 | switch (sa.ss_family) { | | 757 | switch (u.ss.ss_family) { |
755 | case AF_INET6: | | 758 | case AF_INET6: |
756 | sin6 = (struct sockaddr_in6 *)&sa; | | | |
757 | if (e == FTP_EPASSIVE_MODE) | | 759 | if (e == FTP_EPASSIVE_MODE) |
758 | sin6->sin6_port = htons(port); | | 760 | u.sin6.sin6_port = htons(port); |
759 | else { | | 761 | else { |
760 | memcpy(&sin6->sin6_addr, addr + 2, 16); | | 762 | memcpy(&u.sin6.sin6_addr, addr + 2, 16); |
761 | memcpy(&sin6->sin6_port, addr + 19, 2); | | 763 | memcpy(&u.sin6.sin6_port, addr + 19, 2); |
762 | } | | 764 | } |
763 | break; | | 765 | break; |
764 | case AF_INET: | | 766 | case AF_INET: |
765 | sin4 = (struct sockaddr_in *)&sa; | | | |
766 | if (e == FTP_EPASSIVE_MODE) | | 767 | if (e == FTP_EPASSIVE_MODE) |
767 | sin4->sin_port = htons(port); | | 768 | u.sin4.sin_port = htons(port); |
768 | else { | | 769 | else { |
769 | memcpy(&sin4->sin_addr, addr, 4); | | 770 | memcpy(&u.sin4.sin_addr, addr, 4); |
770 | memcpy(&sin4->sin_port, addr + 4, 2); | | 771 | memcpy(&u.sin4.sin_port, addr + 4, 2); |
771 | } | | 772 | } |
772 | break; | | 773 | break; |
773 | default: | | 774 | default: |
774 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | | 775 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ |
775 | break; | | 776 | break; |
776 | } | | 777 | } |
777 | | | 778 | |
778 | /* connect to data port */ | | 779 | /* connect to data port */ |
779 | if (verbose) | | 780 | if (verbose) |
780 | fetch_info("opening data connection"); | | 781 | fetch_info("opening data connection"); |
781 | bindaddr = getenv("FETCH_BIND_ADDRESS"); | | 782 | bindaddr = getenv("FETCH_BIND_ADDRESS"); |
782 | if (bindaddr != NULL && *bindaddr != '\0' && | | 783 | if (bindaddr != NULL && *bindaddr != '\0' && |
783 | fetch_bind(sd, sa.ss_family, bindaddr) != 0) | | 784 | fetch_bind(sd, u.ss.ss_family, bindaddr) != 0) |
784 | goto sysouch; | | 785 | goto sysouch; |
785 | if (connect(sd, (struct sockaddr *)&sa, l) == -1) | | 786 | if (connect(sd, &u.sa, l) == -1) |
786 | goto sysouch; | | 787 | goto sysouch; |
787 | | | 788 | |
788 | /* make the server initiate the transfer */ | | 789 | /* make the server initiate the transfer */ |
789 | if (verbose) | | 790 | if (verbose) |
790 | fetch_info("initiating transfer"); | | 791 | fetch_info("initiating transfer"); |
791 | if (op_arg) | | 792 | if (op_arg) |
792 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); | | 793 | e = ftp_cmd(conn, "%s%s%s", oper, *op_arg ? " " : "", op_arg); |
793 | else | | 794 | else |
794 | e = ftp_cmd(conn, "%s %.*s", oper, | | 795 | e = ftp_cmd(conn, "%s %.*s", oper, |
795 | filenamelen, filename); | | 796 | filenamelen, filename); |
796 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | | 797 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) |
797 | goto ouch; | | 798 | goto ouch; |
798 | | | 799 | |
799 | } else { | | 800 | } else { |
800 | uint32_t a; | | 801 | uint32_t a; |
801 | uint16_t p; | | 802 | uint16_t p; |
802 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) | | 803 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) |
803 | int arg; | | 804 | int arg; |
804 | #endif | | 805 | #endif |
805 | int d; | | 806 | int d; |
806 | char *ap; | | 807 | char *ap; |
807 | char hname[INET6_ADDRSTRLEN]; | | 808 | char hname[INET6_ADDRSTRLEN]; |
808 | | | 809 | |
809 | switch (sa.ss_family) { | | 810 | switch (u.ss.ss_family) { |
810 | case AF_INET6: | | 811 | case AF_INET6: |
811 | ((struct sockaddr_in6 *)&sa)->sin6_port = 0; | | 812 | u.sin6.sin6_port = 0; |
812 | #ifdef IPV6_PORTRANGE | | 813 | #ifdef IPV6_PORTRANGE |
813 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; | | 814 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; |
814 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, | | 815 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, |
815 | (char *)&arg, sizeof(arg)) == -1) | | 816 | (char *)&arg, sizeof(arg)) == -1) |
816 | goto sysouch; | | 817 | goto sysouch; |
817 | #endif | | 818 | #endif |
818 | break; | | 819 | break; |
819 | case AF_INET: | | 820 | case AF_INET: |
820 | ((struct sockaddr_in *)&sa)->sin_port = 0; | | 821 | u.sin4.sin_port = 0; |
821 | #ifdef IP_PORTRANGE | | 822 | #ifdef IP_PORTRANGE |
822 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; | | 823 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; |
823 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, | | 824 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, |
824 | (char *)&arg, sizeof(arg)) == -1) | | 825 | (char *)&arg, sizeof(arg)) == -1) |
825 | goto sysouch; | | 826 | goto sysouch; |
826 | #endif | | 827 | #endif |
827 | break; | | 828 | break; |
828 | } | | 829 | } |
829 | if (verbose) | | 830 | if (verbose) |
830 | fetch_info("binding data socket"); | | 831 | fetch_info("binding data socket"); |
831 | if (bind(sd, (struct sockaddr *)&sa, l) == -1) | | 832 | if (bind(sd, &u.sa, l) == -1) |
832 | goto sysouch; | | 833 | goto sysouch; |
833 | if (listen(sd, 1) == -1) | | 834 | if (listen(sd, 1) == -1) |
834 | goto sysouch; | | 835 | goto sysouch; |
835 | | | 836 | |
836 | /* find what port we're on and tell the server */ | | 837 | /* find what port we're on and tell the server */ |
837 | if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) | | 838 | if (getsockname(sd, &u.sa, &l) == -1) |
838 | goto sysouch; | | 839 | goto sysouch; |
839 | switch (sa.ss_family) { | | 840 | switch (u.ss.ss_family) { |
840 | case AF_INET: | | 841 | case AF_INET: |
841 | sin4 = (struct sockaddr_in *)&sa; | | 842 | a = ntohl(u.sin4.sin_addr.s_addr); |
842 | a = ntohl(sin4->sin_addr.s_addr); | | 843 | p = ntohs(u.sin4.sin_port); |
843 | p = ntohs(sin4->sin_port); | | | |
844 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", | | 844 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", |
845 | (a >> 24) & 0xff, (a >> 16) & 0xff, | | 845 | (a >> 24) & 0xff, (a >> 16) & 0xff, |
846 | (a >> 8) & 0xff, a & 0xff, | | 846 | (a >> 8) & 0xff, a & 0xff, |
847 | (p >> 8) & 0xff, p & 0xff); | | 847 | (p >> 8) & 0xff, p & 0xff); |
848 | break; | | 848 | break; |
849 | case AF_INET6: | | 849 | case AF_INET6: |
850 | #define UC(b) (((int)b)&0xff) | | 850 | #define UC(b) (((int)b)&0xff) |
851 | e = -1; | | 851 | e = -1; |
852 | sin6 = (struct sockaddr_in6 *)&sa; | | 852 | u.sin6.sin6_scope_id = 0; |
853 | sin6->sin6_scope_id = 0; | | 853 | if (getnameinfo(&u.sa, l, |
854 | if (getnameinfo((struct sockaddr *)&sa, l, | | | |
855 | hname, sizeof(hname), | | 854 | hname, sizeof(hname), |
856 | NULL, 0, NI_NUMERICHOST) == 0) { | | 855 | NULL, 0, NI_NUMERICHOST) == 0) { |
857 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, | | 856 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, |
858 | htons(sin6->sin6_port)); | | 857 | htons(u.sin6.sin6_port)); |
859 | if (e == -1) | | 858 | if (e == -1) |
860 | goto ouch; | | 859 | goto ouch; |
861 | } | | 860 | } |
862 | if (e != FTP_OK) { | | 861 | if (e != FTP_OK) { |
863 | ap = (char *)&sin6->sin6_addr; | | 862 | ap = (char *)&u.sin6.sin6_addr; |
864 | e = ftp_cmd(conn, | | 863 | e = ftp_cmd(conn, |
865 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", | | 864 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", |
866 | 6, 16, | | 865 | 6, 16, |
867 | UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), | | 866 | UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), |
868 | UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), | | 867 | UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), |
869 | UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), | | 868 | UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), |
870 | UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), | | 869 | UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), |
871 | 2, | | 870 | 2, |
872 | (ntohs(sin6->sin6_port) >> 8) & 0xff, | | 871 | (ntohs(u.sin6.sin6_port) >> 8) & 0xff, |
873 | ntohs(sin6->sin6_port) & 0xff); | | 872 | ntohs(u.sin6.sin6_port) & 0xff); |
874 | } | | 873 | } |
875 | break; | | 874 | break; |
876 | default: | | 875 | default: |
877 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | | 876 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ |
878 | goto ouch; | | 877 | goto ouch; |
879 | } | | 878 | } |
880 | if (e != FTP_OK) | | 879 | if (e != FTP_OK) |
881 | goto ouch; | | 880 | goto ouch; |
882 | | | 881 | |
883 | /* seek to required offset */ | | 882 | /* seek to required offset */ |
884 | if (offset) | | 883 | if (offset) |
885 | if (ftp_cmd(conn, "REST %llu", (unsigned long long)offset) != FTP_FILE_OK) | | 884 | if (ftp_cmd(conn, "REST %llu", (unsigned long long)offset) != FTP_FILE_OK) |
886 | goto sysouch; | | 885 | goto sysouch; |