113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#include "http_proxy.h"
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
3013498266Sopenharmony_ci
3113498266Sopenharmony_ci#include <curl/curl.h>
3213498266Sopenharmony_ci#ifdef USE_HYPER
3313498266Sopenharmony_ci#include <hyper.h>
3413498266Sopenharmony_ci#endif
3513498266Sopenharmony_ci#include "sendf.h"
3613498266Sopenharmony_ci#include "http.h"
3713498266Sopenharmony_ci#include "url.h"
3813498266Sopenharmony_ci#include "select.h"
3913498266Sopenharmony_ci#include "progress.h"
4013498266Sopenharmony_ci#include "cfilters.h"
4113498266Sopenharmony_ci#include "cf-h1-proxy.h"
4213498266Sopenharmony_ci#include "cf-h2-proxy.h"
4313498266Sopenharmony_ci#include "connect.h"
4413498266Sopenharmony_ci#include "curlx.h"
4513498266Sopenharmony_ci#include "vtls/vtls.h"
4613498266Sopenharmony_ci#include "transfer.h"
4713498266Sopenharmony_ci#include "multiif.h"
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci/* The last 3 #include files should be in this order */
5013498266Sopenharmony_ci#include "curl_printf.h"
5113498266Sopenharmony_ci#include "curl_memory.h"
5213498266Sopenharmony_ci#include "memdebug.h"
5313498266Sopenharmony_ci
5413498266Sopenharmony_ci
5513498266Sopenharmony_ciCURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
5613498266Sopenharmony_ci                                         const char **phostname,
5713498266Sopenharmony_ci                                         int *pport, bool *pipv6_ip)
5813498266Sopenharmony_ci{
5913498266Sopenharmony_ci  DEBUGASSERT(cf);
6013498266Sopenharmony_ci  DEBUGASSERT(cf->conn);
6113498266Sopenharmony_ci
6213498266Sopenharmony_ci  if(cf->conn->bits.conn_to_host)
6313498266Sopenharmony_ci    *phostname = cf->conn->conn_to_host.name;
6413498266Sopenharmony_ci  else if(cf->sockindex == SECONDARYSOCKET)
6513498266Sopenharmony_ci    *phostname = cf->conn->secondaryhostname;
6613498266Sopenharmony_ci  else
6713498266Sopenharmony_ci    *phostname = cf->conn->host.name;
6813498266Sopenharmony_ci
6913498266Sopenharmony_ci  if(cf->sockindex == SECONDARYSOCKET)
7013498266Sopenharmony_ci    *pport = cf->conn->secondary_port;
7113498266Sopenharmony_ci  else if(cf->conn->bits.conn_to_port)
7213498266Sopenharmony_ci    *pport = cf->conn->conn_to_port;
7313498266Sopenharmony_ci  else
7413498266Sopenharmony_ci    *pport = cf->conn->remote_port;
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci  if(*phostname != cf->conn->host.name)
7713498266Sopenharmony_ci    *pipv6_ip = (strchr(*phostname, ':') != NULL);
7813498266Sopenharmony_ci  else
7913498266Sopenharmony_ci    *pipv6_ip = cf->conn->bits.ipv6_ip;
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci  return CURLE_OK;
8213498266Sopenharmony_ci}
8313498266Sopenharmony_ci
8413498266Sopenharmony_ciCURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
8513498266Sopenharmony_ci                                        struct Curl_cfilter *cf,
8613498266Sopenharmony_ci                                        struct Curl_easy *data,
8713498266Sopenharmony_ci                                        int http_version_major)
8813498266Sopenharmony_ci{
8913498266Sopenharmony_ci  const char *hostname = NULL;
9013498266Sopenharmony_ci  char *authority = NULL;
9113498266Sopenharmony_ci  int port;
9213498266Sopenharmony_ci  bool ipv6_ip;
9313498266Sopenharmony_ci  CURLcode result;
9413498266Sopenharmony_ci  struct httpreq *req = NULL;
9513498266Sopenharmony_ci
9613498266Sopenharmony_ci  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
9713498266Sopenharmony_ci  if(result)
9813498266Sopenharmony_ci    goto out;
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci  authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname,
10113498266Sopenharmony_ci                      ipv6_ip?"]":"", port);
10213498266Sopenharmony_ci  if(!authority) {
10313498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
10413498266Sopenharmony_ci    goto out;
10513498266Sopenharmony_ci  }
10613498266Sopenharmony_ci
10713498266Sopenharmony_ci  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
10813498266Sopenharmony_ci                              NULL, 0, authority, strlen(authority),
10913498266Sopenharmony_ci                              NULL, 0);
11013498266Sopenharmony_ci  if(result)
11113498266Sopenharmony_ci    goto out;
11213498266Sopenharmony_ci
11313498266Sopenharmony_ci  /* Setup the proxy-authorization header, if any */
11413498266Sopenharmony_ci  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
11513498266Sopenharmony_ci                                 req->authority, TRUE);
11613498266Sopenharmony_ci  if(result)
11713498266Sopenharmony_ci    goto out;
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci  /* If user is not overriding Host: header, we add for HTTP/1.x */
12013498266Sopenharmony_ci  if(http_version_major == 1 &&
12113498266Sopenharmony_ci     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
12213498266Sopenharmony_ci    result = Curl_dynhds_cadd(&req->headers, "Host", authority);
12313498266Sopenharmony_ci    if(result)
12413498266Sopenharmony_ci      goto out;
12513498266Sopenharmony_ci  }
12613498266Sopenharmony_ci
12713498266Sopenharmony_ci  if(data->state.aptr.proxyuserpwd) {
12813498266Sopenharmony_ci    result = Curl_dynhds_h1_cadd_line(&req->headers,
12913498266Sopenharmony_ci                                      data->state.aptr.proxyuserpwd);
13013498266Sopenharmony_ci    if(result)
13113498266Sopenharmony_ci      goto out;
13213498266Sopenharmony_ci  }
13313498266Sopenharmony_ci
13413498266Sopenharmony_ci  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
13513498266Sopenharmony_ci     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
13613498266Sopenharmony_ci    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
13713498266Sopenharmony_ci                              data->set.str[STRING_USERAGENT]);
13813498266Sopenharmony_ci    if(result)
13913498266Sopenharmony_ci      goto out;
14013498266Sopenharmony_ci  }
14113498266Sopenharmony_ci
14213498266Sopenharmony_ci  if(http_version_major == 1 &&
14313498266Sopenharmony_ci    !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
14413498266Sopenharmony_ci    result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
14513498266Sopenharmony_ci    if(result)
14613498266Sopenharmony_ci      goto out;
14713498266Sopenharmony_ci  }
14813498266Sopenharmony_ci
14913498266Sopenharmony_ci  result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
15013498266Sopenharmony_ci
15113498266Sopenharmony_ciout:
15213498266Sopenharmony_ci  if(result && req) {
15313498266Sopenharmony_ci    Curl_http_req_free(req);
15413498266Sopenharmony_ci    req = NULL;
15513498266Sopenharmony_ci  }
15613498266Sopenharmony_ci  free(authority);
15713498266Sopenharmony_ci  *preq = req;
15813498266Sopenharmony_ci  return result;
15913498266Sopenharmony_ci}
16013498266Sopenharmony_ci
16113498266Sopenharmony_ci
16213498266Sopenharmony_cistruct cf_proxy_ctx {
16313498266Sopenharmony_ci  /* the protocol specific sub-filter we install during connect */
16413498266Sopenharmony_ci  struct Curl_cfilter *cf_protocol;
16513498266Sopenharmony_ci};
16613498266Sopenharmony_ci
16713498266Sopenharmony_cistatic CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
16813498266Sopenharmony_ci                                      struct Curl_easy *data,
16913498266Sopenharmony_ci                                      bool blocking, bool *done)
17013498266Sopenharmony_ci{
17113498266Sopenharmony_ci  struct cf_proxy_ctx *ctx = cf->ctx;
17213498266Sopenharmony_ci  CURLcode result;
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci  if(cf->connected) {
17513498266Sopenharmony_ci    *done = TRUE;
17613498266Sopenharmony_ci    return CURLE_OK;
17713498266Sopenharmony_ci  }
17813498266Sopenharmony_ci
17913498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "connect");
18013498266Sopenharmony_ciconnect_sub:
18113498266Sopenharmony_ci  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
18213498266Sopenharmony_ci  if(result || !*done)
18313498266Sopenharmony_ci    return result;
18413498266Sopenharmony_ci
18513498266Sopenharmony_ci  *done = FALSE;
18613498266Sopenharmony_ci  if(!ctx->cf_protocol) {
18713498266Sopenharmony_ci    struct Curl_cfilter *cf_protocol = NULL;
18813498266Sopenharmony_ci    int alpn = Curl_conn_cf_is_ssl(cf->next)?
18913498266Sopenharmony_ci      cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
19013498266Sopenharmony_ci
19113498266Sopenharmony_ci    /* First time call after the subchain connected */
19213498266Sopenharmony_ci    switch(alpn) {
19313498266Sopenharmony_ci    case CURL_HTTP_VERSION_NONE:
19413498266Sopenharmony_ci    case CURL_HTTP_VERSION_1_0:
19513498266Sopenharmony_ci    case CURL_HTTP_VERSION_1_1:
19613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
19713498266Sopenharmony_ci      infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
19813498266Sopenharmony_ci            (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1);
19913498266Sopenharmony_ci      result = Curl_cf_h1_proxy_insert_after(cf, data);
20013498266Sopenharmony_ci      if(result)
20113498266Sopenharmony_ci        goto out;
20213498266Sopenharmony_ci      cf_protocol = cf->next;
20313498266Sopenharmony_ci      break;
20413498266Sopenharmony_ci#ifdef USE_NGHTTP2
20513498266Sopenharmony_ci    case CURL_HTTP_VERSION_2:
20613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
20713498266Sopenharmony_ci      infof(data, "CONNECT tunnel: HTTP/2 negotiated");
20813498266Sopenharmony_ci      result = Curl_cf_h2_proxy_insert_after(cf, data);
20913498266Sopenharmony_ci      if(result)
21013498266Sopenharmony_ci        goto out;
21113498266Sopenharmony_ci      cf_protocol = cf->next;
21213498266Sopenharmony_ci      break;
21313498266Sopenharmony_ci#endif
21413498266Sopenharmony_ci    default:
21513498266Sopenharmony_ci      infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
21613498266Sopenharmony_ci      result = CURLE_COULDNT_CONNECT;
21713498266Sopenharmony_ci      goto out;
21813498266Sopenharmony_ci    }
21913498266Sopenharmony_ci
22013498266Sopenharmony_ci    ctx->cf_protocol = cf_protocol;
22113498266Sopenharmony_ci    /* after we installed the filter "below" us, we call connect
22213498266Sopenharmony_ci     * on out sub-chain again.
22313498266Sopenharmony_ci     */
22413498266Sopenharmony_ci    goto connect_sub;
22513498266Sopenharmony_ci  }
22613498266Sopenharmony_ci  else {
22713498266Sopenharmony_ci    /* subchain connected and we had already installed the protocol filter.
22813498266Sopenharmony_ci     * This means the protocol tunnel is established, we are done.
22913498266Sopenharmony_ci     */
23013498266Sopenharmony_ci    DEBUGASSERT(ctx->cf_protocol);
23113498266Sopenharmony_ci    result = CURLE_OK;
23213498266Sopenharmony_ci  }
23313498266Sopenharmony_ci
23413498266Sopenharmony_ciout:
23513498266Sopenharmony_ci  if(!result) {
23613498266Sopenharmony_ci    cf->connected = TRUE;
23713498266Sopenharmony_ci    *done = TRUE;
23813498266Sopenharmony_ci  }
23913498266Sopenharmony_ci  return result;
24013498266Sopenharmony_ci}
24113498266Sopenharmony_ci
24213498266Sopenharmony_civoid Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf,
24313498266Sopenharmony_ci                                 struct Curl_easy *data,
24413498266Sopenharmony_ci                                 const char **phost,
24513498266Sopenharmony_ci                                 const char **pdisplay_host,
24613498266Sopenharmony_ci                                 int *pport)
24713498266Sopenharmony_ci{
24813498266Sopenharmony_ci  (void)data;
24913498266Sopenharmony_ci  if(!cf->connected) {
25013498266Sopenharmony_ci    *phost = cf->conn->http_proxy.host.name;
25113498266Sopenharmony_ci    *pdisplay_host = cf->conn->http_proxy.host.dispname;
25213498266Sopenharmony_ci    *pport = (int)cf->conn->http_proxy.port;
25313498266Sopenharmony_ci  }
25413498266Sopenharmony_ci  else {
25513498266Sopenharmony_ci    cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
25613498266Sopenharmony_ci  }
25713498266Sopenharmony_ci}
25813498266Sopenharmony_ci
25913498266Sopenharmony_cistatic void http_proxy_cf_destroy(struct Curl_cfilter *cf,
26013498266Sopenharmony_ci                                  struct Curl_easy *data)
26113498266Sopenharmony_ci{
26213498266Sopenharmony_ci  struct cf_proxy_ctx *ctx = cf->ctx;
26313498266Sopenharmony_ci
26413498266Sopenharmony_ci  (void)data;
26513498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "destroy");
26613498266Sopenharmony_ci  free(ctx);
26713498266Sopenharmony_ci}
26813498266Sopenharmony_ci
26913498266Sopenharmony_cistatic void http_proxy_cf_close(struct Curl_cfilter *cf,
27013498266Sopenharmony_ci                                struct Curl_easy *data)
27113498266Sopenharmony_ci{
27213498266Sopenharmony_ci  struct cf_proxy_ctx *ctx = cf->ctx;
27313498266Sopenharmony_ci
27413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "close");
27513498266Sopenharmony_ci  cf->connected = FALSE;
27613498266Sopenharmony_ci  if(ctx->cf_protocol) {
27713498266Sopenharmony_ci    struct Curl_cfilter *f;
27813498266Sopenharmony_ci    /* if someone already removed it, we assume he also
27913498266Sopenharmony_ci     * took care of destroying it. */
28013498266Sopenharmony_ci    for(f = cf->next; f; f = f->next) {
28113498266Sopenharmony_ci      if(f == ctx->cf_protocol) {
28213498266Sopenharmony_ci        /* still in our sub-chain */
28313498266Sopenharmony_ci        Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE);
28413498266Sopenharmony_ci        break;
28513498266Sopenharmony_ci      }
28613498266Sopenharmony_ci    }
28713498266Sopenharmony_ci    ctx->cf_protocol = NULL;
28813498266Sopenharmony_ci  }
28913498266Sopenharmony_ci  if(cf->next)
29013498266Sopenharmony_ci    cf->next->cft->do_close(cf->next, data);
29113498266Sopenharmony_ci}
29213498266Sopenharmony_ci
29313498266Sopenharmony_ci
29413498266Sopenharmony_cistruct Curl_cftype Curl_cft_http_proxy = {
29513498266Sopenharmony_ci  "HTTP-PROXY",
29613498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
29713498266Sopenharmony_ci  0,
29813498266Sopenharmony_ci  http_proxy_cf_destroy,
29913498266Sopenharmony_ci  http_proxy_cf_connect,
30013498266Sopenharmony_ci  http_proxy_cf_close,
30113498266Sopenharmony_ci  Curl_cf_http_proxy_get_host,
30213498266Sopenharmony_ci  Curl_cf_def_adjust_pollset,
30313498266Sopenharmony_ci  Curl_cf_def_data_pending,
30413498266Sopenharmony_ci  Curl_cf_def_send,
30513498266Sopenharmony_ci  Curl_cf_def_recv,
30613498266Sopenharmony_ci  Curl_cf_def_cntrl,
30713498266Sopenharmony_ci  Curl_cf_def_conn_is_alive,
30813498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
30913498266Sopenharmony_ci  Curl_cf_def_query,
31013498266Sopenharmony_ci};
31113498266Sopenharmony_ci
31213498266Sopenharmony_ciCURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
31313498266Sopenharmony_ci                                         struct Curl_easy *data)
31413498266Sopenharmony_ci{
31513498266Sopenharmony_ci  struct Curl_cfilter *cf;
31613498266Sopenharmony_ci  struct cf_proxy_ctx *ctx = NULL;
31713498266Sopenharmony_ci  CURLcode result;
31813498266Sopenharmony_ci
31913498266Sopenharmony_ci  (void)data;
32013498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
32113498266Sopenharmony_ci  if(!ctx) {
32213498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
32313498266Sopenharmony_ci    goto out;
32413498266Sopenharmony_ci  }
32513498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
32613498266Sopenharmony_ci  if(result)
32713498266Sopenharmony_ci    goto out;
32813498266Sopenharmony_ci  ctx = NULL;
32913498266Sopenharmony_ci  Curl_conn_cf_insert_after(cf_at, cf);
33013498266Sopenharmony_ci
33113498266Sopenharmony_ciout:
33213498266Sopenharmony_ci  free(ctx);
33313498266Sopenharmony_ci  return result;
33413498266Sopenharmony_ci}
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
337