diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index d7ffd834aa..ade789b098 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -18,6 +18,8 @@ #include "io/uring/Queue.hxx" #endif +#include // for INT_MAX + #ifdef HAVE_URING class EventLoop::UringPoll final : Uring::Operation { @@ -346,9 +348,14 @@ EventLoop::RunOneIdle() noexcept static constexpr int ExportTimeoutMS(Event::Duration timeout) noexcept { - return timeout >= timeout.zero() - ? static_cast(std::chrono::ceil(timeout).count()) - : -1; + if (timeout < timeout.zero()) + return -1; + + const auto ms = std::chrono::ceil(timeout).count(); + if (ms > INT_MAX) + return INT_MAX; + + return static_cast(ms); } #ifdef HAVE_URING diff --git a/src/net/IPv4Address.cxx b/src/net/IPv4Address.cxx index 1842aba6a2..7ace24e8ac 100644 --- a/src/net/IPv4Address.cxx +++ b/src/net/IPv4Address.cxx @@ -4,6 +4,12 @@ #include "IPv4Address.hxx" #include +#include // for strlen() +#include // for sprintf() + +#ifndef _WIN32 +#include // for inet_ntop() +#endif IPv4Address::IPv4Address(SocketAddress src) noexcept :address(src.CastTo()) @@ -11,3 +17,24 @@ IPv4Address::IPv4Address(SocketAddress src) noexcept assert(!src.IsNull()); assert(src.GetFamily() == AF_INET); } + +const char * +IPv4Address::Format(std::span buffer) const noexcept +{ + if (buffer.size() <= 7) [[unlikely]] + return nullptr; + + char *p = buffer.data(); + + if (inet_ntop(AF_INET, &address.sin_addr, + p, buffer.size() - 7) == nullptr) + return nullptr; + + const unsigned port = GetPort(); + if (port != 0) { + p += std::strlen(p); + std::sprintf(p, ":%u", port); + } + + return buffer.data(); +} diff --git a/src/net/IPv4Address.hxx b/src/net/IPv4Address.hxx index 3d96e4e0b0..e14ae729c1 100644 --- a/src/net/IPv4Address.hxx +++ b/src/net/IPv4Address.hxx @@ -232,4 +232,12 @@ public: return IPv4Address(ConstructInAddrBE(GetNumericAddressBE() & other.GetNumericAddressBE()), GetPort() & other.GetPort()); } + + /** + * Format this object as a C string into the given buffer. + * + * @return the C string on success, nullptr on error + */ + [[nodiscard]] + const char *Format(std::span buffer) const noexcept; }; diff --git a/src/net/IPv6Address.cxx b/src/net/IPv6Address.cxx index 4689002242..a11f2dc9ca 100644 --- a/src/net/IPv6Address.cxx +++ b/src/net/IPv6Address.cxx @@ -5,8 +5,12 @@ #include "IPv4Address.hxx" #include +#include // for strlen() +#include // for sprintf() -#include +#ifndef _WIN32 +#include // for inet_ntop() +#endif IPv6Address::IPv6Address(SocketAddress src) noexcept :address(src.CastTo()) @@ -65,3 +69,27 @@ IPv6Address::operator&(const IPv6Address &other) const sizeof(result)); return result; } + +const char * +IPv6Address::Format(std::span buffer) const noexcept +{ + if (buffer.size() <= 9) [[unlikely]] + return nullptr; + + char *p = buffer.data(); + + const unsigned port = GetPort(); + if (port != 0) + *p++ = '['; + + if (inet_ntop(AF_INET6, &address.sin6_addr, + p, buffer.size() - 9) == nullptr) + return nullptr; + + if (port != 0) { + p += std::strlen(p); + std::sprintf(p, "]:%u", port); + } + + return buffer.data(); +} diff --git a/src/net/IPv6Address.hxx b/src/net/IPv6Address.hxx index f1377adc8f..85d5634a8d 100644 --- a/src/net/IPv6Address.hxx +++ b/src/net/IPv6Address.hxx @@ -212,6 +212,14 @@ public: [[gnu::pure]] IPv6Address operator&(const IPv6Address &other) const; + /** + * Format this object as a C string into the given buffer. + * + * @return the C string on success, nullptr on error + */ + [[nodiscard]] + const char *Format(std::span buffer) const noexcept; + private: /** * Helper function for MaskFromPrefix(). diff --git a/src/net/InetAddress.hxx b/src/net/InetAddress.hxx index 946cb0201e..37bad170fe 100644 --- a/src/net/InetAddress.hxx +++ b/src/net/InetAddress.hxx @@ -10,6 +10,8 @@ #include "IPv6Address.hxx" #endif +#include + /** * A class that can store either an IPv4 or an IPv6 address. */ @@ -116,4 +118,26 @@ public: return {}; } } + + /** + * Format this object as a C string into the given buffer. + * + * @return the C string on success, nullptr on error + */ + [[nodiscard]] + const char *Format(std::span buffer) const noexcept { + switch (GetFamily()) { + case AF_INET: + return v4.Format(buffer); + +#ifdef HAVE_IPV6 + case AF_INET6: + return v6.Format(buffer); +#endif + + default: + [[unlikely]] + return nullptr; + } + } };