Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, 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 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
     26 
     27 #include "urldata.h"
     28 #include <curl/curl.h>
     29 #include "http_proxy.h"
     30 #include "sendf.h"
     31 #include "http.h"
     32 #include "url.h"
     33 #include "select.h"
     34 #include "rawstr.h"
     35 #include "progress.h"
     36 #include "non-ascii.h"
     37 #include "connect.h"
     38 #include "curlx.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 CURLcode Curl_proxy_connect(struct connectdata *conn)
     46 {
     47   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
     48 #ifndef CURL_DISABLE_PROXY
     49     /* for [protocol] tunneled through HTTP proxy */
     50     struct HTTP http_proxy;
     51     void *prot_save;
     52     const char *hostname;
     53     int remote_port;
     54     CURLcode result;
     55 
     56     /* BLOCKING */
     57     /* We want "seamless" operations through HTTP proxy tunnel */
     58 
     59     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
     60      * member conn->proto.http; we want [protocol] through HTTP and we have
     61      * to change the member temporarily for connecting to the HTTP
     62      * proxy. After Curl_proxyCONNECT we have to set back the member to the
     63      * original pointer
     64      *
     65      * This function might be called several times in the multi interface case
     66      * if the proxy's CONNTECT response is not instant.
     67      */
     68     prot_save = conn->data->req.protop;
     69     memset(&http_proxy, 0, sizeof(http_proxy));
     70     conn->data->req.protop = &http_proxy;
     71     connkeep(conn, "HTTP proxy CONNECT");
     72     if(conn->bits.conn_to_host)
     73       hostname = conn->conn_to_host.name;
     74     else
     75       hostname = conn->host.name;
     76     if(conn->bits.conn_to_port)
     77       remote_port = conn->conn_to_port;
     78     else
     79       remote_port = conn->remote_port;
     80     result = Curl_proxyCONNECT(conn, FIRSTSOCKET, hostname,
     81                                remote_port, FALSE);
     82     conn->data->req.protop = prot_save;
     83     if(CURLE_OK != result)
     84       return result;
     85     Curl_safefree(conn->allocptr.proxyuserpwd);
     86 #else
     87     return CURLE_NOT_BUILT_IN;
     88 #endif
     89   }
     90   /* no HTTP tunnel proxy, just return */
     91   return CURLE_OK;
     92 }
     93 
     94 /*
     95  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
     96  * function will issue the necessary commands to get a seamless tunnel through
     97  * this proxy. After that, the socket can be used just as a normal socket.
     98  *
     99  * 'blocking' set to TRUE means that this function will do the entire CONNECT
    100  * + response in a blocking fashion. Should be avoided!
    101  */
    102 
    103 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
    104                            int sockindex,
    105                            const char *hostname,
    106                            int remote_port,
    107                            bool blocking)
    108 {
    109   int subversion=0;
    110   struct Curl_easy *data=conn->data;
    111   struct SingleRequest *k = &data->req;
    112   CURLcode result;
    113   curl_socket_t tunnelsocket = conn->sock[sockindex];
    114   curl_off_t cl=0;
    115   bool closeConnection = FALSE;
    116   bool chunked_encoding = FALSE;
    117   long check;
    118 
    119 #define SELECT_OK      0
    120 #define SELECT_ERROR   1
    121 #define SELECT_TIMEOUT 2
    122   int error = SELECT_OK;
    123 
    124   if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
    125     return CURLE_OK; /* CONNECT is already completed */
    126 
    127   conn->bits.proxy_connect_closed = FALSE;
    128 
    129   do {
    130     if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
    131       /* BEGIN CONNECT PHASE */
    132       char *host_port;
    133       Curl_send_buffer *req_buffer;
    134 
    135       infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
    136             hostname, remote_port);
    137 
    138         /* This only happens if we've looped here due to authentication
    139            reasons, and we don't really use the newly cloned URL here
    140            then. Just free() it. */
    141       free(data->req.newurl);
    142       data->req.newurl = NULL;
    143 
    144       /* initialize a dynamic send-buffer */
    145       req_buffer = Curl_add_buffer_init();
    146 
    147       if(!req_buffer)
    148         return CURLE_OUT_OF_MEMORY;
    149 
    150       host_port = aprintf("%s:%hu", hostname, remote_port);
    151       if(!host_port) {
    152         Curl_add_buffer_free(req_buffer);
    153         return CURLE_OUT_OF_MEMORY;
    154       }
    155 
    156       /* Setup the proxy-authorization header, if any */
    157       result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
    158 
    159       free(host_port);
    160 
    161       if(!result) {
    162         char *host=(char *)"";
    163         const char *useragent="";
    164         const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
    165           "1.0" : "1.1";
    166         bool ipv6_ip = conn->bits.ipv6_ip;
    167         char *hostheader;
    168 
    169         /* the hostname may be different */
    170         if(hostname != conn->host.name)
    171           ipv6_ip = (strchr(hostname, ':') != NULL);
    172         hostheader= /* host:port with IPv6 support */
    173           aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
    174                   remote_port);
    175         if(!hostheader) {
    176           Curl_add_buffer_free(req_buffer);
    177           return CURLE_OUT_OF_MEMORY;
    178         }
    179 
    180         if(!Curl_checkProxyheaders(conn, "Host:")) {
    181           host = aprintf("Host: %s\r\n", hostheader);
    182           if(!host) {
    183             free(hostheader);
    184             Curl_add_buffer_free(req_buffer);
    185             return CURLE_OUT_OF_MEMORY;
    186           }
    187         }
    188         if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
    189            data->set.str[STRING_USERAGENT])
    190           useragent = conn->allocptr.uagent;
    191 
    192         result =
    193           Curl_add_bufferf(req_buffer,
    194                            "CONNECT %s HTTP/%s\r\n"
    195                            "%s"  /* Host: */
    196                            "%s"  /* Proxy-Authorization */
    197                            "%s", /* User-Agent */
    198                            hostheader,
    199                            http,
    200                            host,
    201                            conn->allocptr.proxyuserpwd?
    202                            conn->allocptr.proxyuserpwd:"",
    203                            useragent);
    204 
    205         if(host && *host)
    206           free(host);
    207         free(hostheader);
    208 
    209         if(!result)
    210           result = Curl_add_custom_headers(conn, TRUE, req_buffer);
    211 
    212         if(!result)
    213           /* CRLF terminate the request */
    214           result = Curl_add_bufferf(req_buffer, "\r\n");
    215 
    216         if(!result) {
    217           /* Send the connect request to the proxy */
    218           /* BLOCKING */
    219           result =
    220             Curl_add_buffer_send(req_buffer, conn,
    221                                  &data->info.request_size, 0, sockindex);
    222         }
    223         req_buffer = NULL;
    224         if(result)
    225           failf(data, "Failed sending CONNECT to proxy");
    226       }
    227 
    228       Curl_add_buffer_free(req_buffer);
    229       if(result)
    230         return result;
    231 
    232       conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
    233     } /* END CONNECT PHASE */
    234 
    235     check = Curl_timeleft(data, NULL, TRUE);
    236     if(check <= 0) {
    237       failf(data, "Proxy CONNECT aborted due to timeout");
    238       return CURLE_RECV_ERROR;
    239     }
    240 
    241     if(!blocking) {
    242       if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
    243         /* return so we'll be called again polling-style */
    244         return CURLE_OK;
    245       else {
    246         DEBUGF(infof(data,
    247                "Read response immediately from proxy CONNECT\n"));
    248       }
    249     }
    250 
    251     /* at this point, the tunnel_connecting phase is over. */
    252 
    253     { /* READING RESPONSE PHASE */
    254       size_t nread;   /* total size read */
    255       int perline; /* count bytes per line */
    256       int keepon=TRUE;
    257       ssize_t gotbytes;
    258       char *ptr;
    259       char *line_start;
    260 
    261       ptr=data->state.buffer;
    262       line_start = ptr;
    263 
    264       nread=0;
    265       perline=0;
    266 
    267       while((nread<BUFSIZE) && (keepon && !error)) {
    268 
    269         check = Curl_timeleft(data, NULL, TRUE);
    270         if(check <= 0) {
    271           failf(data, "Proxy CONNECT aborted due to timeout");
    272           error = SELECT_TIMEOUT; /* already too little time */
    273           break;
    274         }
    275 
    276         /* loop every second at least, less if the timeout is near */
    277         switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
    278                                   check<1000L?check:1000)) {
    279         case -1: /* select() error, stop reading */
    280           error = SELECT_ERROR;
    281           failf(data, "Proxy CONNECT aborted due to select/poll error");
    282           break;
    283         case 0: /* timeout */
    284           break;
    285         default:
    286           DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
    287           result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
    288                              &gotbytes);
    289           if(result==CURLE_AGAIN)
    290             continue; /* go loop yourself */
    291           else if(result)
    292             keepon = FALSE;
    293           else if(gotbytes <= 0) {
    294             keepon = FALSE;
    295             if(data->set.proxyauth && data->state.authproxy.avail) {
    296               /* proxy auth was requested and there was proxy auth available,
    297                  then deem this as "mere" proxy disconnect */
    298               conn->bits.proxy_connect_closed = TRUE;
    299               infof(data, "Proxy CONNECT connection closed\n");
    300             }
    301             else {
    302               error = SELECT_ERROR;
    303               failf(data, "Proxy CONNECT aborted");
    304             }
    305           }
    306           else {
    307             /*
    308              * We got a whole chunk of data, which can be anything from one
    309              * byte to a set of lines and possibly just a piece of the last
    310              * line.
    311              */
    312             int i;
    313 
    314             nread += gotbytes;
    315 
    316             if(keepon > TRUE) {
    317               /* This means we are currently ignoring a response-body */
    318 
    319               nread = 0; /* make next read start over in the read buffer */
    320               ptr=data->state.buffer;
    321               if(cl) {
    322                 /* A Content-Length based body: simply count down the counter
    323                    and make sure to break out of the loop when we're done! */
    324                 cl -= gotbytes;
    325                 if(cl<=0) {
    326                   keepon = FALSE;
    327                   break;
    328                 }
    329               }
    330               else {
    331                 /* chunked-encoded body, so we need to do the chunked dance
    332                    properly to know when the end of the body is reached */
    333                 CHUNKcode r;
    334                 ssize_t tookcareof=0;
    335 
    336                 /* now parse the chunked piece of data so that we can
    337                    properly tell when the stream ends */
    338                 r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
    339                 if(r == CHUNKE_STOP) {
    340                   /* we're done reading chunks! */
    341                   infof(data, "chunk reading DONE\n");
    342                   keepon = FALSE;
    343                   /* we did the full CONNECT treatment, go COMPLETE */
    344                   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    345                 }
    346                 else
    347                   infof(data, "Read %zd bytes of chunk, continue\n",
    348                         tookcareof);
    349               }
    350             }
    351             else
    352               for(i = 0; i < gotbytes; ptr++, i++) {
    353                 perline++; /* amount of bytes in this line so far */
    354                 if(*ptr == 0x0a) {
    355                   char letter;
    356                   int writetype;
    357 
    358                   /* convert from the network encoding */
    359                   result = Curl_convert_from_network(data, line_start,
    360                                                      perline);
    361                   /* Curl_convert_from_network calls failf if unsuccessful */
    362                   if(result)
    363                     return result;
    364 
    365                   /* output debug if that is requested */
    366                   if(data->set.verbose)
    367                     Curl_debug(data, CURLINFO_HEADER_IN,
    368                                line_start, (size_t)perline, conn);
    369 
    370                   /* send the header to the callback */
    371                   writetype = CLIENTWRITE_HEADER;
    372                   if(data->set.include_header)
    373                     writetype |= CLIENTWRITE_BODY;
    374 
    375                   result = Curl_client_write(conn, writetype, line_start,
    376                                              perline);
    377 
    378                   data->info.header_size += (long)perline;
    379                   data->req.headerbytecount += (long)perline;
    380 
    381                   if(result)
    382                     return result;
    383 
    384                   /* Newlines are CRLF, so the CR is ignored as the line isn't
    385                      really terminated until the LF comes. Treat a following CR
    386                      as end-of-headers as well.*/
    387 
    388                   if(('\r' == line_start[0]) ||
    389                      ('\n' == line_start[0])) {
    390                     /* end of response-headers from the proxy */
    391                     nread = 0; /* make next read start over in the read
    392                                   buffer */
    393                     ptr=data->state.buffer;
    394                     if((407 == k->httpcode) && !data->state.authproblem) {
    395                       /* If we get a 407 response code with content length
    396                          when we have no auth problem, we must ignore the
    397                          whole response-body */
    398                       keepon = 2;
    399 
    400                       if(cl) {
    401                         infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
    402                               " bytes of response-body\n", cl);
    403 
    404                         /* remove the remaining chunk of what we already
    405                            read */
    406                         cl -= (gotbytes - i);
    407 
    408                         if(cl<=0)
    409                           /* if the whole thing was already read, we are done!
    410                            */
    411                           keepon=FALSE;
    412                       }
    413                       else if(chunked_encoding) {
    414                         CHUNKcode r;
    415                         /* We set ignorebody true here since the chunked
    416                            decoder function will acknowledge that. Pay
    417                            attention so that this is cleared again when this
    418                            function returns! */
    419                         k->ignorebody = TRUE;
    420                         infof(data, "%zd bytes of chunk left\n", gotbytes-i);
    421 
    422                         if(line_start[1] == '\n') {
    423                           /* this can only be a LF if the letter at index 0
    424                              was a CR */
    425                           line_start++;
    426                           i++;
    427                         }
    428 
    429                         /* now parse the chunked piece of data so that we can
    430                            properly tell when the stream ends */
    431                         r = Curl_httpchunk_read(conn, line_start+1,
    432                                                   gotbytes -i, &gotbytes);
    433                         if(r == CHUNKE_STOP) {
    434                           /* we're done reading chunks! */
    435                           infof(data, "chunk reading DONE\n");
    436                           keepon = FALSE;
    437                           /* we did the full CONNECT treatment, go to
    438                              COMPLETE */
    439                           conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    440                         }
    441                         else
    442                           infof(data, "Read %zd bytes of chunk, continue\n",
    443                                 gotbytes);
    444                       }
    445                       else {
    446                         /* without content-length or chunked encoding, we
    447                            can't keep the connection alive since the close is
    448                            the end signal so we bail out at once instead */
    449                         keepon=FALSE;
    450                       }
    451                     }
    452                     else {
    453                       keepon = FALSE;
    454                       if(200 == data->info.httpproxycode) {
    455                         if(gotbytes - (i+1))
    456                           failf(data, "Proxy CONNECT followed by %zd bytes "
    457                                 "of opaque data. Data ignored (known bug #39)",
    458                                 gotbytes - (i+1));
    459                       }
    460                     }
    461                     /* we did the full CONNECT treatment, go to COMPLETE */
    462                     conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    463                     break; /* breaks out of for-loop, not switch() */
    464                   }
    465 
    466                   /* keep a backup of the position we are about to blank */
    467                   letter = line_start[perline];
    468                   line_start[perline]=0; /* zero terminate the buffer */
    469                   if((checkprefix("WWW-Authenticate:", line_start) &&
    470                       (401 == k->httpcode)) ||
    471                      (checkprefix("Proxy-authenticate:", line_start) &&
    472                       (407 == k->httpcode))) {
    473 
    474                     bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
    475                     char *auth = Curl_copy_header_value(line_start);
    476                     if(!auth)
    477                       return CURLE_OUT_OF_MEMORY;
    478 
    479                     result = Curl_http_input_auth(conn, proxy, auth);
    480 
    481                     free(auth);
    482 
    483                     if(result)
    484                       return result;
    485                   }
    486                   else if(checkprefix("Content-Length:", line_start)) {
    487                     cl = curlx_strtoofft(line_start +
    488                                          strlen("Content-Length:"), NULL, 10);
    489                   }
    490                   else if(Curl_compareheader(line_start,
    491                                              "Connection:", "close"))
    492                     closeConnection = TRUE;
    493                   else if(Curl_compareheader(line_start,
    494                                              "Transfer-Encoding:",
    495                                              "chunked")) {
    496                     infof(data, "CONNECT responded chunked\n");
    497                     chunked_encoding = TRUE;
    498                     /* init our chunky engine */
    499                     Curl_httpchunk_init(conn);
    500                   }
    501                   else if(Curl_compareheader(line_start,
    502                                              "Proxy-Connection:", "close"))
    503                     closeConnection = TRUE;
    504                   else if(2 == sscanf(line_start, "HTTP/1.%d %d",
    505                                       &subversion,
    506                                       &k->httpcode)) {
    507                     /* store the HTTP code from the proxy */
    508                     data->info.httpproxycode = k->httpcode;
    509                   }
    510                   /* put back the letter we blanked out before */
    511                   line_start[perline]= letter;
    512 
    513                   perline=0; /* line starts over here */
    514                   line_start = ptr+1; /* this skips the zero byte we wrote */
    515                 }
    516               }
    517           }
    518           break;
    519         } /* switch */
    520         if(Curl_pgrsUpdate(conn))
    521           return CURLE_ABORTED_BY_CALLBACK;
    522       } /* while there's buffer left and loop is requested */
    523 
    524       if(error)
    525         return CURLE_RECV_ERROR;
    526 
    527       if(data->info.httpproxycode != 200) {
    528         /* Deal with the possibly already received authenticate
    529            headers. 'newurl' is set to a new URL if we must loop. */
    530         result = Curl_http_auth_act(conn);
    531         if(result)
    532           return result;
    533 
    534         if(conn->bits.close)
    535           /* the connection has been marked for closure, most likely in the
    536              Curl_http_auth_act() function and thus we can kill it at once
    537              below
    538           */
    539           closeConnection = TRUE;
    540       }
    541 
    542       if(closeConnection && data->req.newurl) {
    543         /* Connection closed by server. Don't use it anymore */
    544         Curl_closesocket(conn, conn->sock[sockindex]);
    545         conn->sock[sockindex] = CURL_SOCKET_BAD;
    546         break;
    547       }
    548     } /* END READING RESPONSE PHASE */
    549 
    550     /* If we are supposed to continue and request a new URL, which basically
    551      * means the HTTP authentication is still going on so if the tunnel
    552      * is complete we start over in INIT state */
    553     if(data->req.newurl &&
    554        (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
    555       conn->tunnel_state[sockindex] = TUNNEL_INIT;
    556       infof(data, "TUNNEL_STATE switched to: %d\n",
    557             conn->tunnel_state[sockindex]);
    558     }
    559 
    560   } while(data->req.newurl);
    561 
    562   if(200 != data->req.httpcode) {
    563     if(closeConnection && data->req.newurl) {
    564       conn->bits.proxy_connect_closed = TRUE;
    565       infof(data, "Connect me again please\n");
    566     }
    567     else {
    568       free(data->req.newurl);
    569       data->req.newurl = NULL;
    570       /* failure, close this connection to avoid re-use */
    571       connclose(conn, "proxy CONNECT failure");
    572       Curl_closesocket(conn, conn->sock[sockindex]);
    573       conn->sock[sockindex] = CURL_SOCKET_BAD;
    574     }
    575 
    576     /* to back to init state */
    577     conn->tunnel_state[sockindex] = TUNNEL_INIT;
    578 
    579     if(conn->bits.proxy_connect_closed)
    580       /* this is not an error, just part of the connection negotiation */
    581       return CURLE_OK;
    582     else {
    583       failf(data, "Received HTTP code %d from proxy after CONNECT",
    584             data->req.httpcode);
    585       return CURLE_RECV_ERROR;
    586     }
    587   }
    588 
    589   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    590 
    591   /* If a proxy-authorization header was used for the proxy, then we should
    592      make sure that it isn't accidentally used for the document request
    593      after we've connected. So let's free and clear it here. */
    594   Curl_safefree(conn->allocptr.proxyuserpwd);
    595   conn->allocptr.proxyuserpwd = NULL;
    596 
    597   data->state.authproxy.done = TRUE;
    598 
    599   infof (data, "Proxy replied OK to CONNECT request\n");
    600   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
    601   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
    602                                          document request  */
    603   return CURLE_OK;
    604 }
    605 #endif /* CURL_DISABLE_PROXY */
    606