#include "net.hh" namespace shared { namespace net { // Some calls use errno, some use strerr. std::string get_net_error(const int code) noexcept { return make_string_lower(std::string(gai_strerror(code))); } std::string get_errno_error() noexcept { return make_string_lower(std::string(strerror(errno))); } std::shared_ptr get_addr_info(const std::string_view address, const std::string_view port, const addrinfo* const hints) { addrinfo* info; if (int status = getaddrinfo(address.data(), port.data(), hints, &info)) { throw std::runtime_error(get_net_error(status)); } return std::shared_ptr{info, [](auto p) { freeaddrinfo(p); }}; } int make_socket(const addrinfo* const info) { int sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol); if (sock == -1) { throw std::runtime_error(get_errno_error()); } int enable = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); return sock; } void bind_socket(const int sock, const addrinfo* const info) { // Bind to the first we can, otherwise throw. for (const addrinfo* i = info; i; i = i->ai_next) { if (bind(sock, info->ai_addr, info->ai_addrlen) != -1) { return; } } throw std::runtime_error(get_errno_error()); } void connect_socket(const int sock, const addrinfo* const info) { if (connect(sock, info->ai_addr, info->ai_addrlen) == -1) { throw std::runtime_error(get_errno_error()); } } void nonblock_socket(const int sock) { if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { throw std::runtime_error(get_errno_error()); } } void* get_info_address(sockaddr& info) noexcept { if (info.sa_family == AF_INET) { return &(reinterpret_cast(&info)->sin_addr); } return &(reinterpret_cast(&info)->sin6_addr); } void listen_socket(const int sock) { fcntl(sock, F_SETFL, O_NONBLOCK); if (listen(sock, SOMAXCONN) == -1) { throw std::runtime_error(get_errno_error()); } } // Returns std::nullopt on failure! We lose the ability to throw here. std::optional get_accept(const int sock) { accept_ret info; socklen_t size = sizeof(info.storage); info.socket = accept(sock, reinterpret_cast(&info.storage), &size); if (info.socket == -1) { return std::nullopt; } return info; } void close_socket(const int sock) { if (close(sock) == -1) { throw std::runtime_error(get_errno_error()); } } // Returns the size of the receive buffer. // For reliable, returns size of the stream (thanks [][][]). // For unreliable, returns size of the next datagram available for reading. std::size_t get_backlog_size(const int sock) { size_t result = 0; if (ioctl(sock, FIONREAD, &result) == -1) { throw std::runtime_error(get_errno_error()); } return result; } std::string get_socket_host_address(const int sock) { sockaddr_in sin; socklen_t size = sizeof(sin); if (getsockname(sock, reinterpret_cast(&sin), &size) == -1) { throw std::runtime_error(get_errno_error()); } return std::string(inet_ntoa(sin.sin_addr)); } std::string get_socket_host_port(const int sock) { sockaddr_in sin; socklen_t size = sizeof(sin); if (getsockname(sock, reinterpret_cast(&sin), &size) == -1) { throw std::runtime_error(get_errno_error()); } return std::to_string(ntohs(sin.sin_port)); } std::string get_socket_peer_address(const int sock) { sockaddr_in sin; socklen_t size = sizeof(sin); if (getpeername(sock, reinterpret_cast(&sin), &size) == -1) { throw std::runtime_error(get_errno_error()); } return std::string(inet_ntoa(sin.sin_addr)); } std::string get_socket_peer_port(const int sock) { sockaddr_in sin; socklen_t size = sizeof(sin); if (getpeername(sock, reinterpret_cast(&sin), &size) == -1) { throw std::runtime_error(get_errno_error()); } return std::to_string(ntohs(sin.sin_port)); } } // namespace net } // namespace shared