#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