blob: e10a488294544716934aa55581ce64f7667a6651 [file] [log] [blame]
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughescac39802018-04-27 16:19:43 -07008 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070013 *
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
Elliott Hughes82be86d2017-09-20 17:00:17 -070025#include "http_proxy.h"
26
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070027#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
28
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070029#include <curl/curl.h>
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070030#include "sendf.h"
31#include "http.h"
32#include "url.h"
33#include "select.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070034#include "progress.h"
35#include "non-ascii.h"
36#include "connect.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070037#include "curlx.h"
Elliott Hughescee03382017-06-23 12:17:18 -070038#include "vtls/vtls.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070039
Alex Deymod15eaac2016-06-28 14:49:26 -070040/* The last 3 #include files should be in this order */
41#include "curl_printf.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070042#include "curl_memory.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070043#include "memdebug.h"
44
Elliott Hughescee03382017-06-23 12:17:18 -070045/*
46 * Perform SSL initialization for HTTPS proxy. Sets
47 * proxy_ssl_connected connection bit when complete. Can be
48 * called multiple times.
49 */
50static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070051{
Elliott Hughescee03382017-06-23 12:17:18 -070052#ifdef USE_SSL
53 CURLcode result = CURLE_OK;
54 DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
55 if(!conn->bits.proxy_ssl_connected[sockindex]) {
56 /* perform SSL initialization for this socket */
57 result =
58 Curl_ssl_connect_nonblocking(conn, sockindex,
59 &conn->bits.proxy_ssl_connected[sockindex]);
60 if(result)
61 conn->bits.close = TRUE; /* a failed connection is marked for closure to
62 prevent (bad) re-use or similar */
63 }
64 return result;
65#else
66 (void) conn;
67 (void) sockindex;
68 return CURLE_NOT_BUILT_IN;
69#endif
70}
71
72CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
73{
74 if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
75 const CURLcode result = https_proxy_connect(conn, sockindex);
76 if(result)
77 return result;
78 if(!conn->bits.proxy_ssl_connected[sockindex])
79 return result; /* wait for HTTPS proxy SSL initialization to complete */
80 }
81
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070082 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
83#ifndef CURL_DISABLE_PROXY
84 /* for [protocol] tunneled through HTTP proxy */
85 struct HTTP http_proxy;
86 void *prot_save;
Alex Deymod15eaac2016-06-28 14:49:26 -070087 const char *hostname;
88 int remote_port;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070089 CURLcode result;
90
91 /* BLOCKING */
92 /* We want "seamless" operations through HTTP proxy tunnel */
93
94 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
95 * member conn->proto.http; we want [protocol] through HTTP and we have
96 * to change the member temporarily for connecting to the HTTP
97 * proxy. After Curl_proxyCONNECT we have to set back the member to the
98 * original pointer
99 *
100 * This function might be called several times in the multi interface case
Elliott Hughes82be86d2017-09-20 17:00:17 -0700101 * if the proxy's CONNECT response is not instant.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700102 */
103 prot_save = conn->data->req.protop;
104 memset(&http_proxy, 0, sizeof(http_proxy));
105 conn->data->req.protop = &http_proxy;
106 connkeep(conn, "HTTP proxy CONNECT");
Elliott Hughes82be86d2017-09-20 17:00:17 -0700107
108 /* for the secondary socket (FTP), use the "connect to host"
109 * but ignore the "connect to port" (use the secondary port)
110 */
111
112 if(conn->bits.conn_to_host)
Alex Deymod15eaac2016-06-28 14:49:26 -0700113 hostname = conn->conn_to_host.name;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700114 else if(sockindex == SECONDARYSOCKET)
115 hostname = conn->secondaryhostname;
Alex Deymod15eaac2016-06-28 14:49:26 -0700116 else
117 hostname = conn->host.name;
Elliott Hughescee03382017-06-23 12:17:18 -0700118
119 if(sockindex == SECONDARYSOCKET)
120 remote_port = conn->secondary_port;
121 else if(conn->bits.conn_to_port)
Alex Deymod15eaac2016-06-28 14:49:26 -0700122 remote_port = conn->conn_to_port;
123 else
124 remote_port = conn->remote_port;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700125 result = Curl_proxyCONNECT(conn, sockindex, hostname, remote_port);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700126 conn->data->req.protop = prot_save;
127 if(CURLE_OK != result)
128 return result;
129 Curl_safefree(conn->allocptr.proxyuserpwd);
130#else
131 return CURLE_NOT_BUILT_IN;
132#endif
133 }
134 /* no HTTP tunnel proxy, just return */
135 return CURLE_OK;
136}
137
Elliott Hughes82be86d2017-09-20 17:00:17 -0700138bool Curl_connect_complete(struct connectdata *conn)
139{
Alex Deymo486467e2017-12-19 19:04:07 +0100140 return !conn->connect_state ||
Elliott Hughes82be86d2017-09-20 17:00:17 -0700141 (conn->connect_state->tunnel_state == TUNNEL_COMPLETE);
142}
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700143
Elliott Hughes82be86d2017-09-20 17:00:17 -0700144bool Curl_connect_ongoing(struct connectdata *conn)
145{
146 return conn->connect_state &&
147 (conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
148}
149
150static CURLcode connect_init(struct connectdata *conn, bool reinit)
151{
152 struct http_connect_state *s;
153 if(!reinit) {
154 DEBUGASSERT(!conn->connect_state);
155 s = calloc(1, sizeof(struct http_connect_state));
156 if(!s)
157 return CURLE_OUT_OF_MEMORY;
158 infof(conn->data, "allocate connect buffer!\n");
159 conn->connect_state = s;
160 }
161 else {
162 DEBUGASSERT(conn->connect_state);
163 s = conn->connect_state;
164 }
165 s->tunnel_state = TUNNEL_INIT;
Alex Deymo486467e2017-12-19 19:04:07 +0100166 s->keepon = TRUE;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700167 s->line_start = s->connect_buffer;
168 s->ptr = s->line_start;
Alex Deymo486467e2017-12-19 19:04:07 +0100169 s->cl = 0;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800170 s->close_connection = FALSE;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700171 return CURLE_OK;
172}
173
174static void connect_done(struct connectdata *conn)
175{
176 struct http_connect_state *s = conn->connect_state;
177 s->tunnel_state = TUNNEL_COMPLETE;
178 infof(conn->data, "CONNECT phase completed!\n");
179}
180
181static CURLcode CONNECT(struct connectdata *conn,
182 int sockindex,
183 const char *hostname,
184 int remote_port)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700185{
Alex Deymo486467e2017-12-19 19:04:07 +0100186 int subversion = 0;
187 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700188 struct SingleRequest *k = &data->req;
189 CURLcode result;
190 curl_socket_t tunnelsocket = conn->sock[sockindex];
Alex Deymo486467e2017-12-19 19:04:07 +0100191 timediff_t check;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700192 struct http_connect_state *s = conn->connect_state;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700193
194#define SELECT_OK 0
195#define SELECT_ERROR 1
196#define SELECT_TIMEOUT 2
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700197
Elliott Hughes82be86d2017-09-20 17:00:17 -0700198 if(Curl_connect_complete(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700199 return CURLE_OK; /* CONNECT is already completed */
200
201 conn->bits.proxy_connect_closed = FALSE;
202
203 do {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700204 if(TUNNEL_INIT == s->tunnel_state) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700205 /* BEGIN CONNECT PHASE */
206 char *host_port;
207 Curl_send_buffer *req_buffer;
208
209 infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
210 hostname, remote_port);
211
212 /* This only happens if we've looped here due to authentication
213 reasons, and we don't really use the newly cloned URL here
214 then. Just free() it. */
215 free(data->req.newurl);
216 data->req.newurl = NULL;
217
218 /* initialize a dynamic send-buffer */
219 req_buffer = Curl_add_buffer_init();
220
221 if(!req_buffer)
222 return CURLE_OUT_OF_MEMORY;
223
Elliott Hughes1ef06ba2018-05-30 15:43:58 -0700224 host_port = aprintf("%s:%d", hostname, remote_port);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700225 if(!host_port) {
226 Curl_add_buffer_free(req_buffer);
227 return CURLE_OUT_OF_MEMORY;
228 }
229
230 /* Setup the proxy-authorization header, if any */
231 result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
232
233 free(host_port);
234
235 if(!result) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700236 char *host = NULL;
Alex Deymo486467e2017-12-19 19:04:07 +0100237 const char *proxyconn = "";
238 const char *useragent = "";
Elliott Hughescee03382017-06-23 12:17:18 -0700239 const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ?
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700240 "1.0" : "1.1";
Alex Deymod15eaac2016-06-28 14:49:26 -0700241 bool ipv6_ip = conn->bits.ipv6_ip;
242 char *hostheader;
243
244 /* the hostname may be different */
245 if(hostname != conn->host.name)
246 ipv6_ip = (strchr(hostname, ':') != NULL);
Alex Deymo486467e2017-12-19 19:04:07 +0100247 hostheader = /* host:port with IPv6 support */
Elliott Hughes1ef06ba2018-05-30 15:43:58 -0700248 aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700249 remote_port);
250 if(!hostheader) {
251 Curl_add_buffer_free(req_buffer);
252 return CURLE_OUT_OF_MEMORY;
253 }
254
Elliott Hughescac39802018-04-27 16:19:43 -0700255 if(!Curl_checkProxyheaders(conn, "Host")) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700256 host = aprintf("Host: %s\r\n", hostheader);
257 if(!host) {
258 free(hostheader);
259 Curl_add_buffer_free(req_buffer);
260 return CURLE_OUT_OF_MEMORY;
261 }
262 }
Elliott Hughescac39802018-04-27 16:19:43 -0700263 if(!Curl_checkProxyheaders(conn, "Proxy-Connection"))
Elliott Hughescee03382017-06-23 12:17:18 -0700264 proxyconn = "Proxy-Connection: Keep-Alive\r\n";
265
Elliott Hughescac39802018-04-27 16:19:43 -0700266 if(!Curl_checkProxyheaders(conn, "User-Agent") &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700267 data->set.str[STRING_USERAGENT])
268 useragent = conn->allocptr.uagent;
269
270 result =
271 Curl_add_bufferf(req_buffer,
272 "CONNECT %s HTTP/%s\r\n"
273 "%s" /* Host: */
274 "%s" /* Proxy-Authorization */
Elliott Hughescee03382017-06-23 12:17:18 -0700275 "%s" /* User-Agent */
276 "%s", /* Proxy-Connection */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700277 hostheader,
278 http,
Elliott Hughes82be86d2017-09-20 17:00:17 -0700279 host?host:"",
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700280 conn->allocptr.proxyuserpwd?
281 conn->allocptr.proxyuserpwd:"",
Elliott Hughescee03382017-06-23 12:17:18 -0700282 useragent,
283 proxyconn);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700284
Elliott Hughes82be86d2017-09-20 17:00:17 -0700285 if(host)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700286 free(host);
287 free(hostheader);
288
289 if(!result)
290 result = Curl_add_custom_headers(conn, TRUE, req_buffer);
291
292 if(!result)
293 /* CRLF terminate the request */
294 result = Curl_add_bufferf(req_buffer, "\r\n");
295
296 if(!result) {
297 /* Send the connect request to the proxy */
298 /* BLOCKING */
299 result =
300 Curl_add_buffer_send(req_buffer, conn,
301 &data->info.request_size, 0, sockindex);
302 }
303 req_buffer = NULL;
304 if(result)
305 failf(data, "Failed sending CONNECT to proxy");
306 }
307
308 Curl_add_buffer_free(req_buffer);
309 if(result)
310 return result;
311
Elliott Hughes82be86d2017-09-20 17:00:17 -0700312 s->tunnel_state = TUNNEL_CONNECT;
313 s->perline = 0;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700314 } /* END CONNECT PHASE */
315
316 check = Curl_timeleft(data, NULL, TRUE);
317 if(check <= 0) {
318 failf(data, "Proxy CONNECT aborted due to timeout");
Elliott Hughes82be86d2017-09-20 17:00:17 -0700319 return CURLE_OPERATION_TIMEDOUT;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700320 }
321
Elliott Hughes82be86d2017-09-20 17:00:17 -0700322 if(!Curl_conn_data_pending(conn, sockindex))
323 /* return so we'll be called again polling-style */
324 return CURLE_OK;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700325
326 /* at this point, the tunnel_connecting phase is over. */
327
328 { /* READING RESPONSE PHASE */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700329 int error = SELECT_OK;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700330
Elliott Hughes82be86d2017-09-20 17:00:17 -0700331 while(s->keepon && !error) {
332 ssize_t gotbytes;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700333
Elliott Hughes82be86d2017-09-20 17:00:17 -0700334 /* make sure we have space to read more data */
335 if(s->ptr >= &s->connect_buffer[CONNECT_BUFFER_SIZE]) {
336 failf(data, "CONNECT response too large!");
337 return CURLE_RECV_ERROR;
338 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700339
Elliott Hughes82be86d2017-09-20 17:00:17 -0700340 /* Read one byte at a time to avoid a race condition. Wait at most one
341 second before looping to ensure continuous pgrsUpdates. */
342 result = Curl_read(conn, tunnelsocket, s->ptr, 1, &gotbytes);
343 if(result == CURLE_AGAIN)
344 /* socket buffer drained, return */
345 return CURLE_OK;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700346
Elliott Hughes82be86d2017-09-20 17:00:17 -0700347 if(Curl_pgrsUpdate(conn))
348 return CURLE_ABORTED_BY_CALLBACK;
349
350 if(result) {
351 s->keepon = FALSE;
352 break;
353 }
354 else if(gotbytes <= 0) {
355 if(data->set.proxyauth && data->state.authproxy.avail) {
356 /* proxy auth was requested and there was proxy auth available,
357 then deem this as "mere" proxy disconnect */
358 conn->bits.proxy_connect_closed = TRUE;
359 infof(data, "Proxy CONNECT connection closed\n");
360 }
361 else {
362 error = SELECT_ERROR;
363 failf(data, "Proxy CONNECT aborted");
364 }
365 s->keepon = FALSE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700366 break;
367 }
368
Elliott Hughes82be86d2017-09-20 17:00:17 -0700369
370 if(s->keepon > TRUE) {
371 /* This means we are currently ignoring a response-body */
372
373 s->ptr = s->connect_buffer;
374 if(s->cl) {
375 /* A Content-Length based body: simply count down the counter
376 and make sure to break out of the loop when we're done! */
377 s->cl--;
378 if(s->cl <= 0) {
379 s->keepon = FALSE;
380 s->tunnel_state = TUNNEL_COMPLETE;
381 break;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700382 }
383 }
384 else {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700385 /* chunked-encoded body, so we need to do the chunked dance
386 properly to know when the end of the body is reached */
387 CHUNKcode r;
388 ssize_t tookcareof = 0;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700389
Elliott Hughes82be86d2017-09-20 17:00:17 -0700390 /* now parse the chunked piece of data so that we can
391 properly tell when the stream ends */
392 r = Curl_httpchunk_read(conn, s->ptr, 1, &tookcareof);
393 if(r == CHUNKE_STOP) {
394 /* we're done reading chunks! */
395 infof(data, "chunk reading DONE\n");
396 s->keepon = FALSE;
397 /* we did the full CONNECT treatment, go COMPLETE */
398 s->tunnel_state = TUNNEL_COMPLETE;
399 }
400 }
401 continue;
402 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700403
Elliott Hughes82be86d2017-09-20 17:00:17 -0700404 s->perline++; /* amount of bytes in this line so far */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700405
Elliott Hughes82be86d2017-09-20 17:00:17 -0700406 /* if this is not the end of a header line then continue */
407 if(*s->ptr != 0x0a) {
408 s->ptr++;
409 continue;
410 }
411
412 /* convert from the network encoding */
Alex Deymo486467e2017-12-19 19:04:07 +0100413 result = Curl_convert_from_network(data, s->line_start,
414 (size_t)s->perline);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700415 /* Curl_convert_from_network calls failf if unsuccessful */
416 if(result)
417 return result;
418
419 /* output debug if that is requested */
420 if(data->set.verbose)
421 Curl_debug(data, CURLINFO_HEADER_IN,
422 s->line_start, (size_t)s->perline, conn);
423
424 if(!data->set.suppress_connect_headers) {
425 /* send the header to the callback */
426 int writetype = CLIENTWRITE_HEADER;
427 if(data->set.include_header)
428 writetype |= CLIENTWRITE_BODY;
429
430 result = Curl_client_write(conn, writetype,
431 s->line_start, s->perline);
432 if(result)
433 return result;
434 }
435
436 data->info.header_size += (long)s->perline;
437 data->req.headerbytecount += (long)s->perline;
438
439 /* Newlines are CRLF, so the CR is ignored as the line isn't
440 really terminated until the LF comes. Treat a following CR
441 as end-of-headers as well.*/
442
443 if(('\r' == s->line_start[0]) ||
444 ('\n' == s->line_start[0])) {
445 /* end of response-headers from the proxy */
446 s->ptr = s->connect_buffer;
447 if((407 == k->httpcode) && !data->state.authproblem) {
448 /* If we get a 407 response code with content length
449 when we have no auth problem, we must ignore the
450 whole response-body */
451 s->keepon = 2;
452
453 if(s->cl) {
454 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
455 " bytes of response-body\n", s->cl);
456 }
457 else if(s->chunked_encoding) {
458 CHUNKcode r;
459
460 infof(data, "Ignore chunked response-body\n");
461
462 /* We set ignorebody true here since the chunked
463 decoder function will acknowledge that. Pay
464 attention so that this is cleared again when this
465 function returns! */
466 k->ignorebody = TRUE;
467
468 if(s->line_start[1] == '\n') {
469 /* this can only be a LF if the letter at index 0
470 was a CR */
471 s->line_start++;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700472 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700473
Elliott Hughes82be86d2017-09-20 17:00:17 -0700474 /* now parse the chunked piece of data so that we can
475 properly tell when the stream ends */
476 r = Curl_httpchunk_read(conn, s->line_start + 1, 1, &gotbytes);
477 if(r == CHUNKE_STOP) {
478 /* we're done reading chunks! */
479 infof(data, "chunk reading DONE\n");
480 s->keepon = FALSE;
481 /* we did the full CONNECT treatment, go to COMPLETE */
482 s->tunnel_state = TUNNEL_COMPLETE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700483 }
484 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700485 else {
486 /* without content-length or chunked encoding, we
487 can't keep the connection alive since the close is
488 the end signal so we bail out at once instead */
489 s->keepon = FALSE;
490 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700491 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700492 else
493 s->keepon = FALSE;
494 if(!s->cl)
495 /* we did the full CONNECT treatment, go to COMPLETE */
496 s->tunnel_state = TUNNEL_COMPLETE;
497 continue;
498 }
499
500 s->line_start[s->perline] = 0; /* zero terminate the buffer */
501 if((checkprefix("WWW-Authenticate:", s->line_start) &&
502 (401 == k->httpcode)) ||
503 (checkprefix("Proxy-authenticate:", s->line_start) &&
504 (407 == k->httpcode))) {
505
506 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
507 char *auth = Curl_copy_header_value(s->line_start);
508 if(!auth)
509 return CURLE_OUT_OF_MEMORY;
510
511 result = Curl_http_input_auth(conn, proxy, auth);
512
513 free(auth);
514
515 if(result)
516 return result;
517 }
518 else if(checkprefix("Content-Length:", s->line_start)) {
519 if(k->httpcode/100 == 2) {
520 /* A client MUST ignore any Content-Length or Transfer-Encoding
521 header fields received in a successful response to CONNECT.
522 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
523 infof(data, "Ignoring Content-Length in CONNECT %03d response\n",
524 k->httpcode);
525 }
526 else {
Alex Deymo486467e2017-12-19 19:04:07 +0100527 (void)curlx_strtoofft(s->line_start +
528 strlen("Content-Length:"), NULL, 10, &s->cl);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700529 }
530 }
531 else if(Curl_compareheader(s->line_start, "Connection:", "close"))
Elliott Hughes0128fe42018-02-27 14:57:55 -0800532 s->close_connection = TRUE;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700533 else if(checkprefix("Transfer-Encoding:", s->line_start)) {
534 if(k->httpcode/100 == 2) {
535 /* A client MUST ignore any Content-Length or Transfer-Encoding
536 header fields received in a successful response to CONNECT.
537 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
538 infof(data, "Ignoring Transfer-Encoding in "
539 "CONNECT %03d response\n", k->httpcode);
540 }
541 else if(Curl_compareheader(s->line_start,
542 "Transfer-Encoding:", "chunked")) {
543 infof(data, "CONNECT responded chunked\n");
544 s->chunked_encoding = TRUE;
545 /* init our chunky engine */
546 Curl_httpchunk_init(conn);
547 }
548 }
549 else if(Curl_compareheader(s->line_start,
550 "Proxy-Connection:", "close"))
Elliott Hughes0128fe42018-02-27 14:57:55 -0800551 s->close_connection = TRUE;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700552 else if(2 == sscanf(s->line_start, "HTTP/1.%d %d",
553 &subversion,
554 &k->httpcode)) {
555 /* store the HTTP code from the proxy */
556 data->info.httpproxycode = k->httpcode;
557 }
558
559 s->perline = 0; /* line starts over here */
560 s->ptr = s->connect_buffer;
561 s->line_start = s->ptr;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700562 } /* while there's buffer left and loop is requested */
563
Elliott Hughes82be86d2017-09-20 17:00:17 -0700564 if(Curl_pgrsUpdate(conn))
565 return CURLE_ABORTED_BY_CALLBACK;
566
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700567 if(error)
568 return CURLE_RECV_ERROR;
569
Alex Deymo486467e2017-12-19 19:04:07 +0100570 if(data->info.httpproxycode/100 != 2) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700571 /* Deal with the possibly already received authenticate
572 headers. 'newurl' is set to a new URL if we must loop. */
573 result = Curl_http_auth_act(conn);
574 if(result)
575 return result;
576
577 if(conn->bits.close)
578 /* the connection has been marked for closure, most likely in the
579 Curl_http_auth_act() function and thus we can kill it at once
Elliott Hughes82be86d2017-09-20 17:00:17 -0700580 below */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800581 s->close_connection = TRUE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700582 }
583
Elliott Hughes0128fe42018-02-27 14:57:55 -0800584 if(s->close_connection && data->req.newurl) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700585 /* Connection closed by server. Don't use it anymore */
586 Curl_closesocket(conn, conn->sock[sockindex]);
587 conn->sock[sockindex] = CURL_SOCKET_BAD;
588 break;
589 }
590 } /* END READING RESPONSE PHASE */
591
592 /* If we are supposed to continue and request a new URL, which basically
593 * means the HTTP authentication is still going on so if the tunnel
594 * is complete we start over in INIT state */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700595 if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
596 connect_init(conn, TRUE); /* reinit */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700597 }
598
599 } while(data->req.newurl);
600
Alex Deymo486467e2017-12-19 19:04:07 +0100601 if(data->info.httpproxycode/100 != 2) {
Elliott Hughes0128fe42018-02-27 14:57:55 -0800602 if(s->close_connection && data->req.newurl) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700603 conn->bits.proxy_connect_closed = TRUE;
604 infof(data, "Connect me again please\n");
Elliott Hughes82be86d2017-09-20 17:00:17 -0700605 connect_done(conn);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700606 }
607 else {
608 free(data->req.newurl);
609 data->req.newurl = NULL;
610 /* failure, close this connection to avoid re-use */
Elliott Hughescee03382017-06-23 12:17:18 -0700611 streamclose(conn, "proxy CONNECT failure");
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700612 Curl_closesocket(conn, conn->sock[sockindex]);
613 conn->sock[sockindex] = CURL_SOCKET_BAD;
614 }
615
616 /* to back to init state */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700617 s->tunnel_state = TUNNEL_INIT;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700618
619 if(conn->bits.proxy_connect_closed)
620 /* this is not an error, just part of the connection negotiation */
621 return CURLE_OK;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700622 failf(data, "Received HTTP code %d from proxy after CONNECT",
623 data->req.httpcode);
624 return CURLE_RECV_ERROR;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700625 }
626
Elliott Hughes82be86d2017-09-20 17:00:17 -0700627 s->tunnel_state = TUNNEL_COMPLETE;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700628
629 /* If a proxy-authorization header was used for the proxy, then we should
630 make sure that it isn't accidentally used for the document request
631 after we've connected. So let's free and clear it here. */
632 Curl_safefree(conn->allocptr.proxyuserpwd);
633 conn->allocptr.proxyuserpwd = NULL;
634
635 data->state.authproxy.done = TRUE;
636
Alex Deymo486467e2017-12-19 19:04:07 +0100637 infof(data, "Proxy replied %d to CONNECT request\n",
638 data->info.httpproxycode);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700639 data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
640 conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
641 document request */
642 return CURLE_OK;
643}
Elliott Hughes82be86d2017-09-20 17:00:17 -0700644
645void Curl_connect_free(struct Curl_easy *data)
646{
647 struct connectdata *conn = data->easy_conn;
648 struct http_connect_state *s = conn->connect_state;
649 if(s) {
650 free(s);
651 conn->connect_state = NULL;
652 }
653}
654
655/*
656 * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
657 * function will issue the necessary commands to get a seamless tunnel through
658 * this proxy. After that, the socket can be used just as a normal socket.
659 */
660
661CURLcode Curl_proxyCONNECT(struct connectdata *conn,
662 int sockindex,
663 const char *hostname,
664 int remote_port)
665{
666 CURLcode result;
667 if(!conn->connect_state) {
668 result = connect_init(conn, FALSE);
669 if(result)
670 return result;
671 }
672 result = CONNECT(conn, sockindex, hostname, remote_port);
673
674 if(result || Curl_connect_complete(conn))
675 connect_done(conn);
676
677 return result;
678}
679
680#else
681void Curl_connect_free(struct Curl_easy *data)
682{
683 (void)data;
684}
685
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700686#endif /* CURL_DISABLE_PROXY */