wget: add EPSV support
No binary (build failed)
—
(log yok)
| @@ -787,6 +787,7 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags, | |
| socklen_t sa_size) FAST_FUNC; | |
| uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; | |
| + | int parse_pasv_epsv(char *buf) FAST_FUNC; |
| /* 0 if argv[0] is NULL: */ | |
| unsigned string_array_len(char **argv) FAST_FUNC; | |
| @@ -152,57 +152,16 @@ static void ftp_login(void) | |
| static int xconnect_ftpdata(void) | |
| { | |
| - | char *buf_ptr; |
| - | unsigned port_num; |
| + | int port_num; |
| - | /* |
| - | PASV command will not work for IPv6. RFC2428 describes |
| - | IPv6-capable "extended PASV" - EPSV. |
| - | |
| - | "EPSV [protocol]" asks server to bind to and listen on a data port |
| - | in specified protocol. Protocol is 1 for IPv4, 2 for IPv6. |
| - | If not specified, defaults to "same as used for control connection". |
| - | If server understood you, it should answer "229 <some text>(|||port|)" |
| - | where "|" are literal pipe chars and "port" is ASCII decimal port#. |
| - | |
| - | There is also an IPv6-capable replacement for PORT (EPRT), |
| - | but we don't need that. |
| - | |
| - | NB: PASV may still work for some servers even over IPv6. |
| - | For example, vsftp happily answers |
| - | "227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual. |
| - | */ |
| - | if (!ENABLE_FEATURE_IPV6 |
| - | || ftpcmd("EPSV", NULL) != 229 |
| - | ) { |
| - | /* maybe also go straight to PAST if lsa->u.sa.sa_family == AF_INET? */ |
| - | if (ftpcmd("PASV", NULL) != 227) { |
| - | ftp_die("PASV"); |
| - | } |
| - | |
| - | /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]" |
| - | * Server's IP is N1.N2.N3.N4 (we ignore it) |
| - | * Server's port for data connection is P1*256+P2 */ |
| - | buf_ptr = strrchr(buf, ')'); |
| - | if (buf_ptr) *buf_ptr = '\0'; |
| - | |
| - | buf_ptr = strrchr(buf, ','); |
| - | *buf_ptr = '\0'; |
| - | port_num = xatoul_range(buf_ptr + 1, 0, 255); |
| - | |
| - | buf_ptr = strrchr(buf, ','); |
| - | *buf_ptr = '\0'; |
| - | port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; |
| - | } else { |
| - | /* Response is "NNN garbage(|||P1|)" |
| - | * Server's port for data connection is P1 */ |
| - | buf_ptr = strrchr(buf, '|'); |
| - | if (buf_ptr) *buf_ptr = '\0'; |
| - | |
| - | buf_ptr = strrchr(buf, '|'); |
| - | *buf_ptr = '\0'; |
| - | port_num = xatoul_range(buf_ptr + 1, 0, 65535); |
| + | if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL) == 229) { |
| + | /* good */ |
| + | } else if (ftpcmd("PASV", NULL) != 227) { |
| + | ftp_die("PASV"); |
| } | |
| + | port_num = parse_pasv_epsv(buf); |
| + | if (port_num < 0) |
| + | ftp_die("PASV"); |
| set_nport(&lsa->u.sa, htons(port_num)); | |
| return xconnect_stream(lsa); | |
| @@ -0,0 +1,66 @@ | |
| + | /* |
| + | * Utility routines. |
| + | * |
| + | * Copyright (C) 2018 Denys Vlasenko |
| + | * |
| + | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
| + | */ |
| + | //kbuild:lib-$(CONFIG_FTPGET) += ftpgetput.o |
| + | //kbuild:lib-$(CONFIG_FTPPUT) += ftpgetput.o |
| + | //kbuild:lib-$(CONFIG_WGET) += parse_pasv_epsv.o |
| + | |
| + | #include "libbb.h" |
| + | |
| + | int FAST_FUNC parse_pasv_epsv(char *buf) |
| + | { |
| + | /* |
| + | * PASV command will not work for IPv6. RFC2428 describes |
| + | * IPv6-capable "extended PASV" - EPSV. |
| + | * |
| + | * "EPSV [protocol]" asks server to bind to and listen on a data port |
| + | * in specified protocol. Protocol is 1 for IPv4, 2 for IPv6. |
| + | * If not specified, defaults to "same as used for control connection". |
| + | * If server understood you, it should answer "229 <some text>(|||port|)" |
| + | * where "|" are literal pipe chars and "port" is ASCII decimal port#. |
| + | * |
| + | * There is also an IPv6-capable replacement for PORT (EPRT), |
| + | * but we don't need that. |
| + | * |
| + | * NB: PASV may still work for some servers even over IPv6. |
| + | * For example, vsftp happily answers |
| + | * "227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual. |
| + | */ |
| + | char *ptr; |
| + | int port; |
| + | |
| + | if (!ENABLE_FEATURE_IPV6 || buf[2] == '7' /* "227" */) { |
| + | /* Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]" |
| + | * Server's IP is N1.N2.N3.N4 (we ignore it) |
| + | * Server's port for data connection is P1*256+P2 */ |
| + | ptr = strrchr(buf, ')'); |
| + | if (ptr) *ptr = '\0'; |
| + | |
| + | ptr = strrchr(buf, ','); |
| + | if (!ptr) return -1; |
| + | *ptr = '\0'; |
| + | port = xatou_range(ptr + 1, 0, 255); |
| + | |
| + | ptr = strrchr(buf, ','); |
| + | if (!ptr) return -1; |
| + | *ptr = '\0'; |
| + | port += xatou_range(ptr + 1, 0, 255) * 256; |
| + | } else { |
| + | /* Response is "229 garbage(|||P1|)" |
| + | * Server's port for data connection is P1 */ |
| + | ptr = strrchr(buf, '|'); |
| + | if (!ptr) return -1; |
| + | *ptr = '\0'; |
| + | |
| + | ptr = strrchr(buf, '|'); |
| + | if (!ptr) return -1; |
| + | *ptr = '\0'; |
| + | port = xatou_range(ptr + 1, 0, 65535); |
| + | } |
| + | |
| + | return port; |
| + | } |
| @@ -791,22 +791,17 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_ | |
| /* | |
| * Entering passive mode | |
| */ | |
| + | if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL, sfp) == 229) { |
| + | /* good */ |
| + | } else |
| if (ftpcmd("PASV", NULL, sfp) != 227) { | |
| pasv_error: | |
| bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf)); | |
| } | |
| - | // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] |
| - | // Server's IP is N1.N2.N3.N4 (we ignore it) |
| - | // Server's port for data connection is P1*256+P2 |
| - | str = strrchr(G.wget_buf, ')'); |
| - | if (str) str[0] = '\0'; |
| - | str = strrchr(G.wget_buf, ','); |
| - | if (!str) goto pasv_error; |
| - | port = xatou_range(str+1, 0, 255); |
| - | *str = '\0'; |
| - | str = strrchr(G.wget_buf, ','); |
| - | if (!str) goto pasv_error; |
| - | port += xatou_range(str+1, 0, 255) * 256; |
| + | port = parse_pasv_epsv(G.wget_buf); |
| + | if (port < 0) |
| + | goto pasv_error; |
| + | |
| set_nport(&lsa->u.sa, htons(port)); | |
| *dfpp = open_socket(lsa); | |