Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcurl/lib/http_proxy.c
Line
Count
Source
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
#include "curl_setup.h"
25
26
#include "http_proxy.h"
27
28
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
29
30
#include "curl_trc.h"
31
#include "http.h"
32
#include "url.h"
33
#include "cfilters.h"
34
#include "cf-h1-proxy.h"
35
#include "cf-h2-proxy.h"
36
#include "connect.h"
37
#include "vauth/vauth.h"
38
#include "vquic/vquic.h"
39
#include "curlx/strparse.h"
40
41
static CURLcode dynhds_add_custom(struct Curl_easy *data,
42
                                  bool is_connect, int httpversion,
43
                                  bool is_udp, struct dynhds *hds)
44
0
{
45
0
  struct connectdata *conn = data->conn;
46
0
  struct curl_slist *h[2];
47
0
  struct curl_slist *headers;
48
0
  int numlists = 1; /* by default */
49
0
  int i;
50
51
0
  enum Curl_proxy_use proxy;
52
53
0
  if(is_connect && !is_udp)
54
0
    proxy = HEADER_CONNECT;
55
0
  else if(is_connect && is_udp)
56
0
    proxy = HEADER_CONNECT_UDP;
57
0
  else
58
0
    proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
59
60
0
  switch(proxy) {
61
0
  case HEADER_SERVER:
62
0
    h[0] = data->set.headers;
63
0
    break;
64
0
  case HEADER_PROXY:
65
0
    h[0] = data->set.headers;
66
0
    if(data->set.sep_headers) {
67
0
      h[1] = data->set.proxyheaders;
68
0
      numlists++;
69
0
    }
70
0
    break;
71
0
  case HEADER_CONNECT:
72
0
    if(data->set.sep_headers)
73
0
      h[0] = data->set.proxyheaders;
74
0
    else
75
0
      h[0] = data->set.headers;
76
0
    break;
77
0
  case HEADER_CONNECT_UDP:
78
0
    if(data->set.sep_headers)
79
0
      h[0] = data->set.proxyheaders;
80
0
    else
81
0
      h[0] = data->set.headers;
82
0
    break;
83
0
  }
84
85
  /* loop through one or two lists */
86
0
  for(i = 0; i < numlists; i++) {
87
0
    for(headers = h[i]; headers; headers = headers->next) {
88
0
      struct Curl_str name;
89
0
      const char *value = NULL;
90
0
      size_t valuelen = 0;
91
0
      const char *ptr = headers->data;
92
93
      /* There are 2 quirks in place for custom headers:
94
       * 1. setting only 'name:' to suppress a header from being sent
95
       * 2. setting only 'name;' to send an empty (illegal) header
96
       */
97
0
      if(!curlx_str_cspn(&ptr, &name, ";:")) {
98
0
        if(!curlx_str_single(&ptr, ':')) {
99
0
          curlx_str_passblanks(&ptr);
100
0
          if(*ptr) {
101
0
            value = ptr;
102
0
            valuelen = strlen(value);
103
0
          }
104
0
          else {
105
            /* quirk #1, suppress this header */
106
0
            continue;
107
0
          }
108
0
        }
109
0
        else if(!curlx_str_single(&ptr, ';')) {
110
0
          curlx_str_passblanks(&ptr);
111
0
          if(!*ptr) {
112
            /* quirk #2, send an empty header */
113
0
            value = "";
114
0
            valuelen = 0;
115
0
          }
116
0
          else {
117
            /* this may be used for something else in the future,
118
             * ignore this for now */
119
0
            continue;
120
0
          }
121
0
        }
122
0
        else
123
          /* neither : nor ; in provided header value. We ignore this
124
           * silently */
125
0
          continue;
126
0
      }
127
0
      else
128
        /* no name, move on */
129
0
        continue;
130
131
0
      DEBUGASSERT(curlx_strlen(&name) && value);
132
0
      if(data->state.aptr.host &&
133
         /* a Host: header was sent already, do not pass on any custom Host:
134
            header as that will produce *two* in the same request! */
135
0
         curlx_str_casecompare(&name, "Host"))
136
0
        ;
137
0
      else if(data->state.httpreq == HTTPREQ_POST_FORM &&
138
              /* this header (extended by formdata.c) is sent later */
139
0
              curlx_str_casecompare(&name, "Content-Type"))
140
0
        ;
141
0
      else if(data->state.httpreq == HTTPREQ_POST_MIME &&
142
              /* this header is sent later */
143
0
              curlx_str_casecompare(&name, "Content-Type"))
144
0
        ;
145
0
      else if(data->req.authneg &&
146
              /* while doing auth neg, do not allow the custom length since
147
                 we will force length zero then */
148
0
              curlx_str_casecompare(&name, "Content-Length"))
149
0
        ;
150
0
      else if((httpversion >= 20) &&
151
0
              curlx_str_casecompare(&name, "Transfer-Encoding"))
152
0
        ;
153
      /* HTTP/2 and HTTP/3 do not support chunked requests */
154
0
      else if((curlx_str_casecompare(&name, "Authorization") ||
155
0
               curlx_str_casecompare(&name, "Cookie")) &&
156
              /* be careful of sending this potentially sensitive header to
157
                 other hosts */
158
0
              !Curl_auth_allowed_to_host(data))
159
0
        ;
160
0
      else {
161
0
        CURLcode result =
162
0
          Curl_dynhds_add(hds, curlx_str(&name), curlx_strlen(&name),
163
0
                          value, valuelen);
164
0
        if(result)
165
0
          return result;
166
0
      }
167
0
    }
168
0
  }
169
170
0
  return CURLE_OK;
171
0
}
172
173
struct cf_proxy_ctx {
174
  struct Curl_peer *peer; /* proxy */
175
  struct Curl_peer *tunnel_peer; /* tunnel destination */
176
  uint8_t proxytype;
177
  uint8_t tunnel_transport;
178
  BIT(sub_filter_installed);
179
};
180
181
static int proxy_http_ver_major(proxy_http_ver ver)
182
0
{
183
0
  switch(ver) {
184
0
  case PROXY_HTTP_V1:
185
0
    return 11;
186
0
  case PROXY_HTTP_V2:
187
0
    return 20;
188
0
  case PROXY_HTTP_V3:
189
0
    return 30;
190
0
  }
191
0
  return 0;
192
0
}
193
194
static CURLcode http_proxy_create_CONNECT(struct httpreq **preq,
195
                                          struct Curl_cfilter *cf,
196
                                          struct Curl_easy *data,
197
                                          struct Curl_peer *dest,
198
                                          proxy_http_ver ver)
199
0
{
200
0
  char *authority = NULL;
201
0
  int httpversion = proxy_http_ver_major(ver);
202
0
  CURLcode result;
203
0
  struct httpreq *req = NULL;
204
205
0
  authority = curl_maprintf("%s%s%s:%u",
206
0
                            dest->ipv6 ? "[" : "",
207
0
                            dest->hostname,
208
0
                            dest->ipv6 ? "]" : "",
209
0
                            dest->port);
210
0
  if(!authority) {
211
0
    result = CURLE_OUT_OF_MEMORY;
212
0
    goto out;
213
0
  }
214
215
0
  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1,
216
0
                              NULL, 0, authority, strlen(authority),
217
0
                              NULL, 0);
218
0
  if(result)
219
0
    goto out;
220
221
  /* Setup the proxy-authorization header, if any */
222
0
  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
223
0
                                 req->authority, NULL, TRUE);
224
0
  if(result)
225
0
    goto out;
226
227
  /* If user is not overriding Host: header, we add for HTTP/1.x */
228
0
  if(ver == PROXY_HTTP_V1 &&
229
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
230
0
    result = Curl_dynhds_cadd(&req->headers, "Host", authority);
231
0
    if(result)
232
0
      goto out;
233
0
  }
234
235
0
  if(data->req.hd_proxy_auth) {
236
0
    result = Curl_dynhds_h1_cadd_line(&req->headers,
237
0
                                      data->req.hd_proxy_auth);
238
0
    if(result)
239
0
      goto out;
240
0
  }
241
242
0
  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
243
0
     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
244
0
    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
245
0
                              data->set.str[STRING_USERAGENT]);
246
0
    if(result)
247
0
      goto out;
248
0
  }
249
250
0
  if(ver == PROXY_HTTP_V1 &&
251
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
252
0
    result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
253
0
    if(result)
254
0
      goto out;
255
0
  }
256
257
0
  result = dynhds_add_custom(data, TRUE, httpversion,
258
0
                             FALSE, &req->headers);
259
260
0
out:
261
0
  if(result && req) {
262
0
    Curl_http_req_free(req);
263
0
    req = NULL;
264
0
  }
265
0
  curlx_free(authority);
266
0
  *preq = req;
267
0
  return result;
268
0
}
269
270
static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq,
271
                                             struct Curl_cfilter *cf,
272
                                             struct Curl_easy *data,
273
                                             struct Curl_peer *dest,
274
                                             proxy_http_ver ver)
275
0
{
276
0
  const char *proxy_scheme = "http";
277
0
  const char *proxy_host = cf->conn->http_proxy.peer->hostname;
278
0
  int httpversion = proxy_http_ver_major(ver);
279
0
  char *authority = NULL;
280
0
  char *path = NULL;
281
0
  char *encoded_host = NULL;
282
0
  struct httpreq *req = NULL;
283
0
  bool proxy_ipv6_ip;
284
0
  CURLcode result;
285
286
0
  if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS ||
287
0
     cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS2 ||
288
0
     cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS3)
289
0
    proxy_scheme = "https";
290
291
0
  proxy_ipv6_ip = cf->conn->http_proxy.peer->ipv6 != 0;
292
293
0
  authority = curl_maprintf("%s%s%s:%d",
294
0
                            proxy_ipv6_ip ? "[" : "",
295
0
                            proxy_host,
296
0
                            proxy_ipv6_ip ? "]" : "",
297
0
                            cf->conn->http_proxy.peer->port);
298
0
  if(!authority) {
299
0
    result = CURLE_OUT_OF_MEMORY;
300
0
    goto out;
301
0
  }
302
303
0
  if(dest->ipv6) {
304
    /* RFC 9298: colons in IPv6 addresses MUST be percent-encoded
305
     * in the URI template (e.g. "2001:db8::1" -> "2001%3Adb8%3A%3A1") */
306
0
    const char *s = dest->hostname;
307
0
    char *d;
308
0
    size_t hlen = strlen(s);
309
0
    encoded_host = curlx_malloc(hlen * 3 + 1);
310
0
    if(!encoded_host) {
311
0
      result = CURLE_OUT_OF_MEMORY;
312
0
      goto out;
313
0
    }
314
0
    d = encoded_host;
315
0
    while(*s) {
316
0
      if(*s == ':') {
317
0
        *d++ = '%';
318
0
        *d++ = '3';
319
0
        *d++ = 'A';
320
0
      }
321
0
      else
322
0
        *d++ = *s;
323
0
      s++;
324
0
    }
325
0
    *d = '\0';
326
0
    path = curl_maprintf("/.well-known/masque/udp/%s/%u/",
327
0
                         encoded_host, (unsigned int)dest->port);
328
0
  }
329
0
  else {
330
0
    path = curl_maprintf("/.well-known/masque/udp/%s/%u/",
331
0
                         dest->hostname, (unsigned int)dest->port);
332
0
  }
333
334
0
  if(!path) {
335
0
    result = CURLE_OUT_OF_MEMORY;
336
0
    goto out;
337
0
  }
338
339
0
  if(ver == PROXY_HTTP_V1) {
340
0
    result = Curl_http_req_make(&req, "GET", sizeof("GET")-1,
341
0
                                proxy_scheme, strlen(proxy_scheme),
342
0
                                authority, strlen(authority),
343
0
                                path, strlen(path));
344
0
    if(result)
345
0
      goto out;
346
0
  }
347
0
  else if(ver == PROXY_HTTP_V2 || ver == PROXY_HTTP_V3) {
348
0
    result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1,
349
0
                                proxy_scheme, strlen(proxy_scheme),
350
0
                                authority, strlen(authority),
351
0
                                path, strlen(path));
352
0
    if(result)
353
0
      goto out;
354
0
  }
355
0
  else {
356
0
    result = CURLE_FAILED_INIT;
357
0
    goto out;
358
0
  }
359
360
  /* Setup the proxy-authorization header, if any */
361
0
  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
362
0
                                 req->authority, NULL, TRUE);
363
0
  if(result)
364
0
    goto out;
365
366
  /* If user is not overriding Host: header, we add for HTTP/1.x */
367
0
  if(ver == PROXY_HTTP_V1 &&
368
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
369
0
    result = Curl_dynhds_cadd(&req->headers, "Host", authority);
370
0
    if(result)
371
0
      goto out;
372
0
  }
373
374
0
  if(data->req.hd_proxy_auth) {
375
0
    result = Curl_dynhds_h1_cadd_line(&req->headers,
376
0
                                      data->req.hd_proxy_auth);
377
0
    if(result)
378
0
      goto out;
379
0
  }
380
381
0
  if(ver == PROXY_HTTP_V1 &&
382
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
383
0
     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
384
0
    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
385
0
                              data->set.str[STRING_USERAGENT]);
386
0
    if(result)
387
0
      goto out;
388
0
  }
389
390
0
  if(ver == PROXY_HTTP_V1 &&
391
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
392
0
    result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
393
0
    if(result)
394
0
      goto out;
395
0
  }
396
397
0
  if(ver == PROXY_HTTP_V1) {
398
0
    result = Curl_dynhds_cadd(&req->headers, "Connection", "Upgrade");
399
0
    if(result)
400
0
      goto out;
401
402
0
    result = Curl_dynhds_cadd(&req->headers, "Upgrade", "connect-udp");
403
0
    if(result)
404
0
      goto out;
405
406
0
    result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1");
407
0
    if(result)
408
0
      goto out;
409
0
  }
410
0
  else {
411
0
    result = Curl_dynhds_cadd(&req->headers, ":Protocol", "connect-udp");
412
0
    if(result)
413
0
      goto out;
414
415
0
    if(ver >= PROXY_HTTP_V2) {
416
0
      result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1");
417
0
      if(result)
418
0
        goto out;
419
0
    }
420
0
  }
421
422
0
  result = dynhds_add_custom(data, TRUE, httpversion,
423
0
                             TRUE, &req->headers);
424
425
0
out:
426
0
  if(result && req) {
427
0
    Curl_http_req_free(req);
428
0
    req = NULL;
429
0
  }
430
0
  curlx_free(authority);
431
0
  curlx_free(path);
432
0
  curlx_free(encoded_host);
433
0
  *preq = req;
434
0
  return result;
435
0
}
436
437
CURLcode Curl_http_proxy_create_tunnel_request(
438
    struct httpreq **preq, struct Curl_cfilter *cf,
439
    struct Curl_easy *data, struct Curl_peer *dest,
440
    proxy_http_ver ver, bool udp_tunnel)
441
0
{
442
0
  CURLcode result;
443
444
0
  if(udp_tunnel)
445
0
    result = http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver);
446
0
  else
447
0
    result = http_proxy_create_CONNECT(preq, cf, data, dest, ver);
448
0
  if(result)
449
0
    return result;
450
451
0
  if(udp_tunnel)
452
0
    infof(data, "Establishing %s proxy UDP tunnel to %s:%s",
453
0
          (ver == PROXY_HTTP_V2) ? "HTTP/2" :
454
0
          (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP",
455
0
          data->state.up.hostname, data->state.up.port);
456
0
  else
457
0
    infof(data, "Establishing %s proxy tunnel to %s",
458
0
          (ver == PROXY_HTTP_V2) ? "HTTP/2" :
459
0
          (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP",
460
0
          (*preq)->authority);
461
0
  return CURLE_OK;
462
0
}
463
464
CURLcode Curl_http_proxy_inspect_tunnel_response(
465
    struct Curl_cfilter *cf, struct Curl_easy *data,
466
    struct http_resp *resp, bool udp_tunnel,
467
    proxy_inspect_result *presult)
468
0
{
469
0
  struct dynhds_entry *capsule_protocol = NULL;
470
0
  struct dynhds_entry *auth_reply = NULL;
471
0
  size_t i, header_count;
472
0
  CURLcode result = CURLE_OK;
473
474
0
  DEBUGASSERT(resp);
475
476
0
  header_count = Curl_dynhds_count(&resp->headers);
477
0
  if(udp_tunnel)
478
0
    infof(data, "CONNECT-UDP Response Status %d", resp->status);
479
0
  else
480
0
    infof(data, "CONNECT Response Status %d", resp->status);
481
0
  infof(data, "Response Headers (%zu total):", header_count);
482
0
  for(i = 0; i < header_count; i++) {
483
0
    struct dynhds_entry *entry = Curl_dynhds_getn(&resp->headers, i);
484
0
    if(entry)
485
0
      infof(data, "  %s: %s", entry->name, entry->value);
486
0
  }
487
488
0
  if(resp->status == 401) {
489
0
    auth_reply = Curl_dynhds_cget(&resp->headers, "WWW-Authenticate");
490
0
  }
491
0
  else if(resp->status == 407) {
492
0
    auth_reply = Curl_dynhds_cget(&resp->headers, "Proxy-Authenticate");
493
0
  }
494
495
0
  if(auth_reply) {
496
0
    CURL_TRC_CF(data, cf, "[0] CONNECT%s: fwd auth header '%s'",
497
0
                udp_tunnel ? "-UDP" : "", auth_reply->value);
498
0
    result = Curl_http_input_auth(data, resp->status == 407,
499
0
                                  auth_reply->value);
500
0
    if(result)
501
0
      return result;
502
0
    if(data->req.newurl) {
503
0
      curlx_safefree(data->req.newurl);
504
0
      *presult = PROXY_INSPECT_AUTH_RETRY;
505
0
      return CURLE_OK;
506
0
    }
507
0
  }
508
509
0
  if(udp_tunnel) {
510
0
    if(resp->status / 100 == 2) {
511
0
      capsule_protocol = Curl_dynhds_cget(&resp->headers,
512
0
                                           "capsule-protocol");
513
0
      if(capsule_protocol) {
514
0
        if(!strncmp(capsule_protocol->value, "?1", 2) &&
515
0
           !capsule_protocol->value[2]) {
516
0
          infof(data, "CONNECT-UDP tunnel established, response %d",
517
0
                resp->status);
518
0
          *presult = PROXY_INSPECT_OK;
519
0
          return CURLE_OK;
520
0
        }
521
0
        failf(data, "Failed to establish CONNECT-UDP tunnel, response %d, "
522
0
              "unsupported capsule-protocol value '%s'",
523
0
              resp->status, capsule_protocol->value);
524
0
        *presult = PROXY_INSPECT_FAILED;
525
0
        return CURLE_COULDNT_CONNECT;
526
0
      }
527
0
      else {
528
        /* NOTE proxies may not set capsule protocol in the headers */
529
0
        infof(data, "CONNECT-UDP tunnel established, response %d "
530
0
                    "but no capsule-protocol header found", resp->status);
531
0
        *presult = PROXY_INSPECT_OK;
532
0
        return CURLE_OK;
533
0
      }
534
0
    }
535
0
    else {
536
0
      failf(data, "Failed to establish CONNECT-UDP tunnel, "
537
0
                  "response %d", resp->status);
538
0
      *presult = PROXY_INSPECT_FAILED;
539
0
      return CURLE_COULDNT_CONNECT;
540
0
    }
541
0
  }
542
543
0
  if(resp->status / 100 == 2) {
544
0
    infof(data, "CONNECT tunnel established, response %d", resp->status);
545
0
    *presult = PROXY_INSPECT_OK;
546
0
    return CURLE_OK;
547
0
  }
548
549
0
  *presult = PROXY_INSPECT_FAILED;
550
0
  return CURLE_COULDNT_CONNECT;
551
0
}
552
553
static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
554
                                      struct Curl_easy *data,
555
                                      bool *done)
556
0
{
557
0
  struct cf_proxy_ctx *ctx = cf->ctx;
558
0
  CURLcode result;
559
0
  bool udp_tunnel = TRNSPRT_IS_DGRAM(ctx->tunnel_transport);
560
0
  const char *tunnel_type = udp_tunnel ? "CONNECT-UDP" : "CONNECT";
561
562
0
  if(cf->connected) {
563
0
    *done = TRUE;
564
0
    return CURLE_OK;
565
0
  }
566
567
0
  CURL_TRC_CF(data, cf, "%s", tunnel_type);
568
0
connect_sub:
569
  /* in case of h3_proxy, cf->next will be NULL initially */
570
0
  if(cf->next) {
571
0
    result = cf->next->cft->do_connect(cf->next, data, done);
572
0
    if(result || !*done)
573
0
      return result;
574
0
  }
575
576
0
  *done = FALSE;
577
0
  if(!ctx->sub_filter_installed) {
578
0
    const char *alpn = NULL;
579
580
    /* in case of h3_proxy, cf->next will be NULL initially */
581
0
    if(cf->next) {
582
0
      alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
583
0
    }
584
585
0
    if(alpn)
586
0
      infof(data, "%s: '%s' negotiated", tunnel_type, alpn);
587
0
    else if(!alpn) {
588
      /* No ALPN, proxytype rules. Fake ALPN */
589
0
      infof(data, "%s: no ALPN negotiated", tunnel_type);
590
0
      switch(ctx->proxytype) {
591
0
      case CURLPROXY_HTTP_1_0:
592
0
        alpn = "http/1.0";
593
0
        break;
594
0
      case CURLPROXY_HTTPS2:
595
0
        alpn = "h2";
596
0
        break;
597
0
      case CURLPROXY_HTTPS3:
598
0
        alpn = "h3";
599
0
        break;
600
0
      default:
601
0
        alpn = "http/1.1";
602
0
        break;
603
0
      }
604
0
    }
605
606
0
    if(!strcmp(alpn, "http/1.0")) {
607
0
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
608
0
      result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, 10,
609
0
                                             udp_tunnel);
610
0
      if(result)
611
0
        goto out;
612
0
    }
613
0
    else if(!strcmp(alpn, "http/1.1")) {
614
0
      int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11;
615
0
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d",
616
0
                  httpversion % 10);
617
0
      result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer,
618
0
                                             httpversion, udp_tunnel);
619
0
      if(result)
620
0
        goto out;
621
0
    }
622
0
#ifdef USE_NGHTTP2
623
0
    else if(!strcmp(alpn, "h2")) {
624
0
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
625
0
      result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->tunnel_peer,
626
0
                                             udp_tunnel);
627
0
      if(result)
628
0
        goto out;
629
0
    }
630
0
#endif /* USE_NGHTTP2 */
631
#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \
632
  defined(USE_NGTCP2) && defined(USE_OPENSSL)
633
    else if(!strcmp(alpn, "h3")) {
634
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3");
635
      result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->peer, ctx->peer,
636
                                             ctx->tunnel_peer,
637
                                             ctx->tunnel_transport);
638
      if(result)
639
        goto out;
640
    }
641
#endif /* USE_PROXY_HTTP3 && USE_NGHTTP3 && USE_NGTCP2 && USE_OPENSSL */
642
0
    else {
643
0
      failf(data, "%s: negotiated ALPN '%s' not supported", tunnel_type, alpn);
644
0
      result = CURLE_COULDNT_CONNECT;
645
0
      goto out;
646
0
    }
647
648
0
    ctx->sub_filter_installed = TRUE;
649
    /* after we installed the filter "below" us, we call connect
650
     * on out sub-chain again.
651
     */
652
0
    goto connect_sub;
653
0
  }
654
0
  else {
655
    /* subchain connected and we had already installed the protocol filter.
656
     * This means the protocol tunnel is established, we are done. */
657
0
    DEBUGASSERT(ctx->sub_filter_installed);
658
0
    result = CURLE_OK;
659
0
  }
660
661
0
out:
662
0
  if(!result) {
663
0
    cf->connected = TRUE;
664
0
    *done = TRUE;
665
0
  }
666
0
  return result;
667
0
}
668
669
static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf,
670
                                    struct Curl_easy *data,
671
                                    int query, int *pres1, void *pres2)
672
0
{
673
0
  struct cf_proxy_ctx *ctx = cf->ctx;
674
0
  switch(query) {
675
0
  case CF_QUERY_HOST_PORT:
676
0
    *pres1 = (int)ctx->tunnel_peer->port;
677
0
    *((const char **)pres2) = ctx->tunnel_peer->hostname;
678
0
    return CURLE_OK;
679
0
  case CF_QUERY_ALPN_NEGOTIATED: {
680
0
    const char **palpn = pres2;
681
0
    DEBUGASSERT(palpn);
682
0
    *palpn = NULL;
683
0
    return CURLE_OK;
684
0
  }
685
0
  default:
686
0
    break;
687
0
  }
688
0
  return cf->next ?
689
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
690
0
    CURLE_UNKNOWN_OPTION;
691
0
}
692
693
static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx)
694
0
{
695
0
  if(ctx) {
696
0
    Curl_peer_unlink(&ctx->peer);
697
0
    Curl_peer_unlink(&ctx->tunnel_peer);
698
0
    curlx_free(ctx);
699
0
  }
700
0
}
701
702
static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
703
                                  struct Curl_easy *data)
704
0
{
705
0
  struct cf_proxy_ctx *ctx = cf->ctx;
706
0
  if(ctx) {
707
0
    CURL_TRC_CF(data, cf, "destroy");
708
0
    cf_https_proxy_ctx_free(ctx);
709
0
  }
710
0
}
711
712
struct Curl_cftype Curl_cft_http_proxy = {
713
  "HTTP-PROXY",
714
  CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP,
715
  0,
716
  http_proxy_cf_destroy,
717
  http_proxy_cf_connect,
718
  Curl_cf_def_shutdown,
719
  Curl_cf_def_adjust_pollset,
720
  Curl_cf_def_data_pending,
721
  Curl_cf_def_send,
722
  Curl_cf_def_recv,
723
  Curl_cf_def_cntrl,
724
  Curl_cf_def_conn_is_alive,
725
  Curl_cf_def_conn_keep_alive,
726
  cf_http_proxy_query,
727
};
728
729
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
730
                                         struct Curl_easy *data,
731
                                         struct Curl_peer *peer,
732
                                         struct Curl_peer *tunnel_peer,
733
                                         uint8_t tunnel_transport,
734
                                         uint8_t proxytype)
735
0
{
736
0
  struct Curl_cfilter *cf;
737
0
  struct cf_proxy_ctx *ctx = NULL;
738
0
  CURLcode result;
739
740
0
  (void)data;
741
0
  if(!peer || !tunnel_peer)
742
0
    return CURLE_FAILED_INIT;
743
744
0
  ctx = curlx_calloc(1, sizeof(*ctx));
745
0
  if(!ctx) {
746
0
    result = CURLE_OUT_OF_MEMORY;
747
0
    goto out;
748
0
  }
749
0
  Curl_peer_link(&ctx->peer, peer);
750
0
  Curl_peer_link(&ctx->tunnel_peer, tunnel_peer);
751
0
  ctx->proxytype = proxytype;
752
0
  ctx->tunnel_transport = tunnel_transport;
753
754
0
  result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
755
0
  if(result)
756
0
    goto out;
757
0
  ctx = NULL;
758
0
  Curl_conn_cf_insert_after(cf_at, cf);
759
760
0
out:
761
0
  cf_https_proxy_ctx_free(ctx);
762
0
  return result;
763
0
}
764
765
uint8_t Curl_http_proxy_transport(uint8_t proxytype)
766
0
{
767
0
  switch(proxytype) {
768
0
  case CURLPROXY_HTTPS3:
769
0
    return TRNSPRT_QUIC;
770
0
  default:
771
0
    return TRNSPRT_TCP;
772
0
  }
773
0
}
774
775
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */