summaryrefslogtreecommitdiff
path: root/src/net.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2025-02-20 22:54:56 +0100
committerJoel Klinghed <the_jk@spawned.biz>2025-02-20 22:54:56 +0100
commitb4d6df902253637f24647d3db2bc3781d69eec1c (patch)
treed8bf9ac04a270fabdfee1c15628c702471ef8bf5 /src/net.cc
parent441cafc7124f633e5abc684e85a11ce3c991f6ae (diff)
Initial commitHEADmain
Diffstat (limited to 'src/net.cc')
-rw-r--r--src/net.cc136
1 files changed, 136 insertions, 0 deletions
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 <charconv>
+#include <cerrno>
+#include <cstring>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+namespace net {
+
+std::vector<unique_fd> bind_and_listen(
+ Logger* logger, std::string const& host, uint16_t port) {
+ std::vector<unique_fd> 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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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