wget: add EPSV support
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 6 Feb 2018 14:48:12 +0000 (15:48 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 6 Feb 2018 14:48:12 +0000 (15:48 +0100)
function                                             old     new   delta
parse_pasv_epsv                                        -     151    +151
wget_main                                           2440    2382     -58
xconnect_ftpdata                                     223      94    -129
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 0/2 up/down: 151/-187)          Total: -36 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/libbb.h
networking/ftpgetput.c
networking/parse_pasv_epsv.c [new file with mode: 0644]
networking/wget.c

index a4eb6ee676ae473b16a11c1cef21d48be2f99e16..e2bedaf41674dd9bf4d870998daad6aec840ca97 100644 (file)
@@ -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;
index 697955e823b46ef735ef188fd7a0b7626d9a7e83..cdad629da10a87bbe0ef06cf9cd2b2ca9495a27c 100644 (file)
@@ -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);
diff --git a/networking/parse_pasv_epsv.c b/networking/parse_pasv_epsv.c
new file mode 100644 (file)
index 0000000..f320871
--- /dev/null
@@ -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;
+}
index daa728a9d070d486f895bb0ce1ad06d585bc30e6..7ca4bfb33a74eda4472a75cea9d474cbeecde882 100644 (file)
@@ -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);