mirror of
https://github.com/moparisthebest/curl
synced 2024-11-12 04:25:08 -05:00
FTP: make the data connection work when going through proxy
This is a regression since the switch to always-multi internally
c43127414d
.
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.
This commit is contained in:
parent
469b423350
commit
d44b014271
@ -777,7 +777,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
|||||||
/* we are connected with TCP, awesome! */
|
/* we are connected with TCP, awesome! */
|
||||||
|
|
||||||
/* see if we need to do any proxy magic first once we connected */
|
/* 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)
|
if(code)
|
||||||
return code;
|
return code;
|
||||||
|
|
||||||
|
184
lib/ftp.c
184
lib/ftp.c
@ -1802,6 +1802,79 @@ static CURLcode ftp_epsv_disable(struct connectdata *conn)
|
|||||||
return result;
|
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,
|
static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
||||||
int ftpcode)
|
int ftpcode)
|
||||||
{
|
{
|
||||||
@ -1812,13 +1885,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
|||||||
struct Curl_dns_entry *addr=NULL;
|
struct Curl_dns_entry *addr=NULL;
|
||||||
int rc;
|
int rc;
|
||||||
unsigned short connectport; /* the local port connect() should use! */
|
unsigned short connectport; /* the local port connect() should use! */
|
||||||
unsigned short newport=0; /* remote port */
|
|
||||||
bool connected;
|
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 */
|
char *str=&data->state.buffer[4]; /* start on the first letter */
|
||||||
|
|
||||||
if((ftpc->count1 == 0) &&
|
if((ftpc->count1 == 0) &&
|
||||||
@ -1851,7 +1918,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
|||||||
return CURLE_FTP_WEIRD_PASV_REPLY;
|
return CURLE_FTP_WEIRD_PASV_REPLY;
|
||||||
}
|
}
|
||||||
if(ptr) {
|
if(ptr) {
|
||||||
newport = (unsigned short)(num & 0xffff);
|
ftpc->newport = (unsigned short)(num & 0xffff);
|
||||||
|
|
||||||
if(conn->bits.tunnel_proxy ||
|
if(conn->bits.tunnel_proxy ||
|
||||||
conn->proxytype == CURLPROXY_SOCKS5 ||
|
conn->proxytype == CURLPROXY_SOCKS5 ||
|
||||||
@ -1860,10 +1927,11 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
|||||||
conn->proxytype == CURLPROXY_SOCKS4A)
|
conn->proxytype == CURLPROXY_SOCKS4A)
|
||||||
/* proxy tunnel -> use other host info because ip_addr_str is the
|
/* proxy tunnel -> use other host info because ip_addr_str is the
|
||||||
proxy address not the ftp host */
|
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
|
else
|
||||||
/* use the same IP we are already connected to */
|
/* 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
|
else
|
||||||
@ -1916,14 +1984,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
|||||||
conn->proxytype == CURLPROXY_SOCKS4A)
|
conn->proxytype == CURLPROXY_SOCKS4A)
|
||||||
/* proxy tunnel -> use other host info because ip_addr_str is the
|
/* proxy tunnel -> use other host info because ip_addr_str is the
|
||||||
proxy address not the ftp host */
|
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
|
else
|
||||||
snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
|
snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s",
|
||||||
|
conn->ip_addr_str);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
snprintf(newhost, sizeof(newhost),
|
snprintf(ftpc->newhost, sizeof(ftpc->newhost),
|
||||||
"%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
"%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) {
|
else if(ftpc->count1 == 0) {
|
||||||
/* EPSV failed, move on to PASV */
|
/* EPSV failed, move on to PASV */
|
||||||
@ -1957,15 +2026,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* normal, direct, ftp connection */
|
/* normal, direct, ftp connection */
|
||||||
rc = Curl_resolv(conn, newhost, newport, &addr);
|
rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr);
|
||||||
if(rc == CURLRESOLV_PENDING)
|
if(rc == CURLRESOLV_PENDING)
|
||||||
/* BLOCKING */
|
/* BLOCKING */
|
||||||
(void)Curl_resolver_wait_resolv(conn, &addr);
|
(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) {
|
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;
|
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
|
* 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
|
* 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)
|
if(data->set.verbose)
|
||||||
/* this just dumps information about this second connection */
|
/* 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) {
|
if(connected) {
|
||||||
/* FIX: this MUST wait for a proper connect first if 'connected' is
|
/* Only do the proxy connection magic if we're actually connected. We do
|
||||||
* FALSE */
|
this little trick and send in the same 'connected' variable here again
|
||||||
case CURLPROXY_SOCKS5:
|
and it will be set FALSE by proxy_magic() for when for example the
|
||||||
case CURLPROXY_SOCKS5_HOSTNAME:
|
CONNECT procedure doesn't complete */
|
||||||
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
|
infof(data, "Connection to proxy confirmed almost instantly\n");
|
||||||
SECONDARYSOCKET, conn);
|
result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
|
conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
|
||||||
conn->bits.do_more = TRUE;
|
conn->bits.do_more = TRUE;
|
||||||
state(conn, FTP_STOP); /* this phase is completed */
|
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? */
|
/* Ready to do more? */
|
||||||
if(connected) {
|
if(connected) {
|
||||||
DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
|
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 {
|
else {
|
||||||
if(result && (ftpc->count1 == 0)) {
|
if(result && (ftpc->count1 == 0)) {
|
||||||
|
@ -147,6 +147,12 @@ struct ftp_conn {
|
|||||||
curl_off_t known_filesize; /* file size is different from -1, if wildcard
|
curl_off_t known_filesize; /* file size is different from -1, if wildcard
|
||||||
LIST parsing was done and wc_statemach set
|
LIST parsing was done and wc_statemach set
|
||||||
it */
|
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 */
|
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
|
||||||
|
@ -129,6 +129,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
|
|||||||
|
|
||||||
curlx_nonblock(sock, FALSE);
|
curlx_nonblock(sock, FALSE);
|
||||||
|
|
||||||
|
infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compose socks4 request
|
* Compose socks4 request
|
||||||
*
|
*
|
||||||
@ -182,6 +184,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
|
|||||||
else
|
else
|
||||||
hp = NULL; /* fail! */
|
hp = NULL; /* fail! */
|
||||||
|
|
||||||
|
infof(data, "SOCKS4 connect to %s (locally resolved)\n", buf);
|
||||||
|
|
||||||
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
|
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3219,9 +3219,12 @@ static CURLcode ConnectionStore(struct SessionHandle *data,
|
|||||||
Note: this function's sub-functions call failf()
|
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;
|
return CURLE_OK;
|
||||||
|
|
||||||
switch(conn->proxytype) {
|
switch(conn->proxytype) {
|
||||||
@ -3281,7 +3284,7 @@ static CURLcode ConnectPlease(struct SessionHandle *data,
|
|||||||
conn->ip_addr = addr;
|
conn->ip_addr = addr;
|
||||||
|
|
||||||
if(*connected) {
|
if(*connected) {
|
||||||
result = Curl_connected_proxy(conn);
|
result = Curl_connected_proxy(conn, FIRSTSOCKET);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
|
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
|
||||||
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
|
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
|
||||||
|
@ -70,7 +70,7 @@ void Curl_close_connections(struct SessionHandle *data);
|
|||||||
#define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi
|
#define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi
|
||||||
service */
|
service */
|
||||||
|
|
||||||
CURLcode Curl_connected_proxy(struct connectdata *conn);
|
CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex);
|
||||||
|
|
||||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||||
#define Curl_verboseconnect(x) Curl_nop_stmt
|
#define Curl_verboseconnect(x) Curl_nop_stmt
|
||||||
|
Loading…
Reference in New Issue
Block a user