From 88dd1a8a115b1f5ece26fd8941b4464973b7d913 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 11 Feb 2021 16:30:32 +0100 Subject: [PATCH] urldata: don't touch data->set.httpversion at run-time Rename it to 'httpwant' and make a cloned field in the state struct as well for run-time updates. Also: refuse non-supported HTTP versions. Verified with test 129. Closes #6585 --- lib/c-hyper.c | 2 +- lib/http.c | 37 ++++++++++++++++++-------- lib/multi.c | 2 +- lib/setopt.c | 2 +- lib/transfer.c | 4 +-- lib/url.c | 4 +-- lib/urldata.h | 13 +++++----- lib/vtls/bearssl.c | 2 +- lib/vtls/gtls.c | 2 +- lib/vtls/mbedtls.c | 2 +- lib/vtls/nss.c | 2 +- lib/vtls/openssl.c | 4 +-- lib/vtls/schannel.c | 2 +- lib/vtls/sectransp.c | 2 +- lib/vtls/wolfssl.c | 4 +-- tests/data/Makefile.inc | 2 +- tests/data/test129 | 57 +++++++++++++++++++++++++++++++++++++++++ 17 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 tests/data/test129 diff --git a/lib/c-hyper.c b/lib/c-hyper.c index 10bd7ef9b..8b4abd37f 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -741,7 +741,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } - if(data->set.httpversion == CURL_HTTP_VERSION_1_0) { + if(data->state.httpwant == CURL_HTTP_VERSION_1_0) { if(HYPERE_OK != hyper_request_set_version(req, HYPER_HTTP_VERSION_1_0)) { failf(data, "error setting HTTP version"); diff --git a/lib/http.c b/lib/http.c index 1141102f6..d6eceb7d5 100644 --- a/lib/http.c +++ b/lib/http.c @@ -183,7 +183,7 @@ static CURLcode http_setup_conn(struct Curl_easy *data, Curl_mime_initpart(&http->form, data); data->req.p.http = http; - if(data->set.httpversion == CURL_HTTP_VERSION_3) { + if(data->state.httpwant == CURL_HTTP_VERSION_3) { if(conn->handler->flags & PROTOPT_SSL) /* Only go HTTP/3 directly on HTTPS URLs. It needs a UDP socket and does the QUIC dance. */ @@ -595,7 +595,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) conn->httpversion > 11) { infof(data, "Forcing HTTP/1.1 for NTLM"); connclose(conn, "Force HTTP/1.1 connection"); - data->set.httpversion = CURL_HTTP_VERSION_1_1; + data->state.httpwant = CURL_HTTP_VERSION_1_1; } } #ifndef CURL_DISABLE_PROXY @@ -1625,11 +1625,11 @@ static bool use_http_1_1plus(const struct Curl_easy *data, { if((data->state.httpversion == 10) || (conn->httpversion == 10)) return FALSE; - if((data->set.httpversion == CURL_HTTP_VERSION_1_0) && + if((data->state.httpwant == CURL_HTTP_VERSION_1_0) && (conn->httpversion <= 10)) return FALSE; - return ((data->set.httpversion == CURL_HTTP_VERSION_NONE) || - (data->set.httpversion >= CURL_HTTP_VERSION_1_1)); + return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) || + (data->state.httpwant >= CURL_HTTP_VERSION_1_1)); } #ifndef USE_HYPER @@ -1637,7 +1637,7 @@ static const char *get_http_string(const struct Curl_easy *data, const struct connectdata *conn) { #ifdef ENABLE_QUIC - if((data->set.httpversion == CURL_HTTP_VERSION_3) || + if((data->state.httpwant == CURL_HTTP_VERSION_3) || (conn->httpversion == 30)) return "3"; #endif @@ -2950,7 +2950,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) default: /* Check if user wants to use HTTP/2 with clear TCP*/ #ifdef USE_NGHTTP2 - if(data->set.httpversion == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { + if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { /* We don't support HTTP/2 proxies yet. Also it's debatable @@ -3156,7 +3156,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!(conn->handler->flags&PROTOPT_SSL) && conn->httpversion != 20 && - (data->set.httpversion == CURL_HTTP_VERSION_2)) { + (data->state.httpwant == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done over SSL */ result = Curl_http2_request_upgrade(&req, conn); @@ -4148,10 +4148,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, */ char separator; char twoorthree[2]; + int httpversion = 0; nc = sscanf(HEADER1, " HTTP/%1d.%1d%c%3d", &httpversion_major, - &conn->httpversion, + &httpversion, &separator, &k->httpcode); @@ -4163,7 +4164,23 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } if((nc == 4) && (' ' == separator)) { - conn->httpversion += 10 * httpversion_major; + httpversion += 10 * httpversion_major; + switch(httpversion) { + case 10: + case 11: +#if defined(USE_NGHTTP2) || defined(USE_HYPER) + case 20: +#endif +#if defined(ENABLE_QUIC) + case 30: +#endif + conn->httpversion = (unsigned char)httpversion; + break; + default: + failf(data, "Unsupported HTTP version (%u.%d) in response", + httpversion/10, httpversion%10); + return CURLE_UNSUPPORTED_PROTOCOL; + } if(k->upgr101 == UPGR101_RECEIVED) { /* supposedly upgraded to http2 now */ diff --git a/lib/multi.c b/lib/multi.c index e0862551d..bae8398bc 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -2173,7 +2173,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!ret) { infof(data, "Downgrades to HTTP/1.1!\n"); - data->set.httpversion = CURL_HTTP_VERSION_1_1; + data->state.httpwant = CURL_HTTP_VERSION_1_1; /* clear the error message bit too as we ignore the one we got */ data->state.errorbuf = FALSE; if(!newurl) diff --git a/lib/setopt.c b/lib/setopt.c index 715b2f284..a12873760 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -888,7 +888,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(arg == CURL_HTTP_VERSION_NONE) arg = CURL_HTTP_VERSION_2TLS; #endif - data->set.httpversion = arg; + data->set.httpwant = (unsigned char)arg; break; case CURLOPT_EXPECT_100_TIMEOUT_MS: diff --git a/lib/transfer.c b/lib/transfer.c index c3b2d11a2..0b27b3455 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1431,8 +1431,8 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ data->state.errorbuf = FALSE; /* no error has occurred */ - data->state.httpversion = 0; /* don't assume any particular server version */ - + data->state.httpwant = data->set.httpwant; + data->state.httpversion = 0; data->state.authproblem = FALSE; data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; diff --git a/lib/url.c b/lib/url.c index f160b1596..d21e4ad4f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -609,7 +609,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ set->maxage_conn = 118; set->http09_allowed = FALSE; - set->httpversion = + set->httpwant = #ifdef USE_NGHTTP2 CURL_HTTP_VERSION_2TLS #else @@ -887,7 +887,7 @@ static int IsMultiplexingPossible(const struct Curl_easy *handle, (!conn->bits.protoconnstart || !conn->bits.close)) { if(Curl_multiplex_wanted(handle->multi) && - (handle->set.httpversion >= CURL_HTTP_VERSION_2)) + (handle->state.httpwant >= CURL_HTTP_VERSION_2)) /* allows HTTP/2 */ avail |= CURLPIPE_MULTIPLEX; } diff --git a/lib/urldata.h b/lib/urldata.h index 6a9c52c44..b78bf905c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -991,7 +991,7 @@ struct connectdata { char *passwd; /* password string, allocated */ char *options; /* options string, allocated */ char *sasl_authzid; /* authorisation identity string, allocated */ - int httpversion; /* the HTTP version*10 reported by the server */ + unsigned char httpversion; /* the HTTP version*10 reported by the server */ struct curltime now; /* "current" time */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection cache */ @@ -1375,9 +1375,10 @@ struct UrlState { /* a place to store the most recently set FTP entrypath */ char *most_recent_ftp_entrypath; - - int httpversion; /* the lowest HTTP version*10 reported by any server - involved in this request */ + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ + unsigned char httpversion; /* the lowest HTTP version*10 reported by any + server involved in this request */ #if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) /* do FTP line-end conversions on most platforms */ @@ -1718,8 +1719,8 @@ struct UserDefined { curl_proxytype proxytype; /* what kind of proxy that is in use */ time_t timevalue; /* what time to compare with */ Curl_HttpReq method; /* what kind of HTTP request (if any) is this */ - long httpversion; /* when non-zero, a specific HTTP version requested to - be used in the library's request(s) */ + unsigned char httpwant; /* when non-zero, a specific HTTP version requested + to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ #ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index 8efb7b96f..36c32d8d5 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -390,7 +390,7 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, */ #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 + if(data->state.httpversion >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index be2a43077..333ff8b2f 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -618,7 +618,7 @@ gtls_connect_step1(struct Curl_easy *data, gnutls_datum_t protocols[2]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 + if(data->state.httpversion >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 1739b6eb1..99affe092 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -495,7 +495,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(conn->bits.tls_enable_alpn) { const char **p = &backend->protocols[0]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2) + if(data->state.httpversion >= CURL_HTTP_VERSION_2) *p++ = NGHTTP2_PROTO_VERSION_ID; #endif *p++ = ALPN_HTTP_1_1; diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 1314e2c06..bc6c3caeb 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -2080,7 +2080,7 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, unsigned char protocols[128]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 + if(data->state.httpversion >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index c4621d8d3..ed374dc32 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2222,7 +2222,7 @@ select_next_proto_cb(SSL *ssl, (void)ssl; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 && + if(data->state.httpwant >= CURL_HTTP_VERSION_2 && !select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN)) { infof(data, "NPN, negotiated HTTP2 (%s)\n", @@ -2749,7 +2749,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, unsigned char protocols[128]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index bdc0e133b..d7abd5966 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -861,7 +861,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, list_start_index = cur; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2) { + if(data->state.httpversion >= CURL_HTTP_VERSION_2) { memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN); cur += NGHTTP2_PROTO_ALPN_LEN; infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index f117e7f64..def325c24 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -1611,7 +1611,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, &kCFTypeArrayCallBacks); #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 + if(data->state.httpversion >= CURL_HTTP_VERSION_2 #ifndef CURL_DISABLE_PROXY && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) #endif diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 169614c29..dc79d8b14 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -475,7 +475,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, protocols in descending order of preference, eg: "h2,http/1.1" */ #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2) { + if(data->state.httpversion >= CURL_HTTP_VERSION_2) { strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ","); infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } @@ -724,7 +724,7 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) conn->negnpn = CURL_HTTP_VERSION_1_1; #ifdef USE_NGHTTP2 - else if(data->set.httpversion >= CURL_HTTP_VERSION_2 && + else if(data->state.httpversion >= CURL_HTTP_VERSION_2 && protocol_len == NGHTTP2_PROTO_VERSION_ID_LEN && !memcmp(protocol, NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN)) diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index e162abba9..0b0e90d3e 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -34,7 +34,7 @@ test90 test91 test92 test93 test94 test95 test96 test97 test98 test99 \ test100 test101 test102 test103 test104 test105 test106 test107 test108 \ test109 test110 test111 test112 test113 test114 test115 test116 test117 \ test118 test119 test120 test121 test122 test123 test124 test125 test126 \ -test127 test128 test130 test131 test132 test133 test134 test135 \ +test127 test128 test129 test130 test131 test132 test133 test134 test135 \ test136 test137 test138 test139 test140 test141 test142 test143 test144 \ test145 test146 test147 test148 test149 test150 test151 test152 test153 \ test154 test155 test156 test157 test158 test159 test160 test161 test162 \ diff --git a/tests/data/test129 b/tests/data/test129 new file mode 100644 index 000000000..6fe4ad9c5 --- /dev/null +++ b/tests/data/test129 @@ -0,0 +1,57 @@ + + + +HTTP +HTTP GET + + + +# +# Server-side + + +HTTP/1.2 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# +# Client-side + + +http + + +HTTP/1.2 is rejected + + +http://%HOSTIP:%HTTPPORT/129 + + + +# +# Verify data after the test has been "shot" + + +GET /129 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + +# CURLE_UNSUPPORTED_PROTOCOL + +1 + + +