#include "SocketUtil.h" #ifdef _WIN32 # include # include #else # include # include # include # include #endif namespace Feather::Sockets { class TCPSocketHandle { public: TCPSocketHandle() { m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // Can't create IPv6 socket? Create an IPv4 one. if (!(m_ipv6 = IsValid())) m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } ~TCPSocketHandle() { Close(); } bool IsIPV6() const { return m_ipv6; } bool MarkReusable() { constexpr uint32_t one = 1; return setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&one), socklen_t(sizeof(one))) == 0; } bool MarkDualBind() { constexpr uint32_t zero = 0; return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&zero), sizeof(zero)) == 0; } bool Bind(uint16_t port) { sockaddr_in6 name_ipv6 = {}; name_ipv6.sin6_family = AF_INET6; name_ipv6.sin6_port = ntohs(port); sockaddr_in name_ipv4 = {}; name_ipv4.sin_family = AF_INET; name_ipv4.sin_port = ntohs(port); return m_ipv6 ? BindGeneric(name_ipv6) : BindGeneric(name_ipv4); } bool Listen() { return listen(m_socket, SOMAXCONN) == 0; } #ifdef _WIN32 bool IsValid() const { return m_socket != INVALID_SOCKET; } bool MarkNonBlocking() { unsigned long one = 1; return ioctlsocket(m_socket, FIONBIO, &one) != SOCKET_ERROR; } bool Close() { return shutdown(m_socket, SD_BOTH) == 0 && closesocket(m_socket) == 0; } static bool InitSocketSystem() { WSADATA wsa_data; return WSAStartup(MAKEWORD(1, 1), &wsa_data) == 0; } static bool CloseSocketSystem() { return WSACleanup() == 0; } using SocketType = SOCKET; #else bool IsValid() const { return m_socket >= 0; } bool MarkNonBlocking() { return fcntl(fd, F_SETFL, O_NONBLOCK) != -1; } bool Close() { return shutdown(m_socket, SHUT_RDWR) == 0 && close(m_socket) == 0; } static bool InitSocketSystem() { return true; } static bool CloseSocketSystem() { return true; } using SocketType = int; #endif private: template bool BindGeneric(const T& name) { return bind(m_socket, reinterpret_cast(&name), sizeof(name)) == 0; } SocketType m_socket; bool m_ipv6; }; TCPSocket::TCPSocket(uint16_t port) { auto socket = std::make_unique(); if (!socket->IsValid()) return; if (!socket->MarkReusable()) return; if (socket->IsIPV6() && !socket->MarkDualBind()) return; if (!socket->Bind(port)) return; if (socket->Listen()) return; m_socket = std::move(socket); } TCPSocket::~TCPSocket() { } namespace { class SocketInitializer { public: SocketInitializer() { TCPSocketHandle::InitSocketSystem(); } ~SocketInitializer() { TCPSocketHandle::CloseSocketSystem(); } }; static SocketInitializer s_socketInitializer; } }