Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/curl/lib/http_proxy.c
Line
Count
Source (jump to first uncovered line)
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
#ifdef USE_HYPER
33
#include <hyper.h>
34
#endif
35
#include "sendf.h"
36
#include "http.h"
37
#include "url.h"
38
#include "select.h"
39
#include "progress.h"
40
#include "cfilters.h"
41
#include "cf-h1-proxy.h"
42
#include "cf-h2-proxy.h"
43
#include "connect.h"
44
#include "curlx.h"
45
#include "vtls/vtls.h"
46
#include "transfer.h"
47
#include "multiif.h"
48
49
/* The last 3 #include files should be in this order */
50
#include "curl_printf.h"
51
#include "curl_memory.h"
52
#include "memdebug.h"
53
54
55
CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
56
                                         const char **phostname,
57
                                         int *pport, bool *pipv6_ip)
58
0
{
59
0
  DEBUGASSERT(cf);
60
0
  DEBUGASSERT(cf->conn);
61
62
0
  if(cf->conn->bits.conn_to_host)
63
0
    *phostname = cf->conn->conn_to_host.name;
64
0
  else if(cf->sockindex == SECONDARYSOCKET)
65
0
    *phostname = cf->conn->secondaryhostname;
66
0
  else
67
0
    *phostname = cf->conn->host.name;
68
69
0
  if(cf->sockindex == SECONDARYSOCKET)
70
0
    *pport = cf->conn->secondary_port;
71
0
  else if(cf->conn->bits.conn_to_port)
72
0
    *pport = cf->conn->conn_to_port;
73
0
  else
74
0
    *pport = cf->conn->remote_port;
75
76
0
  if(*phostname != cf->conn->host.name)
77
0
    *pipv6_ip = (strchr(*phostname, ':') != NULL);
78
0
  else
79
0
    *pipv6_ip = cf->conn->bits.ipv6_ip;
80
81
0
  return CURLE_OK;
82
0
}
83
84
CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
85
                                        struct Curl_cfilter *cf,
86
                                        struct Curl_easy *data,
87
                                        int http_version_major)
88
0
{
89
0
  const char *hostname = NULL;
90
0
  char *authority = NULL;
91
0
  int port;
92
0
  bool ipv6_ip;
93
0
  CURLcode result;
94
0
  struct httpreq *req = NULL;
95
96
0
  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
97
0
  if(result)
98
0
    goto out;
99
100
0
  authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname,
101
0
                      ipv6_ip?"]":"", port);
102
0
  if(!authority) {
103
0
    result = CURLE_OUT_OF_MEMORY;
104
0
    goto out;
105
0
  }
106
107
0
  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
108
0
                              NULL, 0, authority, strlen(authority),
109
0
                              NULL, 0);
110
0
  if(result)
111
0
    goto out;
112
113
  /* Setup the proxy-authorization header, if any */
114
0
  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
115
0
                                 req->authority, TRUE);
116
0
  if(result)
117
0
    goto out;
118
119
  /* If user is not overriding Host: header, we add for HTTP/1.x */
120
0
  if(http_version_major == 1 &&
121
0
     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
122
0
    result = Curl_dynhds_cadd(&req->headers, "Host", authority);
123
0
    if(result)
124
0
      goto out;
125
0
  }
126
127
0
  if(data->state.aptr.proxyuserpwd) {
128
0
    result = Curl_dynhds_h1_cadd_line(&req->headers,
129
0
                                      data->state.aptr.proxyuserpwd);
130
0
    if(result)
131
0
      goto out;
132
0
  }
133
134
0
  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
135
0
     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
136
0
    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
137
0
                              data->set.str[STRING_USERAGENT]);
138
0
    if(result)
139
0
      goto out;
140
0
  }
141
142
0
  if(http_version_major == 1 &&
143
0
    !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
144
0
    result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
145
0
    if(result)
146
0
      goto out;
147
0
  }
148
149
0
  result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
150
151
0
out:
152
0
  if(result && req) {
153
0
    Curl_http_req_free(req);
154
0
    req = NULL;
155
0
  }
156
0
  free(authority);
157
0
  *preq = req;
158
0
  return result;
159
0
}
160
161
162
struct cf_proxy_ctx {
163
  /* the protocol specific sub-filter we install during connect */
164
  struct Curl_cfilter *cf_protocol;
165
};
166
167
static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
168
                                      struct Curl_easy *data,
169
                                      bool blocking, bool *done)
170
0
{
171
0
  struct cf_proxy_ctx *ctx = cf->ctx;
172
0
  CURLcode result;
173
174
0
  if(cf->connected) {
175
0
    *done = TRUE;
176
0
    return CURLE_OK;
177
0
  }
178
179
0
  CURL_TRC_CF(data, cf, "connect");
180
0
connect_sub:
181
0
  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
182
0
  if(result || !*done)
183
0
    return result;
184
185
0
  *done = FALSE;
186
0
  if(!ctx->cf_protocol) {
187
0
    struct Curl_cfilter *cf_protocol = NULL;
188
0
    int alpn = Curl_conn_cf_is_ssl(cf->next)?
189
0
      cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
190
191
    /* First time call after the subchain connected */
192
0
    switch(alpn) {
193
0
    case CURL_HTTP_VERSION_NONE:
194
0
    case CURL_HTTP_VERSION_1_0:
195
0
    case CURL_HTTP_VERSION_1_1:
196
0
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
197
0
      infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
198
0
            (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1);
199
0
      result = Curl_cf_h1_proxy_insert_after(cf, data);
200
0
      if(result)
201
0
        goto out;
202
0
      cf_protocol = cf->next;
203
0
      break;
204
#ifdef USE_NGHTTP2
205
    case CURL_HTTP_VERSION_2:
206
      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
207
      infof(data, "CONNECT tunnel: HTTP/2 negotiated");
208
      result = Curl_cf_h2_proxy_insert_after(cf, data);
209
      if(result)
210
        goto out;
211
      cf_protocol = cf->next;
212
      break;
213
#endif
214
0
    default:
215
0
      infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
216
0
      result = CURLE_COULDNT_CONNECT;
217
0
      goto out;
218
0
    }
219
220
0
    ctx->cf_protocol = cf_protocol;
221
    /* after we installed the filter "below" us, we call connect
222
     * on out sub-chain again.
223
     */
224
0
    goto connect_sub;
225
0
  }
226
0
  else {
227
    /* subchain connected and we had already installed the protocol filter.
228
     * This means the protocol tunnel is established, we are done.
229
     */
230
0
    DEBUGASSERT(ctx->cf_protocol);
231
0
    result = CURLE_OK;
232
0
  }
233
234
0
out:
235
0
  if(!result) {
236
0
    cf->connected = TRUE;
237
0
    *done = TRUE;
238
0
  }
239
0
  return result;
240
0
}
241
242
void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf,
243
                                 struct Curl_easy *data,
244
                                 const char **phost,
245
                                 const char **pdisplay_host,
246
                                 int *pport)
247
0
{
248
0
  (void)data;
249
0
  if(!cf->connected) {
250
0
    *phost = cf->conn->http_proxy.host.name;
251
0
    *pdisplay_host = cf->conn->http_proxy.host.dispname;
252
0
    *pport = (int)cf->conn->http_proxy.port;
253
0
  }
254
0
  else {
255
0
    cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
256
0
  }
257
0
}
258
259
static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
260
                                  struct Curl_easy *data)
261
0
{
262
0
  struct cf_proxy_ctx *ctx = cf->ctx;
263
264
0
  (void)data;
265
0
  CURL_TRC_CF(data, cf, "destroy");
266
0
  free(ctx);
267
0
}
268
269
static void http_proxy_cf_close(struct Curl_cfilter *cf,
270
                                struct Curl_easy *data)
271
0
{
272
0
  struct cf_proxy_ctx *ctx = cf->ctx;
273
274
0
  CURL_TRC_CF(data, cf, "close");
275
0
  cf->connected = FALSE;
276
0
  if(ctx->cf_protocol) {
277
0
    struct Curl_cfilter *f;
278
    /* if someone already removed it, we assume he also
279
     * took care of destroying it. */
280
0
    for(f = cf->next; f; f = f->next) {
281
0
      if(f == ctx->cf_protocol) {
282
        /* still in our sub-chain */
283
0
        Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE);
284
0
        break;
285
0
      }
286
0
    }
287
0
    ctx->cf_protocol = NULL;
288
0
  }
289
0
  if(cf->next)
290
0
    cf->next->cft->do_close(cf->next, data);
291
0
}
292
293
294
struct Curl_cftype Curl_cft_http_proxy = {
295
  "HTTP-PROXY",
296
  CF_TYPE_IP_CONNECT,
297
  0,
298
  http_proxy_cf_destroy,
299
  http_proxy_cf_connect,
300
  http_proxy_cf_close,
301
  Curl_cf_http_proxy_get_host,
302
  Curl_cf_def_adjust_pollset,
303
  Curl_cf_def_data_pending,
304
  Curl_cf_def_send,
305
  Curl_cf_def_recv,
306
  Curl_cf_def_cntrl,
307
  Curl_cf_def_conn_is_alive,
308
  Curl_cf_def_conn_keep_alive,
309
  Curl_cf_def_query,
310
};
311
312
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
313
                                         struct Curl_easy *data)
314
0
{
315
0
  struct Curl_cfilter *cf;
316
0
  struct cf_proxy_ctx *ctx = NULL;
317
0
  CURLcode result;
318
319
0
  (void)data;
320
0
  ctx = calloc(1, sizeof(*ctx));
321
0
  if(!ctx) {
322
0
    result = CURLE_OUT_OF_MEMORY;
323
0
    goto out;
324
0
  }
325
0
  result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
326
0
  if(result)
327
0
    goto out;
328
0
  ctx = NULL;
329
0
  Curl_conn_cf_insert_after(cf_at, cf);
330
331
0
out:
332
0
  free(ctx);
333
0
  return result;
334
0
}
335
336
#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */