diff options
author | Brad King <brad.king@kitware.com> | 2022-10-31 20:11:41 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2022-10-31 20:11:41 (GMT) |
commit | 9ffe6b0969fc270cc2a1aac2b2c1bf986af291d5 (patch) | |
tree | 75711965f7fd24679383853a840f42efff676e31 /Utilities/cmcurl/lib/http.c | |
parent | fa9bbb8627e8af5153367721eb037b6e094670d1 (diff) | |
parent | ec122fff08ab9a8e56fb90126ecedb99c759011b (diff) | |
download | CMake-9ffe6b0969fc270cc2a1aac2b2c1bf986af291d5.zip CMake-9ffe6b0969fc270cc2a1aac2b2c1bf986af291d5.tar.gz CMake-9ffe6b0969fc270cc2a1aac2b2c1bf986af291d5.tar.bz2 |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2022-10-26 (cd95ee9f)
Diffstat (limited to 'Utilities/cmcurl/lib/http.c')
-rw-r--r-- | Utilities/cmcurl/lib/http.c | 201 |
1 files changed, 155 insertions, 46 deletions
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index b215307..f57859e 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -18,6 +18,8 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * + * SPDX-License-Identifier: curl + * ***************************************************************************/ #include "curl_setup.h" @@ -82,6 +84,7 @@ #include "strdup.h" #include "altsvc.h" #include "hsts.h" +#include "ws.h" #include "c-hyper.h" /* The last 3 #include files should be in this order */ @@ -112,6 +115,10 @@ static int https_getsock(struct Curl_easy *data, #endif static CURLcode http_setup_conn(struct Curl_easy *data, struct connectdata *conn); +#ifdef USE_WEBSOCKETS +static CURLcode ws_setup_conn(struct Curl_easy *data, + struct connectdata *conn); +#endif /* * HTTP handler interface. @@ -140,6 +147,32 @@ const struct Curl_handler Curl_handler_http = { PROTOPT_USERPWDCTRL }; +#ifdef USE_WEBSOCKETS +const struct Curl_handler Curl_handler_ws = { + "WS", /* scheme */ + ws_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + ZERO_NULL, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTP, /* defport */ + CURLPROTO_WS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL +}; +#endif + #ifdef USE_SSL /* * HTTPS handler interface. @@ -164,11 +197,38 @@ const struct Curl_handler Curl_handler_https = { PORT_HTTPS, /* defport */ CURLPROTO_HTTPS, /* protocol */ CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN | /* flags */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ + PROTOPT_USERPWDCTRL +}; + +#ifdef USE_WEBSOCKETS +const struct Curl_handler Curl_handler_wss = { + "WSS", /* scheme */ + ws_setup_conn, /* setup_connection */ + Curl_http, /* do_it */ + Curl_http_done, /* done */ + ZERO_NULL, /* do_more */ + Curl_http_connect, /* connect_it */ + https_connecting, /* connecting */ + ZERO_NULL, /* doing */ + https_getsock, /* proto_getsock */ + http_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + ZERO_NULL, /* perform_getsock */ + ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + ZERO_NULL, /* attach connection */ + PORT_HTTPS, /* defport */ + CURLPROTO_WSS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ PROTOPT_USERPWDCTRL }; #endif +#endif + static CURLcode http_setup_conn(struct Curl_easy *data, struct connectdata *conn) { @@ -203,6 +263,16 @@ static CURLcode http_setup_conn(struct Curl_easy *data, return CURLE_OK; } +#ifdef USE_WEBSOCKETS +static CURLcode ws_setup_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + /* websockets is 1.1 only (for now) */ + data->state.httpwant = CURL_HTTP_VERSION_1_1; + return http_setup_conn(data, conn); +} +#endif + #ifndef CURL_DISABLE_PROXY /* * checkProxyHeaders() checks the linked list of custom proxy headers @@ -651,21 +721,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) return result; } -/* - * Curl_allow_auth_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. - */ -bool Curl_allow_auth_to_host(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - return (!data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_host && - strcasecompare(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->handler->protocol))); -} - #ifndef CURL_DISABLE_HTTP_AUTH /* * Output the correct authentication header depending on the auth type @@ -864,7 +919,7 @@ Curl_http_output_auth(struct Curl_easy *data, /* To prevent the user+password to get sent to other than the original host due to a location-follow */ - if(Curl_allow_auth_to_host(data) + if(Curl_auth_allowed_to_host(data) #ifndef CURL_DISABLE_NETRC || conn->bits.netrc #endif @@ -1516,7 +1571,7 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) } #endif - if(conn->given->protocol & CURLPROTO_HTTPS) { + if(conn->given->flags & PROTOPT_SSL) { /* perform SSL initialization */ result = https_connecting(data, done); if(result) @@ -1641,6 +1696,7 @@ CURLcode Curl_http_done(struct Curl_easy *data, Curl_mime_cleanpart(&http->form); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); + Curl_ws_done(data); if(status) return status; @@ -1917,7 +1973,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, checkprefix("Cookie:", compare)) && /* be careful of sending this potentially sensitive header to other hosts */ - !Curl_allow_auth_to_host(data)) + !Curl_auth_allowed_to_host(data)) ; else { #ifdef USE_HYPER @@ -2030,7 +2086,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, const char **method, Curl_HttpReq *reqp) { - Curl_HttpReq httpreq = data->state.httpreq; + Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && data->set.upload) @@ -2149,9 +2205,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) [brackets] if the host name is a plain IPv6-address. RFC2732-style. */ const char *host = conn->host.name; - if(((conn->given->protocol&CURLPROTO_HTTPS) && + if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) && (conn->remote_port == PORT_HTTPS)) || - ((conn->given->protocol&CURLPROTO_HTTP) && + ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) && (conn->remote_port == PORT_HTTP)) ) /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include the port number in the host string */ @@ -2700,6 +2756,13 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP request"); +#ifdef USE_WEBSOCKETS + else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) && + !(data->set.connect_only)) + /* Set up the transfer for two-way since without CONNECT_ONLY set, this + request probably wants to send data too post upgrade */ + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); +#endif else /* HTTP GET/HEAD download: */ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); @@ -2709,12 +2772,14 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } #if !defined(CURL_DISABLE_COOKIES) + CURLcode Curl_http_cookies(struct Curl_easy *data, struct connectdata *conn, struct dynbuf *r) { CURLcode result = CURLE_OK; char *addcookies = NULL; + bool linecap = FALSE; if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, STRCONST("Cookie"))) addcookies = data->set.str[STRING_COOKIE]; @@ -2727,12 +2792,12 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, const char *host = data->state.aptr.cookiehost ? data->state.aptr.cookiehost : conn->host.name; const bool secure_context = - conn->handler->protocol&CURLPROTO_HTTPS || + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "[::1]") ? TRUE : FALSE; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - co = Curl_cookie_getlist(data->cookies, host, data->state.up.path, + co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, secure_context); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } @@ -2746,6 +2811,13 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, if(result) break; } + if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >= + MAX_COOKIE_HEADER_LEN) { + infof(data, "Restricted outgoing cookies due to header size, " + "'%s' not sent", co->name); + linecap = TRUE; + break; + } result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"", co->name, co->value); if(result) @@ -2756,7 +2828,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, } Curl_cookie_freelist(store); } - if(addcookies && !result) { + if(addcookies && !result && !linecap) { if(!count) result = Curl_dyn_addn(r, STRCONST("Cookie: ")); if(!result) { @@ -3033,7 +3105,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(conn->transport != TRNSPRT_QUIC) { if(conn->httpversion < 20) { /* unless the connection is re-used and already http2 */ - switch(conn->negnpn) { + switch(conn->alpn) { case CURL_HTTP_VERSION_2: conn->httpversion = 20; /* we know we're on HTTP/2 now */ @@ -3245,6 +3317,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } result = Curl_http_cookies(data, conn, &req); + if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + result = Curl_ws_request(data, &req); if(!result) result = Curl_add_timecondition(data, &req); if(!result) @@ -3509,15 +3583,15 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, else if(checkprefix("Retry-After:", headp)) { /* Retry-After = HTTP-date / delay-seconds */ curl_off_t retry_after = 0; /* zero for unknown or "now" */ - time_t date = Curl_getdate_capped(headp + strlen("Retry-After:")); - if(-1 == date) { - /* not a date, try it as a decimal number */ - (void)curlx_strtoofft(headp + strlen("Retry-After:"), - NULL, 10, &retry_after); + /* Try it as a decimal number, if it works it is not a date */ + (void)curlx_strtoofft(headp + strlen("Retry-After:"), + NULL, 10, &retry_after); + if(!retry_after) { + time_t date = Curl_getdate_capped(headp + strlen("Retry-After:")); + if(-1 != date) + /* convert date to number of seconds into the future */ + retry_after = date - time(NULL); } - else - /* convert date to number of seconds into the future */ - retry_after = date - time(NULL); data->info.retry_after = retry_after; /* store it */ } else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) { @@ -3529,7 +3603,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, The second format was added since Sun's webserver JavaWebServer/1.1.1 obviously sends the header this way! The third added since some servers use that! - The forth means the requested range was unsatisfied. + The fourth means the requested range was unsatisfied. */ char *ptr = headp + strlen("Content-Range:"); @@ -3557,7 +3631,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, const char *host = data->state.aptr.cookiehost? data->state.aptr.cookiehost:conn->host.name; const bool secure_context = - conn->handler->protocol&CURLPROTO_HTTPS || + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "[::1]") ? TRUE : FALSE; @@ -3641,7 +3715,14 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) && - (conn->handler->flags & PROTOPT_SSL)) { + ((conn->handler->flags & PROTOPT_SSL) || +#ifdef CURLDEBUG + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_HSTS_HTTP") +#else + 0 +#endif + )) { CURLcode check = Curl_hsts_parse(data->hsts, data->state.up.hostname, headp + strlen("Strict-Transport-Security:")); @@ -3723,7 +3804,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, connclose(conn, "HTTP/1.0 close after body"); } else if(conn->httpversion == 20 || - (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) { + (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) { DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); /* HTTP/2 cannot avoid multiplexing since it is a core functionality of the protocol */ @@ -3799,11 +3880,16 @@ static CURLcode verify_header(struct Curl_easy *data) if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; - ptr = memchr(header, ':', hlen); - if(!ptr) { - /* this is bad, bail out */ - failf(data, "Header without colon"); - return CURLE_WEIRD_SERVER_REPLY; + if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2) + /* line folding, can't happen on line 2 */ + ; + else { + ptr = memchr(header, ':', hlen); + if(!ptr) { + /* this is bad, bail out */ + failf(data, "Header without colon"); + return CURLE_WEIRD_SERVER_REPLY; + } } return CURLE_OK; } @@ -3944,9 +4030,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, break; case 101: /* Switching Protocols */ - if(k->upgr101 == UPGR101_REQUESTED) { + if(k->upgr101 == UPGR101_H2) { /* Switching to HTTP/2 */ - infof(data, "Received 101"); + infof(data, "Received 101, Switching to HTTP/2"); k->upgr101 = UPGR101_RECEIVED; /* we'll get more headers (HTTP/2 response) */ @@ -3960,8 +4046,21 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, return result; *nread = 0; } +#ifdef USE_WEBSOCKETS + else if(k->upgr101 == UPGR101_WS) { + /* verify the response */ + result = Curl_ws_accept(data); + if(result) + return result; + k->header = FALSE; /* no more header to parse! */ + if(data->set.connect_only) { + k->keepon &= ~KEEP_RECV; /* read no more content */ + *nread = 0; + } + } +#endif else { - /* Switching to another protocol (e.g. WebSocket) */ + /* Not switching to another protocol */ k->header = FALSE; /* no more header to parse! */ } break; @@ -4054,6 +4153,16 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, return CURLE_HTTP_RETURNED_ERROR; } +#ifdef USE_WEBSOCKETS + /* All non-101 HTTP status codes are bad when wanting to upgrade to + websockets */ + if(data->req.upgr101 == UPGR101_WS) { + failf(data, "Refused WebSockets upgrade: %d", k->httpcode); + return CURLE_HTTP_RETURNED_ERROR; + } +#endif + + data->req.deductheadercount = (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0; |