From d44b0142714041b784ffd10792318674ecb1ed56 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 26 Oct 2013 20:19:27 +0200 Subject: FTP: make the data connection work when going through proxy This is a regression since the switch to always-multi internally c43127414d89c. Test 1316 was modified since we now clearly call the Curl_client_write() function when doing the LIST transfer part and then the handler->protocol says FTP and ftpc.transfertype is 'A' which implies text converting even though that the response is initially a HTTP CONNECT response in this case. --- lib/connect.c | 2 +- lib/ftp.c | 184 +++++++++++++++++++++++++++++++--------------------------- lib/ftp.h | 6 ++ lib/socks.c | 4 ++ lib/url.c | 9 ++- lib/url.h | 2 +- 6 files changed, 116 insertions(+), 91 deletions(-) (limited to 'lib') diff --git a/lib/connect.c b/lib/connect.c index 2b5719d12..c442c48f8 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -777,7 +777,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, /* we are connected with TCP, awesome! */ /* see if we need to do any proxy magic first once we connected */ - code = Curl_connected_proxy(conn); + code = Curl_connected_proxy(conn, sockindex); if(code) return code; diff --git a/lib/ftp.c b/lib/ftp.c index e818af6c5..487f9d5a4 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1802,6 +1802,79 @@ static CURLcode ftp_epsv_disable(struct connectdata *conn) return result; } +/* + * Perform the necessary magic that needs to be done once the TCP connection + * to the proxy has completed. + */ +static CURLcode proxy_magic(struct connectdata *conn, + char *newhost, unsigned short newport, + bool *magicdone) +{ + struct SessionHandle *data=conn->data; + CURLcode result; + + *magicdone = FALSE; + switch(conn->proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, + newport, SECONDARYSOCKET, conn); + *magicdone = TRUE; + break; + case CURLPROXY_SOCKS4: + result = Curl_SOCKS4(conn->proxyuser, newhost, newport, + SECONDARYSOCKET, conn, FALSE); + *magicdone = TRUE; + break; + case CURLPROXY_SOCKS4A: + result = Curl_SOCKS4(conn->proxyuser, newhost, newport, + SECONDARYSOCKET, conn, TRUE); + *magicdone = TRUE; + break; + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + /* do nothing here. handled later. */ + break; + default: + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + break; + } + + if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { + /* BLOCKING */ + /* We want "seamless" FTP operations through HTTP proxy tunnel */ + + /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the + * member conn->proto.http; we want FTP through HTTP and we have to + * change the member temporarily for connecting to the HTTP proxy. After + * Curl_proxyCONNECT we have to set back the member to the original + * struct FTP pointer + */ + struct HTTP http_proxy; + struct FTP *ftp_save = data->req.protop; + memset(&http_proxy, 0, sizeof(http_proxy)); + data->req.protop = &http_proxy; + + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); + + data->req.protop = ftp_save; + + if(result) + return result; + + if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) { + /* the CONNECT procedure is not complete, the tunnel is not yet up */ + state(conn, FTP_STOP); /* this phase is completed */ + conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; + return result; + } + else + *magicdone = TRUE; + } + return result; +} + static CURLcode ftp_state_pasv_resp(struct connectdata *conn, int ftpcode) { @@ -1812,13 +1885,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, struct Curl_dns_entry *addr=NULL; int rc; unsigned short connectport; /* the local port connect() should use! */ - unsigned short newport=0; /* remote port */ bool connected; - - /* newhost must be able to hold a full IP-style address in ASCII, which - in the IPv6 case means 5*8-1 = 39 letters */ -#define NEWHOST_BUFSIZE 48 - char newhost[NEWHOST_BUFSIZE]; char *str=&data->state.buffer[4]; /* start on the first letter */ if((ftpc->count1 == 0) && @@ -1851,7 +1918,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, return CURLE_FTP_WEIRD_PASV_REPLY; } if(ptr) { - newport = (unsigned short)(num & 0xffff); + ftpc->newport = (unsigned short)(num & 0xffff); if(conn->bits.tunnel_proxy || conn->proxytype == CURLPROXY_SOCKS5 || @@ -1860,10 +1927,11 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, conn->proxytype == CURLPROXY_SOCKS4A) /* proxy tunnel -> use other host info because ip_addr_str is the proxy address not the ftp host */ - snprintf(newhost, sizeof(newhost), "%s", conn->host.name); + snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", + conn->host.name); else /* use the same IP we are already connected to */ - snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str); + snprintf(ftpc->newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str); } } else @@ -1916,14 +1984,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, conn->proxytype == CURLPROXY_SOCKS4A) /* proxy tunnel -> use other host info because ip_addr_str is the proxy address not the ftp host */ - snprintf(newhost, sizeof(newhost), "%s", conn->host.name); + snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", conn->host.name); else - snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str); + snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", + conn->ip_addr_str); } else - snprintf(newhost, sizeof(newhost), + snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); - newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); + ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ @@ -1957,15 +2026,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } else { /* normal, direct, ftp connection */ - rc = Curl_resolv(conn, newhost, newport, &addr); + rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr); if(rc == CURLRESOLV_PENDING) /* BLOCKING */ (void)Curl_resolver_wait_resolv(conn, &addr); - connectport = newport; /* we connect to the remote port */ + connectport = ftpc->newport; /* we connect to the remote port */ if(!addr) { - failf(data, "Can't resolve new host %s:%hu", newhost, connectport); + failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport); return CURLE_FTP_CANT_GET_HOST; } } @@ -1990,82 +2059,21 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, /* * When this is used from the multi interface, this might've returned with * the 'connected' set to FALSE and thus we are now awaiting a non-blocking - * connect to connect and we should not be "hanging" here waiting. + * connect to connect. */ if(data->set.verbose) /* this just dumps information about this second connection */ - ftp_pasv_verbose(conn, conninfo, newhost, connectport); + ftp_pasv_verbose(conn, conninfo, ftpc->newhost, connectport); - switch(conn->proxytype) { - /* FIX: this MUST wait for a proper connect first if 'connected' is - * FALSE */ - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport, - SECONDARYSOCKET, conn); - connected = TRUE; - break; - case CURLPROXY_SOCKS4: - result = Curl_SOCKS4(conn->proxyuser, newhost, newport, - SECONDARYSOCKET, conn, FALSE); - connected = TRUE; - break; - case CURLPROXY_SOCKS4A: - result = Curl_SOCKS4(conn->proxyuser, newhost, newport, - SECONDARYSOCKET, conn, TRUE); - connected = TRUE; - break; - case CURLPROXY_HTTP: - case CURLPROXY_HTTP_1_0: - /* do nothing here. handled later. */ - break; - default: - failf(data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - break; - } - - if(result) { - if(ftpc->count1 == 0 && ftpcode == 229) - return ftp_epsv_disable(conn); - return result; - } - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { - /* FIX: this MUST wait for a proper connect first if 'connected' is - * FALSE */ - - /* BLOCKING */ - /* We want "seamless" FTP operations through HTTP proxy tunnel */ - - /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member - * conn->proto.http; we want FTP through HTTP and we have to change the - * member temporarily for connecting to the HTTP proxy. After - * Curl_proxyCONNECT we have to set back the member to the original struct - * FTP pointer - */ - struct HTTP http_proxy; - struct FTP *ftp_save = data->req.protop; - memset(&http_proxy, 0, sizeof(http_proxy)); - data->req.protop = &http_proxy; - - result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); - - data->req.protop = ftp_save; - - if(result) - return result; - - if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) { - /* the CONNECT procedure is not complete, the tunnel is not yet up */ - state(conn, FTP_STOP); /* this phase is completed */ - conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; - - return result; - } + if(connected) { + /* Only do the proxy connection magic if we're actually connected. We do + this little trick and send in the same 'connected' variable here again + and it will be set FALSE by proxy_magic() for when for example the + CONNECT procedure doesn't complete */ + infof(data, "Connection to proxy confirmed almost instantly\n"); + result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected); } - conn->bits.tcpconnect[SECONDARYSOCKET] = connected; conn->bits.do_more = TRUE; state(conn, FTP_STOP); /* this phase is completed */ @@ -3625,6 +3633,10 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) /* Ready to do more? */ if(connected) { DEBUGF(infof(data, "DO-MORE connected phase starts\n")); + if(conn->bits.proxy) { + infof(data, "Connection to proxy confirmed\n"); + result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected); + } } else { if(result && (ftpc->count1 == 0)) { diff --git a/lib/ftp.h b/lib/ftp.h index bdd59c93d..b6bfc0287 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -147,6 +147,12 @@ struct ftp_conn { curl_off_t known_filesize; /* file size is different from -1, if wildcard LIST parsing was done and wc_statemach set it */ + /* newhost must be able to hold a full IP-style address in ASCII, which + in the IPv6 case means 5*8-1 = 39 letters */ +#define NEWHOST_BUFSIZE 48 + char newhost[NEWHOST_BUFSIZE]; /* this is the pair to connect the DATA... */ + unsigned short newport; /* connection to */ + }; #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ diff --git a/lib/socks.c b/lib/socks.c index b101a0de4..d7136c605 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -129,6 +129,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name, curlx_nonblock(sock, FALSE); + infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port); + /* * Compose socks4 request * @@ -182,6 +184,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name, else hp = NULL; /* fail! */ + infof(data, "SOCKS4 connect to %s (locally resolved)\n", buf); + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } diff --git a/lib/url.c b/lib/url.c index c1672d08b..9973bd273 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3219,9 +3219,12 @@ static CURLcode ConnectionStore(struct SessionHandle *data, Note: this function's sub-functions call failf() */ -CURLcode Curl_connected_proxy(struct connectdata *conn) +CURLcode Curl_connected_proxy(struct connectdata *conn, + int sockindex) { - if(!conn->bits.proxy) + if(!conn->bits.proxy || sockindex) + /* this magic only works for the primary socket as the secondary is used + for FTP only and it has FTP specific magic in ftp.c */ return CURLE_OK; switch(conn->proxytype) { @@ -3281,7 +3284,7 @@ static CURLcode ConnectPlease(struct SessionHandle *data, conn->ip_addr = addr; if(*connected) { - result = Curl_connected_proxy(conn); + result = Curl_connected_proxy(conn, FIRSTSOCKET); if(!result) { conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ diff --git a/lib/url.h b/lib/url.h index 418413c48..cd46a92c3 100644 --- a/lib/url.h +++ b/lib/url.h @@ -70,7 +70,7 @@ void Curl_close_connections(struct SessionHandle *data); #define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi service */ -CURLcode Curl_connected_proxy(struct connectdata *conn); +CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex); #ifdef CURL_DISABLE_VERBOSE_STRINGS #define Curl_verboseconnect(x) Curl_nop_stmt -- cgit v1.2.3