diff --git a/core/include/userver/engine/io/sockaddr.hpp b/core/include/userver/engine/io/sockaddr.hpp index 01b14a50454e..52cbdf393416 100644 --- a/core/include/userver/engine/io/sockaddr.hpp +++ b/core/include/userver/engine/io/sockaddr.hpp @@ -55,9 +55,23 @@ class Sockaddr final { ::memcpy(&data_, data, Sockaddr::Addrlen(domain)); } + /// @brief Creates address of an IP socket with passed address. + static Sockaddr MakeIPSocketAddress(const char* ip_address); + /// @brief Creates address of a Unix socket located at the specified path. static Sockaddr MakeUnixSocketAddress(std::string_view path); + /// @brief Creates the IPv6 wildcard address `[::]:0` that also handles IPv4 + /// connections. + /// + /// A program needs to support only this API type to support IPv4 and IPv6. + static Sockaddr MakeInaddrAny() noexcept; + + /// @brief Creates the IPv4 only wildcard address `0.0.0.0:0`. + /// + /// Prefer a more generic MakeInaddrAny() function if not sure. + static Sockaddr MakeIPv4InaddrAny() noexcept; + /// @brief Creates the IPv6 loopback address `[::1]:0` that also handles IPv4 /// connections. /// diff --git a/core/src/engine/io/sockaddr.cpp b/core/src/engine/io/sockaddr.cpp index d49696e63e44..1df563149ef6 100644 --- a/core/src/engine/io/sockaddr.cpp +++ b/core/src/engine/io/sockaddr.cpp @@ -18,6 +18,21 @@ USERVER_NAMESPACE_BEGIN namespace engine::io { +Sockaddr Sockaddr::MakeIPSocketAddress(const char* ip_address) { + Sockaddr addr; + auto* ipv4_sa = addr.As(); + if (inet_pton(AF_INET, ip_address, &ipv4_sa->sin_addr) == 1) { + ipv4_sa->sin_family = AF_INET; + return addr; + } + auto* ipv6_sa = addr.As(); + if (inet_pton(AF_INET6, ip_address, &ipv6_sa->sin6_addr) == 1) { + ipv6_sa->sin6_family = AF_INET6; + return addr; + } + throw AddrException(fmt::format("Invalid IP address: {}", ip_address)); +} + Sockaddr Sockaddr::MakeUnixSocketAddress(std::string_view path) { Sockaddr addr; auto* sa = addr.As(); @@ -39,6 +54,26 @@ Sockaddr Sockaddr::MakeUnixSocketAddress(std::string_view path) { return addr; } +Sockaddr Sockaddr::MakeInaddrAny() noexcept { + Sockaddr addr; + auto* sa = addr.As(); + sa->sin6_family = AF_INET6; + sa->sin6_addr = in6addr_any; + UASSERT(sa->sin6_port == 0); + return addr; +} + +Sockaddr Sockaddr::MakeIPv4InaddrAny() noexcept { + Sockaddr addr; + auto* sa = addr.As(); + sa->sin_family = AF_INET; + // may be implemented as a macro + // NOLINTNEXTLINE(hicpp-no-assembler, readability-isolate-declaration) + sa->sin_addr.s_addr = htonl(INADDR_ANY); + UASSERT(sa->sin_port == 0); + return addr; +} + Sockaddr Sockaddr::MakeLoopbackAddress() noexcept { Sockaddr addr; auto* sa = addr.As(); diff --git a/core/src/engine/io/socket_test.cpp b/core/src/engine/io/socket_test.cpp index 2ac93ce589df..30f40ebc840c 100644 --- a/core/src/engine/io/socket_test.cpp +++ b/core/src/engine/io/socket_test.cpp @@ -474,10 +474,8 @@ UTEST(Socket, UdpIpMreqIPv4) { auto receiver = engine::io::Socket(engine::io::AddrDomain::kInet, engine::io::SocketType::kDgram); - sockaddr_in any_addr{}; - any_addr.sin_family = AF_INET; - any_addr.sin_port = htons(kPort); - any_addr.sin_addr.s_addr = htonl(INADDR_ANY); + auto any_addr = engine::io::Sockaddr::MakeIPv4InaddrAny(); + any_addr.SetPort(kPort); receiver.Bind(engine::io::Sockaddr(&any_addr)); engine::io::IpMreq mreq(kGroup, 0); @@ -500,10 +498,8 @@ UTEST(Socket, UdpIpMreqIPv6) { auto receiver = engine::io::Socket(engine::io::AddrDomain::kInet6, engine::io::SocketType::kDgram); - sockaddr_in6 any_addr{}; - any_addr.sin6_family = AF_INET6; - any_addr.sin6_port = htons(kPort); - any_addr.sin6_addr = in6addr_any; + auto any_addr = engine::io::Sockaddr::MakeInaddrAny(); + any_addr.SetPort(kPort); receiver.Bind(engine::io::Sockaddr(&any_addr)); engine::io::IpMreq mreq(kGroup, 0); @@ -522,11 +518,8 @@ UTEST_MT(Socket, UdpIpMreqMultipleReceiversIPv6, 3) { static constexpr const char* kGroup = "ff02::42"; static constexpr int packets_count = 3; - sockaddr_in6 raw_multiaddr{}; - raw_multiaddr.sin6_family = AF_INET6; - raw_multiaddr.sin6_port = htons(kPort); - inet_pton(AF_INET6, kGroup, &raw_multiaddr.sin6_addr); - io::Sockaddr multiaddr(&raw_multiaddr); + auto multiaddr = engine::io::Sockaddr::MakeIPSocketAddress(kGroup); + multiaddr.SetPort(kPort); io::IpMreq mreq(kGroup, 0); std::vector> tasks; @@ -535,10 +528,8 @@ UTEST_MT(Socket, UdpIpMreqMultipleReceiversIPv6, 3) { const auto& receiver = receivers.emplace_back(std::make_shared(io::AddrDomain::kInet6, io::SocketType::kDgram)); - sockaddr_in6 any{}; - any.sin6_family = AF_INET6; - any.sin6_port = htons(kPort); - any.sin6_addr = in6addr_any; + auto any = engine::io::Sockaddr::MakeInaddrAny(); + any.SetPort(kPort); receiver->Bind(io::Sockaddr(&any)); io::AddMembership(*receiver, mreq);