#include "TCPSocket.h" #include "NetworkManager.h" #include "logging/Logger.h" #include #include namespace Feather::Network { TCPSocket::TCPSocket() { m_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // Can't create IPv6 socket? Create an IPv4 one. if (!(m_ipv6 = IsValid())) { Log::Info("Failed to create IPv6 socket, falling back to IPv4."); m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } } TCPSocket::~TCPSocket() { Close(); } bool TCPSocket::IsIPV6() const { return m_ipv6; } bool TCPSocket::MarkReusable() { return evutil_make_listen_socket_reuseable(m_socket) == 0; } bool TCPSocket::MarkDualBind() { constexpr uint32_t zero = 0; return setsockopt(m_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&zero), sizeof(zero)) == 0; } bool TCPSocket::MarkNonBlocking() { return evutil_make_socket_nonblocking(m_socket) == 0; } bool TCPSocket::Bind(uint16_t port) { auto BindGeneric = [&](const auto& name) { return bind(m_socket, reinterpret_cast(&name), sizeof(name)) == 0; }; 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 TCPSocket::Close() { if (!IsValid()) return true; return evutil_closesocket(m_socket) == 0; } evconnlistener* TCPSocket::Listen(evconnlistener_cb callback, void* self) { if (listen(m_socket, SOMAXCONN) != 0) return nullptr; return evconnlistener_new( NetworkManager::Instance().GetEventBase(), callback, self, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, m_socket); } bool TCPSocket::IsValid() const { return m_socket != EVUTIL_INVALID_SOCKET; } }