diff --git a/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp index c480b67b2..a50f7aff6 100644 --- a/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp +++ b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp @@ -35,9 +35,12 @@ inline std::wstring HttpMethodToWideString(HttpMethod method) // This assumes the input string is always null-terminated. std::wstring StringToWideString(const std::string& str) { + // Since the strings being converted to wstring can be provided by the end user, and can contain + // invalid characters, use the MB_ERR_INVALID_CHARS to validate and fail. + // Passing in -1 so that the function processes the entire input string, including the terminating // null character. - int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); + int sizeNeeded = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), -1, 0, 0); if (sizeNeeded == 0) { // Errors include: @@ -52,7 +55,8 @@ std::wstring StringToWideString(const std::string& str) } std::wstring wideStr(sizeNeeded, L'\0'); - if (MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], sizeNeeded) == 0) + if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), -1, &wideStr[0], sizeNeeded) + == 0) { DWORD error = GetLastError(); throw Azure::Core::Http::TransportException( @@ -74,6 +78,11 @@ std::string WideStringToString(const std::wstring& wideString) "Input wide string is too large to fit within a 32-bit int."); } + // Note, we are not using the flag WC_ERR_INVALID_CHARS here, because it is assumed the service + // returns correctly encoded response headers and reason phrase strings. + // The transport layer shouldn't do additional validation, and if WideCharToMultiByte replaces + // invalid characters with the replacement character, that is fine. + int wideStrLength = static_cast(wideStrSize); int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideString.c_str(), wideStrLength, NULL, 0, NULL, NULL); diff --git a/sdk/core/azure-core/test/ut/transport_adapter_base.cpp b/sdk/core/azure-core/test/ut/transport_adapter_base.cpp index 184c97498..9071e25aa 100644 --- a/sdk/core/azure-core/test/ut/transport_adapter_base.cpp +++ b/sdk/core/azure-core/test/ut/transport_adapter_base.cpp @@ -408,6 +408,70 @@ namespace Azure { namespace Core { namespace Test { Azure::Core::RequestFailedException); } + TEST_P(TransportAdapter, validNonAsciiHost) + { + { + Azure::Core::Url host(u8"http://unresolvedHost\u6F22\u5B57.org/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + { + Azure::Core::Url host("http://unresolvedHost\xE9\x87\x91.org/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + { + Azure::Core::Url host(u8"http://unresolvedHost\uC328.org/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + { + Azure::Core::Url host("http://\0/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + } + + TEST_P(TransportAdapter, invalidNonAsciiHost) + { + { + Azure::Core::Url host("http://unresolvedHost\xC0\x41\x42\xFE\xFE\xFF\xFF.org/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + { + Azure::Core::Url host("http://\xC0\x76\x77/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + { + Azure::Core::Url host("http://\xD8\x00\x01\x00/get"); + + auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, host); + EXPECT_THROW( + m_pipeline->Send(request, Azure::Core::Context::ApplicationContext), + Azure::Core::Http::TransportException); + } + } + TEST_P(TransportAdapter, dynamicCast) { Azure::Core::Url host("http://unresolvedHost.org/get");