diff --git a/httplib.h b/httplib.h index 88b306fb..3cefcfde 100644 --- a/httplib.h +++ b/httplib.h @@ -21467,6 +21467,9 @@ inline TlsError connect(session_t session) { } inline TlsError accept(session_t session) { + // A failed handshake leaves its SNI behind, so start without it + impl::mbedpending_sni().clear(); + // Same as connect for Mbed TLS - handshake works for both client and server auto result = connect(session); @@ -21474,7 +21477,6 @@ inline TlsError accept(session_t session) { if (result.code == ErrorCode::Success && session) { auto msession = static_cast(session); msession->sni_hostname = std::move(impl::mbedpending_sni()); - impl::mbedpending_sni().clear(); } return result; @@ -21519,6 +21521,9 @@ inline bool connect_nonblocking(session_t session, socket_t sock, inline bool accept_nonblocking(session_t session, socket_t sock, time_t timeout_sec, time_t timeout_usec, TlsError *err) { + // A failed handshake leaves its SNI behind, so start without it + impl::mbedpending_sni().clear(); + // Same implementation as connect for Mbed TLS bool result = connect_nonblocking(session, sock, timeout_sec, timeout_usec, err); @@ -21527,7 +21532,6 @@ inline bool accept_nonblocking(session_t session, socket_t sock, if (result && session) { auto msession = static_cast(session); msession->sni_hostname = std::move(impl::mbedpending_sni()); - impl::mbedpending_sni().clear(); } return result; diff --git a/test/test.cc b/test/test.cc index aa73f965..d32001a8 100644 --- a/test/test.cc +++ b/test/test.cc @@ -12977,6 +12977,44 @@ TEST(SNI_AutoDetectionTest, SNI_Logic) { } } } + +// The SNI of a failed handshake does not reach the next connection that the +// same worker thread serves +TEST(SNI_AutoDetectionTest, FailedHandshakeLeavesNoSNI) { + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); + ASSERT_TRUE(svr.is_valid()); + svr.new_task_queue = [] { return new ThreadPool(1); }; + svr.Get("/sni", [](const Request &req, Response &res) { + res.set_content(req.sni(), "text/plain"); + }); + + auto port = svr.bind_to_any_port("127.0.0.1"); + ASSERT_LT(0, port); + thread t = thread([&] { svr.listen_after_bind(); }); + auto se = detail::scope_exit([&] { + svr.stop(); + t.join(); + ASSERT_FALSE(svr.is_running()); + }); + svr.wait_until_ready(); + + { + // Sends SNI, then rejects the server certificate, which the CA did not sign + SSLClient cli("localhost", port); + cli.set_hostname_addr_map({{"localhost", "127.0.0.1"}}); + cli.set_ca_cert_path(CLIENT_CA_CERT_FILE); + auto res = cli.Get("/sni"); + ASSERT_FALSE(res); + EXPECT_EQ(Error::SSLServerVerification, res.error()); + } + + // An IP host sends no SNI (RFC 6066) + SSLClient cli("127.0.0.1", port); + cli.enable_server_certificate_verification(false); + auto res = cli.Get("/sni"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ("", res->body); +} #endif TEST(ClientProblemDetectionTest, ContentProvider) {