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