From c19349951df1fde96c31d68e054e3a36a3b03860 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 11 Feb 2015 23:18:32 +0100 Subject: [PATCH] multi: fix *getsock() with CONNECT The code used some happy eyeballs logic even _after_ CONNECT has been sent to a proxy, while the happy eyeball phase is already (should be) over by then. This is solved by splitting the multi state into two separate states introducing the new SENDPROTOCONNECT state. Bug: http://curl.haxx.se/mail/lib-2015-01/0170.html Reported-by: Peter Laser --- lib/http_proxy.c | 1 + lib/multi.c | 90 +++++++++++++++++++++++------------------------ lib/multihandle.h | 31 ++++++++-------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/lib/http_proxy.c b/lib/http_proxy.c index aa43ef9b2..fd8ccee31 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -72,6 +72,7 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) conn->data->req.protop = prot_save; if(CURLE_OK != result) return result; + Curl_safefree(conn->allocptr.proxyuserpwd); #else return CURLE_NOT_BUILT_IN; #endif diff --git a/lib/multi.c b/lib/multi.c index caf51de77..6d21b138d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -86,6 +86,7 @@ static const char * const statename[]={ "WAITRESOLVE", "WAITCONNECT", "WAITPROXYCONNECT", + "SENDPROTOCONNECT", "PROTOCONNECT", "WAITDO", "DO", @@ -646,14 +647,24 @@ static int waitconnect_getsock(struct connectdata *conn, } } + return rc; +} + +static int waitproxyconnect_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) +{ + if(!numsocks) + return GETSOCK_BLANK; + + sock[0] = conn->sock[FIRSTSOCKET]; + /* when we've sent a CONNECT to a proxy, we should rather wait for the socket to become readable to be able to get the response headers */ - if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) { - sock[0] = conn->sock[FIRSTSOCKET]; - rc = GETSOCK_READSOCK(0); - } + if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + return GETSOCK_READSOCK(0); - return rc; + return GETSOCK_WRITESOCK(0); } static int domore_getsock(struct connectdata *conn, @@ -706,6 +717,7 @@ static int multi_getsock(struct SessionHandle *data, return Curl_resolver_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: + case CURLM_STATE_SENDPROTOCONNECT: return Curl_protocol_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO: @@ -713,6 +725,8 @@ static int multi_getsock(struct SessionHandle *data, return Curl_doing_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_WAITPROXYCONNECT: + return waitproxyconnect_getsock(data->easy_conn, socks, numsocks); + case CURLM_STATE_WAITCONNECT: return waitconnect_getsock(data->easy_conn, socks, numsocks); @@ -1164,40 +1178,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ result = Curl_http_connect(data->easy_conn, &protocol_connect); + rc = CURLM_CALL_MULTI_PERFORM; if(data->easy_conn->bits.proxy_connect_closed) { /* connect back to proxy again */ result = CURLE_OK; - rc = CURLM_CALL_MULTI_PERFORM; multistate(data, CURLM_STATE_CONNECT); } else if(!result) { if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) - multistate(data, CURLM_STATE_WAITCONNECT); + /* initiate protocol connect phase */ + multistate(data, CURLM_STATE_SENDPROTOCONNECT); } break; #endif case CURLM_STATE_WAITCONNECT: - /* awaiting a completion of an asynch connect */ - result = Curl_is_connected(data->easy_conn, - FIRSTSOCKET, - &connected); - if(connected) { - - if(!result) - /* if everything is still fine we do the protocol-specific connect - setup */ - result = Curl_protocol_connect(data->easy_conn, - &protocol_connect); - } - - if(data->easy_conn->bits.proxy_connect_closed) { - /* connect back to proxy again since it was closed in a proxy CONNECT - setup */ - result = CURLE_OK; + /* awaiting a completion of an asynch TCP connect */ + result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected); + if(connected && !result) { rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, CURLM_STATE_CONNECT); - break; + multistate(data, data->easy_conn->bits.tunnel_proxy? + CURLM_STATE_WAITPROXYCONNECT: + CURLM_STATE_SENDPROTOCONNECT); } else if(result) { /* failure detected */ @@ -1205,29 +1207,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, disconnect_conn = TRUE; break; } + break; - if(connected) { - if(!protocol_connect) { - /* We have a TCP connection, but 'protocol_connect' may be false - and then we continue to 'STATE_PROTOCONNECT'. If protocol - connect is TRUE, we move on to STATE_DO. - BUT if we are using a proxy we must change to WAITPROXYCONNECT - */ -#ifndef CURL_DISABLE_HTTP - if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(data, CURLM_STATE_WAITPROXYCONNECT); - else -#endif - multistate(data, CURLM_STATE_PROTOCONNECT); - - } - else - /* after the connect has completed, go WAITDO or DO */ - multistate(data, multi->pipelining_enabled? - CURLM_STATE_WAITDO:CURLM_STATE_DO); - + case CURLM_STATE_SENDPROTOCONNECT: + result = Curl_protocol_connect(data->easy_conn, &protocol_connect); + if(!protocol_connect) + /* switch to waiting state */ + multistate(data, CURLM_STATE_PROTOCONNECT); + else if(!result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; } + else if(result) { + /* failure detected */ + Curl_posttransfer(data); + Curl_done(&data->easy_conn, result, TRUE); + disconnect_conn = TRUE; + } break; case CURLM_STATE_PROTOCONNECT: diff --git a/lib/multihandle.h b/lib/multihandle.h index 1a4b1d966..d8b9d8892 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2015, 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 @@ -35,22 +35,23 @@ typedef enum { CURLM_STATE_CONNECT_PEND, /* 1 - no connections, waiting for one */ CURLM_STATE_CONNECT, /* 2 - resolve/connect has been sent off */ CURLM_STATE_WAITRESOLVE, /* 3 - awaiting the resolve to finalize */ - CURLM_STATE_WAITCONNECT, /* 4 - awaiting the connect to finalize */ + CURLM_STATE_WAITCONNECT, /* 4 - awaiting the TCP connect to finalize */ CURLM_STATE_WAITPROXYCONNECT, /* 5 - awaiting proxy CONNECT to finalize */ - CURLM_STATE_PROTOCONNECT, /* 6 - completing the protocol-specific connect + CURLM_STATE_SENDPROTOCONNECT, /* 6 - initiate protocol connect procedure */ + CURLM_STATE_PROTOCONNECT, /* 7 - completing the protocol-specific connect phase */ - CURLM_STATE_WAITDO, /* 7 - wait for our turn to send the request */ - CURLM_STATE_DO, /* 8 - start send off the request (part 1) */ - CURLM_STATE_DOING, /* 9 - sending off the request (part 1) */ - CURLM_STATE_DO_MORE, /* 10 - send off the request (part 2) */ - CURLM_STATE_DO_DONE, /* 11 - done sending off request */ - CURLM_STATE_WAITPERFORM, /* 12 - wait for our turn to read the response */ - CURLM_STATE_PERFORM, /* 13 - transfer data */ - CURLM_STATE_TOOFAST, /* 14 - wait because limit-rate exceeded */ - CURLM_STATE_DONE, /* 15 - post data transfer operation */ - CURLM_STATE_COMPLETED, /* 16 - operation complete */ - CURLM_STATE_MSGSENT, /* 17 - the operation complete message is sent */ - CURLM_STATE_LAST /* 18 - not a true state, never use this */ + CURLM_STATE_WAITDO, /* 8 - wait for our turn to send the request */ + CURLM_STATE_DO, /* 9 - start send off the request (part 1) */ + CURLM_STATE_DOING, /* 10 - sending off the request (part 1) */ + CURLM_STATE_DO_MORE, /* 11 - send off the request (part 2) */ + CURLM_STATE_DO_DONE, /* 12 - done sending off request */ + CURLM_STATE_WAITPERFORM, /* 13 - wait for our turn to read the response */ + CURLM_STATE_PERFORM, /* 14 - transfer data */ + CURLM_STATE_TOOFAST, /* 15 - wait because limit-rate exceeded */ + CURLM_STATE_DONE, /* 16 - post data transfer operation */ + CURLM_STATE_COMPLETED, /* 17 - operation complete */ + CURLM_STATE_MSGSENT, /* 18 - the operation complete message is sent */ + CURLM_STATE_LAST /* 19 - not a true state, never use this */ } CURLMstate; /* we support N sockets per easy handle. Set the corresponding bit to what