// SPDX-License-Identifier: BSD-2-Clause // Copyright CM4all GmbH // author: Max Kellermann #include "FormatAddress.hxx" #include "SocketAddress.hxx" #include "IPv4Address.hxx" #include "net/Features.hxx" // for HAVE_TCP, HAVE_IPV6, HAVE_UN #include #include #include #ifdef _WIN32 #include #else #include #ifdef HAVE_TCP #include #endif #endif #ifdef HAVE_UN #include #endif #ifdef HAVE_UN static bool LocalToString(std::span buffer, std::string_view raw) noexcept { if (raw.empty()) return false; if (raw.size() >= buffer.size()) /* truncate to the buffer size */ raw = raw.substr(0, buffer.size() - 1); if (raw.front() != '\0' && raw.back() == '\0') /* don't convert the null terminator of a non-abstract socket to a '@' */ raw.remove_suffix(1); *std::copy(raw.begin(), raw.end(), buffer.begin()) = '\0'; /* replace all null bytes with '@'; this also handles abstract addresses (Linux specific) */ const auto result = buffer.first(raw.size()); std::replace(result.begin(), result.end(), '\0', '@'); return true; } #endif bool ToString(std::span buffer, SocketAddress address) noexcept { if (address.IsNull() || address.GetSize() == 0) return false; #ifdef HAVE_UN if (address.GetFamily() == AF_LOCAL) /* return path of local socket */ return LocalToString(buffer, address.GetLocalRaw()); #endif #if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) IPv4Address ipv4_buffer; if (address.IsV4Mapped()) address = ipv4_buffer = address.UnmapV4(); #endif char serv[NI_MAXSERV]; int ret = getnameinfo(address.GetAddress(), address.GetSize(), buffer.data(), buffer.size(), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) return false; if (serv[0] != 0 && (serv[0] != '0' || serv[1] != 0)) { #ifdef HAVE_IPV6 if (address.GetFamily() == AF_INET6) { /* enclose IPv6 address in square brackets */ std::size_t length = strlen(buffer.data()); if (length + 4 >= buffer.size()) /* no more room */ return false; memmove(buffer.data() + 1, buffer.data(), length); buffer[0] = '['; buffer[++length] = ']'; buffer[++length] = 0; } #endif if (strlen(buffer.data()) + 1 + strlen(serv) >= buffer.size()) /* no more room */ return false; strcat(buffer.data(), ":"); strcat(buffer.data(), serv); } return true; } const char * ToString(std::span buffer, SocketAddress address, const char *fallback) noexcept { return ToString(buffer, address) ? buffer.data() : fallback; } bool HostToString(std::span buffer, SocketAddress address) noexcept { if (address.IsNull() || address.GetSize() == 0) return false; #ifdef HAVE_UN if (address.GetFamily() == AF_LOCAL) /* return path of local socket */ return LocalToString(buffer, address.GetLocalRaw()); #endif #if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) IPv4Address ipv4_buffer; if (address.IsV4Mapped()) address = ipv4_buffer = address.UnmapV4(); #endif return getnameinfo(address.GetAddress(), address.GetSize(), buffer.data(), buffer.size(), nullptr, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0; }