diff options
Diffstat (limited to 'Utilities/cmcurl/lib/http_proxy.c')
-rw-r--r-- | Utilities/cmcurl/lib/http_proxy.c | 386 |
1 files changed, 312 insertions, 74 deletions
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index 4242251..a03a27f 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,6 +27,9 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) #include <curl/curl.h> +#ifdef USE_HYPER +#include <hyper.h> +#endif #include "sendf.h" #include "http.h" #include "url.h" @@ -47,15 +50,16 @@ * proxy_ssl_connected connection bit when complete. Can be * called multiple times. */ -static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex) +static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex) { #ifdef USE_SSL + struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS); if(!conn->bits.proxy_ssl_connected[sockindex]) { /* perform SSL initialization for this socket */ result = - Curl_ssl_connect_nonblocking(conn, sockindex, + Curl_ssl_connect_nonblocking(data, conn, sockindex, &conn->bits.proxy_ssl_connected[sockindex]); if(result) /* a failed connection is marked for closure to prevent (bad) re-use or @@ -64,17 +68,17 @@ static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex) } return result; #else - (void) conn; + (void) data; (void) sockindex; return CURLE_NOT_BUILT_IN; #endif } -CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex) +CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) { - struct Curl_easy *data = conn->data; + struct connectdata *conn = data->conn; if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { - const CURLcode result = https_proxy_connect(conn, sockindex); + const CURLcode result = https_proxy_connect(data, sockindex); if(result) return result; if(!conn->bits.proxy_ssl_connected[sockindex]) @@ -102,9 +106,9 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex) * This function might be called several times in the multi interface case * if the proxy's CONNECT response is not instant. */ - prot_save = conn->data->req.p.http; + prot_save = data->req.p.http; memset(&http_proxy, 0, sizeof(http_proxy)); - conn->data->req.p.http = &http_proxy; + data->req.p.http = &http_proxy; connkeep(conn, "HTTP proxy CONNECT"); /* for the secondary socket (FTP), use the "connect to host" @@ -124,8 +128,8 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex) remote_port = conn->conn_to_port; else remote_port = conn->remote_port; - result = Curl_proxyCONNECT(conn, sockindex, hostname, remote_port); - conn->data->req.p.http = prot_save; + result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); + data->req.p.http = prot_save; if(CURLE_OK != result) return result; Curl_safefree(data->state.aptr.proxyuserpwd); @@ -149,15 +153,16 @@ bool Curl_connect_ongoing(struct connectdata *conn) (conn->connect_state->tunnel_state != TUNNEL_COMPLETE); } -static CURLcode connect_init(struct connectdata *conn, bool reinit) +static CURLcode connect_init(struct Curl_easy *data, bool reinit) { struct http_connect_state *s; + struct connectdata *conn = data->conn; if(!reinit) { DEBUGASSERT(!conn->connect_state); s = calloc(1, sizeof(struct http_connect_state)); if(!s) return CURLE_OUT_OF_MEMORY; - infof(conn->data, "allocate connect buffer!\n"); + infof(data, "allocate connect buffer!\n"); conn->connect_state = s; Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); } @@ -173,23 +178,57 @@ static CURLcode connect_init(struct connectdata *conn, bool reinit) return CURLE_OK; } -static void connect_done(struct connectdata *conn) +static void connect_done(struct Curl_easy *data) { + struct connectdata *conn = data->conn; struct http_connect_state *s = conn->connect_state; s->tunnel_state = TUNNEL_COMPLETE; Curl_dyn_free(&s->rcvbuf); - infof(conn->data, "CONNECT phase completed!\n"); + infof(data, "CONNECT phase completed!\n"); +} + +static CURLcode CONNECT_host(struct Curl_easy *data, + struct connectdata *conn, + const char *hostname, + int remote_port, + char **connecthostp, + char **hostp) +{ + char *hostheader; /* for CONNECT */ + char *host = NULL; /* Host: */ + bool ipv6_ip = conn->bits.ipv6_ip; + + /* the hostname may be different */ + if(hostname != conn->host.name) + ipv6_ip = (strchr(hostname, ':') != NULL); + hostheader = /* host:port with IPv6 support */ + aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", + remote_port); + if(!hostheader) + return CURLE_OUT_OF_MEMORY; + + if(!Curl_checkProxyheaders(data, conn, "Host")) { + host = aprintf("Host: %s\r\n", hostheader); + if(!host) { + free(hostheader); + return CURLE_OUT_OF_MEMORY; + } + } + *connecthostp = hostheader; + *hostp = host; + return CURLE_OK; } -static CURLcode CONNECT(struct connectdata *conn, +static CURLcode CONNECT(struct Curl_easy *data, int sockindex, const char *hostname, int remote_port) +#ifndef USE_HYPER { int subversion = 0; - struct Curl_easy *data = conn->data; struct SingleRequest *k = &data->req; CURLcode result; + struct connectdata *conn = data->conn; curl_socket_t tunnelsocket = conn->sock[sockindex]; struct http_connect_state *s = conn->connect_state; char *linep; @@ -207,8 +246,9 @@ static CURLcode CONNECT(struct connectdata *conn, timediff_t check; if(TUNNEL_INIT == s->tunnel_state) { /* BEGIN CONNECT PHASE */ - char *host_port; struct dynbuf req; + char *hostheader = NULL; + char *host = NULL; infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port); @@ -219,50 +259,28 @@ static CURLcode CONNECT(struct connectdata *conn, free(data->req.newurl); data->req.newurl = NULL; - host_port = aprintf("%s:%d", hostname, remote_port); - if(!host_port) - return CURLE_OUT_OF_MEMORY; - /* initialize a dynamic send-buffer */ Curl_dyn_init(&req, DYN_HTTP_REQUEST); - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE); + result = CONNECT_host(data, conn, + hostname, remote_port, &hostheader, &host); + if(result) + return result; - free(host_port); + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, + hostheader, TRUE); if(!result) { - char *host = NULL; const char *proxyconn = ""; const char *useragent = ""; const char *httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; - bool ipv6_ip = conn->bits.ipv6_ip; - char *hostheader; - - /* the hostname may be different */ - if(hostname != conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); - hostheader = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", - remote_port); - if(!hostheader) { - Curl_dyn_free(&req); - return CURLE_OUT_OF_MEMORY; - } - if(!Curl_checkProxyheaders(conn, "Host")) { - host = aprintf("Host: %s\r\n", hostheader); - if(!host) { - free(hostheader); - Curl_dyn_free(&req); - return CURLE_OUT_OF_MEMORY; - } - } - if(!Curl_checkProxyheaders(conn, "Proxy-Connection")) + if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection")) proxyconn = "Proxy-Connection: Keep-Alive\r\n"; - if(!Curl_checkProxyheaders(conn, "User-Agent") && + if(!Curl_checkProxyheaders(data, conn, "User-Agent") && data->set.str[STRING_USERAGENT]) useragent = data->state.aptr.uagent; @@ -281,12 +299,8 @@ static CURLcode CONNECT(struct connectdata *conn, useragent, proxyconn); - if(host) - free(host); - free(hostheader); - if(!result) - result = Curl_add_custom_headers(conn, TRUE, &req); + result = Curl_add_custom_headers(data, TRUE, &req); if(!result) /* CRLF terminate the request */ @@ -295,13 +309,14 @@ static CURLcode CONNECT(struct connectdata *conn, if(!result) { /* Send the connect request to the proxy */ /* BLOCKING */ - result = Curl_buffer_send(&req, conn, &data->info.request_size, 0, + result = Curl_buffer_send(&req, data, &data->info.request_size, 0, sockindex); } if(result) failf(data, "Failed sending CONNECT to proxy"); } - + free(host); + free(hostheader); Curl_dyn_free(&req); if(result) return result; @@ -330,12 +345,12 @@ static CURLcode CONNECT(struct connectdata *conn, /* Read one byte at a time to avoid a race condition. Wait at most one second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(conn, tunnelsocket, &byte, 1, &gotbytes); + result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes); if(result == CURLE_AGAIN) /* socket buffer drained, return */ return CURLE_OK; - if(Curl_pgrsUpdate(conn)) + if(Curl_pgrsUpdate(data)) return CURLE_ABORTED_BY_CALLBACK; if(result) { @@ -379,7 +394,7 @@ static CURLcode CONNECT(struct connectdata *conn, /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(conn, &byte, 1, &tookcareof, &extra); + r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ infof(data, "chunk reading DONE\n"); @@ -418,7 +433,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(data->set.include_header) writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(conn, writetype, linep, perline); + result = Curl_client_write(data, writetype, linep, perline); if(result) return result; } @@ -460,7 +475,7 @@ static CURLcode CONNECT(struct connectdata *conn, /* now parse the chunked piece of data so that we can properly tell when the stream ends */ - r = Curl_httpchunk_read(conn, linep + 1, 1, &gotbytes, + r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, &extra); if(r == CHUNKE_STOP) { /* we're done reading chunks! */ @@ -479,9 +494,12 @@ static CURLcode CONNECT(struct connectdata *conn, } else s->keepon = KEEPON_DONE; - if(!s->cl) + + if(s->keepon == KEEPON_DONE && !s->cl) /* we did the full CONNECT treatment, go to COMPLETE */ s->tunnel_state = TUNNEL_COMPLETE; + + DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE); continue; } @@ -495,7 +513,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(!auth) return CURLE_OUT_OF_MEMORY; - result = Curl_http_input_auth(conn, proxy, auth); + result = Curl_http_input_auth(data, proxy, auth); free(auth); @@ -530,7 +548,7 @@ static CURLcode CONNECT(struct connectdata *conn, infof(data, "CONNECT responded chunked\n"); s->chunked_encoding = TRUE; /* init our chunky engine */ - Curl_httpchunk_init(conn); + Curl_httpchunk_init(data); } } else if(Curl_compareheader(linep, "Proxy-Connection:", "close")) @@ -545,7 +563,7 @@ static CURLcode CONNECT(struct connectdata *conn, Curl_dyn_reset(&s->rcvbuf); } /* while there's buffer left and loop is requested */ - if(Curl_pgrsUpdate(conn)) + if(Curl_pgrsUpdate(data)) return CURLE_ABORTED_BY_CALLBACK; if(error) @@ -554,7 +572,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(data->info.httpproxycode/100 != 2) { /* Deal with the possibly already received authenticate headers. 'newurl' is set to a new URL if we must loop. */ - result = Curl_http_auth_act(conn); + result = Curl_http_auth_act(data); if(result) return result; @@ -567,7 +585,7 @@ static CURLcode CONNECT(struct connectdata *conn, if(s->close_connection && data->req.newurl) { /* Connection closed by server. Don't use it anymore */ - Curl_closesocket(conn, conn->sock[sockindex]); + Curl_closesocket(data, conn, conn->sock[sockindex]); conn->sock[sockindex] = CURL_SOCKET_BAD; break; } @@ -577,7 +595,7 @@ static CURLcode CONNECT(struct connectdata *conn, * means the HTTP authentication is still going on so if the tunnel * is complete we start over in INIT state */ if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { - connect_init(conn, TRUE); /* reinit */ + connect_init(data, TRUE); /* reinit */ } } while(data->req.newurl); @@ -586,14 +604,14 @@ static CURLcode CONNECT(struct connectdata *conn, if(s->close_connection && data->req.newurl) { conn->bits.proxy_connect_closed = TRUE; infof(data, "Connect me again please\n"); - connect_done(conn); + connect_done(data); } else { free(data->req.newurl); data->req.newurl = NULL; /* failure, close this connection to avoid re-use */ streamclose(conn, "proxy CONNECT failure"); - Curl_closesocket(conn, conn->sock[sockindex]); + Curl_closesocket(data, conn, conn->sock[sockindex]); conn->sock[sockindex] = CURL_SOCKET_BAD; } @@ -628,6 +646,225 @@ static CURLcode CONNECT(struct connectdata *conn, Curl_dyn_free(&s->rcvbuf); return CURLE_OK; } +#else +/* The Hyper version of CONNECT */ +{ + struct connectdata *conn = data->conn; + struct hyptransfer *h = &data->hyp; + curl_socket_t tunnelsocket = conn->sock[sockindex]; + struct http_connect_state *s = conn->connect_state; + CURLcode result = CURLE_OUT_OF_MEMORY; + hyper_io *io = NULL; + hyper_request *req = NULL; + hyper_headers *headers = NULL; + hyper_clientconn_options *options = NULL; + hyper_task *handshake = NULL; + hyper_task *task = NULL; /* for the handshake */ + hyper_task *sendtask = NULL; /* for the send */ + hyper_clientconn *client = NULL; + hyper_error *hypererr = NULL; + char *hostheader = NULL; /* for CONNECT */ + char *host = NULL; /* Host: */ + + if(Curl_connect_complete(conn)) + return CURLE_OK; /* CONNECT is already completed */ + + conn->bits.proxy_connect_closed = FALSE; + + do { + switch(s->tunnel_state) { + case TUNNEL_INIT: + /* BEGIN CONNECT PHASE */ + io = hyper_io_new(); + if(!io) { + failf(data, "Couldn't create hyper IO"); + goto error; + } + /* tell Hyper how to read/write network data */ + hyper_io_set_userdata(io, data); + hyper_io_set_read(io, Curl_hyper_recv); + hyper_io_set_write(io, Curl_hyper_send); + conn->sockfd = tunnelsocket; + + /* create an executor to poll futures */ + if(!h->exec) { + h->exec = hyper_executor_new(); + if(!h->exec) { + failf(data, "Couldn't create hyper executor"); + goto error; + } + } + + options = hyper_clientconn_options_new(); + if(!options) { + failf(data, "Couldn't create hyper client options"); + goto error; + } + + hyper_clientconn_options_exec(options, h->exec); + + /* "Both the `io` and the `options` are consumed in this function + call" */ + handshake = hyper_clientconn_handshake(io, options); + if(!handshake) { + failf(data, "Couldn't create hyper client handshake"); + goto error; + } + io = NULL; + options = NULL; + + if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { + failf(data, "Couldn't hyper_executor_push the handshake"); + goto error; + } + handshake = NULL; /* ownership passed on */ + + task = hyper_executor_poll(h->exec); + if(!task) { + failf(data, "Couldn't hyper_executor_poll the handshake"); + goto error; + } + + client = hyper_task_value(task); + hyper_task_free(task); + req = hyper_request_new(); + if(!req) { + failf(data, "Couldn't hyper_request_new"); + goto error; + } + if(hyper_request_set_method(req, (uint8_t *)"CONNECT", + strlen("CONNECT"))) { + failf(data, "error setting method"); + goto error; + } + + result = CONNECT_host(data, conn, hostname, remote_port, + &hostheader, &host); + if(result) + goto error; + + if(hyper_request_set_uri(req, (uint8_t *)hostheader, + strlen(hostheader))) { + failf(data, "error setting path"); + result = CURLE_OUT_OF_MEMORY; + } + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, + hostheader, TRUE); + if(result) + goto error; + Curl_safefree(hostheader); + + /* default is 1.1 */ + if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && + (HYPERE_OK != hyper_request_set_version(req, + HYPER_HTTP_VERSION_1_0))) { + failf(data, "error settting HTTP version"); + goto error; + } + + headers = hyper_request_headers(req); + if(!headers) { + failf(data, "hyper_request_headers"); + goto error; + } + if(host && Curl_hyper_header(data, headers, host)) + goto error; + Curl_safefree(host); + + if(data->state.aptr.proxyuserpwd && + Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd)) + goto error; + + if(data->set.str[STRING_USERAGENT] && + *data->set.str[STRING_USERAGENT] && + data->state.aptr.uagent && + Curl_hyper_header(data, headers, data->state.aptr.uagent)) + goto error; + + if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") && + Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive")) + goto error; + + sendtask = hyper_clientconn_send(client, req); + if(!sendtask) { + failf(data, "hyper_clientconn_send"); + goto error; + } + + if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { + failf(data, "Couldn't hyper_executor_push the send"); + goto error; + } + + hyper_clientconn_free(client); + + do { + task = hyper_executor_poll(h->exec); + if(task) { + bool error = hyper_task_type(task) == HYPER_TASK_ERROR; + if(error) + hypererr = hyper_task_value(task); + hyper_task_free(task); + if(error) + goto error; + } + } while(task); + s->tunnel_state = TUNNEL_CONNECT; + /* FALLTHROUGH */ + case TUNNEL_CONNECT: { + int didwhat; + bool done = FALSE; + result = Curl_hyper_stream(data, conn, &didwhat, &done, + CURL_CSELECT_IN | CURL_CSELECT_OUT); + if(result) + goto error; + if(!done) + break; + fprintf(stderr, "done\n"); + s->tunnel_state = TUNNEL_COMPLETE; + if(h->exec) { + hyper_executor_free(h->exec); + h->exec = NULL; + } + if(h->read_waker) { + hyper_waker_free(h->read_waker); + h->read_waker = NULL; + } + if(h->write_waker) { + hyper_waker_free(h->write_waker); + h->write_waker = NULL; + } + } + /* FALLTHROUGH */ + default: + break; + } + } while(data->req.newurl); + + result = CURLE_OK; + error: + free(host); + free(hostheader); + if(io) + hyper_io_free(io); + + if(options) + hyper_clientconn_options_free(options); + + if(handshake) + hyper_task_free(handshake); + + if(hypererr) { + uint8_t errbuf[256]; + size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); + failf(data, "Hyper: %.*s", (int)errlen, errbuf); + hyper_error_free(hypererr); + } + return result; +} + +#endif void Curl_connect_free(struct Curl_easy *data) { @@ -645,21 +882,22 @@ void Curl_connect_free(struct Curl_easy *data) * this proxy. After that, the socket can be used just as a normal socket. */ -CURLcode Curl_proxyCONNECT(struct connectdata *conn, +CURLcode Curl_proxyCONNECT(struct Curl_easy *data, int sockindex, const char *hostname, int remote_port) { CURLcode result; + struct connectdata *conn = data->conn; if(!conn->connect_state) { - result = connect_init(conn, FALSE); + result = connect_init(data, FALSE); if(result) return result; } - result = CONNECT(conn, sockindex, hostname, remote_port); + result = CONNECT(data, sockindex, hostname, remote_port); if(result || Curl_connect_complete(conn)) - connect_done(conn); + connect_done(data); return result; } |