http_proxy.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
00026 
00027 #include "urldata.h"
00028 #include <curl/curl.h>
00029 #include "http_proxy.h"
00030 #include "sendf.h"
00031 #include "http.h"
00032 #include "url.h"
00033 #include "select.h"
00034 #include "progress.h"
00035 #include "non-ascii.h"
00036 #include "connect.h"
00037 #include "curlx.h"
00038 #include "vtls/vtls.h"
00039 
00040 /* The last 3 #include files should be in this order */
00041 #include "curl_printf.h"
00042 #include "curl_memory.h"
00043 #include "memdebug.h"
00044 
00045 /*
00046  * Perform SSL initialization for HTTPS proxy.  Sets
00047  * proxy_ssl_connected connection bit when complete.  Can be
00048  * called multiple times.
00049  */
00050 static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
00051 {
00052 #ifdef USE_SSL
00053   CURLcode result = CURLE_OK;
00054   DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
00055   if(!conn->bits.proxy_ssl_connected[sockindex]) {
00056     /* perform SSL initialization for this socket */
00057     result =
00058       Curl_ssl_connect_nonblocking(conn, sockindex,
00059                                    &conn->bits.proxy_ssl_connected[sockindex]);
00060     if(result)
00061       conn->bits.close = TRUE; /* a failed connection is marked for closure to
00062                                   prevent (bad) re-use or similar */
00063   }
00064   return result;
00065 #else
00066   (void) conn;
00067   (void) sockindex;
00068   return CURLE_NOT_BUILT_IN;
00069 #endif
00070 }
00071 
00072 CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
00073 {
00074   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
00075     const CURLcode result = https_proxy_connect(conn, sockindex);
00076     if(result)
00077       return result;
00078     if(!conn->bits.proxy_ssl_connected[sockindex])
00079       return result; /* wait for HTTPS proxy SSL initialization to complete */
00080   }
00081 
00082   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
00083 #ifndef CURL_DISABLE_PROXY
00084     /* for [protocol] tunneled through HTTP proxy */
00085     struct HTTP http_proxy;
00086     void *prot_save;
00087     const char *hostname;
00088     int remote_port;
00089     CURLcode result;
00090 
00091     /* BLOCKING */
00092     /* We want "seamless" operations through HTTP proxy tunnel */
00093 
00094     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
00095      * member conn->proto.http; we want [protocol] through HTTP and we have
00096      * to change the member temporarily for connecting to the HTTP
00097      * proxy. After Curl_proxyCONNECT we have to set back the member to the
00098      * original pointer
00099      *
00100      * This function might be called several times in the multi interface case
00101      * if the proxy's CONNTECT response is not instant.
00102      */
00103     prot_save = conn->data->req.protop;
00104     memset(&http_proxy, 0, sizeof(http_proxy));
00105     conn->data->req.protop = &http_proxy;
00106     connkeep(conn, "HTTP proxy CONNECT");
00107     if(sockindex == SECONDARYSOCKET)
00108       hostname = conn->secondaryhostname;
00109     else if(conn->bits.conn_to_host)
00110       hostname = conn->conn_to_host.name;
00111     else
00112       hostname = conn->host.name;
00113 
00114     if(sockindex == SECONDARYSOCKET)
00115       remote_port = conn->secondary_port;
00116     else if(conn->bits.conn_to_port)
00117       remote_port = conn->conn_to_port;
00118     else
00119       remote_port = conn->remote_port;
00120     result = Curl_proxyCONNECT(conn, sockindex, hostname,
00121                                remote_port, FALSE);
00122     conn->data->req.protop = prot_save;
00123     if(CURLE_OK != result)
00124       return result;
00125     Curl_safefree(conn->allocptr.proxyuserpwd);
00126 #else
00127     return CURLE_NOT_BUILT_IN;
00128 #endif
00129   }
00130   /* no HTTP tunnel proxy, just return */
00131   return CURLE_OK;
00132 }
00133 
00134 /*
00135  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
00136  * function will issue the necessary commands to get a seamless tunnel through
00137  * this proxy. After that, the socket can be used just as a normal socket.
00138  *
00139  * 'blocking' set to TRUE means that this function will do the entire CONNECT
00140  * + response in a blocking fashion. Should be avoided!
00141  */
00142 
00143 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
00144                            int sockindex,
00145                            const char *hostname,
00146                            int remote_port,
00147                            bool blocking)
00148 {
00149   int subversion=0;
00150   struct Curl_easy *data=conn->data;
00151   struct SingleRequest *k = &data->req;
00152   CURLcode result;
00153   curl_socket_t tunnelsocket = conn->sock[sockindex];
00154   curl_off_t cl=0;
00155   bool closeConnection = FALSE;
00156   bool chunked_encoding = FALSE;
00157   time_t check;
00158 
00159 #define SELECT_OK      0
00160 #define SELECT_ERROR   1
00161 #define SELECT_TIMEOUT 2
00162   int error = SELECT_OK;
00163 
00164   if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
00165     return CURLE_OK; /* CONNECT is already completed */
00166 
00167   conn->bits.proxy_connect_closed = FALSE;
00168 
00169   do {
00170     if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
00171       /* BEGIN CONNECT PHASE */
00172       char *host_port;
00173       Curl_send_buffer *req_buffer;
00174 
00175       infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
00176             hostname, remote_port);
00177 
00178         /* This only happens if we've looped here due to authentication
00179            reasons, and we don't really use the newly cloned URL here
00180            then. Just free() it. */
00181       free(data->req.newurl);
00182       data->req.newurl = NULL;
00183 
00184       /* initialize a dynamic send-buffer */
00185       req_buffer = Curl_add_buffer_init();
00186 
00187       if(!req_buffer)
00188         return CURLE_OUT_OF_MEMORY;
00189 
00190       host_port = aprintf("%s:%hu", hostname, remote_port);
00191       if(!host_port) {
00192         Curl_add_buffer_free(req_buffer);
00193         return CURLE_OUT_OF_MEMORY;
00194       }
00195 
00196       /* Setup the proxy-authorization header, if any */
00197       result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
00198 
00199       free(host_port);
00200 
00201       if(!result) {
00202         char *host=(char *)"";
00203         const char *proxyconn="";
00204         const char *useragent="";
00205         const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ?
00206           "1.0" : "1.1";
00207         bool ipv6_ip = conn->bits.ipv6_ip;
00208         char *hostheader;
00209 
00210         /* the hostname may be different */
00211         if(hostname != conn->host.name)
00212           ipv6_ip = (strchr(hostname, ':') != NULL);
00213         hostheader= /* host:port with IPv6 support */
00214           aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
00215                   remote_port);
00216         if(!hostheader) {
00217           Curl_add_buffer_free(req_buffer);
00218           return CURLE_OUT_OF_MEMORY;
00219         }
00220 
00221         if(!Curl_checkProxyheaders(conn, "Host:")) {
00222           host = aprintf("Host: %s\r\n", hostheader);
00223           if(!host) {
00224             free(hostheader);
00225             Curl_add_buffer_free(req_buffer);
00226             return CURLE_OUT_OF_MEMORY;
00227           }
00228         }
00229         if(!Curl_checkProxyheaders(conn, "Proxy-Connection:"))
00230           proxyconn = "Proxy-Connection: Keep-Alive\r\n";
00231 
00232         if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
00233            data->set.str[STRING_USERAGENT])
00234           useragent = conn->allocptr.uagent;
00235 
00236         result =
00237           Curl_add_bufferf(req_buffer,
00238                            "CONNECT %s HTTP/%s\r\n"
00239                            "%s"  /* Host: */
00240                            "%s"  /* Proxy-Authorization */
00241                            "%s"  /* User-Agent */
00242                            "%s", /* Proxy-Connection */
00243                            hostheader,
00244                            http,
00245                            host,
00246                            conn->allocptr.proxyuserpwd?
00247                            conn->allocptr.proxyuserpwd:"",
00248                            useragent,
00249                            proxyconn);
00250 
00251         if(host && *host)
00252           free(host);
00253         free(hostheader);
00254 
00255         if(!result)
00256           result = Curl_add_custom_headers(conn, TRUE, req_buffer);
00257 
00258         if(!result)
00259           /* CRLF terminate the request */
00260           result = Curl_add_bufferf(req_buffer, "\r\n");
00261 
00262         if(!result) {
00263           /* Send the connect request to the proxy */
00264           /* BLOCKING */
00265           result =
00266             Curl_add_buffer_send(req_buffer, conn,
00267                                  &data->info.request_size, 0, sockindex);
00268         }
00269         req_buffer = NULL;
00270         if(result)
00271           failf(data, "Failed sending CONNECT to proxy");
00272       }
00273 
00274       Curl_add_buffer_free(req_buffer);
00275       if(result)
00276         return result;
00277 
00278       conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
00279     } /* END CONNECT PHASE */
00280 
00281     check = Curl_timeleft(data, NULL, TRUE);
00282     if(check <= 0) {
00283       failf(data, "Proxy CONNECT aborted due to timeout");
00284       return CURLE_RECV_ERROR;
00285     }
00286 
00287     if(!blocking) {
00288       if(!Curl_conn_data_pending(conn, sockindex))
00289         /* return so we'll be called again polling-style */
00290         return CURLE_OK;
00291       else {
00292         DEBUGF(infof(data,
00293                "Read response immediately from proxy CONNECT\n"));
00294       }
00295     }
00296 
00297     /* at this point, the tunnel_connecting phase is over. */
00298 
00299     { /* READING RESPONSE PHASE */
00300       size_t nread;   /* total size read */
00301       int perline; /* count bytes per line */
00302       int keepon=TRUE;
00303       ssize_t gotbytes;
00304       char *ptr;
00305       char *line_start;
00306 
00307       ptr = data->state.buffer;
00308       line_start = ptr;
00309 
00310       nread = 0;
00311       perline = 0;
00312 
00313       while(nread < BUFSIZE && keepon && !error) {
00314         int writetype;
00315 
00316         if(Curl_pgrsUpdate(conn))
00317           return CURLE_ABORTED_BY_CALLBACK;
00318 
00319         if(ptr >= &data->state.buffer[BUFSIZE]) {
00320           failf(data, "CONNECT response too large!");
00321           return CURLE_RECV_ERROR;
00322         }
00323 
00324         check = Curl_timeleft(data, NULL, TRUE);
00325         if(check <= 0) {
00326           failf(data, "Proxy CONNECT aborted due to timeout");
00327           error = SELECT_TIMEOUT; /* already too little time */
00328           break;
00329         }
00330 
00331         /* Read one byte at a time to avoid a race condition. Wait at most one
00332            second before looping to ensure continuous pgrsUpdates. */
00333         result = Curl_read(conn, tunnelsocket, ptr, 1, &gotbytes);
00334         if(result == CURLE_AGAIN) {
00335           if(SOCKET_READABLE(tunnelsocket, check<1000L?check:1000) == -1) {
00336             error = SELECT_ERROR;
00337             failf(data, "Proxy CONNECT aborted due to select/poll error");
00338             break;
00339           }
00340           continue;
00341         }
00342         else if(result) {
00343           keepon = FALSE;
00344           break;
00345         }
00346         else if(gotbytes <= 0) {
00347           if(data->set.proxyauth && data->state.authproxy.avail) {
00348             /* proxy auth was requested and there was proxy auth available,
00349                then deem this as "mere" proxy disconnect */
00350             conn->bits.proxy_connect_closed = TRUE;
00351             infof(data, "Proxy CONNECT connection closed\n");
00352           }
00353           else {
00354             error = SELECT_ERROR;
00355             failf(data, "Proxy CONNECT aborted");
00356           }
00357           keepon = FALSE;
00358           break;
00359         }
00360 
00361         /* We got a byte of data */
00362         nread++;
00363 
00364         if(keepon > TRUE) {
00365           /* This means we are currently ignoring a response-body */
00366 
00367           nread = 0; /* make next read start over in the read buffer */
00368           ptr = data->state.buffer;
00369           if(cl) {
00370             /* A Content-Length based body: simply count down the counter
00371                and make sure to break out of the loop when we're done! */
00372             cl--;
00373             if(cl <= 0) {
00374               keepon = FALSE;
00375               break;
00376             }
00377           }
00378           else {
00379             /* chunked-encoded body, so we need to do the chunked dance
00380                properly to know when the end of the body is reached */
00381             CHUNKcode r;
00382             ssize_t tookcareof = 0;
00383 
00384             /* now parse the chunked piece of data so that we can
00385                properly tell when the stream ends */
00386             r = Curl_httpchunk_read(conn, ptr, 1, &tookcareof);
00387             if(r == CHUNKE_STOP) {
00388               /* we're done reading chunks! */
00389               infof(data, "chunk reading DONE\n");
00390               keepon = FALSE;
00391               /* we did the full CONNECT treatment, go COMPLETE */
00392               conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
00393             }
00394           }
00395           continue;
00396         }
00397 
00398         perline++; /* amount of bytes in this line so far */
00399 
00400         /* if this is not the end of a header line then continue */
00401         if(*ptr != 0x0a) {
00402           ptr++;
00403           continue;
00404         }
00405 
00406         /* convert from the network encoding */
00407         result = Curl_convert_from_network(data, line_start, perline);
00408         /* Curl_convert_from_network calls failf if unsuccessful */
00409         if(result)
00410           return result;
00411 
00412         /* output debug if that is requested */
00413         if(data->set.verbose)
00414           Curl_debug(data, CURLINFO_HEADER_IN,
00415                      line_start, (size_t)perline, conn);
00416 
00417         /* send the header to the callback */
00418         writetype = CLIENTWRITE_HEADER;
00419         if(data->set.include_header)
00420           writetype |= CLIENTWRITE_BODY;
00421 
00422         result = Curl_client_write(conn, writetype, line_start, perline);
00423 
00424         data->info.header_size += (long)perline;
00425         data->req.headerbytecount += (long)perline;
00426 
00427         if(result)
00428           return result;
00429 
00430         /* Newlines are CRLF, so the CR is ignored as the line isn't
00431            really terminated until the LF comes. Treat a following CR
00432            as end-of-headers as well.*/
00433 
00434         if(('\r' == line_start[0]) ||
00435            ('\n' == line_start[0])) {
00436           /* end of response-headers from the proxy */
00437           nread = 0; /* make next read start over in the read
00438                         buffer */
00439           ptr = data->state.buffer;
00440           if((407 == k->httpcode) && !data->state.authproblem) {
00441             /* If we get a 407 response code with content length
00442                when we have no auth problem, we must ignore the
00443                whole response-body */
00444             keepon = 2;
00445 
00446             if(cl) {
00447               infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
00448                     " bytes of response-body\n", cl);
00449             }
00450             else if(chunked_encoding) {
00451               CHUNKcode r;
00452 
00453               infof(data, "Ignore chunked response-body\n");
00454 
00455               /* We set ignorebody true here since the chunked
00456                  decoder function will acknowledge that. Pay
00457                  attention so that this is cleared again when this
00458                  function returns! */
00459               k->ignorebody = TRUE;
00460 
00461               if(line_start[1] == '\n') {
00462                 /* this can only be a LF if the letter at index 0
00463                    was a CR */
00464                 line_start++;
00465               }
00466 
00467               /* now parse the chunked piece of data so that we can
00468                  properly tell when the stream ends */
00469               r = Curl_httpchunk_read(conn, line_start + 1, 1, &gotbytes);
00470               if(r == CHUNKE_STOP) {
00471                 /* we're done reading chunks! */
00472                 infof(data, "chunk reading DONE\n");
00473                 keepon = FALSE;
00474                 /* we did the full CONNECT treatment, go to
00475                    COMPLETE */
00476                 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
00477               }
00478             }
00479             else {
00480               /* without content-length or chunked encoding, we
00481                  can't keep the connection alive since the close is
00482                  the end signal so we bail out at once instead */
00483               keepon = FALSE;
00484             }
00485           }
00486           else
00487             keepon = FALSE;
00488           /* we did the full CONNECT treatment, go to COMPLETE */
00489           conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
00490           continue;
00491         }
00492 
00493         line_start[perline] = 0; /* zero terminate the buffer */
00494         if((checkprefix("WWW-Authenticate:", line_start) &&
00495             (401 == k->httpcode)) ||
00496            (checkprefix("Proxy-authenticate:", line_start) &&
00497             (407 == k->httpcode))) {
00498 
00499           bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
00500           char *auth = Curl_copy_header_value(line_start);
00501           if(!auth)
00502             return CURLE_OUT_OF_MEMORY;
00503 
00504           result = Curl_http_input_auth(conn, proxy, auth);
00505 
00506           free(auth);
00507 
00508           if(result)
00509             return result;
00510         }
00511         else if(checkprefix("Content-Length:", line_start)) {
00512           if(k->httpcode/100 == 2) {
00513             /* A server MUST NOT send any Transfer-Encoding or
00514                Content-Length header fields in a 2xx (Successful)
00515                response to CONNECT. (RFC 7231 section 4.3.6) */
00516             failf(data, "Content-Length: in %03d response",
00517                   k->httpcode);
00518             return CURLE_RECV_ERROR;
00519           }
00520 
00521           cl = curlx_strtoofft(line_start +
00522                                strlen("Content-Length:"), NULL, 10);
00523         }
00524         else if(Curl_compareheader(line_start, "Connection:", "close"))
00525           closeConnection = TRUE;
00526         else if(Curl_compareheader(line_start,
00527                                    "Transfer-Encoding:",
00528                                    "chunked")) {
00529           if(k->httpcode/100 == 2) {
00530             /* A server MUST NOT send any Transfer-Encoding or
00531                Content-Length header fields in a 2xx (Successful)
00532                response to CONNECT. (RFC 7231 section 4.3.6) */
00533             failf(data, "Transfer-Encoding: in %03d response", k->httpcode);
00534             return CURLE_RECV_ERROR;
00535           }
00536           infof(data, "CONNECT responded chunked\n");
00537           chunked_encoding = TRUE;
00538           /* init our chunky engine */
00539           Curl_httpchunk_init(conn);
00540         }
00541         else if(Curl_compareheader(line_start, "Proxy-Connection:", "close"))
00542           closeConnection = TRUE;
00543         else if(2 == sscanf(line_start, "HTTP/1.%d %d",
00544                             &subversion,
00545                             &k->httpcode)) {
00546           /* store the HTTP code from the proxy */
00547           data->info.httpproxycode = k->httpcode;
00548         }
00549 
00550         perline = 0; /* line starts over here */
00551         ptr = data->state.buffer;
00552         line_start = ptr;
00553       } /* while there's buffer left and loop is requested */
00554 
00555       if(Curl_pgrsUpdate(conn))
00556         return CURLE_ABORTED_BY_CALLBACK;
00557 
00558       if(error)
00559         return CURLE_RECV_ERROR;
00560 
00561       if(data->info.httpproxycode != 200) {
00562         /* Deal with the possibly already received authenticate
00563            headers. 'newurl' is set to a new URL if we must loop. */
00564         result = Curl_http_auth_act(conn);
00565         if(result)
00566           return result;
00567 
00568         if(conn->bits.close)
00569           /* the connection has been marked for closure, most likely in the
00570              Curl_http_auth_act() function and thus we can kill it at once
00571              below */
00572           closeConnection = TRUE;
00573       }
00574 
00575       if(closeConnection && data->req.newurl) {
00576         /* Connection closed by server. Don't use it anymore */
00577         Curl_closesocket(conn, conn->sock[sockindex]);
00578         conn->sock[sockindex] = CURL_SOCKET_BAD;
00579         break;
00580       }
00581     } /* END READING RESPONSE PHASE */
00582 
00583     /* If we are supposed to continue and request a new URL, which basically
00584      * means the HTTP authentication is still going on so if the tunnel
00585      * is complete we start over in INIT state */
00586     if(data->req.newurl &&
00587        (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
00588       conn->tunnel_state[sockindex] = TUNNEL_INIT;
00589       infof(data, "TUNNEL_STATE switched to: %d\n",
00590             conn->tunnel_state[sockindex]);
00591     }
00592 
00593   } while(data->req.newurl);
00594 
00595   if(200 != data->req.httpcode) {
00596     if(closeConnection && data->req.newurl) {
00597       conn->bits.proxy_connect_closed = TRUE;
00598       infof(data, "Connect me again please\n");
00599     }
00600     else {
00601       free(data->req.newurl);
00602       data->req.newurl = NULL;
00603       /* failure, close this connection to avoid re-use */
00604       streamclose(conn, "proxy CONNECT failure");
00605       Curl_closesocket(conn, conn->sock[sockindex]);
00606       conn->sock[sockindex] = CURL_SOCKET_BAD;
00607     }
00608 
00609     /* to back to init state */
00610     conn->tunnel_state[sockindex] = TUNNEL_INIT;
00611 
00612     if(conn->bits.proxy_connect_closed)
00613       /* this is not an error, just part of the connection negotiation */
00614       return CURLE_OK;
00615     else {
00616       failf(data, "Received HTTP code %d from proxy after CONNECT",
00617             data->req.httpcode);
00618       return CURLE_RECV_ERROR;
00619     }
00620   }
00621 
00622   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
00623 
00624   /* If a proxy-authorization header was used for the proxy, then we should
00625      make sure that it isn't accidentally used for the document request
00626      after we've connected. So let's free and clear it here. */
00627   Curl_safefree(conn->allocptr.proxyuserpwd);
00628   conn->allocptr.proxyuserpwd = NULL;
00629 
00630   data->state.authproxy.done = TRUE;
00631 
00632   infof(data, "Proxy replied OK to CONNECT request\n");
00633   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
00634   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
00635                                          document request  */
00636   return CURLE_OK;
00637 }
00638 #endif /* CURL_DISABLE_PROXY */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:04