diff --git a/src/core/library.c b/src/core/library.c index 8b3b2f96eb..32c3c8c841 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -1667,8 +1667,10 @@ QuicLibraryGetParam( case QUIC_PARAM_PREFIX_TLS: case QUIC_PARAM_PREFIX_TLS_SCHANNEL: - if (Connection == NULL || Connection->Crypto.TLS == NULL) { + if (Connection == NULL) { Status = QUIC_STATUS_INVALID_PARAMETER; + } else if (Connection->Crypto.TLS == NULL) { + Status = QUIC_STATUS_INVALID_STATE; } else { Status = CxPlatTlsParamGet(Connection->Crypto.TLS, Param, BufferLength, Buffer); } diff --git a/src/test/lib/ApiTest.cpp b/src/test/lib/ApiTest.cpp index af6ee42a00..b5cbed444a 100644 --- a/src/test/lib/ApiTest.cpp +++ b/src/test/lib/ApiTest.cpp @@ -4484,6 +4484,39 @@ void QuicTestConnectionParam() QuicTest_QUIC_PARAM_CONN_ORIG_DEST_CID(Registration, ClientConfiguration); } +struct TestTlsParamServerContext { + MsQuicConnection** Server; + CXPLAT_EVENT Event; + QUIC_STATUS GetParamStatus; +}; + +bool +TestTlsParamListenerCallback( + _In_ TestListener* Listener, + _In_ HQUIC ConnectionHandle) +{ + TestTlsParamServerContext* Context = (TestTlsParamServerContext*)Listener->Context; + *Context->Server = new(std::nothrow) MsQuicConnection( + ConnectionHandle, + CleanUpManual, + [](MsQuicConnection* Connection, void* Context, QUIC_CONNECTION_EVENT* Event) { + if (Event->Type == QUIC_CONNECTION_EVENT_CONNECTED) { + QUIC_HANDSHAKE_INFO Info = {}; + uint32_t Length = sizeof(Info); + ((TestTlsParamServerContext*)Context)->GetParamStatus = + MsQuic->GetParam( + *Connection, + QUIC_PARAM_TLS_HANDSHAKE_INFO, + &Length, + &Info); + CxPlatEventSet(((TestTlsParamServerContext*)Context)->Event); + } + return QUIC_STATUS_SUCCESS; + }, + Context); + return true; +} + // // This test uses TEST_NOT_EQUAL(XXX, QUIC_STATUS_SUCCESS) to cover both // OpenSSL and Schannel which return different error code. @@ -4557,6 +4590,136 @@ void QuicTestTlsParam() ), QUIC_STATUS_SUCCESS); } + // + // After handshake, no resumption + // + { + TestScopeLogger LogScope2("After handshake - no resumption"); + MsQuicConfiguration ServerConfiguration(Registration, Alpn, ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + TestListener Listener( + Registration, + TestTlsParamListenerCallback, + ServerConfiguration); + TEST_QUIC_SUCCEEDED(Listener.IsValid()); + UniquePtr Server; + TestTlsParamServerContext ServerContext = { (MsQuicConnection**)&Server, {}, QUIC_STATUS_SUCCESS }; + CxPlatEventInitialize(&ServerContext.Event, FALSE, FALSE); + Listener.Context = &ServerContext; + + QuicAddr ServerLocalAddr(QUIC_ADDRESS_FAMILY_INET); + TEST_QUIC_SUCCEEDED(Listener.Start(Alpn)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + TestConnection Client(Registration); + TEST_TRUE(Client.IsValid()); + TEST_QUIC_SUCCEEDED( + Client.Start( + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + QUIC_LOCALHOST_FOR_AF(QUIC_ADDRESS_FAMILY_INET), + ServerLocalAddr.GetPort())); + + TEST_TRUE(Client.WaitForConnectionComplete()); + TEST_TRUE(Server); + CxPlatEventWaitForever(ServerContext.Event); + + // + // Validate the GetParam succeeded in the CONNECTED callback. + // + TEST_QUIC_SUCCEEDED(ServerContext.GetParamStatus); + + QUIC_HANDSHAKE_INFO Info = {}; + Length = sizeof(Info); + TEST_QUIC_SUCCEEDED( + MsQuic->GetParam( + Client.GetConnection(), + QUIC_PARAM_TLS_HANDSHAKE_INFO, + &Length, + &Info + )); + + // + // The server should have freed the TLS state by now, so this + // should fail. + // + Length = sizeof(Info); + TEST_EQUAL( + MsQuic->GetParam( + *Server, + QUIC_PARAM_TLS_HANDSHAKE_INFO, + &Length, + &Info), + QUIC_STATUS_INVALID_STATE); + } + + // + // After handshake, with resumption + // + { + TestScopeLogger LogScope2("After handshake - with resumption"); + MsQuicSettings Settings; + Settings.SetServerResumptionLevel(QUIC_SERVER_RESUME_ONLY); + MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + TestListener Listener( + Registration, + TestTlsParamListenerCallback, + ServerConfiguration); + TEST_QUIC_SUCCEEDED(Listener.IsValid()); + UniquePtr Server; + TestTlsParamServerContext ServerContext = { (MsQuicConnection**)&Server, {}, QUIC_STATUS_SUCCESS }; + CxPlatEventInitialize(&ServerContext.Event, FALSE, FALSE); + Listener.Context = &ServerContext; + + QuicAddr ServerLocalAddr(QUIC_ADDRESS_FAMILY_INET); + TEST_QUIC_SUCCEEDED(Listener.Start(Alpn)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + TestConnection Client(Registration); + TEST_TRUE(Client.IsValid()); + TEST_QUIC_SUCCEEDED( + Client.Start( + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + QUIC_LOCALHOST_FOR_AF(QUIC_ADDRESS_FAMILY_INET), + ServerLocalAddr.GetPort())); + + TEST_TRUE(Client.WaitForConnectionComplete()); + TEST_TRUE(Server); + CxPlatEventWaitForever(ServerContext.Event); + + // + // Validate the GetParam succeeded in the CONNECTED callback. + // + TEST_QUIC_SUCCEEDED(ServerContext.GetParamStatus); + + // + // Validate the client always can call GetParam after handshake. + // + QUIC_HANDSHAKE_INFO Info = {}; + Length = sizeof(Info); + TEST_QUIC_SUCCEEDED( + MsQuic->GetParam( + Client.GetConnection(), + QUIC_PARAM_TLS_HANDSHAKE_INFO, + &Length, + &Info + )); + + // + // The server should NOT have freed the TLS state, so this + // should succeed. + // + TEST_EQUAL( + MsQuic->GetParam( + *Server, + QUIC_PARAM_TLS_HANDSHAKE_INFO, + &Length, + &Info), + QUIC_STATUS_SUCCESS); + } + { TestScopeLogger LogScope2("Successful case is covered by TlsTest.HandshakeParamInfo*"); }