1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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.haxx.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 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) 26 27 #include "urldata.h" 28 #include <curl/curl.h> 29 #include "http_proxy.h" 30 #include "sendf.h" 31 #include "http.h" 32 #include "url.h" 33 #include "select.h" 34 #include "progress.h" 35 #include "non-ascii.h" 36 #include "connect.h" 37 #include "curlx.h" 38 #include "vtls/vtls.h" 39 40 /* The last 3 #include files should be in this order */ 41 #include "curl_printf.h" 42 #include "curl_memory.h" 43 #include "memdebug.h" 44 45 /* 46 * Perform SSL initialization for HTTPS proxy. Sets 47 * proxy_ssl_connected connection bit when complete. Can be 48 * called multiple times. 49 */ 50 static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex) 51 { 52 #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 72 CURLcode 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 82 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; 87 const char *hostname; 88 int remote_port; 89 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 101 * if the proxy's CONNTECT response is not instant. 102 */ 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"); 107 if(sockindex == SECONDARYSOCKET) 108 hostname = conn->secondaryhostname; 109 else if(conn->bits.conn_to_host) 110 hostname = conn->conn_to_host.name; 111 else 112 hostname = conn->host.name; 113 114 if(sockindex == SECONDARYSOCKET) 115 remote_port = conn->secondary_port; 116 else if(conn->bits.conn_to_port) 117 remote_port = conn->conn_to_port; 118 else 119 remote_port = conn->remote_port; 120 result = Curl_proxyCONNECT(conn, sockindex, hostname, 121 remote_port, FALSE); 122 conn->data->req.protop = prot_save; 123 if(CURLE_OK != result) 124 return result; 125 Curl_safefree(conn->allocptr.proxyuserpwd); 126 #else 127 return CURLE_NOT_BUILT_IN; 128 #endif 129 } 130 /* no HTTP tunnel proxy, just return */ 131 return CURLE_OK; 132 } 133 134 /* 135 * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This 136 * function will issue the necessary commands to get a seamless tunnel through 137 * this proxy. After that, the socket can be used just as a normal socket. 138 * 139 * 'blocking' set to TRUE means that this function will do the entire CONNECT 140 * + response in a blocking fashion. Should be avoided! 141 */ 142 143 CURLcode Curl_proxyCONNECT(struct connectdata *conn, 144 int sockindex, 145 const char *hostname, 146 int remote_port, 147 bool blocking) 148 { 149 int subversion=0; 150 struct Curl_easy *data=conn->data; 151 struct SingleRequest *k = &data->req; 152 CURLcode result; 153 curl_socket_t tunnelsocket = conn->sock[sockindex]; 154 curl_off_t cl=0; 155 bool closeConnection = FALSE; 156 bool chunked_encoding = FALSE; 157 time_t check; 158 159 #define SELECT_OK 0 160 #define SELECT_ERROR 1 161 #define SELECT_TIMEOUT 2 162 int error = SELECT_OK; 163 164 if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE) 165 return CURLE_OK; /* CONNECT is already completed */ 166 167 conn->bits.proxy_connect_closed = FALSE; 168 169 do { 170 if(TUNNEL_INIT == conn->tunnel_state[sockindex]) { 171 /* BEGIN CONNECT PHASE */ 172 char *host_port; 173 Curl_send_buffer *req_buffer; 174 175 infof(data, "Establish HTTP proxy tunnel to %s:%hu\n", 176 hostname, remote_port); 177 178 /* This only happens if we've looped here due to authentication 179 reasons, and we don't really use the newly cloned URL here 180 then. Just free() it. */ 181 free(data->req.newurl); 182 data->req.newurl = NULL; 183 184 /* initialize a dynamic send-buffer */ 185 req_buffer = Curl_add_buffer_init(); 186 187 if(!req_buffer) 188 return CURLE_OUT_OF_MEMORY; 189 190 host_port = aprintf("%s:%hu", hostname, remote_port); 191 if(!host_port) { 192 Curl_add_buffer_free(req_buffer); 193 return CURLE_OUT_OF_MEMORY; 194 } 195 196 /* Setup the proxy-authorization header, if any */ 197 result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE); 198 199 free(host_port); 200 201 if(!result) { 202 char *host=(char *)""; 203 const char *proxyconn=""; 204 const char *useragent=""; 205 const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 206 "1.0" : "1.1"; 207 bool ipv6_ip = conn->bits.ipv6_ip; 208 char *hostheader; 209 210 /* the hostname may be different */ 211 if(hostname != conn->host.name) 212 ipv6_ip = (strchr(hostname, ':') != NULL); 213 hostheader= /* host:port with IPv6 support */ 214 aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", 215 remote_port); 216 if(!hostheader) { 217 Curl_add_buffer_free(req_buffer); 218 return CURLE_OUT_OF_MEMORY; 219 } 220 221 if(!Curl_checkProxyheaders(conn, "Host:")) { 222 host = aprintf("Host: %s\r\n", hostheader); 223 if(!host) { 224 free(hostheader); 225 Curl_add_buffer_free(req_buffer); 226 return CURLE_OUT_OF_MEMORY; 227 } 228 } 229 if(!Curl_checkProxyheaders(conn, "Proxy-Connection:")) 230 proxyconn = "Proxy-Connection: Keep-Alive\r\n"; 231 232 if(!Curl_checkProxyheaders(conn, "User-Agent:") && 233 data->set.str[STRING_USERAGENT]) 234 useragent = conn->allocptr.uagent; 235 236 result = 237 Curl_add_bufferf(req_buffer, 238 "CONNECT %s HTTP/%s\r\n" 239 "%s" /* Host: */ 240 "%s" /* Proxy-Authorization */ 241 "%s" /* User-Agent */ 242 "%s", /* Proxy-Connection */ 243 hostheader, 244 http, 245 host, 246 conn->allocptr.proxyuserpwd? 247 conn->allocptr.proxyuserpwd:"", 248 useragent, 249 proxyconn); 250 251 if(host && *host) 252 free(host); 253 free(hostheader); 254 255 if(!result) 256 result = Curl_add_custom_headers(conn, TRUE, req_buffer); 257 258 if(!result) 259 /* CRLF terminate the request */ 260 result = Curl_add_bufferf(req_buffer, "\r\n"); 261 262 if(!result) { 263 /* Send the connect request to the proxy */ 264 /* BLOCKING */ 265 result = 266 Curl_add_buffer_send(req_buffer, conn, 267 &data->info.request_size, 0, sockindex); 268 } 269 req_buffer = NULL; 270 if(result) 271 failf(data, "Failed sending CONNECT to proxy"); 272 } 273 274 Curl_add_buffer_free(req_buffer); 275 if(result) 276 return result; 277 278 conn->tunnel_state[sockindex] = TUNNEL_CONNECT; 279 } /* END CONNECT PHASE */ 280 281 check = Curl_timeleft(data, NULL, TRUE); 282 if(check <= 0) { 283 failf(data, "Proxy CONNECT aborted due to timeout"); 284 return CURLE_RECV_ERROR; 285 } 286 287 if(!blocking) { 288 if(0 == SOCKET_READABLE(tunnelsocket, 0)) 289 /* return so we'll be called again polling-style */ 290 return CURLE_OK; 291 else { 292 DEBUGF(infof(data, 293 "Read response immediately from proxy CONNECT\n")); 294 } 295 } 296 297 /* at this point, the tunnel_connecting phase is over. */ 298 299 { /* READING RESPONSE PHASE */ 300 size_t nread; /* total size read */ 301 int perline; /* count bytes per line */ 302 int keepon=TRUE; 303 ssize_t gotbytes; 304 char *ptr; 305 char *line_start; 306 307 ptr=data->state.buffer; 308 line_start = ptr; 309 310 nread=0; 311 perline=0; 312 313 while((nread<BUFSIZE) && (keepon && !error)) { 314 315 check = Curl_timeleft(data, NULL, TRUE); 316 if(check <= 0) { 317 failf(data, "Proxy CONNECT aborted due to timeout"); 318 error = SELECT_TIMEOUT; /* already too little time */ 319 break; 320 } 321 322 /* loop every second at least, less if the timeout is near */ 323 switch (SOCKET_READABLE(tunnelsocket, check<1000L?check:1000)) { 324 case -1: /* select() error, stop reading */ 325 error = SELECT_ERROR; 326 failf(data, "Proxy CONNECT aborted due to select/poll error"); 327 break; 328 case 0: /* timeout */ 329 break; 330 default: 331 DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1); 332 result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, 333 &gotbytes); 334 if(result==CURLE_AGAIN) 335 continue; /* go loop yourself */ 336 else if(result) 337 keepon = FALSE; 338 else if(gotbytes <= 0) { 339 keepon = FALSE; 340 if(data->set.proxyauth && data->state.authproxy.avail) { 341 /* proxy auth was requested and there was proxy auth available, 342 then deem this as "mere" proxy disconnect */ 343 conn->bits.proxy_connect_closed = TRUE; 344 infof(data, "Proxy CONNECT connection closed\n"); 345 } 346 else { 347 error = SELECT_ERROR; 348 failf(data, "Proxy CONNECT aborted"); 349 } 350 } 351 else { 352 /* 353 * We got a whole chunk of data, which can be anything from one 354 * byte to a set of lines and possibly just a piece of the last 355 * line. 356 */ 357 int i; 358 359 nread += gotbytes; 360 361 if(keepon > TRUE) { 362 /* This means we are currently ignoring a response-body */ 363 364 nread = 0; /* make next read start over in the read buffer */ 365 ptr=data->state.buffer; 366 if(cl) { 367 /* A Content-Length based body: simply count down the counter 368 and make sure to break out of the loop when we're done! */ 369 cl -= gotbytes; 370 if(cl<=0) { 371 keepon = FALSE; 372 break; 373 } 374 } 375 else { 376 /* chunked-encoded body, so we need to do the chunked dance 377 properly to know when the end of the body is reached */ 378 CHUNKcode r; 379 ssize_t tookcareof=0; 380 381 /* now parse the chunked piece of data so that we can 382 properly tell when the stream ends */ 383 r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof); 384 if(r == CHUNKE_STOP) { 385 /* we're done reading chunks! */ 386 infof(data, "chunk reading DONE\n"); 387 keepon = FALSE; 388 /* we did the full CONNECT treatment, go COMPLETE */ 389 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE; 390 } 391 else 392 infof(data, "Read %zd bytes of chunk, continue\n", 393 tookcareof); 394 } 395 } 396 else 397 for(i = 0; i < gotbytes; ptr++, i++) { 398 perline++; /* amount of bytes in this line so far */ 399 if(*ptr == 0x0a) { 400 char letter; 401 int writetype; 402 403 /* convert from the network encoding */ 404 result = Curl_convert_from_network(data, line_start, 405 perline); 406 /* Curl_convert_from_network calls failf if unsuccessful */ 407 if(result) 408 return result; 409 410 /* output debug if that is requested */ 411 if(data->set.verbose) 412 Curl_debug(data, CURLINFO_HEADER_IN, 413 line_start, (size_t)perline, conn); 414 415 /* send the header to the callback */ 416 writetype = CLIENTWRITE_HEADER; 417 if(data->set.include_header) 418 writetype |= CLIENTWRITE_BODY; 419 420 result = Curl_client_write(conn, writetype, line_start, 421 perline); 422 423 data->info.header_size += (long)perline; 424 data->req.headerbytecount += (long)perline; 425 426 if(result) 427 return result; 428 429 /* Newlines are CRLF, so the CR is ignored as the line isn't 430 really terminated until the LF comes. Treat a following CR 431 as end-of-headers as well.*/ 432 433 if(('\r' == line_start[0]) || 434 ('\n' == line_start[0])) { 435 /* end of response-headers from the proxy */ 436 nread = 0; /* make next read start over in the read 437 buffer */ 438 ptr=data->state.buffer; 439 if((407 == k->httpcode) && !data->state.authproblem) { 440 /* If we get a 407 response code with content length 441 when we have no auth problem, we must ignore the 442 whole response-body */ 443 keepon = 2; 444 445 if(cl) { 446 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T 447 " bytes of response-body\n", cl); 448 449 /* remove the remaining chunk of what we already 450 read */ 451 cl -= (gotbytes - i); 452 453 if(cl<=0) 454 /* if the whole thing was already read, we are done! 455 */ 456 keepon=FALSE; 457 } 458 else if(chunked_encoding) { 459 CHUNKcode r; 460 /* We set ignorebody true here since the chunked 461 decoder function will acknowledge that. Pay 462 attention so that this is cleared again when this 463 function returns! */ 464 k->ignorebody = TRUE; 465 infof(data, "%zd bytes of chunk left\n", gotbytes-i); 466 467 if(line_start[1] == '\n') { 468 /* this can only be a LF if the letter at index 0 469 was a CR */ 470 line_start++; 471 i++; 472 } 473 474 /* now parse the chunked piece of data so that we can 475 properly tell when the stream ends */ 476 r = Curl_httpchunk_read(conn, line_start+1, 477 gotbytes -i, &gotbytes); 478 if(r == CHUNKE_STOP) { 479 /* we're done reading chunks! */ 480 infof(data, "chunk reading DONE\n"); 481 keepon = FALSE; 482 /* we did the full CONNECT treatment, go to 483 COMPLETE */ 484 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE; 485 } 486 else 487 infof(data, "Read %zd bytes of chunk, continue\n", 488 gotbytes); 489 } 490 else { 491 /* without content-length or chunked encoding, we 492 can't keep the connection alive since the close is 493 the end signal so we bail out at once instead */ 494 keepon=FALSE; 495 } 496 } 497 else { 498 keepon = FALSE; 499 if(200 == data->info.httpproxycode) { 500 if(gotbytes - (i+1)) 501 failf(data, "Proxy CONNECT followed by %zd bytes " 502 "of opaque data. Data ignored (known bug #39)", 503 gotbytes - (i+1)); 504 } 505 } 506 /* we did the full CONNECT treatment, go to COMPLETE */ 507 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE; 508 break; /* breaks out of for-loop, not switch() */ 509 } 510 511 /* keep a backup of the position we are about to blank */ 512 letter = line_start[perline]; 513 line_start[perline]=0; /* zero terminate the buffer */ 514 if((checkprefix("WWW-Authenticate:", line_start) && 515 (401 == k->httpcode)) || 516 (checkprefix("Proxy-authenticate:", line_start) && 517 (407 == k->httpcode))) { 518 519 bool proxy = (k->httpcode == 407) ? TRUE : FALSE; 520 char *auth = Curl_copy_header_value(line_start); 521 if(!auth) 522 return CURLE_OUT_OF_MEMORY; 523 524 result = Curl_http_input_auth(conn, proxy, auth); 525 526 free(auth); 527 528 if(result) 529 return result; 530 } 531 else if(checkprefix("Content-Length:", line_start)) { 532 cl = curlx_strtoofft(line_start + 533 strlen("Content-Length:"), NULL, 10); 534 } 535 else if(Curl_compareheader(line_start, 536 "Connection:", "close")) 537 closeConnection = TRUE; 538 else if(Curl_compareheader(line_start, 539 "Transfer-Encoding:", 540 "chunked")) { 541 infof(data, "CONNECT responded chunked\n"); 542 chunked_encoding = TRUE; 543 /* init our chunky engine */ 544 Curl_httpchunk_init(conn); 545 } 546 else if(Curl_compareheader(line_start, 547 "Proxy-Connection:", "close")) 548 closeConnection = TRUE; 549 else if(2 == sscanf(line_start, "HTTP/1.%d %d", 550 &subversion, 551 &k->httpcode)) { 552 /* store the HTTP code from the proxy */ 553 data->info.httpproxycode = k->httpcode; 554 } 555 /* put back the letter we blanked out before */ 556 line_start[perline]= letter; 557 558 perline=0; /* line starts over here */ 559 line_start = ptr+1; /* this skips the zero byte we wrote */ 560 } 561 } 562 } 563 break; 564 } /* switch */ 565 if(Curl_pgrsUpdate(conn)) 566 return CURLE_ABORTED_BY_CALLBACK; 567 } /* while there's buffer left and loop is requested */ 568 569 if(error) 570 return CURLE_RECV_ERROR; 571 572 if(data->info.httpproxycode != 200) { 573 /* Deal with the possibly already received authenticate 574 headers. 'newurl' is set to a new URL if we must loop. */ 575 result = Curl_http_auth_act(conn); 576 if(result) 577 return result; 578 579 if(conn->bits.close) 580 /* the connection has been marked for closure, most likely in the 581 Curl_http_auth_act() function and thus we can kill it at once 582 below 583 */ 584 closeConnection = TRUE; 585 } 586 587 if(closeConnection && data->req.newurl) { 588 /* Connection closed by server. Don't use it anymore */ 589 Curl_closesocket(conn, conn->sock[sockindex]); 590 conn->sock[sockindex] = CURL_SOCKET_BAD; 591 break; 592 } 593 } /* END READING RESPONSE PHASE */ 594 595 /* If we are supposed to continue and request a new URL, which basically 596 * means the HTTP authentication is still going on so if the tunnel 597 * is complete we start over in INIT state */ 598 if(data->req.newurl && 599 (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) { 600 conn->tunnel_state[sockindex] = TUNNEL_INIT; 601 infof(data, "TUNNEL_STATE switched to: %d\n", 602 conn->tunnel_state[sockindex]); 603 } 604 605 } while(data->req.newurl); 606 607 if(200 != data->req.httpcode) { 608 if(closeConnection && data->req.newurl) { 609 conn->bits.proxy_connect_closed = TRUE; 610 infof(data, "Connect me again please\n"); 611 } 612 else { 613 free(data->req.newurl); 614 data->req.newurl = NULL; 615 /* failure, close this connection to avoid re-use */ 616 streamclose(conn, "proxy CONNECT failure"); 617 Curl_closesocket(conn, conn->sock[sockindex]); 618 conn->sock[sockindex] = CURL_SOCKET_BAD; 619 } 620 621 /* to back to init state */ 622 conn->tunnel_state[sockindex] = TUNNEL_INIT; 623 624 if(conn->bits.proxy_connect_closed) 625 /* this is not an error, just part of the connection negotiation */ 626 return CURLE_OK; 627 else { 628 failf(data, "Received HTTP code %d from proxy after CONNECT", 629 data->req.httpcode); 630 return CURLE_RECV_ERROR; 631 } 632 } 633 634 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE; 635 636 /* If a proxy-authorization header was used for the proxy, then we should 637 make sure that it isn't accidentally used for the document request 638 after we've connected. So let's free and clear it here. */ 639 Curl_safefree(conn->allocptr.proxyuserpwd); 640 conn->allocptr.proxyuserpwd = NULL; 641 642 data->state.authproxy.done = TRUE; 643 644 infof (data, "Proxy replied OK to CONNECT request\n"); 645 data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ 646 conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the 647 document request */ 648 return CURLE_OK; 649 } 650 #endif /* CURL_DISABLE_PROXY */ 651