diff --git a/apps/desktop/src/platform/windows/system_info.cpp b/apps/desktop/src/platform/windows/system_info.cpp index 99c9bfc4..c3aa6851 100644 --- a/apps/desktop/src/platform/windows/system_info.cpp +++ b/apps/desktop/src/platform/windows/system_info.cpp @@ -1,6 +1,7 @@ #include "platform.h" #include +#include #include #include @@ -25,19 +26,38 @@ std::string GetMac() { } std::string GetHostName() { - WSADATA data = {}; - if (WSAStartup(MAKEWORD(2, 2), &data) != 0) { - LOG_ERROR("WSAStartup failed"); + // Read Unicode directly; Winsock's narrow hostname is not guaranteed UTF-8. + DWORD size = 0; + if (!GetComputerNameExW(ComputerNameDnsHostname, nullptr, &size) && + GetLastError() != ERROR_MORE_DATA) { + LOG_ERROR("GetComputerNameExW size query failed: {}", GetLastError()); return {}; } - char hostname[256] = {}; - if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { - LOG_ERROR("gethostname failed: {}", WSAGetLastError()); - WSACleanup(); + if (size == 0) return {}; + + std::wstring hostname(size, L'\0'); + if (!GetComputerNameExW(ComputerNameDnsHostname, hostname.data(), &size)) { + LOG_ERROR("GetComputerNameExW failed: {}", GetLastError()); return {}; } - WSACleanup(); - return hostname; + if (size == 0) return {}; + + const int length = static_cast(size); + const int utf8_size = + WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname.data(), + length, nullptr, 0, nullptr, nullptr); + if (utf8_size == 0) { + LOG_ERROR("Hostname UTF-8 size query failed: {}", GetLastError()); + return {}; + } + std::string utf8_name(utf8_size, '\0'); + if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname.data(), + length, utf8_name.data(), utf8_size, nullptr, + nullptr) == 0) { + LOG_ERROR("Hostname UTF-8 conversion failed: {}", GetLastError()); + return {}; + } + return utf8_name; } bool IsWaylandSession() { return false; } diff --git a/libs/wire/include/remote_action.h b/libs/wire/include/remote_action.h index 655cb656..a73e1800 100644 --- a/libs/wire/include/remote_action.h +++ b/libs/wire/include/remote_action.h @@ -236,10 +236,10 @@ struct HostDisplay { }; // Builds the host_infomation message advertised on every new connection. -// |host_name| is truncated to the wire field size. The display arrays are -// heap-allocated with the same layout the decoder produces; release the -// result with FreeRemoteAction. An allocation failure yields an empty display -// list rather than a partially filled one. +// |host_name| is UTF-8 and truncated at a character boundary to fit the wire +// field size. The display arrays are heap-allocated with the same layout the +// decoder produces; release the result with FreeRemoteAction. An allocation +// failure yields an empty display list rather than a partially filled one. // Missing or oversized app versions are advertised as unknown (empty). RemoteAction MakeHostInformation(const std::string& host_name, const std::vector& displays, diff --git a/libs/wire/src/remote_action.cpp b/libs/wire/src/remote_action.cpp index 05f4e866..17fe6c1b 100644 --- a/libs/wire/src/remote_action.cpp +++ b/libs/wire/src/remote_action.cpp @@ -34,6 +34,19 @@ void ResetHostInfo(HostInfo& info) { info.supports_deskhub_bandwidth = false; } +void CopyHostName(HostInfo& info, const std::string& host_name) { + std::size_t size = std::min( + {host_name.size(), host_name.find('\0'), sizeof(info.host_name) - 1}); + // Keep the fixed-size UTF-8 field from ending inside a multibyte character. + while (size > 0 && size < host_name.size() && + (static_cast(host_name[size]) & 0xC0) == 0x80) { + --size; + } + std::memcpy(info.host_name, host_name.data(), size); + info.host_name[size] = '\0'; + info.host_name_size = size; +} + bool AllocateHostDisplays(HostInfo& info, std::size_t count) { if (count == 0) return true; @@ -104,11 +117,7 @@ RemoteAction MakeHostInformation(const std::string& host_name, action.i.supports_privacy_screen = supports_privacy_screen; action.i.platform = platform; - const std::size_t name_size = - std::min(host_name.size(), sizeof(action.i.host_name) - 1); - std::memcpy(action.i.host_name, host_name.data(), name_size); - action.i.host_name[name_size] = '\0'; - action.i.host_name_size = name_size; + CopyHostName(action.i, host_name); // Never advertise a truncated version as a different, valid release. if (app_version.size() < sizeof(action.i.app_version) && app_version.find('\0') == std::string::npos) { @@ -462,10 +471,7 @@ bool RemoteAction::FromJson(const std::string& json_string, host_info_object.value("supports_deskhub_bandwidth", false); const std::string host_name = host_info_object.at("host_name").get(); - std::strncpy(output.i.host_name, host_name.c_str(), - sizeof(output.i.host_name) - 1); - output.i.host_name[sizeof(output.i.host_name) - 1] = '\0'; - output.i.host_name_size = std::strlen(output.i.host_name); + CopyHostName(output.i, host_name); const auto& displays = host_info_object.at("displays"); if (!displays.is_array()) return false;