Skip to content

Commit

Permalink
net/PToString: do not resolve IPv6 scope ids to interface names
Browse files Browse the repository at this point in the history
Lacking NI_NUMERICSCOPE, every getnameinfo() call causes three
additional system calls:

 socket(AF_UNIX, SOCK_DGRAM, 0)
 ioctl(SIOCGIFNAME)
 close()

This is a rather large price for something that is called pretty
often.
  • Loading branch information
MaxKellermann committed Jan 16, 2025
1 parent 43b52d9 commit 9a78447
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 6 deletions.
1 change: 1 addition & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ cm4all-beng-proxy (19.2) unstable; urgency=low
* http/server: receive using io_uring
* was: reduce the number of epoll_ctl() system calls again
* was: receive from control channel using io_uring
* do not resolve IPv6 scope ids to interface names

--

Expand Down
112 changes: 106 additions & 6 deletions src/net/PToString.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,76 @@
#include "pool/pool.hxx"
#include "net/SocketAddress.hxx"
#include "net/FormatAddress.hxx"
#include "net/IPv4Address.hxx"
#include "net/IPv6Address.hxx"

#include <fmt/core.h>

#include <arpa/inet.h>
#include <string.h>

static bool
V4HostToString(std::span<char> buffer, const IPv4Address &address) noexcept
{
return inet_ntop(AF_INET, &address.GetAddress(), buffer.data(), buffer.size()) != nullptr;
}

static bool
V4ToString(std::span<char> buffer, const IPv4Address &address) noexcept
{
const auto port = address.GetPort();
if (port == 0)
return V4HostToString(buffer, address);

if (!V4HostToString(buffer.first(buffer.size() - 8), address))
return false;

buffer = buffer.subspan(strlen(buffer.data()));
*fmt::format_to(buffer.data(), ":{}", port) = '\0';
return true;
}

static bool
V6HostToString(std::span<char> buffer, const IPv6Address &address) noexcept
{
return inet_ntop(AF_INET6, &address.GetAddress(), buffer.data(), buffer.size()) != nullptr;
}

static bool
V6HostWithScopeToString(std::span<char> buffer, const IPv6Address &address) noexcept
{
const auto scope_id = address.GetScopeId();
if (scope_id == 0)
return V6HostToString(buffer, address);

if (!V6HostToString(buffer.first(buffer.size() - 8), address))
return false;

buffer = buffer.subspan(strlen(buffer.data()));
*fmt::format_to(buffer.data(), "%{}", scope_id) = '\0';
return true;
}

static bool
V6ToString(std::span<char> buffer, const IPv6Address &address) noexcept
{
const auto port = address.GetPort();
if (port == 0)
return V6HostWithScopeToString(buffer, address);

buffer.front() = '[';
buffer = buffer.subspan(1);

if (!V6HostWithScopeToString(buffer.first(buffer.size() - 8), address))
return false;

buffer = buffer.subspan(strlen(buffer.data()));
buffer.front() = ']';
buffer = buffer.subspan(1);

*fmt::format_to(buffer.data(), ":{}", port) = '\0';
return true;
}

const char *
address_to_string(struct pool &pool, SocketAddress address)
Expand All @@ -14,9 +84,24 @@ address_to_string(struct pool &pool, SocketAddress address)
return nullptr;

char host[512];
bool success = ToString(host, address);
if (!success || *host == 0)
return nullptr;

/* optimizations for IPv4 and IPv6 because glibc does not have
NI_NUMERICSCOPE */
switch (address.GetFamily()) {
case AF_INET:
if (!V4ToString(std::span{host}, IPv4Address::Cast(address)))
return nullptr;
break;

case AF_INET6:
if (!V6ToString(std::span{host}, IPv6Address::Cast(address)))
return nullptr;
break;

default:
if (!ToString(host, address) || *host == 0)
return nullptr;
}

return p_strdup(&pool, host);
}
Expand All @@ -28,9 +113,24 @@ address_to_host_string(struct pool &pool, SocketAddress address)
return nullptr;

char host[512];
bool success = HostToString(host, address);
if (!success || *host == 0)
return nullptr;

/* optimizations for IPv4 and IPv6 because glibc does not have
NI_NUMERICSCOPE */
switch (address.GetFamily()) {
case AF_INET:
if (!V4HostToString(std::span{host}, IPv4Address::Cast(address)))
return nullptr;
break;

case AF_INET6:
if (!V6HostWithScopeToString(std::span{host}, IPv6Address::Cast(address)))
return nullptr;
break;

default:
if (!HostToString(host, address) || *host == 0)
return nullptr;
}

return p_strdup(&pool, host);
}

0 comments on commit 9a78447

Please sign in to comment.