From fcfd6d950408d41f0ff7e664205f76146f180da8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 19 Jan 2006 23:52:03 +0000 Subject: [PATCH] Duane Cathey was one of our friends who reported that curl -P [IP] (CURLOPT_FTPPORT) didn't work for ipv6-enabed curls if the IP wasn't a "native" IP while it works fine for ipv6-disabled builds! In the process of fixing this, I removed the support for LPRT since I can't think of many reasons to keep doing it and asking on the mailing list didn't reveal anyone else that could either. The code that sends EPRT and PORT is now also a lot simpler than before (IMHO). --- CHANGES | 14 +++- lib/ftp.c | 197 +++++++++++++++++++++------------------------ lib/url.c | 5 +- lib/urldata.h | 6 +- tests/data/test212 | 5 +- 5 files changed, 106 insertions(+), 121 deletions(-) diff --git a/CHANGES b/CHANGES index d2e35cd64..16de2cb7c 100644 --- a/CHANGES +++ b/CHANGES @@ -6,9 +6,19 @@ Changelog +Daniel (20 January 2006) +- Duane Cathey was one of our friends who reported that curl -P [IP] + (CURLOPT_FTPPORT) didn't work for ipv6-enabed curls if the IP wasn't a + "native" IP while it works fine for ipv6-disabled builds! + + In the process of fixing this, I removed the support for LPRT since I can't + think of many reasons to keep doing it and asking on the mailing list didn't + reveal anyone else that could either. The code that sends EPRT and PORT is + now also a lot simpler than before (IMHO). + Daniel (19 January 2006) -- Jon Turner pointed out that doing -P [hostname] with curl (built ipv4-only) - didn't work. +- Jon Turner pointed out that doing -P [hostname] (CURLOPT_FTPPORT) with curl + (built ipv4-only) didn't work. Daniel (18 January 2006) - As reported in bug #1408742 (http://curl.haxx.se/bug/view.cgi?id=1408742), diff --git a/lib/ftp.c b/lib/ftp.c index 2361c89ec..3cdcc32ef 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -763,7 +763,11 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) return result; } -typedef enum { EPRT, LPRT, PORT, DONE } ftpport; +typedef enum { + EPRT, + PORT, + DONE +} ftpport; static CURLcode ftp_state_use_port(struct connectdata *conn, ftpport fcmd) /* start with this */ @@ -773,6 +777,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, struct FTP *ftp = conn->proto.ftp; struct SessionHandle *data=conn->data; curl_socket_t portsock= CURL_SOCKET_BAD; + char myhost[256] = ""; #ifdef ENABLE_IPV6 /****************************************************************** @@ -783,14 +788,15 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, socklen_t sslen; char hbuf[NI_MAXHOST]; struct sockaddr *sa=(struct sockaddr *)&ss; - unsigned char *ap; - unsigned char *pp; - char portmsgbuf[1024], tmp[1024]; - const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; + char tmp[1024]; + const char *mode[] = { "EPRT", "PORT", NULL }; int rc; int error; char *host=NULL; struct Curl_dns_entry *h=NULL; + unsigned short port; + + /* Step 1, figure out what address that is requested */ if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) { /* attempt to get the address of the given interface name */ @@ -833,6 +839,9 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, else res = NULL; /* failure! */ + + /* step 2, create a socket for the requested address */ + portsock = CURL_SOCKET_BAD; error = 0; for (ai = res; ai; ai = ai->ai_next) { @@ -847,35 +856,65 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, error = Curl_ourerrno(); continue; } - - if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { - error = Curl_ourerrno(); - sclose(portsock); - portsock = CURL_SOCKET_BAD; - continue; - } - - if (listen(portsock, 1) < 0) { - error = Curl_ourerrno(); - sclose(portsock); - portsock = CURL_SOCKET_BAD; - continue; - } - break; } - - if (portsock == CURL_SOCKET_BAD) { - failf(data, "socket failure: %s", Curl_strerror(conn,error)); + if(!ai) { + failf(data, "socket failure: %s", Curl_strerror(conn, error)); return CURLE_FTP_PORT_FAILED; } + /* step 3, bind to a suitable local address */ + + /* Try binding the given address. */ + if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { + + /* It failed. Bind the address used for the control connection instead */ + sslen = sizeof(ss); + + if (getsockname(conn->sock[FIRSTSOCKET], + (struct sockaddr *)sa, &sslen) < 0) { + failf(data, "getsockname() failed"); + sclose(portsock); + return CURLE_FTP_PORT_FAILED; + } + + /* set port number to zero to make bind() pick "any" */ + if(((struct sockaddr *)sa)->sa_family == AF_INET) + ((struct sockaddr_in *)sa)->sin_port=0; + else + ((struct sockaddr_in6 *)sa)->sin6_port =0; + + if(bind(portsock, (struct sockaddr *)sa, sslen) < 0) { + failf(data, "bind failed: %s", Curl_strerror(conn, Curl_ourerrno())); + sclose(portsock); + return CURLE_FTP_PORT_FAILED; + } + } + + /* get the name again after the bind() so that we can extract the + port number it uses now */ sslen = sizeof(ss); - if (getsockname(portsock, sa, &sslen) < 0) { - failf(data, "getsockname(): %s", Curl_strerror(conn,Curl_ourerrno())); + if(getsockname(portsock, (struct sockaddr *)sa, &sslen)<0) { + failf(data, "getsockname() failed: %s", + Curl_strerror(conn, Curl_ourerrno()) ); return CURLE_FTP_PORT_FAILED; } + /* step 4, listen on the socket */ + + if (listen(portsock, 1) < 0) { + error = Curl_ourerrno(); + sclose(portsock); + failf(data, "socket failure: %s", Curl_strerror(conn, error)); + return CURLE_FTP_PORT_FAILED; + } + + /* step 5, send the proper FTP command */ + + /* get a plain printable version of the numerical address to work with + below */ + Curl_printable_address(ai, myhost, sizeof(myhost)); + #ifdef PF_INET6 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the @@ -884,107 +923,58 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, #endif for (; fcmd != DONE; fcmd++) { - int lprtaf, eprtaf; - int alen=0, plen=0; if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) /* if disabled, goto next */ continue; - if(!conn->bits.ftp_use_lprt && (LPRT == fcmd)) - /* if disabled, goto next */ - continue; - switch (sa->sa_family) { case AF_INET: - ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; - alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); - pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; - plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); - lprtaf = 4; - eprtaf = 1; + port = ntohs(((struct sockaddr_in *)sa)->sin_port); break; case AF_INET6: - ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; - alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); - pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; - plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); - lprtaf = 6; - eprtaf = 2; + port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); break; default: - ap = pp = NULL; - lprtaf = eprtaf = -1; break; } if (EPRT == fcmd) { - if (eprtaf < 0) - continue; - if (getnameinfo((struct sockaddr *)&ss, sslen, - portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), - NIFLAGS)) - continue; + /* + * Two fine examples from RFC2428; + * + * EPRT |1|132.235.1.2|6275| + * + * EPRT |2|1080::8:800:200C:417A|5282| + */ - /* do not transmit IPv6 scope identifier to the wire */ - if (sa->sa_family == AF_INET6) { - char *q = strchr(portmsgbuf, '%'); - if (q) - *q = '\0'; - } - - result = Curl_nbftpsendf(conn, "%s |%d|%s|%s|", mode[fcmd], eprtaf, - portmsgbuf, tmp); + result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd], + ai->ai_family == AF_INET?1:2, + myhost, port); if(result) return result; break; } - else if ((LPRT == fcmd) || (PORT == fcmd)) { - int i; + else if (PORT == fcmd) { + char *source = myhost; + char *dest = tmp; - if ((LPRT == fcmd) && lprtaf < 0) - continue; - if ((PORT == fcmd) && sa->sa_family != AF_INET) + if ((PORT == fcmd) && ai->ai_family != AF_INET) continue; - portmsgbuf[0] = '\0'; - if (LPRT == fcmd) { - snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } - } - - for (i = 0; i < alen; i++) { - if (portmsgbuf[0]) - snprintf(tmp, sizeof(tmp), ",%u", ap[i]); + /* translate x.x.x.x to x,x,x,x */ + while(source && *source) { + if(*source == '.') + *dest=','; else - snprintf(tmp, sizeof(tmp), "%u", ap[i]); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } + *dest = *source; + dest++; + source++; } + *dest = 0; + snprintf(dest, 20, ",%d,%d", port>>8, port&0xff); - if (LPRT == fcmd) { - snprintf(tmp, sizeof(tmp), ",%d", plen); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) - continue; - } - - for (i = 0; i < plen; i++) { - snprintf(tmp, sizeof(tmp), ",%u", pp[i]); - - if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= - sizeof(portmsgbuf)) { - continue; - } - } - - result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], portmsgbuf); + result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp); if(result) return result; break; @@ -1007,7 +997,6 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, */ struct sockaddr_in sa; unsigned short porttouse; - char myhost[256] = ""; bool sa_filled_in = FALSE; Curl_addrinfo *addr = NULL; unsigned short ip[4]; @@ -1735,10 +1724,6 @@ static CURLcode ftp_state_port_resp(struct connectdata *conn, infof(data, "disabling EPRT usage\n"); conn->bits.ftp_use_eprt = FALSE; } - else if (LPRT == fcmd) { - infof(data, "disabling LPRT usage\n"); - conn->bits.ftp_use_lprt = FALSE; - } fcmd++; if(fcmd == DONE) { diff --git a/lib/url.c b/lib/url.c index b8927fbcd..a4bd0f509 100644 --- a/lib/url.c +++ b/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -326,7 +326,6 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */ data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ data->set.ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ - data->set.ftp_use_lprt = TRUE; /* FTP defaults to EPRT operations */ data->set.ftp_filemethod = FTPFILE_MULTICWD; data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ @@ -964,7 +963,6 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, case CURLOPT_FTP_USE_EPRT: data->set.ftp_use_eprt = va_arg(param, long)?TRUE:FALSE; - data->set.ftp_use_lprt = data->set.ftp_use_eprt; break; case CURLOPT_FTP_USE_EPSV: @@ -2365,7 +2363,6 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; - conn->bits.ftp_use_lprt = data->set.ftp_use_lprt; /* This initing continues below, see the comment "Continue connectdata * initialization here" */ diff --git a/lib/urldata.h b/lib/urldata.h index 94889234a..c3a76b45c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -427,9 +427,6 @@ struct ConnectBits { bool ftp_use_eprt; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out EPRT doesn't work we disable it for the forthcoming requests */ - bool ftp_use_lprt; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out - LPRT doesn't work we disable it for the forthcoming - requests */ bool netrc; /* name+password provided by netrc */ bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response. @@ -1075,7 +1072,6 @@ struct UserDefined { bool expect100header; /* TRUE if we added Expect: 100-continue */ bool ftp_use_epsv; /* if EPSV is to be attempted or not */ bool ftp_use_eprt; /* if EPRT is to be attempted or not */ - bool ftp_use_lprt; /* if LPRT is to be attempted or not */ curl_ftpssl ftp_ssl; /* if AUTH TLS is to be attempted etc */ curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */ bool no_signal; /* do not use any signal/alarm handler */ diff --git a/tests/data/test212 b/tests/data/test212 index 8633ae5d4..3fac26bde 100644 --- a/tests/data/test212 +++ b/tests/data/test212 @@ -15,14 +15,13 @@ ipv6 ftp -Get two FTP files with no remote EPRT or LPRT support +Get two FTP files with no remote EPRT support ftp://%HOSTIP:%FTPPORT/a/path/212 ftp://%HOSTIP:%FTPPORT/a/path/212 -P - REPLY EPRT 500 no such command -REPLY LPRT 500 no such command data blobb @@ -34,7 +33,6 @@ data blobb s/^(EPRT \|1\|127.0.0.1).*/$1/ -s/^(LPRT 4,4,127,0,0,1,).*/$1/ s/^(PORT 127,0,0,1,).*/$1/ @@ -44,7 +42,6 @@ PWD CWD a CWD path EPRT |1|127.0.0.1 -LPRT 4,4,127,0,0,1, PORT 127,0,0,1, TYPE I SIZE 212