Discard BOM header when parsing the Windows 'hosts' file (#13079)

* Discard BOM header when parsing the Windows 'hosts' file * Cleanup after tests * Format

Blue committed Jun 10, 2025 at 15:04 UTC 5fd4ffb064ec3ab73c045e2bf86194c18180079f
4 files changed +39 -8
src/windows/common/filesystem.cpp
+8 -6
@@ -874,16 +874,18 @@ std::filesystem::path wsl::windows::common::filesystem::GetTempFolderPath(_In_ H
874 return GetLocalAppDataPath(userToken) / L"temp";
875 }
876
877 -std::string wsl::windows::common::filesystem::GetWindowsHosts()
877 +std::string wsl::windows::common::filesystem::GetWindowsHosts(const std::filesystem::path& Path)
878 {
879 - // Parse the Windows hosts file.
880 - std::wstring SystemDirectory;
881 - THROW_IF_FAILED(wil::GetSystemDirectoryW(SystemDirectory));
882 -
883 - auto Path = std::filesystem::path(std::move(SystemDirectory)) / L"drivers" / L"etc" / L"hosts";
879 std::ifstream Stream(Path.c_str());
880 THROW_HR_IF_MSG(E_FAIL, (Stream.bad() || !Stream.is_open()), "errno = %d", errno);
881
882 + // Discard any BOM header.
883 + int potentialHeader[] = {Stream.get(), Stream.get(), Stream.get()};
884 + if (potentialHeader[0] != 0xEF || potentialHeader[1] != 0xBB || potentialHeader[2] != 0xBF)
885 + {
886 + Stream.seekg(0); // Reset the position to beginning of the file if no BOM header is found.
887 + }
888 +
889 std::string WindowsHosts;
890 std::string Line;
891 while (std::getline(Stream, Line))
src/windows/common/filesystem.hpp
+1 -1
@@ -156,7 +156,7 @@ std::filesystem::path GetTempFilename();
156
157 std::filesystem::path GetTempFolderPath(_In_ HANDLE userToken);
158
159 -std::string GetWindowsHosts();
159 +std::string GetWindowsHosts(const std::filesystem::path& Path);
160
161 /// <summary>
162 /// Opens a directory handle with read/execute, optionally also write, & full sharing. The path
src/windows/common/helpers.cpp
+7 -1
@@ -294,7 +294,13 @@ std::vector<gsl::byte> wsl::windows::common::helpers::GenerateConfigurationMessa
294 // N.B. failures generating the hosts string are non-fatal.
295 try
296 {
297 - windowsHosts = filesystem::GetWindowsHosts();
297 +
298 + // Parse the Windows hosts file.
299 + std::wstring SystemDirectory;
300 + THROW_IF_FAILED(wil::GetSystemDirectoryW(SystemDirectory));
301 +
302 + windowsHosts =
303 + filesystem::GetWindowsHosts(std::filesystem::path(std::move(SystemDirectory)) / L"drivers" / L"etc" / L"hosts");
304 }
305 CATCH_LOG()
306 }
test/windows/UnitTests.cpp
+23
@@ -5995,5 +5995,28 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n",
5995 VERIFY_ARE_EQUAL(err, L"");
5996 }
5997
5998 + TEST_METHOD(EtcHostsParsing)
5999 + {
6000 + constexpr auto inputFileName = L"test-etc-hosts.txt";
6001 +
6002 + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { DeleteFile(inputFileName); });
6003 +
6004 + auto validate = [](const std::string& Input, const std::string& ExpectedOutput) {
6005 + wil::unique_handle inputFile{CreateFile(inputFileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 0, nullptr)};
6006 +
6007 + VERIFY_IS_TRUE(WriteFile(inputFile.get(), Input.c_str(), static_cast<DWORD>(Input.size()), nullptr, nullptr));
6008 +
6009 + auto output = wsl::windows::common::filesystem::GetWindowsHosts(inputFileName);
6010 +
6011 + VERIFY_ARE_EQUAL(ExpectedOutput, output);
6012 + };
6013 +
6014 + validate("127.0.0.1 microsoft.com", "127.0.0.1\tmicrosoft.com\n");
6015 + validate("\xEF\xBB\xBF 127.0.0.1 microsoft.com", "127.0.0.1\tmicrosoft.com\n"); // Validate that BOM headers are ignored.
6016 + validate("#Comment 127.0.0.1 microsoft.com windows.microsoft.com\n#AnotherComment", "");
6017 + validate(
6018 + "#Comment 127.0.0.1 microsoft.com windows.microsoft.com\n#AnotherComment\n127.0.0.1 wsl.dev", "127.0.0.1\twsl.dev\n");
6019 + }
6020 +
6021 }; // namespace UnitTests
6022 } // namespace UnitTests