#include "../Common.h" #include "Protocol.h" #include "TCPListener.h" #include "NetworkManager.h" #include "NetworkMessage.h" #include "PacketReader.h" #include "ClientHandle.h" #include #include #include #include #include #include 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 final : public ClientHandle { private: friend class TCPListener; // Protocol for this specific client Protocol m_protocol; TCPListener *m_parent; bufferevent *m_bufferEvent; std::vector m_incomingData; std::mutex m_mutex; public: TCPListenerClient(TCPListener* parent, evutil_socket_t socket) : m_parent(parent) , m_protocol(this) , 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)) { printf("Created TCPListenerClient\n"); 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() { printf("Read callback!\n"); auto lock = std::unique_lock(m_mutex); uint8_t data[1KB]; size_t length; size_t originalSize = m_incomingData.size(); // TODO: could switch to bufferevent_read_buffer and use evbuffer to avoid copies 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); } uint8_t* dataPtr = &(m_incomingData.data()[originalSize]); PacketReader reader(dataPtr); m_protocol.HandlePacket(reader); } template void WritePingMessage(T& message, std::string json) { // Packet ID message.WriteVarInt(0); // JSON Contents message.WriteString(json.c_str(), static_cast(json.length())); } DEFINE_MESSAGE_WRAPPER(WritePingMessage); bool SendShitTest() { auto lock = std::unique_lock(m_mutex); const std::string 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 = WritePingMessage(json_template); bufferevent_write(m_bufferEvent, message.GetData(), message.GetDataSize()); return true; } void WriteCallback() { printf("Write callback!\n"); //SendShitTest(); } void EventCallback(short event) { printf("Event! %d\n", event); } }; 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() { } void TCPListener::DispatchQueuedPackets() { for (const auto& client : m_clients) { while (!client->m_queue.empty()) { NetworkMessage *msg = client->m_queue.front(); printf("Writing packet of size %u\n", msg->GetDataSize()); bufferevent_write(client->m_bufferEvent, msg->GetData(), msg->GetDataSize()); client->m_queue.pop(); } } } }