#include "TCPListener.h" #include "NetworkManager.h" #include #include #include #include #include #include #include "Buffer.h" namespace Feather::Network { class TCPSocket { public: TCPSocket() { 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); } ~TCPSocket() { Close(); } bool IsIPV6() const { return m_ipv6; } bool MarkReusable() { return evutil_make_listen_socket_reuseable(m_socket) == 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 MarkNonBlocking() { return evutil_make_socket_nonblocking(m_socket) == 0; } bool Close() { if (!IsValid()) return true; return evutil_closesocket(m_socket) == 0; } template evconnlistener* Listen(evconnlistener_cb callback, T* self) { if (listen(m_socket, SOMAXCONN) != 0) return nullptr; return evconnlistener_new( NetworkManager::Instance().GetEventBase(), callback, reinterpret_cast(self), LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, m_socket); } bool IsValid() const { return m_socket != EVUTIL_INVALID_SOCKET; } private: template bool BindGeneric(const T& name) { return bind(m_socket, reinterpret_cast(&name), sizeof(name)) == 0; } evutil_socket_t m_socket; bool m_ipv6; }; class TCPListenerClient { public: TCPListenerClient(TCPListener* parent, evutil_socket_t socket) : m_parent(parent) , m_bufferEvent(bufferevent_socket_new(NetworkManager::Instance().GetEventBase(), socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS)) { bufferevent_setcb(m_bufferEvent, [](bufferevent* be, void* self) { static_cast(self)->ReadCallback(); }, [](bufferevent* be, void* self) { static_cast(self)->WriteCallback(); }, [](bufferevent* be, short event, void* self) { static_cast(self)->EventCallback(event); }, this); bufferevent_enable(m_bufferEvent, EV_READ | EV_WRITE); } ~TCPListenerClient() { bufferevent_free(m_bufferEvent); } void ReadCallback() { auto lock = std::unique_lock(m_mutex); char data[1024]; size_t length; while ((length = bufferevent_read(m_bufferEvent, data, sizeof(data))) > 0) { size_t oldSize = m_incomingData.size(); m_incomingData.resize(oldSize + length); std::memcpy(&m_incomingData[oldSize], data, length); } } void writeVarInt(int value, std::vector& msg) { do { byte temp = (byte)(value & 0b01111111); // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone value >>= 7; if (value != 0) { temp |= 0b10000000; } msg.push_back(temp); } while (value != 0); } int fuckvarintsize(int value) { int i = 0; do { byte temp = (byte)(value & 0b01111111); // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone value >>= 7; if (value != 0) { temp |= 0b10000000; } i++; } while (value != 0); return i; } std::vector EncodeMessageTest(int packetid, const unsigned char* data, size_t size) { Buffer msg; // TODO: message size prepender. could potentially be done without copying int msg_size = fuckvarintsize(packetid) + fuckvarintsize(int(size)) + int(size); msg.WriteVarInt(msg_size); msg.WriteVarInt(packetid); msg.WriteVarInt(size); for (size_t i = 0; i < size; i++) msg.WriteByte(data[i]); return msg.GetData(); } bool SendShitTest() { auto lock = std::unique_lock(m_mutex); const char json_template[] = R"({ "version": { "name": "1.16.1", "protocol": 736 }, "players": { "max": 10, "online": 10, "sample": [ { "name": "thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20" } ] }, "description": { "text": "Hello Nukem!" } })"; auto message = EncodeMessageTest(0, (const unsigned char*)json_template, sizeof(json_template)); bufferevent_write(m_bufferEvent, message.data(), message.size()); return true; } void WriteCallback() { printf("Write callback!\n"); SendShitTest(); } void EventCallback(short event) { printf("Event!\n"); } private: TCPListener* m_parent; bufferevent* m_bufferEvent; std::vector m_incomingData; std::mutex m_mutex; }; TCPListener::TCPListener(uint16_t port) { // Setup the listen socket. 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->MarkNonBlocking()) return; auto ListenerCallback = [](evconnlistener* evListener, evutil_socket_t socket, sockaddr* addr, int len, void* self) { TCPListener* listener = static_cast(self); listener->m_clients.emplace_back(std::make_unique(listener, socket)); }; if (!socket->Listen(ListenerCallback, this)) return; m_socket = std::move(socket); } TCPListener::~TCPListener() { } }