Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #include "http_proxy.h"
     26 
     27 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
     28 
     29 #include <curl/curl.h>
     30 #include "sendf.h"
     31 #include "http.h"
     32 #include "url.h"
     33 #include "select.h"
     34 #include "progress.h"
     35 #include "non-ascii.h"
     36 #include "connect.h"
     37 #include "curlx.h"
     38 #include "vtls/vtls.h"
     39 
     40 /* The last 3 #include files should be in this order */
     41 #include "curl_printf.h"
     42 #include "curl_memory.h"
     43 #include "memdebug.h"
     44 
     45 /*
     46  * Perform SSL initialization for HTTPS proxy.  Sets
     47  * proxy_ssl_connected connection bit when complete.  Can be
     48  * called multiple times.
     49  */
     50 static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
     51 {
     52 #ifdef USE_SSL
     53   CURLcode result = CURLE_OK;
     54   DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
     55   if(!conn->bits.proxy_ssl_connected[sockindex]) {
     56     /* perform SSL initialization for this socket */
     57     result =
     58       Curl_ssl_connect_nonblocking(conn, sockindex,
     59                                    &conn->bits.proxy_ssl_connected[sockindex]);
     60     if(result)
     61       conn->bits.close = TRUE; /* a failed connection is marked for closure to
     62                                   prevent (bad) re-use or similar */
     63   }
     64   return result;
     65 #else
     66   (void) conn;
     67   (void) sockindex;
     68   return CURLE_NOT_BUILT_IN;
     69 #endif
     70 }
     71 
     72 CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
     73 {
     74   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
     75     const CURLcode result = https_proxy_connect(conn, sockindex);
     76     if(result)
     77       return result;
     78     if(!conn->bits.proxy_ssl_connected[sockindex])
     79       return result; /* wait for HTTPS proxy SSL initialization to complete */
     80   }
     81 
     82   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
     83 #ifndef CURL_DISABLE_PROXY
     84     /* for [protocol] tunneled through HTTP proxy */
     85     struct HTTP http_proxy;
     86     void *prot_save;
     87     const char *hostname;
     88     int remote_port;
     89     CURLcode result;
     90 
     91     /* BLOCKING */
     92     /* We want "seamless" operations through HTTP proxy tunnel */
     93 
     94     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
     95      * member conn->proto.http; we want [protocol] through HTTP and we have
     96      * to change the member temporarily for connecting to the HTTP
     97      * proxy. After Curl_proxyCONNECT we have to set back the member to the
     98      * original pointer
     99      *
    100      * This function might be called several times in the multi interface case
    101      * if the proxy's CONNECT response is not instant.
    102      */
    103     prot_save = conn->data->req.protop;
    104     memset(&http_proxy, 0, sizeof(http_proxy));
    105     conn->data->req.protop = &http_proxy;
    106     connkeep(conn, "HTTP proxy CONNECT");
    107 
    108     /* for the secondary socket (FTP), use the "connect to host"
    109      * but ignore the "connect to port" (use the secondary port)
    110      */
    111 
    112     if(conn->bits.conn_to_host)
    113       hostname = conn->conn_to_host.name;
    114     else if(sockindex == SECONDARYSOCKET)
    115       hostname = conn->secondaryhostname;
    116     else
    117       hostname = conn->host.name;
    118 
    119     if(sockindex == SECONDARYSOCKET)
    120       remote_port = conn->secondary_port;
    121     else if(conn->bits.conn_to_port)
    122       remote_port = conn->conn_to_port;
    123     else
    124       remote_port = conn->remote_port;
    125     result = Curl_proxyCONNECT(conn, sockindex, hostname, remote_port);
    126     conn->data->req.protop = prot_save;
    127     if(CURLE_OK != result)
    128       return result;
    129     Curl_safefree(conn->allocptr.proxyuserpwd);
    130 #else
    131     return CURLE_NOT_BUILT_IN;
    132 #endif
    133   }
    134   /* no HTTP tunnel proxy, just return */
    135   return CURLE_OK;
    136 }
    137 
    138 bool Curl_connect_complete(struct connectdata *conn)
    139 {
    140   return !conn->connect_state ||
    141     (conn->connect_state->tunnel_state == TUNNEL_COMPLETE);
    142 }
    143 
    144 bool Curl_connect_ongoing(struct connectdata *conn)
    145 {
    146   return conn->connect_state &&
    147     (conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
    148 }
    149 
    150 static CURLcode connect_init(struct connectdata *conn, bool reinit)
    151 {
    152   struct http_connect_state *s;
    153   if(!reinit) {
    154     DEBUGASSERT(!conn->connect_state);
    155     s = calloc(1, sizeof(struct http_connect_state));
    156     if(!s)
    157       return CURLE_OUT_OF_MEMORY;
    158     infof(conn->data, "allocate connect buffer!\n");
    159     conn->connect_state = s;
    160   }
    161   else {
    162     DEBUGASSERT(conn->connect_state);
    163     s = conn->connect_state;
    164   }
    165   s->tunnel_state = TUNNEL_INIT;
    166   s->keepon = TRUE;
    167   s->line_start = s->connect_buffer;
    168   s->ptr = s->line_start;
    169   s->cl = 0;
    170   s->close_connection = FALSE;
    171   return CURLE_OK;
    172 }
    173 
    174 static void connect_done(struct connectdata *conn)
    175 {
    176   struct http_connect_state *s = conn->connect_state;
    177   s->tunnel_state = TUNNEL_COMPLETE;
    178   infof(conn->data, "CONNECT phase completed!\n");
    179 }
    180 
    181 static CURLcode CONNECT(struct connectdata *conn,
    182                         int sockindex,
    183                         const char *hostname,
    184                         int remote_port)
    185 {
    186   int subversion = 0;
    187   struct Curl_easy *data = conn->data;
    188   struct SingleRequest *k = &data->req;
    189   CURLcode result;
    190   curl_socket_t tunnelsocket = conn->sock[sockindex];
    191   timediff_t check;
    192   struct http_connect_state *s = conn->connect_state;
    193 
    194 #define SELECT_OK      0
    195 #define SELECT_ERROR   1
    196 #define SELECT_TIMEOUT 2
    197 
    198   if(Curl_connect_complete(conn))
    199     return CURLE_OK; /* CONNECT is already completed */
    200 
    201   conn->bits.proxy_connect_closed = FALSE;
    202 
    203   do {
    204     if(TUNNEL_INIT == s->tunnel_state) {
    205       /* BEGIN CONNECT PHASE */
    206       char *host_port;
    207       Curl_send_buffer *req_buffer;
    208 
    209       infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
    210             hostname, remote_port);
    211 
    212         /* This only happens if we've looped here due to authentication
    213            reasons, and we don't really use the newly cloned URL here
    214            then. Just free() it. */
    215       free(data->req.newurl);
    216       data->req.newurl = NULL;
    217 
    218       /* initialize a dynamic send-buffer */
    219       req_buffer = Curl_add_buffer_init();
    220 
    221       if(!req_buffer)
    222         return CURLE_OUT_OF_MEMORY;
    223 
    224       host_port = aprintf("%s:%hu", hostname, remote_port);
    225       if(!host_port) {
    226         Curl_add_buffer_free(req_buffer);
    227         return CURLE_OUT_OF_MEMORY;
    228       }
    229 
    230       /* Setup the proxy-authorization header, if any */
    231       result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
    232 
    233       free(host_port);
    234 
    235       if(!result) {
    236         char *host = NULL;
    237         const char *proxyconn = "";
    238         const char *useragent = "";
    239         const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ?
    240           "1.0" : "1.1";
    241         bool ipv6_ip = conn->bits.ipv6_ip;
    242         char *hostheader;
    243 
    244         /* the hostname may be different */
    245         if(hostname != conn->host.name)
    246           ipv6_ip = (strchr(hostname, ':') != NULL);
    247         hostheader = /* host:port with IPv6 support */
    248           aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
    249                   remote_port);
    250         if(!hostheader) {
    251           Curl_add_buffer_free(req_buffer);
    252           return CURLE_OUT_OF_MEMORY;
    253         }
    254 
    255         if(!Curl_checkProxyheaders(conn, "Host:")) {
    256           host = aprintf("Host: %s\r\n", hostheader);
    257           if(!host) {
    258             free(hostheader);
    259             Curl_add_buffer_free(req_buffer);
    260             return CURLE_OUT_OF_MEMORY;
    261           }
    262         }
    263         if(!Curl_checkProxyheaders(conn, "Proxy-Connection:"))
    264           proxyconn = "Proxy-Connection: Keep-Alive\r\n";
    265 
    266         if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
    267            data->set.str[STRING_USERAGENT])
    268           useragent = conn->allocptr.uagent;
    269 
    270         result =
    271           Curl_add_bufferf(req_buffer,
    272                            "CONNECT %s HTTP/%s\r\n"
    273                            "%s"  /* Host: */
    274                            "%s"  /* Proxy-Authorization */
    275                            "%s"  /* User-Agent */
    276                            "%s", /* Proxy-Connection */
    277                            hostheader,
    278                            http,
    279                            host?host:"",
    280                            conn->allocptr.proxyuserpwd?
    281                            conn->allocptr.proxyuserpwd:"",
    282                            useragent,
    283                            proxyconn);
    284 
    285         if(host)
    286           free(host);
    287         free(hostheader);
    288 
    289         if(!result)
    290           result = Curl_add_custom_headers(conn, TRUE, req_buffer);
    291 
    292         if(!result)
    293           /* CRLF terminate the request */
    294           result = Curl_add_bufferf(req_buffer, "\r\n");
    295 
    296         if(!result) {
    297           /* Send the connect request to the proxy */
    298           /* BLOCKING */
    299           result =
    300             Curl_add_buffer_send(req_buffer, conn,
    301                                  &data->info.request_size, 0, sockindex);
    302         }
    303         req_buffer = NULL;
    304         if(result)
    305           failf(data, "Failed sending CONNECT to proxy");
    306       }
    307 
    308       Curl_add_buffer_free(req_buffer);
    309       if(result)
    310         return result;
    311 
    312       s->tunnel_state = TUNNEL_CONNECT;
    313       s->perline = 0;
    314     } /* END CONNECT PHASE */
    315 
    316     check = Curl_timeleft(data, NULL, TRUE);
    317     if(check <= 0) {
    318       failf(data, "Proxy CONNECT aborted due to timeout");
    319       return CURLE_OPERATION_TIMEDOUT;
    320     }
    321 
    322     if(!Curl_conn_data_pending(conn, sockindex))
    323       /* return so we'll be called again polling-style */
    324       return CURLE_OK;
    325 
    326     /* at this point, the tunnel_connecting phase is over. */
    327 
    328     { /* READING RESPONSE PHASE */
    329       int error = SELECT_OK;
    330 
    331       while(s->keepon && !error) {
    332         ssize_t gotbytes;
    333 
    334         /* make sure we have space to read more data */
    335         if(s->ptr >= &s->connect_buffer[CONNECT_BUFFER_SIZE]) {
    336           failf(data, "CONNECT response too large!");
    337           return CURLE_RECV_ERROR;
    338         }
    339 
    340         /* Read one byte at a time to avoid a race condition. Wait at most one
    341            second before looping to ensure continuous pgrsUpdates. */
    342         result = Curl_read(conn, tunnelsocket, s->ptr, 1, &gotbytes);
    343         if(result == CURLE_AGAIN)
    344           /* socket buffer drained, return */
    345           return CURLE_OK;
    346 
    347         if(Curl_pgrsUpdate(conn))
    348           return CURLE_ABORTED_BY_CALLBACK;
    349 
    350         if(result) {
    351           s->keepon = FALSE;
    352           break;
    353         }
    354         else if(gotbytes <= 0) {
    355           if(data->set.proxyauth && data->state.authproxy.avail) {
    356             /* proxy auth was requested and there was proxy auth available,
    357                then deem this as "mere" proxy disconnect */
    358             conn->bits.proxy_connect_closed = TRUE;
    359             infof(data, "Proxy CONNECT connection closed\n");
    360           }
    361           else {
    362             error = SELECT_ERROR;
    363             failf(data, "Proxy CONNECT aborted");
    364           }
    365           s->keepon = FALSE;
    366           break;
    367         }
    368 
    369 
    370         if(s->keepon > TRUE) {
    371           /* This means we are currently ignoring a response-body */
    372 
    373           s->ptr = s->connect_buffer;
    374           if(s->cl) {
    375             /* A Content-Length based body: simply count down the counter
    376                and make sure to break out of the loop when we're done! */
    377             s->cl--;
    378             if(s->cl <= 0) {
    379               s->keepon = FALSE;
    380               s->tunnel_state = TUNNEL_COMPLETE;
    381               break;
    382             }
    383           }
    384           else {
    385             /* chunked-encoded body, so we need to do the chunked dance
    386                properly to know when the end of the body is reached */
    387             CHUNKcode r;
    388             ssize_t tookcareof = 0;
    389 
    390             /* now parse the chunked piece of data so that we can
    391                properly tell when the stream ends */
    392             r = Curl_httpchunk_read(conn, s->ptr, 1, &tookcareof);
    393             if(r == CHUNKE_STOP) {
    394               /* we're done reading chunks! */
    395               infof(data, "chunk reading DONE\n");
    396               s->keepon = FALSE;
    397               /* we did the full CONNECT treatment, go COMPLETE */
    398               s->tunnel_state = TUNNEL_COMPLETE;
    399             }
    400           }
    401           continue;
    402         }
    403 
    404         s->perline++; /* amount of bytes in this line so far */
    405 
    406         /* if this is not the end of a header line then continue */
    407         if(*s->ptr != 0x0a) {
    408           s->ptr++;
    409           continue;
    410         }
    411 
    412         /* convert from the network encoding */
    413         result = Curl_convert_from_network(data, s->line_start,
    414                                            (size_t)s->perline);
    415         /* Curl_convert_from_network calls failf if unsuccessful */
    416         if(result)
    417           return result;
    418 
    419         /* output debug if that is requested */
    420         if(data->set.verbose)
    421           Curl_debug(data, CURLINFO_HEADER_IN,
    422                      s->line_start, (size_t)s->perline, conn);
    423 
    424         if(!data->set.suppress_connect_headers) {
    425           /* send the header to the callback */
    426           int writetype = CLIENTWRITE_HEADER;
    427           if(data->set.include_header)
    428             writetype |= CLIENTWRITE_BODY;
    429 
    430           result = Curl_client_write(conn, writetype,
    431                                      s->line_start, s->perline);
    432           if(result)
    433             return result;
    434         }
    435 
    436         data->info.header_size += (long)s->perline;
    437         data->req.headerbytecount += (long)s->perline;
    438 
    439         /* Newlines are CRLF, so the CR is ignored as the line isn't
    440            really terminated until the LF comes. Treat a following CR
    441            as end-of-headers as well.*/
    442 
    443         if(('\r' == s->line_start[0]) ||
    444            ('\n' == s->line_start[0])) {
    445           /* end of response-headers from the proxy */
    446           s->ptr = s->connect_buffer;
    447           if((407 == k->httpcode) && !data->state.authproblem) {
    448             /* If we get a 407 response code with content length
    449                when we have no auth problem, we must ignore the
    450                whole response-body */
    451             s->keepon = 2;
    452 
    453             if(s->cl) {
    454               infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
    455                     " bytes of response-body\n", s->cl);
    456             }
    457             else if(s->chunked_encoding) {
    458               CHUNKcode r;
    459 
    460               infof(data, "Ignore chunked response-body\n");
    461 
    462               /* We set ignorebody true here since the chunked
    463                  decoder function will acknowledge that. Pay
    464                  attention so that this is cleared again when this
    465                  function returns! */
    466               k->ignorebody = TRUE;
    467 
    468               if(s->line_start[1] == '\n') {
    469                 /* this can only be a LF if the letter at index 0
    470                    was a CR */
    471                 s->line_start++;
    472               }
    473 
    474               /* now parse the chunked piece of data so that we can
    475                  properly tell when the stream ends */
    476               r = Curl_httpchunk_read(conn, s->line_start + 1, 1, &gotbytes);
    477               if(r == CHUNKE_STOP) {
    478                 /* we're done reading chunks! */
    479                 infof(data, "chunk reading DONE\n");
    480                 s->keepon = FALSE;
    481                 /* we did the full CONNECT treatment, go to COMPLETE */
    482                 s->tunnel_state = TUNNEL_COMPLETE;
    483               }
    484             }
    485             else {
    486               /* without content-length or chunked encoding, we
    487                  can't keep the connection alive since the close is
    488                  the end signal so we bail out at once instead */
    489               s->keepon = FALSE;
    490             }
    491           }
    492           else
    493             s->keepon = FALSE;
    494           if(!s->cl)
    495             /* we did the full CONNECT treatment, go to COMPLETE */
    496             s->tunnel_state = TUNNEL_COMPLETE;
    497           continue;
    498         }
    499 
    500         s->line_start[s->perline] = 0; /* zero terminate the buffer */
    501         if((checkprefix("WWW-Authenticate:", s->line_start) &&
    502             (401 == k->httpcode)) ||
    503            (checkprefix("Proxy-authenticate:", s->line_start) &&
    504             (407 == k->httpcode))) {
    505 
    506           bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
    507           char *auth = Curl_copy_header_value(s->line_start);
    508           if(!auth)
    509             return CURLE_OUT_OF_MEMORY;
    510 
    511           result = Curl_http_input_auth(conn, proxy, auth);
    512 
    513           free(auth);
    514 
    515           if(result)
    516             return result;
    517         }
    518         else if(checkprefix("Content-Length:", s->line_start)) {
    519           if(k->httpcode/100 == 2) {
    520             /* A client MUST ignore any Content-Length or Transfer-Encoding
    521                header fields received in a successful response to CONNECT.
    522                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
    523             infof(data, "Ignoring Content-Length in CONNECT %03d response\n",
    524                   k->httpcode);
    525           }
    526           else {
    527             (void)curlx_strtoofft(s->line_start +
    528                                   strlen("Content-Length:"), NULL, 10, &s->cl);
    529           }
    530         }
    531         else if(Curl_compareheader(s->line_start, "Connection:", "close"))
    532           s->close_connection = TRUE;
    533         else if(checkprefix("Transfer-Encoding:", s->line_start)) {
    534           if(k->httpcode/100 == 2) {
    535             /* A client MUST ignore any Content-Length or Transfer-Encoding
    536                header fields received in a successful response to CONNECT.
    537                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
    538             infof(data, "Ignoring Transfer-Encoding in "
    539                   "CONNECT %03d response\n", k->httpcode);
    540           }
    541           else if(Curl_compareheader(s->line_start,
    542                                      "Transfer-Encoding:", "chunked")) {
    543             infof(data, "CONNECT responded chunked\n");
    544             s->chunked_encoding = TRUE;
    545             /* init our chunky engine */
    546             Curl_httpchunk_init(conn);
    547           }
    548         }
    549         else if(Curl_compareheader(s->line_start,
    550                                    "Proxy-Connection:", "close"))
    551           s->close_connection = TRUE;
    552         else if(2 == sscanf(s->line_start, "HTTP/1.%d %d",
    553                             &subversion,
    554                             &k->httpcode)) {
    555           /* store the HTTP code from the proxy */
    556           data->info.httpproxycode = k->httpcode;
    557         }
    558 
    559         s->perline = 0; /* line starts over here */
    560         s->ptr = s->connect_buffer;
    561         s->line_start = s->ptr;
    562       } /* while there's buffer left and loop is requested */
    563 
    564       if(Curl_pgrsUpdate(conn))
    565         return CURLE_ABORTED_BY_CALLBACK;
    566 
    567       if(error)
    568         return CURLE_RECV_ERROR;
    569 
    570       if(data->info.httpproxycode/100 != 2) {
    571         /* Deal with the possibly already received authenticate
    572            headers. 'newurl' is set to a new URL if we must loop. */
    573         result = Curl_http_auth_act(conn);
    574         if(result)
    575           return result;
    576 
    577         if(conn->bits.close)
    578           /* the connection has been marked for closure, most likely in the
    579              Curl_http_auth_act() function and thus we can kill it at once
    580              below */
    581           s->close_connection = TRUE;
    582       }
    583 
    584       if(s->close_connection && data->req.newurl) {
    585         /* Connection closed by server. Don't use it anymore */
    586         Curl_closesocket(conn, conn->sock[sockindex]);
    587         conn->sock[sockindex] = CURL_SOCKET_BAD;
    588         break;
    589       }
    590     } /* END READING RESPONSE PHASE */
    591 
    592     /* If we are supposed to continue and request a new URL, which basically
    593      * means the HTTP authentication is still going on so if the tunnel
    594      * is complete we start over in INIT state */
    595     if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
    596       connect_init(conn, TRUE); /* reinit */
    597     }
    598 
    599   } while(data->req.newurl);
    600 
    601   if(data->info.httpproxycode/100 != 2) {
    602     if(s->close_connection && data->req.newurl) {
    603       conn->bits.proxy_connect_closed = TRUE;
    604       infof(data, "Connect me again please\n");
    605       connect_done(conn);
    606     }
    607     else {
    608       free(data->req.newurl);
    609       data->req.newurl = NULL;
    610       /* failure, close this connection to avoid re-use */
    611       streamclose(conn, "proxy CONNECT failure");
    612       Curl_closesocket(conn, conn->sock[sockindex]);
    613       conn->sock[sockindex] = CURL_SOCKET_BAD;
    614     }
    615 
    616     /* to back to init state */
    617     s->tunnel_state = TUNNEL_INIT;
    618 
    619     if(conn->bits.proxy_connect_closed)
    620       /* this is not an error, just part of the connection negotiation */
    621       return CURLE_OK;
    622     failf(data, "Received HTTP code %d from proxy after CONNECT",
    623           data->req.httpcode);
    624     return CURLE_RECV_ERROR;
    625   }
    626 
    627   s->tunnel_state = TUNNEL_COMPLETE;
    628 
    629   /* If a proxy-authorization header was used for the proxy, then we should
    630      make sure that it isn't accidentally used for the document request
    631      after we've connected. So let's free and clear it here. */
    632   Curl_safefree(conn->allocptr.proxyuserpwd);
    633   conn->allocptr.proxyuserpwd = NULL;
    634 
    635   data->state.authproxy.done = TRUE;
    636 
    637   infof(data, "Proxy replied %d to CONNECT request\n",
    638         data->info.httpproxycode);
    639   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
    640   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
    641                                          document request  */
    642   return CURLE_OK;
    643 }
    644 
    645 void Curl_connect_free(struct Curl_easy *data)
    646 {
    647   struct connectdata *conn = data->easy_conn;
    648   struct http_connect_state *s = conn->connect_state;
    649   if(s) {
    650     free(s);
    651     conn->connect_state = NULL;
    652   }
    653 }
    654 
    655 /*
    656  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
    657  * function will issue the necessary commands to get a seamless tunnel through
    658  * this proxy. After that, the socket can be used just as a normal socket.
    659  */
    660 
    661 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
    662                            int sockindex,
    663                            const char *hostname,
    664                            int remote_port)
    665 {
    666   CURLcode result;
    667   if(!conn->connect_state) {
    668     result = connect_init(conn, FALSE);
    669     if(result)
    670       return result;
    671   }
    672   result = CONNECT(conn, sockindex, hostname, remote_port);
    673 
    674   if(result || Curl_connect_complete(conn))
    675     connect_done(conn);
    676 
    677   return result;
    678 }
    679 
    680 #else
    681 void Curl_connect_free(struct Curl_easy *data)
    682 {
    683   (void)data;
    684 }
    685 
    686 #endif /* CURL_DISABLE_PROXY */
    687