Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions include/boost/corosio/host_name.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Copyright (c) 2026 Steve Gerbino
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//

#ifndef BOOST_COROSIO_HOST_NAME_HPP
#define BOOST_COROSIO_HOST_NAME_HPP

#include <boost/corosio/detail/config.hpp>

#include <string>

namespace boost::corosio {

/** Return the local machine's hostname.

On POSIX systems this calls `gethostname(2)`. On Windows this
calls `GetComputerNameExW(ComputerNameDnsHostname, ...)` and
converts the result from UTF-16 to UTF-8.

The function is synchronous and does not require an
`io_context`. On Windows it does not require winsock to have
been initialized.

@par Exception Safety
Strong guarantee.

@par Example
@code
std::string h = boost::corosio::host_name();
std::cout << "running on " << h << "\n";
@endcode

@return The hostname as a UTF-8 string.

@throws std::runtime_error If the underlying system call fails.
*/
BOOST_COROSIO_DECL
std::string
host_name();

} // namespace boost::corosio

#endif
108 changes: 108 additions & 0 deletions src/corosio/src/host_name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// Copyright (c) 2026 Steve Gerbino
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//

#include <boost/corosio/host_name.hpp>
#include <boost/corosio/detail/platform.hpp>

#include <stdexcept>
#include <string>

#if BOOST_COROSIO_POSIX
#include <cerrno>
#include <cstring>
#include <unistd.h>
#elif BOOST_COROSIO_HAS_IOCP
#include <windows.h>
#endif

namespace boost::corosio {

#if BOOST_COROSIO_POSIX

std::string
host_name()
{
// 256 exceeds POSIX's _POSIX_HOST_NAME_MAX floor of 255 and
// every mainstream OS's actual cap (Linux 64, macOS/BSD 255).
char buf[256];
if (::gethostname(buf, sizeof(buf)) != 0)
{
int e = errno;
throw std::runtime_error(
std::string("gethostname failed: ") + std::strerror(e));
}

// POSIX does not guarantee NUL termination on truncation.
if (std::memchr(buf, '\0', sizeof(buf)) == nullptr)
throw std::runtime_error("gethostname: hostname truncated");

return std::string(buf);
}

#elif BOOST_COROSIO_HAS_IOCP

std::string
host_name()
{
// Size query: returns ERROR_MORE_DATA and writes the required
// wide-char count (including the trailing NUL) into `size`.
DWORD size = 0;
BOOL ok = ::GetComputerNameExW(
ComputerNameDnsHostname, nullptr, &size);
DWORD err = ::GetLastError();
if (ok)
{
throw std::runtime_error(
"GetComputerNameExW (size query) unexpectedly succeeded");
}
if (err != ERROR_MORE_DATA)
{
throw std::runtime_error(
"GetComputerNameExW (size query) failed: error " +
std::to_string(err));
}

// On success, GetComputerNameExW rewrites `size` to the count
// without the NUL, so resize(size) below trims to the hostname.
std::wstring wide(size, L'\0');
if (!::GetComputerNameExW(
ComputerNameDnsHostname, wide.data(), &size))
{
throw std::runtime_error(
"GetComputerNameExW failed: error " +
std::to_string(::GetLastError()));
}
wide.resize(size);

int needed = ::WideCharToMultiByte(
CP_UTF8, 0, wide.data(), static_cast<int>(wide.size()),
nullptr, 0, nullptr, nullptr);
if (needed <= 0)
{
throw std::runtime_error(
"WideCharToMultiByte (size query) failed: error " +
std::to_string(::GetLastError()));
}

std::string out(static_cast<std::size_t>(needed), '\0');
int written = ::WideCharToMultiByte(
CP_UTF8, 0, wide.data(), static_cast<int>(wide.size()),
out.data(), needed, nullptr, nullptr);
if (written != needed)
{
throw std::runtime_error(
"WideCharToMultiByte failed: error " +
std::to_string(::GetLastError()));
}
return out;
}

#endif

} // namespace boost::corosio
80 changes: 80 additions & 0 deletions test/unit/host_name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// Copyright (c) 2026 Steve Gerbino
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//

// Test that header file is self-contained.
#include <boost/corosio/host_name.hpp>

#include <string>

#include "test_suite.hpp"

namespace boost::corosio {

struct host_name_test
{
// Every configured machine has a hostname.
void testReturnsNonEmpty()
{
std::string h = host_name();
BOOST_TEST(!h.empty());
}

// Catches buffer or string-lifetime bugs across calls.
void testStable()
{
std::string a = host_name();
std::string b = host_name();
BOOST_TEST_EQ(a, b);
}

// 255 is the DNS hostname ceiling; anything longer is garbage
// from a miscounted buffer.
void testReasonableLength()
{
std::string h = host_name();
BOOST_TEST(h.size() > 0);
BOOST_TEST(h.size() <= 255);
}

// Regression guard for the Windows implementation choice: a
// switch to winsock gethostname() would fail here because
// corosio's WSAStartup is lazy (inside io_context).
void testNoIoContextNeeded()
{
std::string h = host_name();
BOOST_TEST(!h.empty());
}

// Catches encoding regressions, especially the Windows
// UTF-16 -> UTF-8 conversion. Non-ASCII hostnames are valid, so
// accept any printable ASCII byte or high-bit byte.
void testCharsetSanity()
{
std::string h = host_name();
for (unsigned char c : h)
{
bool printable_ascii = (c >= 0x20 && c <= 0x7E);
bool high_bit = (c >= 0x80);
BOOST_TEST(printable_ascii || high_bit);
}
}

void run()
{
testReturnsNonEmpty();
testStable();
testReasonableLength();
testNoIoContextNeeded();
testCharsetSanity();
}
};

TEST_SUITE(host_name_test, "boost.corosio.host_name");

} // namespace boost::corosio
Loading