From b4d6df902253637f24647d3db2bc3781d69eec1c Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Thu, 20 Feb 2025 22:54:56 +0100 Subject: Initial commit --- src/net.cc | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/net.cc (limited to 'src/net.cc') diff --git a/src/net.cc b/src/net.cc new file mode 100644 index 0000000..bb25e69 --- /dev/null +++ b/src/net.cc @@ -0,0 +1,136 @@ +#include "net.hh" + +#include +#include +#include +#include +#include +#include + +namespace net { + +std::vector bind_and_listen( + Logger* logger, std::string const& host, uint16_t port) { + std::vector fds; + + std::string service; + service.resize(10); + { + auto [ptr, err] = std::to_chars(service.data(), + service.data() + service.size(), port); + if (err != std::errc{}) { + logger->err("Invalid port: %u", static_cast(port)); + return {}; + } + service.resize(ptr - service.data()); + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG; + struct addrinfo* result; + + if (auto err = getaddrinfo(host.empty() ? nullptr : host.c_str(), + service.c_str(), + &hints, &result)) { + logger->err("Unable to resolve %s:%u: %s", + host.c_str(), static_cast(port), + gai_strerror(err)); + return {}; + } + + for (auto* rp = result; rp; rp = rp->ai_next) { + unique_fd fd(socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)); + if (!fd) { + logger->warn("Unable to create socket: %s", strerror(errno)); + continue; + } + + if (bind(fd.get(), rp->ai_addr, rp->ai_addrlen)) { + logger->warn("Unable to bind socket: %s", strerror(errno)); + continue; + } + + fds.push_back(std::move(fd)); + } + + freeaddrinfo(result); + + { + auto it = fds.begin(); + while (it != fds.end()) { + if (listen(it->get(), SOMAXCONN)) { + logger->err("Unable to listen: %s", strerror(errno)); + it = fds.erase(it); + } else { + ++it; + } + } + } + return fds; +} + +unique_fd connect(Logger* logger, std::string const& host, uint16_t port) { + unique_fd fd; + std::string service; + service.resize(10); + { + auto [ptr, err] = std::to_chars(service.data(), + service.data() + service.size(), port); + if (err != std::errc{}) { + logger->err("Invalid port: %u", static_cast(port)); + return fd; + } + service.resize(ptr - service.data()); + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; + struct addrinfo* result; + + if (auto err = getaddrinfo(host.c_str(), service.c_str(), + &hints, &result)) { + logger->err("Unable to resolve %s:%u: %s", + host.c_str(), static_cast(port), + gai_strerror(err)); + return fd; + } + + for (auto* rp = result; rp; rp = rp->ai_next) { + // Note: SOCK_NONBLOCK is a linux 2.6.27+ thing. + fd.reset(socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, + rp->ai_protocol)); + if (!fd) { + logger->warn("Unable to create socket: %s", strerror(errno)); + continue; + } + + if (connect(fd.get(), rp->ai_addr, rp->ai_addrlen)) { + logger->warn("Unable to connect socket: %s", strerror(errno)); + fd.reset(); + continue; + } + + break; + } + + freeaddrinfo(result); + return fd; +} + +unique_fd accept(Logger* logger, int fd) { + unique_fd ret(accept4(fd, nullptr, nullptr, SOCK_NONBLOCK)); + if (!ret) { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { + logger->warn("Error accepting connection: %s", strerror(errno)); + } + } + return ret; +} + +} // namespace net -- cgit v1.2.3-70-g09d2