quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

http_proxy.c (13860B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@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.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  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #include "http_proxy.h"
     28 
     29 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
     30 
     31 #include <curl/curl.h>
     32 #include "sendf.h"
     33 #include "http.h"
     34 #include "url.h"
     35 #include "select.h"
     36 #include "progress.h"
     37 #include "cfilters.h"
     38 #include "cf-h1-proxy.h"
     39 #include "cf-h2-proxy.h"
     40 #include "connect.h"
     41 #include "vtls/vtls.h"
     42 #include "transfer.h"
     43 #include "multiif.h"
     44 #include "vauth/vauth.h"
     45 #include "curlx/strparse.h"
     46 
     47 /* The last 3 #include files should be in this order */
     48 #include "curl_printf.h"
     49 #include "curl_memory.h"
     50 #include "memdebug.h"
     51 
     52 static bool hd_name_eq(const char *n1, size_t n1len,
     53                        const char *n2, size_t n2len)
     54 {
     55   return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE;
     56 }
     57 
     58 static CURLcode dynhds_add_custom(struct Curl_easy *data,
     59                                   bool is_connect, int httpversion,
     60                                   struct dynhds *hds)
     61 {
     62   struct connectdata *conn = data->conn;
     63   const char *ptr;
     64   struct curl_slist *h[2];
     65   struct curl_slist *headers;
     66   int numlists = 1; /* by default */
     67   int i;
     68 
     69   enum Curl_proxy_use proxy;
     70 
     71   if(is_connect)
     72     proxy = HEADER_CONNECT;
     73   else
     74     proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
     75       HEADER_PROXY : HEADER_SERVER;
     76 
     77   switch(proxy) {
     78   case HEADER_SERVER:
     79     h[0] = data->set.headers;
     80     break;
     81   case HEADER_PROXY:
     82     h[0] = data->set.headers;
     83     if(data->set.sep_headers) {
     84       h[1] = data->set.proxyheaders;
     85       numlists++;
     86     }
     87     break;
     88   case HEADER_CONNECT:
     89     if(data->set.sep_headers)
     90       h[0] = data->set.proxyheaders;
     91     else
     92       h[0] = data->set.headers;
     93     break;
     94   }
     95 
     96   /* loop through one or two lists */
     97   for(i = 0; i < numlists; i++) {
     98     for(headers = h[i]; headers; headers = headers->next) {
     99       const char *name, *value;
    100       size_t namelen, valuelen;
    101 
    102       /* There are 2 quirks in place for custom headers:
    103        * 1. setting only 'name:' to suppress a header from being sent
    104        * 2. setting only 'name;' to send an empty (illegal) header
    105        */
    106       ptr = strchr(headers->data, ':');
    107       if(ptr) {
    108         name = headers->data;
    109         namelen = ptr - headers->data;
    110         ptr++; /* pass the colon */
    111         curlx_str_passblanks(&ptr);
    112         if(*ptr) {
    113           value = ptr;
    114           valuelen = strlen(value);
    115         }
    116         else {
    117           /* quirk #1, suppress this header */
    118           continue;
    119         }
    120       }
    121       else {
    122         ptr = strchr(headers->data, ';');
    123 
    124         if(!ptr) {
    125           /* neither : nor ; in provided header value. We seem
    126            * to ignore this silently */
    127           continue;
    128         }
    129 
    130         name = headers->data;
    131         namelen = ptr - headers->data;
    132         ptr++; /* pass the semicolon */
    133         curlx_str_passblanks(&ptr);
    134         if(!*ptr) {
    135           /* quirk #2, send an empty header */
    136           value = "";
    137           valuelen = 0;
    138         }
    139         else {
    140           /* this may be used for something else in the future,
    141            * ignore this for now */
    142           continue;
    143         }
    144       }
    145 
    146       DEBUGASSERT(name && value);
    147       if(data->state.aptr.host &&
    148          /* a Host: header was sent already, do not pass on any custom Host:
    149             header as that will produce *two* in the same request! */
    150          hd_name_eq(name, namelen, STRCONST("Host:")))
    151         ;
    152       else if(data->state.httpreq == HTTPREQ_POST_FORM &&
    153               /* this header (extended by formdata.c) is sent later */
    154               hd_name_eq(name, namelen, STRCONST("Content-Type:")))
    155         ;
    156       else if(data->state.httpreq == HTTPREQ_POST_MIME &&
    157               /* this header is sent later */
    158               hd_name_eq(name, namelen, STRCONST("Content-Type:")))
    159         ;
    160       else if(data->req.authneg &&
    161               /* while doing auth neg, do not allow the custom length since
    162                  we will force length zero then */
    163               hd_name_eq(name, namelen, STRCONST("Content-Length:")))
    164         ;
    165       else if(data->state.aptr.te &&
    166               /* when asking for Transfer-Encoding, do not pass on a custom
    167                  Connection: */
    168               hd_name_eq(name, namelen, STRCONST("Connection:")))
    169         ;
    170       else if((httpversion >= 20) &&
    171               hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
    172         /* HTTP/2 and HTTP/3 do not support chunked requests */
    173         ;
    174       else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
    175                hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
    176               /* be careful of sending this potentially sensitive header to
    177                  other hosts */
    178               !Curl_auth_allowed_to_host(data))
    179         ;
    180       else {
    181         CURLcode result;
    182 
    183         result = Curl_dynhds_add(hds, name, namelen, value, valuelen);
    184         if(result)
    185           return result;
    186       }
    187     }
    188   }
    189 
    190   return CURLE_OK;
    191 }
    192 
    193 CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
    194                                          const char **phostname,
    195                                          int *pport, bool *pipv6_ip)
    196 {
    197   DEBUGASSERT(cf);
    198   DEBUGASSERT(cf->conn);
    199 
    200   if(cf->conn->bits.conn_to_host)
    201     *phostname = cf->conn->conn_to_host.name;
    202   else if(cf->sockindex == SECONDARYSOCKET)
    203     *phostname = cf->conn->secondaryhostname;
    204   else
    205     *phostname = cf->conn->host.name;
    206 
    207   if(cf->sockindex == SECONDARYSOCKET)
    208     *pport = cf->conn->secondary_port;
    209   else if(cf->conn->bits.conn_to_port)
    210     *pport = cf->conn->conn_to_port;
    211   else
    212     *pport = cf->conn->remote_port;
    213 
    214   if(*phostname != cf->conn->host.name)
    215     *pipv6_ip = (strchr(*phostname, ':') != NULL);
    216   else
    217     *pipv6_ip = cf->conn->bits.ipv6_ip;
    218 
    219   return CURLE_OK;
    220 }
    221 
    222 struct cf_proxy_ctx {
    223   /* the protocol specific sub-filter we install during connect */
    224   struct Curl_cfilter *cf_protocol;
    225   int httpversion; /* HTTP version used to CONNECT */
    226 };
    227 
    228 CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
    229                                         struct Curl_cfilter *cf,
    230                                         struct Curl_easy *data,
    231                                         int http_version_major)
    232 {
    233   struct cf_proxy_ctx *ctx = cf->ctx;
    234   const char *hostname = NULL;
    235   char *authority = NULL;
    236   int port;
    237   bool ipv6_ip;
    238   CURLcode result;
    239   struct httpreq *req = NULL;
    240 
    241   result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
    242   if(result)
    243     goto out;
    244 
    245   authority = aprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname,
    246                       ipv6_ip ?"]" : "", port);
    247   if(!authority) {
    248     result = CURLE_OUT_OF_MEMORY;
    249     goto out;
    250   }
    251 
    252   result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
    253                               NULL, 0, authority, strlen(authority),
    254                               NULL, 0);
    255   if(result)
    256     goto out;
    257 
    258   /* Setup the proxy-authorization header, if any */
    259   result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
    260                                  req->authority, TRUE);
    261   if(result)
    262     goto out;
    263 
    264   /* If user is not overriding Host: header, we add for HTTP/1.x */
    265   if(http_version_major == 1 &&
    266      !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
    267     result = Curl_dynhds_cadd(&req->headers, "Host", authority);
    268     if(result)
    269       goto out;
    270   }
    271 
    272   if(data->state.aptr.proxyuserpwd) {
    273     result = Curl_dynhds_h1_cadd_line(&req->headers,
    274                                       data->state.aptr.proxyuserpwd);
    275     if(result)
    276       goto out;
    277   }
    278 
    279   if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
    280      data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
    281     result = Curl_dynhds_cadd(&req->headers, "User-Agent",
    282                               data->set.str[STRING_USERAGENT]);
    283     if(result)
    284       goto out;
    285   }
    286 
    287   if(http_version_major == 1 &&
    288     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
    289     result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
    290     if(result)
    291       goto out;
    292   }
    293 
    294   result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers);
    295 
    296 out:
    297   if(result && req) {
    298     Curl_http_req_free(req);
    299     req = NULL;
    300   }
    301   free(authority);
    302   *preq = req;
    303   return result;
    304 }
    305 
    306 static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
    307                                       struct Curl_easy *data,
    308                                       bool *done)
    309 {
    310   struct cf_proxy_ctx *ctx = cf->ctx;
    311   CURLcode result;
    312 
    313   if(cf->connected) {
    314     *done = TRUE;
    315     return CURLE_OK;
    316   }
    317 
    318   CURL_TRC_CF(data, cf, "connect");
    319 connect_sub:
    320   result = cf->next->cft->do_connect(cf->next, data, done);
    321   if(result || !*done)
    322     return result;
    323 
    324   *done = FALSE;
    325   if(!ctx->cf_protocol) {
    326     struct Curl_cfilter *cf_protocol = NULL;
    327     int httpversion = 0;
    328     int alpn = Curl_conn_cf_is_ssl(cf->next) ?
    329       cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
    330 
    331     /* First time call after the subchain connected */
    332     switch(alpn) {
    333     case CURL_HTTP_VERSION_NONE:
    334     case CURL_HTTP_VERSION_1_0:
    335     case CURL_HTTP_VERSION_1_1:
    336       CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
    337       infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
    338             (alpn == CURL_HTTP_VERSION_1_0) ? 0 : 1);
    339       result = Curl_cf_h1_proxy_insert_after(cf, data);
    340       if(result)
    341         goto out;
    342       cf_protocol = cf->next;
    343       httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11;
    344       break;
    345 #ifdef USE_NGHTTP2
    346     case CURL_HTTP_VERSION_2:
    347       CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
    348       infof(data, "CONNECT tunnel: HTTP/2 negotiated");
    349       result = Curl_cf_h2_proxy_insert_after(cf, data);
    350       if(result)
    351         goto out;
    352       cf_protocol = cf->next;
    353       httpversion = 20;
    354       break;
    355 #endif
    356     default:
    357       infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
    358       result = CURLE_COULDNT_CONNECT;
    359       goto out;
    360     }
    361 
    362     ctx->cf_protocol = cf_protocol;
    363     ctx->httpversion = httpversion;
    364     /* after we installed the filter "below" us, we call connect
    365      * on out sub-chain again.
    366      */
    367     goto connect_sub;
    368   }
    369   else {
    370     /* subchain connected and we had already installed the protocol filter.
    371      * This means the protocol tunnel is established, we are done.
    372      */
    373     DEBUGASSERT(ctx->cf_protocol);
    374     result = CURLE_OK;
    375   }
    376 
    377 out:
    378   if(!result) {
    379     cf->connected = TRUE;
    380     *done = TRUE;
    381   }
    382   return result;
    383 }
    384 
    385 CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
    386                                   struct Curl_easy *data,
    387                                   int query, int *pres1, void *pres2)
    388 {
    389   switch(query) {
    390   case CF_QUERY_HOST_PORT:
    391     *pres1 = (int)cf->conn->http_proxy.port;
    392     *((const char **)pres2) = cf->conn->http_proxy.host.name;
    393     return CURLE_OK;
    394   default:
    395     break;
    396   }
    397   return cf->next ?
    398     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
    399     CURLE_UNKNOWN_OPTION;
    400 }
    401 
    402 static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
    403                                   struct Curl_easy *data)
    404 {
    405   struct cf_proxy_ctx *ctx = cf->ctx;
    406 
    407   (void)data;
    408   CURL_TRC_CF(data, cf, "destroy");
    409   free(ctx);
    410 }
    411 
    412 static void http_proxy_cf_close(struct Curl_cfilter *cf,
    413                                 struct Curl_easy *data)
    414 {
    415   struct cf_proxy_ctx *ctx = cf->ctx;
    416 
    417   CURL_TRC_CF(data, cf, "close");
    418   cf->connected = FALSE;
    419   if(ctx->cf_protocol) {
    420     struct Curl_cfilter *f;
    421     /* if someone already removed it, we assume he also
    422      * took care of destroying it. */
    423     for(f = cf->next; f; f = f->next) {
    424       if(f == ctx->cf_protocol) {
    425         /* still in our sub-chain */
    426         Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE);
    427         break;
    428       }
    429     }
    430     ctx->cf_protocol = NULL;
    431   }
    432   if(cf->next)
    433     cf->next->cft->do_close(cf->next, data);
    434 }
    435 
    436 
    437 struct Curl_cftype Curl_cft_http_proxy = {
    438   "HTTP-PROXY",
    439   CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
    440   0,
    441   http_proxy_cf_destroy,
    442   http_proxy_cf_connect,
    443   http_proxy_cf_close,
    444   Curl_cf_def_shutdown,
    445   Curl_cf_def_adjust_pollset,
    446   Curl_cf_def_data_pending,
    447   Curl_cf_def_send,
    448   Curl_cf_def_recv,
    449   Curl_cf_def_cntrl,
    450   Curl_cf_def_conn_is_alive,
    451   Curl_cf_def_conn_keep_alive,
    452   Curl_cf_http_proxy_query,
    453 };
    454 
    455 CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
    456                                          struct Curl_easy *data)
    457 {
    458   struct Curl_cfilter *cf;
    459   struct cf_proxy_ctx *ctx = NULL;
    460   CURLcode result;
    461 
    462   (void)data;
    463   ctx = calloc(1, sizeof(*ctx));
    464   if(!ctx) {
    465     result = CURLE_OUT_OF_MEMORY;
    466     goto out;
    467   }
    468   result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
    469   if(result)
    470     goto out;
    471   ctx = NULL;
    472   Curl_conn_cf_insert_after(cf_at, cf);
    473 
    474 out:
    475   free(ctx);
    476   return result;
    477 }
    478 
    479 #endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */