VirtualBox

source: vbox/trunk/src/libs/curl-8.11.1/lib/http_proxy.c@ 108257

Last change on this file since 108257 was 108048, checked in by vboxsync, 3 months ago

curl-8.11.1: Applied and adjusted our curl changes to 8.7.1. jiraref:VBP-1535

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette