1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2021, 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 | ***************************************************************************/ |
22 | |
23 | #include "curl_setup.h" |
24 | |
25 | #include "http_proxy.h" |
26 | |
27 | #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) |
28 | |
29 | #include <curl/curl.h> |
30 | #ifdef USE_HYPER |
31 | #include <hyper.h> |
32 | #endif |
33 | #include "sendf.h" |
34 | #include "http.h" |
35 | #include "url.h" |
36 | #include "select.h" |
37 | #include "progress.h" |
38 | #include "non-ascii.h" |
39 | #include "connect.h" |
40 | #include "curlx.h" |
41 | #include "vtls/vtls.h" |
42 | #include "transfer.h" |
43 | #include "multiif.h" |
44 | |
45 | /* The last 3 #include files should be in this order */ |
46 | #include "curl_printf.h" |
47 | #include "curl_memory.h" |
48 | #include "memdebug.h" |
49 | |
50 | /* |
51 | * Perform SSL initialization for HTTPS proxy. Sets |
52 | * proxy_ssl_connected connection bit when complete. Can be |
53 | * called multiple times. |
54 | */ |
55 | static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex) |
56 | { |
57 | #ifdef USE_SSL |
58 | struct connectdata *conn = data->conn; |
59 | CURLcode result = CURLE_OK; |
60 | DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS); |
61 | if(!conn->bits.proxy_ssl_connected[sockindex]) { |
62 | /* perform SSL initialization for this socket */ |
63 | result = |
64 | Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex, |
65 | &conn->bits.proxy_ssl_connected[sockindex]); |
66 | if(result) |
67 | /* a failed connection is marked for closure to prevent (bad) re-use or |
68 | similar */ |
69 | connclose(conn, "TLS handshake failed" ); |
70 | } |
71 | return result; |
72 | #else |
73 | (void) data; |
74 | (void) sockindex; |
75 | return CURLE_NOT_BUILT_IN; |
76 | #endif |
77 | } |
78 | |
79 | CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) |
80 | { |
81 | struct connectdata *conn = data->conn; |
82 | if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { |
83 | const CURLcode result = https_proxy_connect(data, sockindex); |
84 | if(result) |
85 | return result; |
86 | if(!conn->bits.proxy_ssl_connected[sockindex]) |
87 | return result; /* wait for HTTPS proxy SSL initialization to complete */ |
88 | } |
89 | |
90 | if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { |
91 | #ifndef CURL_DISABLE_PROXY |
92 | /* for [protocol] tunneled through HTTP proxy */ |
93 | const char *hostname; |
94 | int remote_port; |
95 | CURLcode result; |
96 | |
97 | /* We want "seamless" operations through HTTP proxy tunnel */ |
98 | |
99 | /* for the secondary socket (FTP), use the "connect to host" |
100 | * but ignore the "connect to port" (use the secondary port) |
101 | */ |
102 | |
103 | if(conn->bits.conn_to_host) |
104 | hostname = conn->conn_to_host.name; |
105 | else if(sockindex == SECONDARYSOCKET) |
106 | hostname = conn->secondaryhostname; |
107 | else |
108 | hostname = conn->host.name; |
109 | |
110 | if(sockindex == SECONDARYSOCKET) |
111 | remote_port = conn->secondary_port; |
112 | else if(conn->bits.conn_to_port) |
113 | remote_port = conn->conn_to_port; |
114 | else |
115 | remote_port = conn->remote_port; |
116 | |
117 | result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); |
118 | if(CURLE_OK != result) |
119 | return result; |
120 | Curl_safefree(data->state.aptr.proxyuserpwd); |
121 | #else |
122 | return CURLE_NOT_BUILT_IN; |
123 | #endif |
124 | } |
125 | /* no HTTP tunnel proxy, just return */ |
126 | return CURLE_OK; |
127 | } |
128 | |
129 | bool Curl_connect_complete(struct connectdata *conn) |
130 | { |
131 | return !conn->connect_state || |
132 | (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE); |
133 | } |
134 | |
135 | bool Curl_connect_ongoing(struct connectdata *conn) |
136 | { |
137 | return conn->connect_state && |
138 | (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE); |
139 | } |
140 | |
141 | /* when we've sent a CONNECT to a proxy, we should rather either wait for the |
142 | socket to become readable to be able to get the response headers or if |
143 | we're still sending the request, wait for write. */ |
144 | int Curl_connect_getsock(struct connectdata *conn) |
145 | { |
146 | struct HTTP *http; |
147 | DEBUGASSERT(conn); |
148 | DEBUGASSERT(conn->connect_state); |
149 | http = &conn->connect_state->http_proxy; |
150 | |
151 | if(http->sending == HTTPSEND_REQUEST) |
152 | return GETSOCK_WRITESOCK(0); |
153 | |
154 | return GETSOCK_READSOCK(0); |
155 | } |
156 | |
157 | static CURLcode connect_init(struct Curl_easy *data, bool reinit) |
158 | { |
159 | struct http_connect_state *s; |
160 | struct connectdata *conn = data->conn; |
161 | if(!reinit) { |
162 | CURLcode result; |
163 | DEBUGASSERT(!conn->connect_state); |
164 | /* we might need the upload buffer for streaming a partial request */ |
165 | result = Curl_get_upload_buffer(data); |
166 | if(result) |
167 | return result; |
168 | |
169 | s = calloc(1, sizeof(struct http_connect_state)); |
170 | if(!s) |
171 | return CURLE_OUT_OF_MEMORY; |
172 | infof(data, "allocate connect buffer!" ); |
173 | conn->connect_state = s; |
174 | Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); |
175 | |
176 | /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the |
177 | * member conn->proto.http; we want [protocol] through HTTP and we have |
178 | * to change the member temporarily for connecting to the HTTP |
179 | * proxy. After Curl_proxyCONNECT we have to set back the member to the |
180 | * original pointer |
181 | * |
182 | * This function might be called several times in the multi interface case |
183 | * if the proxy's CONNECT response is not instant. |
184 | */ |
185 | s->prot_save = data->req.p.http; |
186 | data->req.p.http = &s->http_proxy; |
187 | connkeep(conn, "HTTP proxy CONNECT" ); |
188 | } |
189 | else { |
190 | DEBUGASSERT(conn->connect_state); |
191 | s = conn->connect_state; |
192 | Curl_dyn_reset(&s->rcvbuf); |
193 | } |
194 | s->tunnel_state = TUNNEL_INIT; |
195 | s->keepon = KEEPON_CONNECT; |
196 | s->cl = 0; |
197 | s->close_connection = FALSE; |
198 | return CURLE_OK; |
199 | } |
200 | |
201 | static void connect_done(struct Curl_easy *data) |
202 | { |
203 | struct connectdata *conn = data->conn; |
204 | struct http_connect_state *s = conn->connect_state; |
205 | if(s->tunnel_state != TUNNEL_EXIT) { |
206 | s->tunnel_state = TUNNEL_EXIT; |
207 | Curl_dyn_free(&s->rcvbuf); |
208 | Curl_dyn_free(&s->req); |
209 | |
210 | /* retore the protocol pointer */ |
211 | data->req.p.http = s->prot_save; |
212 | s->prot_save = NULL; |
213 | infof(data, "CONNECT phase completed!" ); |
214 | } |
215 | } |
216 | |
217 | static CURLcode CONNECT_host(struct Curl_easy *data, |
218 | struct connectdata *conn, |
219 | const char *hostname, |
220 | int remote_port, |
221 | char **connecthostp, |
222 | char **hostp) |
223 | { |
224 | char *; /* for CONNECT */ |
225 | char *host = NULL; /* Host: */ |
226 | bool ipv6_ip = conn->bits.ipv6_ip; |
227 | |
228 | /* the hostname may be different */ |
229 | if(hostname != conn->host.name) |
230 | ipv6_ip = (strchr(hostname, ':') != NULL); |
231 | hostheader = /* host:port with IPv6 support */ |
232 | aprintf("%s%s%s:%d" , ipv6_ip?"[" :"" , hostname, ipv6_ip?"]" :"" , |
233 | remote_port); |
234 | if(!hostheader) |
235 | return CURLE_OUT_OF_MEMORY; |
236 | |
237 | if(!Curl_checkProxyheaders(data, conn, "Host" )) { |
238 | host = aprintf("Host: %s\r\n" , hostheader); |
239 | if(!host) { |
240 | free(hostheader); |
241 | return CURLE_OUT_OF_MEMORY; |
242 | } |
243 | } |
244 | *connecthostp = hostheader; |
245 | *hostp = host; |
246 | return CURLE_OK; |
247 | } |
248 | |
249 | #ifndef USE_HYPER |
250 | static CURLcode CONNECT(struct Curl_easy *data, |
251 | int sockindex, |
252 | const char *hostname, |
253 | int remote_port) |
254 | { |
255 | int subversion = 0; |
256 | struct SingleRequest *k = &data->req; |
257 | CURLcode result; |
258 | struct connectdata *conn = data->conn; |
259 | curl_socket_t tunnelsocket = conn->sock[sockindex]; |
260 | struct http_connect_state *s = conn->connect_state; |
261 | struct HTTP *http = data->req.p.http; |
262 | char *linep; |
263 | size_t perline; |
264 | |
265 | #define SELECT_OK 0 |
266 | #define SELECT_ERROR 1 |
267 | |
268 | if(Curl_connect_complete(conn)) |
269 | return CURLE_OK; /* CONNECT is already completed */ |
270 | |
271 | conn->bits.proxy_connect_closed = FALSE; |
272 | |
273 | do { |
274 | timediff_t check; |
275 | if(TUNNEL_INIT == s->tunnel_state) { |
276 | /* BEGIN CONNECT PHASE */ |
277 | struct dynbuf *req = &s->req; |
278 | char * = NULL; |
279 | char *host = NULL; |
280 | |
281 | infof(data, "Establish HTTP proxy tunnel to %s:%d" , |
282 | hostname, remote_port); |
283 | |
284 | /* This only happens if we've looped here due to authentication |
285 | reasons, and we don't really use the newly cloned URL here |
286 | then. Just free() it. */ |
287 | free(data->req.newurl); |
288 | data->req.newurl = NULL; |
289 | |
290 | /* initialize send-buffer */ |
291 | Curl_dyn_init(req, DYN_HTTP_REQUEST); |
292 | |
293 | result = CONNECT_host(data, conn, |
294 | hostname, remote_port, &hostheader, &host); |
295 | if(result) |
296 | return result; |
297 | |
298 | /* Setup the proxy-authorization header, if any */ |
299 | result = Curl_http_output_auth(data, conn, "CONNECT" , HTTPREQ_GET, |
300 | hostheader, TRUE); |
301 | |
302 | if(!result) { |
303 | const char *httpv = |
304 | (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1" ; |
305 | |
306 | result = |
307 | Curl_dyn_addf(req, |
308 | "CONNECT %s HTTP/%s\r\n" |
309 | "%s" /* Host: */ |
310 | "%s" , /* Proxy-Authorization */ |
311 | hostheader, |
312 | httpv, |
313 | host?host:"" , |
314 | data->state.aptr.proxyuserpwd? |
315 | data->state.aptr.proxyuserpwd:"" ); |
316 | |
317 | if(!result && !Curl_checkProxyheaders(data, conn, "User-Agent" ) && |
318 | data->set.str[STRING_USERAGENT]) |
319 | result = Curl_dyn_addf(req, "User-Agent: %s\r\n" , |
320 | data->set.str[STRING_USERAGENT]); |
321 | |
322 | if(!result && !Curl_checkProxyheaders(data, conn, "Proxy-Connection" )) |
323 | result = Curl_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n" ); |
324 | |
325 | if(!result) |
326 | result = Curl_add_custom_headers(data, TRUE, req); |
327 | |
328 | if(!result) |
329 | /* CRLF terminate the request */ |
330 | result = Curl_dyn_add(req, "\r\n" ); |
331 | |
332 | if(!result) { |
333 | /* Send the connect request to the proxy */ |
334 | result = Curl_buffer_send(req, data, &data->info.request_size, 0, |
335 | sockindex); |
336 | } |
337 | if(result) |
338 | failf(data, "Failed sending CONNECT to proxy" ); |
339 | } |
340 | free(host); |
341 | free(hostheader); |
342 | if(result) |
343 | return result; |
344 | |
345 | s->tunnel_state = TUNNEL_CONNECT; |
346 | } /* END CONNECT PHASE */ |
347 | |
348 | check = Curl_timeleft(data, NULL, TRUE); |
349 | if(check <= 0) { |
350 | failf(data, "Proxy CONNECT aborted due to timeout" ); |
351 | return CURLE_OPERATION_TIMEDOUT; |
352 | } |
353 | |
354 | if(!Curl_conn_data_pending(conn, sockindex) && !http->sending) |
355 | /* return so we'll be called again polling-style */ |
356 | return CURLE_OK; |
357 | |
358 | /* at this point, the tunnel_connecting phase is over. */ |
359 | |
360 | if(http->sending == HTTPSEND_REQUEST) { |
361 | if(!s->nsend) { |
362 | size_t fillcount; |
363 | k->upload_fromhere = data->state.ulbuf; |
364 | result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, |
365 | &fillcount); |
366 | if(result) |
367 | return result; |
368 | s->nsend = fillcount; |
369 | } |
370 | if(s->nsend) { |
371 | ssize_t bytes_written; |
372 | /* write to socket (send away data) */ |
373 | result = Curl_write(data, |
374 | conn->writesockfd, /* socket to send to */ |
375 | k->upload_fromhere, /* buffer pointer */ |
376 | s->nsend, /* buffer size */ |
377 | &bytes_written); /* actually sent */ |
378 | |
379 | if(!result) |
380 | /* send to debug callback! */ |
381 | result = Curl_debug(data, CURLINFO_HEADER_OUT, |
382 | k->upload_fromhere, bytes_written); |
383 | |
384 | s->nsend -= bytes_written; |
385 | k->upload_fromhere += bytes_written; |
386 | return result; |
387 | } |
388 | http->sending = HTTPSEND_NADA; |
389 | /* if nothing left to send, continue */ |
390 | } |
391 | { /* READING RESPONSE PHASE */ |
392 | int error = SELECT_OK; |
393 | |
394 | while(s->keepon) { |
395 | ssize_t gotbytes; |
396 | char byte; |
397 | |
398 | /* Read one byte at a time to avoid a race condition. Wait at most one |
399 | second before looping to ensure continuous pgrsUpdates. */ |
400 | result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); |
401 | if(result == CURLE_AGAIN) |
402 | /* socket buffer drained, return */ |
403 | return CURLE_OK; |
404 | |
405 | if(Curl_pgrsUpdate(data)) |
406 | return CURLE_ABORTED_BY_CALLBACK; |
407 | |
408 | if(result) { |
409 | s->keepon = KEEPON_DONE; |
410 | break; |
411 | } |
412 | else if(gotbytes <= 0) { |
413 | if(data->set.proxyauth && data->state.authproxy.avail && |
414 | data->state.aptr.proxyuserpwd) { |
415 | /* proxy auth was requested and there was proxy auth available, |
416 | then deem this as "mere" proxy disconnect */ |
417 | conn->bits.proxy_connect_closed = TRUE; |
418 | infof(data, "Proxy CONNECT connection closed" ); |
419 | } |
420 | else { |
421 | error = SELECT_ERROR; |
422 | failf(data, "Proxy CONNECT aborted" ); |
423 | } |
424 | s->keepon = KEEPON_DONE; |
425 | break; |
426 | } |
427 | |
428 | if(s->keepon == KEEPON_IGNORE) { |
429 | /* This means we are currently ignoring a response-body */ |
430 | |
431 | if(s->cl) { |
432 | /* A Content-Length based body: simply count down the counter |
433 | and make sure to break out of the loop when we're done! */ |
434 | s->cl--; |
435 | if(s->cl <= 0) { |
436 | s->keepon = KEEPON_DONE; |
437 | s->tunnel_state = TUNNEL_COMPLETE; |
438 | break; |
439 | } |
440 | } |
441 | else { |
442 | /* chunked-encoded body, so we need to do the chunked dance |
443 | properly to know when the end of the body is reached */ |
444 | CHUNKcode r; |
445 | CURLcode ; |
446 | ssize_t tookcareof = 0; |
447 | |
448 | /* now parse the chunked piece of data so that we can |
449 | properly tell when the stream ends */ |
450 | r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); |
451 | if(r == CHUNKE_STOP) { |
452 | /* we're done reading chunks! */ |
453 | infof(data, "chunk reading DONE" ); |
454 | s->keepon = KEEPON_DONE; |
455 | /* we did the full CONNECT treatment, go COMPLETE */ |
456 | s->tunnel_state = TUNNEL_COMPLETE; |
457 | } |
458 | } |
459 | continue; |
460 | } |
461 | |
462 | if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) { |
463 | failf(data, "CONNECT response too large!" ); |
464 | return CURLE_RECV_ERROR; |
465 | } |
466 | |
467 | /* if this is not the end of a header line then continue */ |
468 | if(byte != 0x0a) |
469 | continue; |
470 | |
471 | linep = Curl_dyn_ptr(&s->rcvbuf); |
472 | perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ |
473 | |
474 | /* convert from the network encoding */ |
475 | result = Curl_convert_from_network(data, linep, perline); |
476 | /* Curl_convert_from_network calls failf if unsuccessful */ |
477 | if(result) |
478 | return result; |
479 | |
480 | /* output debug if that is requested */ |
481 | Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); |
482 | |
483 | if(!data->set.suppress_connect_headers) { |
484 | /* send the header to the callback */ |
485 | int writetype = CLIENTWRITE_HEADER; |
486 | if(data->set.include_header) |
487 | writetype |= CLIENTWRITE_BODY; |
488 | |
489 | result = Curl_client_write(data, writetype, linep, perline); |
490 | if(result) |
491 | return result; |
492 | } |
493 | |
494 | data->info.header_size += (long)perline; |
495 | |
496 | /* Newlines are CRLF, so the CR is ignored as the line isn't |
497 | really terminated until the LF comes. Treat a following CR |
498 | as end-of-headers as well.*/ |
499 | |
500 | if(('\r' == linep[0]) || |
501 | ('\n' == linep[0])) { |
502 | /* end of response-headers from the proxy */ |
503 | |
504 | if((407 == k->httpcode) && !data->state.authproblem) { |
505 | /* If we get a 407 response code with content length |
506 | when we have no auth problem, we must ignore the |
507 | whole response-body */ |
508 | s->keepon = KEEPON_IGNORE; |
509 | |
510 | if(s->cl) { |
511 | infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T |
512 | " bytes of response-body" , s->cl); |
513 | } |
514 | else if(s->chunked_encoding) { |
515 | CHUNKcode r; |
516 | CURLcode ; |
517 | |
518 | infof(data, "Ignore chunked response-body" ); |
519 | |
520 | /* We set ignorebody true here since the chunked decoder |
521 | function will acknowledge that. Pay attention so that this is |
522 | cleared again when this function returns! */ |
523 | k->ignorebody = TRUE; |
524 | |
525 | if(linep[1] == '\n') |
526 | /* this can only be a LF if the letter at index 0 was a CR */ |
527 | linep++; |
528 | |
529 | /* now parse the chunked piece of data so that we can properly |
530 | tell when the stream ends */ |
531 | r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, |
532 | &extra); |
533 | if(r == CHUNKE_STOP) { |
534 | /* we're done reading chunks! */ |
535 | infof(data, "chunk reading DONE" ); |
536 | s->keepon = KEEPON_DONE; |
537 | /* we did the full CONNECT treatment, go to COMPLETE */ |
538 | s->tunnel_state = TUNNEL_COMPLETE; |
539 | } |
540 | } |
541 | else { |
542 | /* without content-length or chunked encoding, we |
543 | can't keep the connection alive since the close is |
544 | the end signal so we bail out at once instead */ |
545 | s->keepon = KEEPON_DONE; |
546 | } |
547 | } |
548 | else |
549 | s->keepon = KEEPON_DONE; |
550 | |
551 | if(s->keepon == KEEPON_DONE && !s->cl) |
552 | /* we did the full CONNECT treatment, go to COMPLETE */ |
553 | s->tunnel_state = TUNNEL_COMPLETE; |
554 | |
555 | DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE); |
556 | continue; |
557 | } |
558 | |
559 | if((checkprefix("WWW-Authenticate:" , linep) && |
560 | (401 == k->httpcode)) || |
561 | (checkprefix("Proxy-authenticate:" , linep) && |
562 | (407 == k->httpcode))) { |
563 | |
564 | bool proxy = (k->httpcode == 407) ? TRUE : FALSE; |
565 | char *auth = Curl_copy_header_value(linep); |
566 | if(!auth) |
567 | return CURLE_OUT_OF_MEMORY; |
568 | |
569 | result = Curl_http_input_auth(data, proxy, auth); |
570 | |
571 | free(auth); |
572 | |
573 | if(result) |
574 | return result; |
575 | } |
576 | else if(checkprefix("Content-Length:" , linep)) { |
577 | if(k->httpcode/100 == 2) { |
578 | /* A client MUST ignore any Content-Length or Transfer-Encoding |
579 | header fields received in a successful response to CONNECT. |
580 | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
581 | infof(data, "Ignoring Content-Length in CONNECT %03d response" , |
582 | k->httpcode); |
583 | } |
584 | else { |
585 | (void)curlx_strtoofft(linep + |
586 | strlen("Content-Length:" ), NULL, 10, &s->cl); |
587 | } |
588 | } |
589 | else if(Curl_compareheader(linep, "Connection:" , "close" )) |
590 | s->close_connection = TRUE; |
591 | else if(checkprefix("Transfer-Encoding:" , linep)) { |
592 | if(k->httpcode/100 == 2) { |
593 | /* A client MUST ignore any Content-Length or Transfer-Encoding |
594 | header fields received in a successful response to CONNECT. |
595 | "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ |
596 | infof(data, "Ignoring Transfer-Encoding in " |
597 | "CONNECT %03d response" , k->httpcode); |
598 | } |
599 | else if(Curl_compareheader(linep, |
600 | "Transfer-Encoding:" , "chunked" )) { |
601 | infof(data, "CONNECT responded chunked" ); |
602 | s->chunked_encoding = TRUE; |
603 | /* init our chunky engine */ |
604 | Curl_httpchunk_init(data); |
605 | } |
606 | } |
607 | else if(Curl_compareheader(linep, "Proxy-Connection:" , "close" )) |
608 | s->close_connection = TRUE; |
609 | else if(2 == sscanf(linep, "HTTP/1.%d %d" , |
610 | &subversion, |
611 | &k->httpcode)) { |
612 | /* store the HTTP code from the proxy */ |
613 | data->info.httpproxycode = k->httpcode; |
614 | } |
615 | |
616 | Curl_dyn_reset(&s->rcvbuf); |
617 | } /* while there's buffer left and loop is requested */ |
618 | |
619 | if(Curl_pgrsUpdate(data)) |
620 | return CURLE_ABORTED_BY_CALLBACK; |
621 | |
622 | if(error) |
623 | return CURLE_RECV_ERROR; |
624 | |
625 | if(data->info.httpproxycode/100 != 2) { |
626 | /* Deal with the possibly already received authenticate |
627 | headers. 'newurl' is set to a new URL if we must loop. */ |
628 | result = Curl_http_auth_act(data); |
629 | if(result) |
630 | return result; |
631 | |
632 | if(conn->bits.close) |
633 | /* the connection has been marked for closure, most likely in the |
634 | Curl_http_auth_act() function and thus we can kill it at once |
635 | below */ |
636 | s->close_connection = TRUE; |
637 | } |
638 | |
639 | if(s->close_connection && data->req.newurl) { |
640 | /* Connection closed by server. Don't use it anymore */ |
641 | Curl_closesocket(data, conn, conn->sock[sockindex]); |
642 | conn->sock[sockindex] = CURL_SOCKET_BAD; |
643 | break; |
644 | } |
645 | } /* END READING RESPONSE PHASE */ |
646 | |
647 | /* If we are supposed to continue and request a new URL, which basically |
648 | * means the HTTP authentication is still going on so if the tunnel |
649 | * is complete we start over in INIT state */ |
650 | if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { |
651 | connect_init(data, TRUE); /* reinit */ |
652 | } |
653 | |
654 | } while(data->req.newurl); |
655 | |
656 | if(data->info.httpproxycode/100 != 2) { |
657 | if(s->close_connection && data->req.newurl) { |
658 | conn->bits.proxy_connect_closed = TRUE; |
659 | infof(data, "Connect me again please" ); |
660 | connect_done(data); |
661 | } |
662 | else { |
663 | free(data->req.newurl); |
664 | data->req.newurl = NULL; |
665 | /* failure, close this connection to avoid re-use */ |
666 | streamclose(conn, "proxy CONNECT failure" ); |
667 | Curl_closesocket(data, conn, conn->sock[sockindex]); |
668 | conn->sock[sockindex] = CURL_SOCKET_BAD; |
669 | } |
670 | |
671 | /* to back to init state */ |
672 | s->tunnel_state = TUNNEL_INIT; |
673 | |
674 | if(conn->bits.proxy_connect_closed) |
675 | /* this is not an error, just part of the connection negotiation */ |
676 | return CURLE_OK; |
677 | Curl_dyn_free(&s->rcvbuf); |
678 | failf(data, "Received HTTP code %d from proxy after CONNECT" , |
679 | data->req.httpcode); |
680 | return CURLE_RECV_ERROR; |
681 | } |
682 | |
683 | s->tunnel_state = TUNNEL_COMPLETE; |
684 | |
685 | /* If a proxy-authorization header was used for the proxy, then we should |
686 | make sure that it isn't accidentally used for the document request |
687 | after we've connected. So let's free and clear it here. */ |
688 | Curl_safefree(data->state.aptr.proxyuserpwd); |
689 | data->state.aptr.proxyuserpwd = NULL; |
690 | |
691 | data->state.authproxy.done = TRUE; |
692 | data->state.authproxy.multipass = FALSE; |
693 | |
694 | infof(data, "Proxy replied %d to CONNECT request" , |
695 | data->info.httpproxycode); |
696 | data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ |
697 | conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the |
698 | document request */ |
699 | Curl_dyn_free(&s->rcvbuf); |
700 | return CURLE_OK; |
701 | } |
702 | #else |
703 | /* The Hyper version of CONNECT */ |
704 | static CURLcode CONNECT(struct Curl_easy *data, |
705 | int sockindex, |
706 | const char *hostname, |
707 | int remote_port) |
708 | { |
709 | struct connectdata *conn = data->conn; |
710 | struct hyptransfer *h = &data->hyp; |
711 | curl_socket_t tunnelsocket = conn->sock[sockindex]; |
712 | struct http_connect_state *s = conn->connect_state; |
713 | CURLcode result = CURLE_OUT_OF_MEMORY; |
714 | hyper_io *io = NULL; |
715 | hyper_request *req = NULL; |
716 | hyper_headers *headers = NULL; |
717 | hyper_clientconn_options *options = NULL; |
718 | hyper_task *handshake = NULL; |
719 | hyper_task *task = NULL; /* for the handshake */ |
720 | hyper_task *sendtask = NULL; /* for the send */ |
721 | hyper_clientconn *client = NULL; |
722 | hyper_error *hypererr = NULL; |
723 | char *hostheader = NULL; /* for CONNECT */ |
724 | char *host = NULL; /* Host: */ |
725 | |
726 | if(Curl_connect_complete(conn)) |
727 | return CURLE_OK; /* CONNECT is already completed */ |
728 | |
729 | conn->bits.proxy_connect_closed = FALSE; |
730 | |
731 | do { |
732 | switch(s->tunnel_state) { |
733 | case TUNNEL_INIT: |
734 | /* BEGIN CONNECT PHASE */ |
735 | io = hyper_io_new(); |
736 | if(!io) { |
737 | failf(data, "Couldn't create hyper IO" ); |
738 | goto error; |
739 | } |
740 | /* tell Hyper how to read/write network data */ |
741 | hyper_io_set_userdata(io, data); |
742 | hyper_io_set_read(io, Curl_hyper_recv); |
743 | hyper_io_set_write(io, Curl_hyper_send); |
744 | conn->sockfd = tunnelsocket; |
745 | |
746 | data->state.hconnect = TRUE; |
747 | |
748 | /* create an executor to poll futures */ |
749 | if(!h->exec) { |
750 | h->exec = hyper_executor_new(); |
751 | if(!h->exec) { |
752 | failf(data, "Couldn't create hyper executor" ); |
753 | goto error; |
754 | } |
755 | } |
756 | |
757 | options = hyper_clientconn_options_new(); |
758 | if(!options) { |
759 | failf(data, "Couldn't create hyper client options" ); |
760 | goto error; |
761 | } |
762 | |
763 | hyper_clientconn_options_exec(options, h->exec); |
764 | |
765 | /* "Both the `io` and the `options` are consumed in this function |
766 | call" */ |
767 | handshake = hyper_clientconn_handshake(io, options); |
768 | if(!handshake) { |
769 | failf(data, "Couldn't create hyper client handshake" ); |
770 | goto error; |
771 | } |
772 | io = NULL; |
773 | options = NULL; |
774 | |
775 | if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { |
776 | failf(data, "Couldn't hyper_executor_push the handshake" ); |
777 | goto error; |
778 | } |
779 | handshake = NULL; /* ownership passed on */ |
780 | |
781 | task = hyper_executor_poll(h->exec); |
782 | if(!task) { |
783 | failf(data, "Couldn't hyper_executor_poll the handshake" ); |
784 | goto error; |
785 | } |
786 | |
787 | client = hyper_task_value(task); |
788 | hyper_task_free(task); |
789 | req = hyper_request_new(); |
790 | if(!req) { |
791 | failf(data, "Couldn't hyper_request_new" ); |
792 | goto error; |
793 | } |
794 | if(hyper_request_set_method(req, (uint8_t *)"CONNECT" , |
795 | strlen("CONNECT" ))) { |
796 | failf(data, "error setting method" ); |
797 | goto error; |
798 | } |
799 | |
800 | result = CONNECT_host(data, conn, hostname, remote_port, |
801 | &hostheader, &host); |
802 | if(result) |
803 | goto error; |
804 | |
805 | if(hyper_request_set_uri(req, (uint8_t *)hostheader, |
806 | strlen(hostheader))) { |
807 | failf(data, "error setting path" ); |
808 | result = CURLE_OUT_OF_MEMORY; |
809 | } |
810 | /* Setup the proxy-authorization header, if any */ |
811 | result = Curl_http_output_auth(data, conn, "CONNECT" , HTTPREQ_GET, |
812 | hostheader, TRUE); |
813 | if(result) |
814 | goto error; |
815 | Curl_safefree(hostheader); |
816 | |
817 | /* default is 1.1 */ |
818 | if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && |
819 | (HYPERE_OK != hyper_request_set_version(req, |
820 | HYPER_HTTP_VERSION_1_0))) { |
821 | failf(data, "error setting HTTP version" ); |
822 | goto error; |
823 | } |
824 | |
825 | headers = hyper_request_headers(req); |
826 | if(!headers) { |
827 | failf(data, "hyper_request_headers" ); |
828 | goto error; |
829 | } |
830 | if(host && Curl_hyper_header(data, headers, host)) |
831 | goto error; |
832 | Curl_safefree(host); |
833 | |
834 | if(data->state.aptr.proxyuserpwd && |
835 | Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd)) |
836 | goto error; |
837 | |
838 | if(!Curl_checkProxyheaders(data, conn, "User-Agent" ) && |
839 | data->set.str[STRING_USERAGENT]) { |
840 | struct dynbuf ua; |
841 | Curl_dyn_init(&ua, DYN_HTTP_REQUEST); |
842 | result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n" , |
843 | data->set.str[STRING_USERAGENT]); |
844 | if(result) |
845 | goto error; |
846 | if(Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua))) |
847 | goto error; |
848 | Curl_dyn_free(&ua); |
849 | } |
850 | |
851 | if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection" ) && |
852 | Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive" )) |
853 | goto error; |
854 | |
855 | if(Curl_add_custom_headers(data, TRUE, headers)) |
856 | goto error; |
857 | |
858 | sendtask = hyper_clientconn_send(client, req); |
859 | if(!sendtask) { |
860 | failf(data, "hyper_clientconn_send" ); |
861 | goto error; |
862 | } |
863 | |
864 | if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { |
865 | failf(data, "Couldn't hyper_executor_push the send" ); |
866 | goto error; |
867 | } |
868 | |
869 | hyper_clientconn_free(client); |
870 | |
871 | do { |
872 | task = hyper_executor_poll(h->exec); |
873 | if(task) { |
874 | bool error = hyper_task_type(task) == HYPER_TASK_ERROR; |
875 | if(error) |
876 | hypererr = hyper_task_value(task); |
877 | hyper_task_free(task); |
878 | if(error) |
879 | goto error; |
880 | } |
881 | } while(task); |
882 | s->tunnel_state = TUNNEL_CONNECT; |
883 | /* FALLTHROUGH */ |
884 | case TUNNEL_CONNECT: { |
885 | int didwhat; |
886 | bool done = FALSE; |
887 | result = Curl_hyper_stream(data, conn, &didwhat, &done, |
888 | CURL_CSELECT_IN | CURL_CSELECT_OUT); |
889 | if(result) |
890 | goto error; |
891 | if(!done) |
892 | break; |
893 | s->tunnel_state = TUNNEL_COMPLETE; |
894 | if(h->exec) { |
895 | hyper_executor_free(h->exec); |
896 | h->exec = NULL; |
897 | } |
898 | if(h->read_waker) { |
899 | hyper_waker_free(h->read_waker); |
900 | h->read_waker = NULL; |
901 | } |
902 | if(h->write_waker) { |
903 | hyper_waker_free(h->write_waker); |
904 | h->write_waker = NULL; |
905 | } |
906 | } |
907 | /* FALLTHROUGH */ |
908 | default: |
909 | break; |
910 | } |
911 | } while(data->req.newurl); |
912 | |
913 | result = CURLE_OK; |
914 | if(s->tunnel_state == TUNNEL_COMPLETE) { |
915 | data->info.httpproxycode = data->req.httpcode; |
916 | if(data->info.httpproxycode/100 != 2) { |
917 | if(conn->bits.close && data->req.newurl) { |
918 | conn->bits.proxy_connect_closed = TRUE; |
919 | infof(data, "Connect me again please" ); |
920 | connect_done(data); |
921 | } |
922 | else { |
923 | free(data->req.newurl); |
924 | data->req.newurl = NULL; |
925 | /* failure, close this connection to avoid re-use */ |
926 | streamclose(conn, "proxy CONNECT failure" ); |
927 | Curl_closesocket(data, conn, conn->sock[sockindex]); |
928 | conn->sock[sockindex] = CURL_SOCKET_BAD; |
929 | } |
930 | |
931 | /* to back to init state */ |
932 | s->tunnel_state = TUNNEL_INIT; |
933 | |
934 | if(!conn->bits.proxy_connect_closed) { |
935 | failf(data, "Received HTTP code %d from proxy after CONNECT" , |
936 | data->req.httpcode); |
937 | result = CURLE_RECV_ERROR; |
938 | } |
939 | } |
940 | } |
941 | error: |
942 | free(host); |
943 | free(hostheader); |
944 | if(io) |
945 | hyper_io_free(io); |
946 | |
947 | if(options) |
948 | hyper_clientconn_options_free(options); |
949 | |
950 | if(handshake) |
951 | hyper_task_free(handshake); |
952 | |
953 | if(hypererr) { |
954 | uint8_t errbuf[256]; |
955 | size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); |
956 | failf(data, "Hyper: %.*s" , (int)errlen, errbuf); |
957 | hyper_error_free(hypererr); |
958 | } |
959 | return result; |
960 | } |
961 | #endif |
962 | |
963 | void Curl_connect_free(struct Curl_easy *data) |
964 | { |
965 | struct connectdata *conn = data->conn; |
966 | struct http_connect_state *s = conn->connect_state; |
967 | if(s) { |
968 | free(s); |
969 | conn->connect_state = NULL; |
970 | } |
971 | } |
972 | |
973 | /* |
974 | * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This |
975 | * function will issue the necessary commands to get a seamless tunnel through |
976 | * this proxy. After that, the socket can be used just as a normal socket. |
977 | */ |
978 | |
979 | CURLcode Curl_proxyCONNECT(struct Curl_easy *data, |
980 | int sockindex, |
981 | const char *hostname, |
982 | int remote_port) |
983 | { |
984 | CURLcode result; |
985 | struct connectdata *conn = data->conn; |
986 | if(!conn->connect_state) { |
987 | result = connect_init(data, FALSE); |
988 | if(result) |
989 | return result; |
990 | } |
991 | result = CONNECT(data, sockindex, hostname, remote_port); |
992 | |
993 | if(result || Curl_connect_complete(conn)) |
994 | connect_done(data); |
995 | |
996 | return result; |
997 | } |
998 | |
999 | #else |
1000 | void Curl_connect_free(struct Curl_easy *data) |
1001 | { |
1002 | (void)data; |
1003 | } |
1004 | |
1005 | #endif /* CURL_DISABLE_PROXY */ |
1006 | |