/src/curl/lib/http_proxy.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) 1998 - 2022, 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_PROXY) && !defined(CURL_DISABLE_HTTP) |
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 "connect.h" |
42 | | #include "curlx.h" |
43 | | #include "vtls/vtls.h" |
44 | | #include "transfer.h" |
45 | | #include "multiif.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 | | typedef enum { |
53 | | TUNNEL_INIT, /* init/default/no tunnel state */ |
54 | | TUNNEL_CONNECT, /* CONNECT request is being send */ |
55 | | TUNNEL_RECEIVE, /* CONNECT answer is being received */ |
56 | | TUNNEL_RESPONSE, /* CONNECT response received completely */ |
57 | | TUNNEL_ESTABLISHED, |
58 | | TUNNEL_FAILED |
59 | | } tunnel_state; |
60 | | |
61 | | /* struct for HTTP CONNECT tunneling */ |
62 | | struct tunnel_state { |
63 | | int sockindex; |
64 | | const char *hostname; |
65 | | int remote_port; |
66 | | struct HTTP http_proxy; |
67 | | struct HTTP *prot_save; |
68 | | struct dynbuf rcvbuf; |
69 | | struct dynbuf req; |
70 | | size_t nsend; |
71 | | size_t headerlines; |
72 | | enum keeponval { |
73 | | KEEPON_DONE, |
74 | | KEEPON_CONNECT, |
75 | | KEEPON_IGNORE |
76 | | } keepon; |
77 | | curl_off_t cl; /* size of content to read and ignore */ |
78 | | tunnel_state tunnel_state; |
79 | | BIT(chunked_encoding); |
80 | | BIT(close_connection); |
81 | | }; |
82 | | |
83 | | |
84 | | static bool tunnel_is_established(struct tunnel_state *ts) |
85 | 0 | { |
86 | 0 | return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED); |
87 | 0 | } |
88 | | |
89 | | static bool tunnel_is_failed(struct tunnel_state *ts) |
90 | 0 | { |
91 | 0 | return ts && (ts->tunnel_state == TUNNEL_FAILED); |
92 | 0 | } |
93 | | |
94 | | static CURLcode tunnel_reinit(struct tunnel_state *ts, |
95 | | struct connectdata *conn, |
96 | | struct Curl_easy *data) |
97 | 0 | { |
98 | 0 | (void)data; |
99 | 0 | DEBUGASSERT(ts); |
100 | 0 | Curl_dyn_reset(&ts->rcvbuf); |
101 | 0 | Curl_dyn_reset(&ts->req); |
102 | 0 | ts->tunnel_state = TUNNEL_INIT; |
103 | 0 | ts->keepon = KEEPON_CONNECT; |
104 | 0 | ts->cl = 0; |
105 | 0 | ts->close_connection = FALSE; |
106 | |
|
107 | 0 | if(conn->bits.conn_to_host) |
108 | 0 | ts->hostname = conn->conn_to_host.name; |
109 | 0 | else if(ts->sockindex == SECONDARYSOCKET) |
110 | 0 | ts->hostname = conn->secondaryhostname; |
111 | 0 | else |
112 | 0 | ts->hostname = conn->host.name; |
113 | |
|
114 | 0 | if(ts->sockindex == SECONDARYSOCKET) |
115 | 0 | ts->remote_port = conn->secondary_port; |
116 | 0 | else if(conn->bits.conn_to_port) |
117 | 0 | ts->remote_port = conn->conn_to_port; |
118 | 0 | else |
119 | 0 | ts->remote_port = conn->remote_port; |
120 | |
|
121 | 0 | return CURLE_OK; |
122 | 0 | } |
123 | | |
124 | | static CURLcode tunnel_init(struct tunnel_state **pts, |
125 | | struct Curl_easy *data, |
126 | | struct connectdata *conn, |
127 | | int sockindex) |
128 | 0 | { |
129 | 0 | struct tunnel_state *ts; |
130 | 0 | CURLcode result; |
131 | |
|
132 | 0 | if(conn->handler->flags & PROTOPT_NOTCPPROXY) { |
133 | 0 | failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); |
134 | 0 | return CURLE_UNSUPPORTED_PROTOCOL; |
135 | 0 | } |
136 | | |
137 | | /* we might need the upload buffer for streaming a partial request */ |
138 | 0 | result = Curl_get_upload_buffer(data); |
139 | 0 | if(result) |
140 | 0 | return result; |
141 | | |
142 | 0 | ts = calloc(1, sizeof(*ts)); |
143 | 0 | if(!ts) |
144 | 0 | return CURLE_OUT_OF_MEMORY; |
145 | | |
146 | 0 | ts->sockindex = sockindex; |
147 | 0 | infof(data, "allocate connect buffer"); |
148 | |
|
149 | 0 | Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); |
150 | 0 | Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST); |
151 | | |
152 | | /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the |
153 | | * member conn->proto.http; we want [protocol] through HTTP and we have |
154 | | * to change the member temporarily for connecting to the HTTP |
155 | | * proxy. After Curl_proxyCONNECT we have to set back the member to the |
156 | | * original pointer |
157 | | * |
158 | | * This function might be called several times in the multi interface case |
159 | | * if the proxy's CONNECT response is not instant. |
160 | | */ |
161 | 0 | ts->prot_save = data->req.p.http; |
162 | 0 | data->req.p.http = &ts->http_proxy; |
163 | 0 | *pts = ts; |
164 | 0 | connkeep(conn, "HTTP proxy CONNECT"); |
165 | 0 | return tunnel_reinit(ts, conn, data); |
166 | 0 | } |
167 | | |
168 | | static void tunnel_go_state(struct Curl_cfilter *cf, |
169 | | struct tunnel_state *ts, |
170 | | tunnel_state new_state, |
171 | | struct Curl_easy *data) |
172 | 0 | { |
173 | 0 | if(ts->tunnel_state == new_state) |
174 | 0 | return; |
175 | | /* leaving this one */ |
176 | 0 | switch(ts->tunnel_state) { |
177 | 0 | case TUNNEL_CONNECT: |
178 | 0 | data->req.ignorebody = FALSE; |
179 | 0 | break; |
180 | 0 | default: |
181 | 0 | break; |
182 | 0 | } |
183 | | /* entering this one */ |
184 | 0 | switch(new_state) { |
185 | 0 | case TUNNEL_INIT: |
186 | 0 | tunnel_reinit(ts, cf->conn, data); |
187 | 0 | break; |
188 | | |
189 | 0 | case TUNNEL_CONNECT: |
190 | 0 | ts->tunnel_state = TUNNEL_CONNECT; |
191 | 0 | ts->keepon = KEEPON_CONNECT; |
192 | 0 | Curl_dyn_reset(&ts->rcvbuf); |
193 | 0 | break; |
194 | | |
195 | 0 | case TUNNEL_RECEIVE: |
196 | 0 | ts->tunnel_state = TUNNEL_RECEIVE; |
197 | 0 | break; |
198 | | |
199 | 0 | case TUNNEL_RESPONSE: |
200 | 0 | ts->tunnel_state = TUNNEL_RESPONSE; |
201 | 0 | break; |
202 | | |
203 | 0 | case TUNNEL_ESTABLISHED: |
204 | 0 | infof(data, "CONNECT phase completed"); |
205 | 0 | data->state.authproxy.done = TRUE; |
206 | 0 | data->state.authproxy.multipass = FALSE; |
207 | | /* FALLTHROUGH */ |
208 | 0 | case TUNNEL_FAILED: |
209 | 0 | ts->tunnel_state = new_state; |
210 | 0 | Curl_dyn_reset(&ts->rcvbuf); |
211 | 0 | Curl_dyn_reset(&ts->req); |
212 | | /* restore the protocol pointer */ |
213 | 0 | data->req.p.http = ts->prot_save; |
214 | 0 | data->info.httpcode = 0; /* clear it as it might've been used for the |
215 | | proxy */ |
216 | | /* If a proxy-authorization header was used for the proxy, then we should |
217 | | make sure that it isn't accidentally used for the document request |
218 | | after we've connected. So let's free and clear it here. */ |
219 | 0 | Curl_safefree(data->state.aptr.proxyuserpwd); |
220 | 0 | data->state.aptr.proxyuserpwd = NULL; |
221 | | #ifdef USE_HYPER |
222 | | data->state.hconnect = FALSE; |
223 | | #endif |
224 | 0 | break; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | static void tunnel_free(struct Curl_cfilter *cf, |
229 | | struct Curl_easy *data) |
230 | 0 | { |
231 | 0 | struct tunnel_state *ts = cf->ctx; |
232 | 0 | if(ts) { |
233 | 0 | tunnel_go_state(cf, ts, TUNNEL_FAILED, data); |
234 | 0 | Curl_dyn_free(&ts->rcvbuf); |
235 | 0 | Curl_dyn_free(&ts->req); |
236 | 0 | free(ts); |
237 | 0 | cf->ctx = NULL; |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | static CURLcode CONNECT_host(struct Curl_easy *data, |
242 | | struct connectdata *conn, |
243 | | const char *hostname, |
244 | | int remote_port, |
245 | | char **connecthostp, |
246 | | char **hostp) |
247 | 0 | { |
248 | 0 | char *hostheader; /* for CONNECT */ |
249 | 0 | char *host = NULL; /* Host: */ |
250 | 0 | bool ipv6_ip = conn->bits.ipv6_ip; |
251 | | |
252 | | /* the hostname may be different */ |
253 | 0 | if(hostname != conn->host.name) |
254 | 0 | ipv6_ip = (strchr(hostname, ':') != NULL); |
255 | 0 | hostheader = /* host:port with IPv6 support */ |
256 | 0 | aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", |
257 | 0 | remote_port); |
258 | 0 | if(!hostheader) |
259 | 0 | return CURLE_OUT_OF_MEMORY; |
260 | | |
261 | 0 | if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { |
262 | 0 | host = aprintf("Host: %s\r\n", hostheader); |
263 | 0 | if(!host) { |
264 | 0 | free(hostheader); |
265 | 0 | return CURLE_OUT_OF_MEMORY; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | *connecthostp = hostheader; |
269 | 0 | *hostp = host; |
270 | 0 | return CURLE_OK; |
271 | 0 | } |
272 | | |
273 | | #ifndef USE_HYPER |
274 | | static CURLcode start_CONNECT(struct Curl_easy *data, |
275 | | struct connectdata *conn, |
276 | | struct tunnel_state *ts) |
277 | 0 | { |
278 | 0 | char *hostheader = NULL; |
279 | 0 | char *host = NULL; |
280 | 0 | const char *httpv; |
281 | 0 | CURLcode result; |
282 | |
|
283 | 0 | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
284 | 0 | ts->hostname, ts->remote_port); |
285 | | |
286 | | /* This only happens if we've looped here due to authentication |
287 | | reasons, and we don't really use the newly cloned URL here |
288 | | then. Just free() it. */ |
289 | 0 | Curl_safefree(data->req.newurl); |
290 | |
|
291 | 0 | result = CONNECT_host(data, conn, |
292 | 0 | ts->hostname, ts->remote_port, |
293 | 0 | &hostheader, &host); |
294 | 0 | if(result) |
295 | 0 | goto out; |
296 | | |
297 | | /* Setup the proxy-authorization header, if any */ |
298 | 0 | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
299 | 0 | hostheader, TRUE); |
300 | 0 | if(result) |
301 | 0 | goto out; |
302 | | |
303 | 0 | httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; |
304 | |
|
305 | 0 | result = |
306 | 0 | Curl_dyn_addf(&ts->req, |
307 | 0 | "CONNECT %s HTTP/%s\r\n" |
308 | 0 | "%s" /* Host: */ |
309 | 0 | "%s", /* Proxy-Authorization */ |
310 | 0 | hostheader, |
311 | 0 | httpv, |
312 | 0 | host?host:"", |
313 | 0 | data->state.aptr.proxyuserpwd? |
314 | 0 | data->state.aptr.proxyuserpwd:""); |
315 | 0 | if(result) |
316 | 0 | goto out; |
317 | | |
318 | 0 | if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) |
319 | 0 | && data->set.str[STRING_USERAGENT]) |
320 | 0 | result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n", |
321 | 0 | data->set.str[STRING_USERAGENT]); |
322 | 0 | if(result) |
323 | 0 | goto out; |
324 | | |
325 | 0 | if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) |
326 | 0 | result = Curl_dyn_addn(&ts->req, |
327 | 0 | STRCONST("Proxy-Connection: Keep-Alive\r\n")); |
328 | 0 | if(result) |
329 | 0 | goto out; |
330 | | |
331 | 0 | result = Curl_add_custom_headers(data, TRUE, &ts->req); |
332 | 0 | if(result) |
333 | 0 | goto out; |
334 | | |
335 | | /* CRLF terminate the request */ |
336 | 0 | result = Curl_dyn_addn(&ts->req, STRCONST("\r\n")); |
337 | 0 | if(result) |
338 | 0 | goto out; |
339 | | |
340 | | /* Send the connect request to the proxy */ |
341 | 0 | result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0, |
342 | 0 | ts->sockindex); |
343 | 0 | ts->headerlines = 0; |
344 | |
|
345 | 0 | out: |
346 | 0 | if(result) |
347 | 0 | failf(data, "Failed sending CONNECT to proxy"); |
348 | 0 | free(host); |
349 | 0 | free(hostheader); |
350 | 0 | return result; |
351 | 0 | } |
352 | | |
353 | | static CURLcode send_CONNECT(struct Curl_easy *data, |
354 | | struct connectdata *conn, |
355 | | struct tunnel_state *ts, |
356 | | bool *done) |
357 | 0 | { |
358 | 0 | struct SingleRequest *k = &data->req; |
359 | 0 | struct HTTP *http = data->req.p.http; |
360 | 0 | CURLcode result = CURLE_OK; |
361 | |
|
362 | 0 | if(http->sending != HTTPSEND_REQUEST) |
363 | 0 | goto out; |
364 | | |
365 | 0 | if(!ts->nsend) { |
366 | 0 | size_t fillcount; |
367 | 0 | k->upload_fromhere = data->state.ulbuf; |
368 | 0 | result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, |
369 | 0 | &fillcount); |
370 | 0 | if(result) |
371 | 0 | goto out; |
372 | 0 | ts->nsend = fillcount; |
373 | 0 | } |
374 | 0 | if(ts->nsend) { |
375 | 0 | ssize_t bytes_written; |
376 | | /* write to socket (send away data) */ |
377 | 0 | result = Curl_write(data, |
378 | 0 | conn->writesockfd, /* socket to send to */ |
379 | 0 | k->upload_fromhere, /* buffer pointer */ |
380 | 0 | ts->nsend, /* buffer size */ |
381 | 0 | &bytes_written); /* actually sent */ |
382 | 0 | if(result) |
383 | 0 | goto out; |
384 | | /* send to debug callback! */ |
385 | 0 | Curl_debug(data, CURLINFO_HEADER_OUT, |
386 | 0 | k->upload_fromhere, bytes_written); |
387 | |
|
388 | 0 | ts->nsend -= bytes_written; |
389 | 0 | k->upload_fromhere += bytes_written; |
390 | 0 | } |
391 | 0 | if(!ts->nsend) |
392 | 0 | http->sending = HTTPSEND_NADA; |
393 | |
|
394 | 0 | out: |
395 | 0 | if(result) |
396 | 0 | failf(data, "Failed sending CONNECT to proxy"); |
397 | 0 | *done = (http->sending != HTTPSEND_REQUEST); |
398 | 0 | return result; |
399 | 0 | } |
400 | | |
401 | | static CURLcode on_resp_header(struct Curl_easy *data, |
402 | | struct tunnel_state *ts, |
403 | | const char *header) |
404 | 0 | { |
405 | 0 | CURLcode result = CURLE_OK; |
406 | 0 | struct SingleRequest *k = &data->req; |
407 | 0 | int subversion = 0; |
408 | |
|
409 | 0 | if((checkprefix("WWW-Authenticate:", header) && |
410 | 0 | (401 == k->httpcode)) || |
411 | 0 | (checkprefix("Proxy-authenticate:", header) && |
412 | 0 | (407 == k->httpcode))) { |
413 | |
|
414 | 0 | bool proxy = (k->httpcode == 407) ? TRUE : FALSE; |
415 | 0 | char *auth = Curl_copy_header_value(header); |
416 | 0 | if(!auth) |
417 | 0 | return CURLE_OUT_OF_MEMORY; |
418 | | |
419 | 0 | DEBUGF(infof(data, "CONNECT: fwd auth header '%s'", |
420 | 0 | header)); |
421 | 0 | result = Curl_http_input_auth(data, proxy, auth); |
422 | |
|
423 | 0 | free(auth); |
424 | |
|
425 | 0 | if(result) |
426 | 0 | return result; |
427 | 0 | } |
428 | 0 | else if(checkprefix("Content-Length:", header)) { |
429 | 0 | if(k->httpcode/100 == 2) { |
430 | | /* A client MUST ignore any Content-Length or Transfer-Encoding |
431 | | header fields received in a successful response to CONNECT. |
432 | | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
433 | 0 | infof(data, "Ignoring Content-Length in CONNECT %03d response", |
434 | 0 | k->httpcode); |
435 | 0 | } |
436 | 0 | else { |
437 | 0 | (void)curlx_strtoofft(header + strlen("Content-Length:"), |
438 | 0 | NULL, 10, &ts->cl); |
439 | 0 | } |
440 | 0 | } |
441 | 0 | else if(Curl_compareheader(header, |
442 | 0 | STRCONST("Connection:"), STRCONST("close"))) |
443 | 0 | ts->close_connection = TRUE; |
444 | 0 | else if(checkprefix("Transfer-Encoding:", header)) { |
445 | 0 | if(k->httpcode/100 == 2) { |
446 | | /* A client MUST ignore any Content-Length or Transfer-Encoding |
447 | | header fields received in a successful response to CONNECT. |
448 | | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
449 | 0 | infof(data, "Ignoring Transfer-Encoding in " |
450 | 0 | "CONNECT %03d response", k->httpcode); |
451 | 0 | } |
452 | 0 | else if(Curl_compareheader(header, |
453 | 0 | STRCONST("Transfer-Encoding:"), |
454 | 0 | STRCONST("chunked"))) { |
455 | 0 | infof(data, "CONNECT responded chunked"); |
456 | 0 | ts->chunked_encoding = TRUE; |
457 | | /* init our chunky engine */ |
458 | 0 | Curl_httpchunk_init(data); |
459 | 0 | } |
460 | 0 | } |
461 | 0 | else if(Curl_compareheader(header, |
462 | 0 | STRCONST("Proxy-Connection:"), |
463 | 0 | STRCONST("close"))) |
464 | 0 | ts->close_connection = TRUE; |
465 | 0 | else if(2 == sscanf(header, "HTTP/1.%d %d", |
466 | 0 | &subversion, |
467 | 0 | &k->httpcode)) { |
468 | | /* store the HTTP code from the proxy */ |
469 | 0 | data->info.httpproxycode = k->httpcode; |
470 | 0 | } |
471 | 0 | return result; |
472 | 0 | } |
473 | | |
474 | | static CURLcode recv_CONNECT_resp(struct Curl_easy *data, |
475 | | struct connectdata *conn, |
476 | | struct tunnel_state *ts, |
477 | | bool *done) |
478 | 0 | { |
479 | 0 | CURLcode result = CURLE_OK; |
480 | 0 | struct SingleRequest *k = &data->req; |
481 | 0 | curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; |
482 | 0 | char *linep; |
483 | 0 | size_t perline; |
484 | 0 | int error; |
485 | |
|
486 | 0 | #define SELECT_OK 0 |
487 | 0 | #define SELECT_ERROR 1 |
488 | |
|
489 | 0 | error = SELECT_OK; |
490 | 0 | *done = FALSE; |
491 | |
|
492 | 0 | if(!Curl_conn_data_pending(data, ts->sockindex)) |
493 | 0 | return CURLE_OK; |
494 | | |
495 | 0 | while(ts->keepon) { |
496 | 0 | ssize_t gotbytes; |
497 | 0 | char byte; |
498 | | |
499 | | /* Read one byte at a time to avoid a race condition. Wait at most one |
500 | | second before looping to ensure continuous pgrsUpdates. */ |
501 | 0 | result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); |
502 | 0 | if(result == CURLE_AGAIN) |
503 | | /* socket buffer drained, return */ |
504 | 0 | return CURLE_OK; |
505 | | |
506 | 0 | if(Curl_pgrsUpdate(data)) |
507 | 0 | return CURLE_ABORTED_BY_CALLBACK; |
508 | | |
509 | 0 | if(result) { |
510 | 0 | ts->keepon = KEEPON_DONE; |
511 | 0 | break; |
512 | 0 | } |
513 | | |
514 | 0 | if(gotbytes <= 0) { |
515 | 0 | if(data->set.proxyauth && data->state.authproxy.avail && |
516 | 0 | data->state.aptr.proxyuserpwd) { |
517 | | /* proxy auth was requested and there was proxy auth available, |
518 | | then deem this as "mere" proxy disconnect */ |
519 | 0 | ts->close_connection = TRUE; |
520 | 0 | infof(data, "Proxy CONNECT connection closed"); |
521 | 0 | } |
522 | 0 | else { |
523 | 0 | error = SELECT_ERROR; |
524 | 0 | failf(data, "Proxy CONNECT aborted"); |
525 | 0 | } |
526 | 0 | ts->keepon = KEEPON_DONE; |
527 | 0 | break; |
528 | 0 | } |
529 | | |
530 | 0 | if(ts->keepon == KEEPON_IGNORE) { |
531 | | /* This means we are currently ignoring a response-body */ |
532 | |
|
533 | 0 | if(ts->cl) { |
534 | | /* A Content-Length based body: simply count down the counter |
535 | | and make sure to break out of the loop when we're done! */ |
536 | 0 | ts->cl--; |
537 | 0 | if(ts->cl <= 0) { |
538 | 0 | ts->keepon = KEEPON_DONE; |
539 | 0 | break; |
540 | 0 | } |
541 | 0 | } |
542 | 0 | else { |
543 | | /* chunked-encoded body, so we need to do the chunked dance |
544 | | properly to know when the end of the body is reached */ |
545 | 0 | CHUNKcode r; |
546 | 0 | CURLcode extra; |
547 | 0 | ssize_t tookcareof = 0; |
548 | | |
549 | | /* now parse the chunked piece of data so that we can |
550 | | properly tell when the stream ends */ |
551 | 0 | r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); |
552 | 0 | if(r == CHUNKE_STOP) { |
553 | | /* we're done reading chunks! */ |
554 | 0 | infof(data, "chunk reading DONE"); |
555 | 0 | ts->keepon = KEEPON_DONE; |
556 | 0 | } |
557 | 0 | } |
558 | 0 | continue; |
559 | 0 | } |
560 | | |
561 | 0 | if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { |
562 | 0 | failf(data, "CONNECT response too large"); |
563 | 0 | return CURLE_RECV_ERROR; |
564 | 0 | } |
565 | | |
566 | | /* if this is not the end of a header line then continue */ |
567 | 0 | if(byte != 0x0a) |
568 | 0 | continue; |
569 | | |
570 | 0 | ts->headerlines++; |
571 | 0 | linep = Curl_dyn_ptr(&ts->rcvbuf); |
572 | 0 | perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ |
573 | | |
574 | | /* output debug if that is requested */ |
575 | 0 | Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); |
576 | |
|
577 | 0 | if(!data->set.suppress_connect_headers) { |
578 | | /* send the header to the callback */ |
579 | 0 | int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | |
580 | 0 | (data->set.include_header ? CLIENTWRITE_BODY : 0) | |
581 | 0 | (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); |
582 | |
|
583 | 0 | result = Curl_client_write(data, writetype, linep, perline); |
584 | 0 | if(result) |
585 | 0 | return result; |
586 | 0 | } |
587 | | |
588 | 0 | data->info.header_size += (long)perline; |
589 | | |
590 | | /* Newlines are CRLF, so the CR is ignored as the line isn't |
591 | | really terminated until the LF comes. Treat a following CR |
592 | | as end-of-headers as well.*/ |
593 | |
|
594 | 0 | if(('\r' == linep[0]) || |
595 | 0 | ('\n' == linep[0])) { |
596 | | /* end of response-headers from the proxy */ |
597 | |
|
598 | 0 | if((407 == k->httpcode) && !data->state.authproblem) { |
599 | | /* If we get a 407 response code with content length |
600 | | when we have no auth problem, we must ignore the |
601 | | whole response-body */ |
602 | 0 | ts->keepon = KEEPON_IGNORE; |
603 | |
|
604 | 0 | if(ts->cl) { |
605 | 0 | infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T |
606 | 0 | " bytes of response-body", ts->cl); |
607 | 0 | } |
608 | 0 | else if(ts->chunked_encoding) { |
609 | 0 | CHUNKcode r; |
610 | 0 | CURLcode extra; |
611 | |
|
612 | 0 | infof(data, "Ignore chunked response-body"); |
613 | | |
614 | | /* We set ignorebody true here since the chunked decoder |
615 | | function will acknowledge that. Pay attention so that this is |
616 | | cleared again when this function returns! */ |
617 | 0 | k->ignorebody = TRUE; |
618 | |
|
619 | 0 | if(linep[1] == '\n') |
620 | | /* this can only be a LF if the letter at index 0 was a CR */ |
621 | 0 | linep++; |
622 | | |
623 | | /* now parse the chunked piece of data so that we can properly |
624 | | tell when the stream ends */ |
625 | 0 | r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, |
626 | 0 | &extra); |
627 | 0 | if(r == CHUNKE_STOP) { |
628 | | /* we're done reading chunks! */ |
629 | 0 | infof(data, "chunk reading DONE"); |
630 | 0 | ts->keepon = KEEPON_DONE; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | else { |
634 | | /* without content-length or chunked encoding, we |
635 | | can't keep the connection alive since the close is |
636 | | the end signal so we bail out at once instead */ |
637 | 0 | DEBUGF(infof(data, "CONNECT: no content-length or chunked")); |
638 | 0 | ts->keepon = KEEPON_DONE; |
639 | 0 | } |
640 | 0 | } |
641 | 0 | else { |
642 | 0 | ts->keepon = KEEPON_DONE; |
643 | 0 | } |
644 | |
|
645 | 0 | DEBUGASSERT(ts->keepon == KEEPON_IGNORE |
646 | 0 | || ts->keepon == KEEPON_DONE); |
647 | 0 | continue; |
648 | 0 | } |
649 | | |
650 | 0 | result = on_resp_header(data, ts, linep); |
651 | 0 | if(result) |
652 | 0 | return result; |
653 | | |
654 | 0 | Curl_dyn_reset(&ts->rcvbuf); |
655 | 0 | } /* while there's buffer left and loop is requested */ |
656 | | |
657 | 0 | if(error) |
658 | 0 | result = CURLE_RECV_ERROR; |
659 | 0 | *done = (ts->keepon == KEEPON_DONE); |
660 | 0 | if(!result && *done && data->info.httpproxycode/100 != 2) { |
661 | | /* Deal with the possibly already received authenticate |
662 | | headers. 'newurl' is set to a new URL if we must loop. */ |
663 | 0 | result = Curl_http_auth_act(data); |
664 | 0 | } |
665 | 0 | return result; |
666 | 0 | } |
667 | | |
668 | | #else /* USE_HYPER */ |
669 | | /* The Hyper version of CONNECT */ |
670 | | static CURLcode start_CONNECT(struct Curl_easy *data, |
671 | | struct connectdata *conn, |
672 | | struct tunnel_state *ts) |
673 | | { |
674 | | struct hyptransfer *h = &data->hyp; |
675 | | curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; |
676 | | hyper_io *io = NULL; |
677 | | hyper_request *req = NULL; |
678 | | hyper_headers *headers = NULL; |
679 | | hyper_clientconn_options *options = NULL; |
680 | | hyper_task *handshake = NULL; |
681 | | hyper_task *task = NULL; /* for the handshake */ |
682 | | hyper_clientconn *client = NULL; |
683 | | hyper_task *sendtask = NULL; /* for the send */ |
684 | | char *hostheader = NULL; /* for CONNECT */ |
685 | | char *host = NULL; /* Host: */ |
686 | | CURLcode result = CURLE_OUT_OF_MEMORY; |
687 | | |
688 | | io = hyper_io_new(); |
689 | | if(!io) { |
690 | | failf(data, "Couldn't create hyper IO"); |
691 | | result = CURLE_OUT_OF_MEMORY; |
692 | | goto error; |
693 | | } |
694 | | /* tell Hyper how to read/write network data */ |
695 | | hyper_io_set_userdata(io, data); |
696 | | hyper_io_set_read(io, Curl_hyper_recv); |
697 | | hyper_io_set_write(io, Curl_hyper_send); |
698 | | conn->sockfd = tunnelsocket; |
699 | | |
700 | | data->state.hconnect = TRUE; |
701 | | |
702 | | /* create an executor to poll futures */ |
703 | | if(!h->exec) { |
704 | | h->exec = hyper_executor_new(); |
705 | | if(!h->exec) { |
706 | | failf(data, "Couldn't create hyper executor"); |
707 | | result = CURLE_OUT_OF_MEMORY; |
708 | | goto error; |
709 | | } |
710 | | } |
711 | | |
712 | | options = hyper_clientconn_options_new(); |
713 | | hyper_clientconn_options_set_preserve_header_case(options, 1); |
714 | | hyper_clientconn_options_set_preserve_header_order(options, 1); |
715 | | |
716 | | if(!options) { |
717 | | failf(data, "Couldn't create hyper client options"); |
718 | | result = CURLE_OUT_OF_MEMORY; |
719 | | goto error; |
720 | | } |
721 | | |
722 | | hyper_clientconn_options_exec(options, h->exec); |
723 | | |
724 | | /* "Both the `io` and the `options` are consumed in this function |
725 | | call" */ |
726 | | handshake = hyper_clientconn_handshake(io, options); |
727 | | if(!handshake) { |
728 | | failf(data, "Couldn't create hyper client handshake"); |
729 | | result = CURLE_OUT_OF_MEMORY; |
730 | | goto error; |
731 | | } |
732 | | io = NULL; |
733 | | options = NULL; |
734 | | |
735 | | if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { |
736 | | failf(data, "Couldn't hyper_executor_push the handshake"); |
737 | | result = CURLE_OUT_OF_MEMORY; |
738 | | goto error; |
739 | | } |
740 | | handshake = NULL; /* ownership passed on */ |
741 | | |
742 | | task = hyper_executor_poll(h->exec); |
743 | | if(!task) { |
744 | | failf(data, "Couldn't hyper_executor_poll the handshake"); |
745 | | result = CURLE_OUT_OF_MEMORY; |
746 | | goto error; |
747 | | } |
748 | | |
749 | | client = hyper_task_value(task); |
750 | | hyper_task_free(task); |
751 | | req = hyper_request_new(); |
752 | | if(!req) { |
753 | | failf(data, "Couldn't hyper_request_new"); |
754 | | result = CURLE_OUT_OF_MEMORY; |
755 | | goto error; |
756 | | } |
757 | | if(hyper_request_set_method(req, (uint8_t *)"CONNECT", |
758 | | strlen("CONNECT"))) { |
759 | | failf(data, "error setting method"); |
760 | | result = CURLE_OUT_OF_MEMORY; |
761 | | goto error; |
762 | | } |
763 | | |
764 | | infof(data, "Establish HTTP proxy tunnel to %s:%d", |
765 | | ts->hostname, ts->remote_port); |
766 | | |
767 | | /* This only happens if we've looped here due to authentication |
768 | | reasons, and we don't really use the newly cloned URL here |
769 | | then. Just free() it. */ |
770 | | Curl_safefree(data->req.newurl); |
771 | | |
772 | | result = CONNECT_host(data, conn, ts->hostname, ts->remote_port, |
773 | | &hostheader, &host); |
774 | | if(result) |
775 | | goto error; |
776 | | |
777 | | if(hyper_request_set_uri(req, (uint8_t *)hostheader, |
778 | | strlen(hostheader))) { |
779 | | failf(data, "error setting path"); |
780 | | result = CURLE_OUT_OF_MEMORY; |
781 | | goto error; |
782 | | } |
783 | | if(data->set.verbose) { |
784 | | char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); |
785 | | if(!se) { |
786 | | result = CURLE_OUT_OF_MEMORY; |
787 | | goto error; |
788 | | } |
789 | | Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); |
790 | | free(se); |
791 | | } |
792 | | /* Setup the proxy-authorization header, if any */ |
793 | | result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, |
794 | | hostheader, TRUE); |
795 | | if(result) |
796 | | goto error; |
797 | | Curl_safefree(hostheader); |
798 | | |
799 | | /* default is 1.1 */ |
800 | | if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && |
801 | | (HYPERE_OK != hyper_request_set_version(req, |
802 | | HYPER_HTTP_VERSION_1_0))) { |
803 | | failf(data, "error setting HTTP version"); |
804 | | result = CURLE_OUT_OF_MEMORY; |
805 | | goto error; |
806 | | } |
807 | | |
808 | | headers = hyper_request_headers(req); |
809 | | if(!headers) { |
810 | | failf(data, "hyper_request_headers"); |
811 | | result = CURLE_OUT_OF_MEMORY; |
812 | | goto error; |
813 | | } |
814 | | if(host) { |
815 | | result = Curl_hyper_header(data, headers, host); |
816 | | if(result) |
817 | | goto error; |
818 | | Curl_safefree(host); |
819 | | } |
820 | | |
821 | | if(data->state.aptr.proxyuserpwd) { |
822 | | result = Curl_hyper_header(data, headers, |
823 | | data->state.aptr.proxyuserpwd); |
824 | | if(result) |
825 | | goto error; |
826 | | } |
827 | | |
828 | | if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && |
829 | | data->set.str[STRING_USERAGENT]) { |
830 | | struct dynbuf ua; |
831 | | Curl_dyn_init(&ua, DYN_HTTP_REQUEST); |
832 | | result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", |
833 | | data->set.str[STRING_USERAGENT]); |
834 | | if(result) |
835 | | goto error; |
836 | | result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); |
837 | | if(result) |
838 | | goto error; |
839 | | Curl_dyn_free(&ua); |
840 | | } |
841 | | |
842 | | if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { |
843 | | result = Curl_hyper_header(data, headers, |
844 | | "Proxy-Connection: Keep-Alive"); |
845 | | if(result) |
846 | | goto error; |
847 | | } |
848 | | |
849 | | result = Curl_add_custom_headers(data, TRUE, headers); |
850 | | if(result) |
851 | | goto error; |
852 | | |
853 | | sendtask = hyper_clientconn_send(client, req); |
854 | | if(!sendtask) { |
855 | | failf(data, "hyper_clientconn_send"); |
856 | | result = CURLE_OUT_OF_MEMORY; |
857 | | goto error; |
858 | | } |
859 | | |
860 | | if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { |
861 | | failf(data, "Couldn't hyper_executor_push the send"); |
862 | | result = CURLE_OUT_OF_MEMORY; |
863 | | goto error; |
864 | | } |
865 | | |
866 | | error: |
867 | | free(host); |
868 | | free(hostheader); |
869 | | if(io) |
870 | | hyper_io_free(io); |
871 | | if(options) |
872 | | hyper_clientconn_options_free(options); |
873 | | if(handshake) |
874 | | hyper_task_free(handshake); |
875 | | if(client) |
876 | | hyper_clientconn_free(client); |
877 | | return result; |
878 | | } |
879 | | |
880 | | static CURLcode send_CONNECT(struct Curl_easy *data, |
881 | | struct connectdata *conn, |
882 | | struct tunnel_state *ts, |
883 | | bool *done) |
884 | | { |
885 | | struct hyptransfer *h = &data->hyp; |
886 | | hyper_task *task = NULL; |
887 | | hyper_error *hypererr = NULL; |
888 | | CURLcode result = CURLE_OK; |
889 | | |
890 | | (void)ts; |
891 | | (void)conn; |
892 | | do { |
893 | | task = hyper_executor_poll(h->exec); |
894 | | if(task) { |
895 | | bool error = hyper_task_type(task) == HYPER_TASK_ERROR; |
896 | | if(error) |
897 | | hypererr = hyper_task_value(task); |
898 | | hyper_task_free(task); |
899 | | if(error) { |
900 | | /* this could probably use a better error code? */ |
901 | | result = CURLE_OUT_OF_MEMORY; |
902 | | goto error; |
903 | | } |
904 | | } |
905 | | } while(task); |
906 | | error: |
907 | | *done = (result == CURLE_OK); |
908 | | if(hypererr) { |
909 | | uint8_t errbuf[256]; |
910 | | size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); |
911 | | failf(data, "Hyper: %.*s", (int)errlen, errbuf); |
912 | | hyper_error_free(hypererr); |
913 | | } |
914 | | return result; |
915 | | } |
916 | | |
917 | | static CURLcode recv_CONNECT_resp(struct Curl_easy *data, |
918 | | struct connectdata *conn, |
919 | | struct tunnel_state *ts, |
920 | | bool *done) |
921 | | { |
922 | | struct hyptransfer *h = &data->hyp; |
923 | | CURLcode result; |
924 | | int didwhat; |
925 | | |
926 | | (void)ts; |
927 | | *done = FALSE; |
928 | | result = Curl_hyper_stream(data, conn, &didwhat, done, |
929 | | CURL_CSELECT_IN | CURL_CSELECT_OUT); |
930 | | if(result || !*done) |
931 | | return result; |
932 | | if(h->exec) { |
933 | | hyper_executor_free(h->exec); |
934 | | h->exec = NULL; |
935 | | } |
936 | | if(h->read_waker) { |
937 | | hyper_waker_free(h->read_waker); |
938 | | h->read_waker = NULL; |
939 | | } |
940 | | if(h->write_waker) { |
941 | | hyper_waker_free(h->write_waker); |
942 | | h->write_waker = NULL; |
943 | | } |
944 | | return result; |
945 | | } |
946 | | |
947 | | #endif /* USE_HYPER */ |
948 | | |
949 | | static CURLcode CONNECT(struct Curl_cfilter *cf, |
950 | | struct Curl_easy *data, |
951 | | struct tunnel_state *ts) |
952 | 0 | { |
953 | 0 | struct connectdata *conn = cf->conn; |
954 | 0 | CURLcode result; |
955 | 0 | bool done; |
956 | |
|
957 | 0 | if(tunnel_is_established(ts)) |
958 | 0 | return CURLE_OK; |
959 | 0 | if(tunnel_is_failed(ts)) |
960 | 0 | return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ |
961 | | |
962 | 0 | do { |
963 | 0 | timediff_t check; |
964 | |
|
965 | 0 | check = Curl_timeleft(data, NULL, TRUE); |
966 | 0 | if(check <= 0) { |
967 | 0 | failf(data, "Proxy CONNECT aborted due to timeout"); |
968 | 0 | result = CURLE_OPERATION_TIMEDOUT; |
969 | 0 | goto out; |
970 | 0 | } |
971 | | |
972 | 0 | switch(ts->tunnel_state) { |
973 | 0 | case TUNNEL_INIT: |
974 | | /* Prepare the CONNECT request and make a first attempt to send. */ |
975 | 0 | result = start_CONNECT(data, cf->conn, ts); |
976 | 0 | if(result) |
977 | 0 | goto out; |
978 | 0 | tunnel_go_state(cf, ts, TUNNEL_CONNECT, data); |
979 | | /* FALLTHROUGH */ |
980 | |
|
981 | 0 | case TUNNEL_CONNECT: |
982 | | /* see that the request is completely sent */ |
983 | 0 | result = send_CONNECT(data, cf->conn, ts, &done); |
984 | 0 | if(result || !done) |
985 | 0 | goto out; |
986 | 0 | tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data); |
987 | | /* FALLTHROUGH */ |
988 | |
|
989 | 0 | case TUNNEL_RECEIVE: |
990 | | /* read what is there */ |
991 | 0 | result = recv_CONNECT_resp(data, cf->conn, ts, &done); |
992 | 0 | if(Curl_pgrsUpdate(data)) { |
993 | 0 | result = CURLE_ABORTED_BY_CALLBACK; |
994 | 0 | goto out; |
995 | 0 | } |
996 | | /* error or not complete yet. return for more multi-multi */ |
997 | 0 | if(result || !done) |
998 | 0 | goto out; |
999 | | /* got it */ |
1000 | 0 | tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data); |
1001 | | /* FALLTHROUGH */ |
1002 | |
|
1003 | 0 | case TUNNEL_RESPONSE: |
1004 | 0 | if(data->req.newurl) { |
1005 | | /* not the "final" response, we need to do a follow up request. |
1006 | | * If the other side indicated a connection close, or if someone |
1007 | | * else told us to close this connection, do so now. */ |
1008 | 0 | if(ts->close_connection || conn->bits.close) { |
1009 | | /* Close the filter chain and trigger connect, non-blocking |
1010 | | * again, so the process is ongoing. This will |
1011 | | * a) the close resets our tunnel state |
1012 | | * b) the connect makes sure that there will be a socket |
1013 | | * to select on again. |
1014 | | * We return and expect to be called again. */ |
1015 | 0 | infof(data, "Connect me again please"); |
1016 | 0 | Curl_conn_close(data, cf->sockindex); |
1017 | 0 | result = cf->next->cft->connect(cf->next, data, FALSE, &done); |
1018 | 0 | goto out; |
1019 | 0 | } |
1020 | | /* staying on this connection, reset state */ |
1021 | 0 | tunnel_go_state(cf, ts, TUNNEL_INIT, data); |
1022 | 0 | } |
1023 | 0 | break; |
1024 | | |
1025 | 0 | default: |
1026 | 0 | break; |
1027 | 0 | } |
1028 | |
|
1029 | 0 | } while(data->req.newurl); |
1030 | | |
1031 | 0 | DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE); |
1032 | 0 | if(data->info.httpproxycode/100 != 2) { |
1033 | | /* a non-2xx response and we have no next url to try. */ |
1034 | 0 | free(data->req.newurl); |
1035 | 0 | data->req.newurl = NULL; |
1036 | | /* failure, close this connection to avoid re-use */ |
1037 | 0 | streamclose(conn, "proxy CONNECT failure"); |
1038 | 0 | tunnel_go_state(cf, ts, TUNNEL_FAILED, data); |
1039 | 0 | failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); |
1040 | 0 | return CURLE_RECV_ERROR; |
1041 | 0 | } |
1042 | | /* 2xx response, SUCCESS! */ |
1043 | 0 | tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data); |
1044 | 0 | infof(data, "CONNECT tunnel established, response %d", |
1045 | 0 | data->info.httpproxycode); |
1046 | 0 | result = CURLE_OK; |
1047 | |
|
1048 | 0 | out: |
1049 | 0 | if(result) |
1050 | 0 | tunnel_go_state(cf, ts, TUNNEL_FAILED, data); |
1051 | 0 | return result; |
1052 | 0 | } |
1053 | | |
1054 | | static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, |
1055 | | struct Curl_easy *data, |
1056 | | bool blocking, bool *done) |
1057 | 0 | { |
1058 | 0 | CURLcode result; |
1059 | 0 | struct tunnel_state *ts = cf->ctx; |
1060 | |
|
1061 | 0 | if(cf->connected) { |
1062 | 0 | *done = TRUE; |
1063 | 0 | return CURLE_OK; |
1064 | 0 | } |
1065 | | |
1066 | 0 | result = cf->next->cft->connect(cf->next, data, blocking, done); |
1067 | 0 | if(result || !*done) |
1068 | 0 | return result; |
1069 | | |
1070 | | /* TODO: can we do blocking? */ |
1071 | | /* We want "seamless" operations through HTTP proxy tunnel */ |
1072 | | |
1073 | | /* for the secondary socket (FTP), use the "connect to host" |
1074 | | * but ignore the "connect to port" (use the secondary port) |
1075 | | */ |
1076 | 0 | *done = FALSE; |
1077 | 0 | if(!ts) { |
1078 | 0 | result = tunnel_init(&ts, data, cf->conn, cf->sockindex); |
1079 | 0 | if(result) |
1080 | 0 | return result; |
1081 | 0 | cf->ctx = ts; |
1082 | 0 | } |
1083 | | |
1084 | 0 | result = CONNECT(cf, data, ts); |
1085 | 0 | if(result) |
1086 | 0 | goto out; |
1087 | 0 | Curl_safefree(data->state.aptr.proxyuserpwd); |
1088 | |
|
1089 | 0 | out: |
1090 | 0 | *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); |
1091 | 0 | if (*done) { |
1092 | 0 | cf->connected = TRUE; |
1093 | 0 | tunnel_free(cf, data); |
1094 | 0 | } |
1095 | 0 | return result; |
1096 | 0 | } |
1097 | | |
1098 | | static void http_proxy_cf_get_host(struct Curl_cfilter *cf, |
1099 | | struct Curl_easy *data, |
1100 | | const char **phost, |
1101 | | const char **pdisplay_host, |
1102 | | int *pport) |
1103 | 0 | { |
1104 | 0 | (void)data; |
1105 | 0 | if(!cf->connected) { |
1106 | 0 | *phost = cf->conn->http_proxy.host.name; |
1107 | 0 | *pdisplay_host = cf->conn->http_proxy.host.dispname; |
1108 | 0 | *pport = (int)cf->conn->http_proxy.port; |
1109 | 0 | } |
1110 | 0 | else { |
1111 | 0 | cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); |
1112 | 0 | } |
1113 | 0 | } |
1114 | | |
1115 | | static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf, |
1116 | | struct Curl_easy *data, |
1117 | | curl_socket_t *socks) |
1118 | 0 | { |
1119 | 0 | struct tunnel_state *ts = cf->ctx; |
1120 | 0 | struct connectdata *conn = cf->conn; |
1121 | 0 | int fds; |
1122 | |
|
1123 | 0 | DEBUGASSERT(conn); |
1124 | 0 | fds = cf->next->cft->get_select_socks(cf->next, data, socks); |
1125 | 0 | if(!fds && cf->next->connected && !cf->connected) { |
1126 | | /* If we are not connected, but the filter "below" is |
1127 | | * and not waiting on something, we are tunneling. */ |
1128 | 0 | socks[0] = conn->sock[cf->sockindex]; |
1129 | 0 | if(ts) { |
1130 | | /* when we've sent a CONNECT to a proxy, we should rather either |
1131 | | wait for the socket to become readable to be able to get the |
1132 | | response headers or if we're still sending the request, wait |
1133 | | for write. */ |
1134 | 0 | if(ts->http_proxy.sending == HTTPSEND_REQUEST) |
1135 | 0 | return GETSOCK_WRITESOCK(0); |
1136 | 0 | return GETSOCK_READSOCK(0); |
1137 | 0 | } |
1138 | 0 | return GETSOCK_WRITESOCK(0); |
1139 | 0 | } |
1140 | 0 | return fds; |
1141 | 0 | } |
1142 | | |
1143 | | static void http_proxy_cf_detach_data(struct Curl_cfilter *cf, |
1144 | | struct Curl_easy *data) |
1145 | 0 | { |
1146 | 0 | if(cf->ctx) { |
1147 | 0 | tunnel_free(cf, data); |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | | static void http_proxy_cf_destroy(struct Curl_cfilter *cf, |
1152 | | struct Curl_easy *data) |
1153 | 0 | { |
1154 | 0 | http_proxy_cf_detach_data(cf, data); |
1155 | 0 | } |
1156 | | |
1157 | | static void http_proxy_cf_close(struct Curl_cfilter *cf, |
1158 | | struct Curl_easy *data) |
1159 | 0 | { |
1160 | 0 | DEBUGASSERT(cf->next); |
1161 | 0 | cf->connected = FALSE; |
1162 | 0 | cf->next->cft->close(cf->next, data); |
1163 | 0 | if(cf->ctx) { |
1164 | 0 | tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data); |
1165 | 0 | } |
1166 | 0 | } |
1167 | | |
1168 | | |
1169 | | static const struct Curl_cftype cft_http_proxy = { |
1170 | | "HTTP-PROXY", |
1171 | | CF_TYPE_IP_CONNECT, |
1172 | | http_proxy_cf_destroy, |
1173 | | Curl_cf_def_setup, |
1174 | | http_proxy_cf_connect, |
1175 | | http_proxy_cf_close, |
1176 | | http_proxy_cf_get_host, |
1177 | | http_proxy_cf_get_select_socks, |
1178 | | Curl_cf_def_data_pending, |
1179 | | Curl_cf_def_send, |
1180 | | Curl_cf_def_recv, |
1181 | | Curl_cf_def_attach_data, |
1182 | | http_proxy_cf_detach_data, |
1183 | | }; |
1184 | | |
1185 | | CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data, |
1186 | | struct connectdata *conn, |
1187 | | int sockindex) |
1188 | 0 | { |
1189 | 0 | struct Curl_cfilter *cf; |
1190 | 0 | CURLcode result; |
1191 | |
|
1192 | 0 | result = Curl_cf_create(&cf, &cft_http_proxy, NULL); |
1193 | 0 | if(!result) |
1194 | 0 | Curl_conn_cf_add(data, conn, sockindex, cf); |
1195 | 0 | return result; |
1196 | 0 | } |
1197 | | |
1198 | | |
1199 | | static CURLcode send_haproxy_header(struct Curl_cfilter*cf, |
1200 | | struct Curl_easy *data) |
1201 | 0 | { |
1202 | 0 | struct dynbuf req; |
1203 | 0 | CURLcode result; |
1204 | 0 | const char *tcp_version; |
1205 | 0 | Curl_dyn_init(&req, DYN_HAXPROXY); |
1206 | |
|
1207 | 0 | #ifdef USE_UNIX_SOCKETS |
1208 | 0 | if(cf->conn->unix_domain_socket) |
1209 | | /* the buffer is large enough to hold this! */ |
1210 | 0 | result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n")); |
1211 | 0 | else { |
1212 | 0 | #endif /* USE_UNIX_SOCKETS */ |
1213 | | /* Emit the correct prefix for IPv6 */ |
1214 | 0 | tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; |
1215 | |
|
1216 | 0 | result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n", |
1217 | 0 | tcp_version, |
1218 | 0 | data->info.conn_local_ip, |
1219 | 0 | data->info.conn_primary_ip, |
1220 | 0 | data->info.conn_local_port, |
1221 | 0 | data->info.conn_primary_port); |
1222 | |
|
1223 | 0 | #ifdef USE_UNIX_SOCKETS |
1224 | 0 | } |
1225 | 0 | #endif /* USE_UNIX_SOCKETS */ |
1226 | |
|
1227 | 0 | if(!result) |
1228 | 0 | result = Curl_buffer_send(&req, data, &data->info.request_size, |
1229 | 0 | 0, FIRSTSOCKET); |
1230 | 0 | return result; |
1231 | 0 | } |
1232 | | |
1233 | | static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf, |
1234 | | struct Curl_easy *data, |
1235 | | bool blocking, bool *done) |
1236 | 0 | { |
1237 | 0 | CURLcode result; |
1238 | |
|
1239 | 0 | if(cf->connected) { |
1240 | 0 | *done = TRUE; |
1241 | 0 | return CURLE_OK; |
1242 | 0 | } |
1243 | | |
1244 | 0 | result = cf->next->cft->connect(cf->next, data, blocking, done); |
1245 | 0 | if(result || !*done) |
1246 | 0 | return result; |
1247 | | |
1248 | 0 | result = send_haproxy_header(cf, data); |
1249 | 0 | *done = (!result); |
1250 | 0 | cf->connected = *done; |
1251 | 0 | return result; |
1252 | 0 | } |
1253 | | |
1254 | | static const struct Curl_cftype cft_haproxy = { |
1255 | | "HAPROXY", |
1256 | | 0, |
1257 | | Curl_cf_def_destroy_this, |
1258 | | Curl_cf_def_setup, |
1259 | | haproxy_cf_connect, |
1260 | | Curl_cf_def_close, |
1261 | | Curl_cf_def_get_host, |
1262 | | Curl_cf_def_get_select_socks, |
1263 | | Curl_cf_def_data_pending, |
1264 | | Curl_cf_def_send, |
1265 | | Curl_cf_def_recv, |
1266 | | Curl_cf_def_attach_data, |
1267 | | Curl_cf_def_detach_data, |
1268 | | }; |
1269 | | |
1270 | | CURLcode Curl_conn_haproxy_add(struct Curl_easy *data, |
1271 | | struct connectdata *conn, |
1272 | | int sockindex) |
1273 | 0 | { |
1274 | 0 | struct Curl_cfilter *cf; |
1275 | 0 | CURLcode result; |
1276 | |
|
1277 | 0 | result = Curl_cf_create(&cf, &cft_haproxy, NULL); |
1278 | 0 | if(!result) |
1279 | 0 | Curl_conn_cf_add(data, conn, sockindex, cf); |
1280 | 0 | return result; |
1281 | 0 | } |
1282 | | |
1283 | | #endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */ |