Initial Commit
This commit is contained in:
112
include/columnlynx/client/net/tcp/tcp_client.hpp
Normal file
112
include/columnlynx/client/net/tcp/tcp_client.hpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// tcp_client.hpp - TCP Client for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <asio/asio.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_handler.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_type.hpp>
|
||||
#include <columnlynx/common/net/tcp/net_helper.hpp>
|
||||
#include <columnlynx/common/utils.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
class TCPClient : public std::enable_shared_from_this<TCPClient> {
|
||||
public:
|
||||
TCPClient(asio::io_context& ioContext,
|
||||
const std::string& host,
|
||||
const std::string& port)
|
||||
: mResolver(ioContext), mSocket(ioContext), mHost(host), mPort(port) {}
|
||||
|
||||
void start() {
|
||||
auto self = shared_from_this();
|
||||
mResolver.async_resolve(mHost, mPort,
|
||||
[this, self](asio::error_code ec, tcp::resolver::results_type endpoints) {
|
||||
if (!ec) {
|
||||
asio::async_connect(mSocket, endpoints,
|
||||
[this, self](asio::error_code ec, const tcp::endpoint&) {
|
||||
if (!NetHelper::isExpectedDisconnect(ec)) {
|
||||
mConnected = true;
|
||||
Utils::log("Client connected.");
|
||||
mHandler = std::make_shared<MessageHandler>(std::move(mSocket));
|
||||
mHandler->onMessage([this](AnyMessageType type, const std::string& data) {
|
||||
mHandleMessage(static_cast<ServerMessageType>(MessageHandler::toUint8(type)), data);
|
||||
});
|
||||
mHandler->start();
|
||||
|
||||
// Init connection handshake
|
||||
Utils::log("Sending handshake init to server.");
|
||||
mHandler->sendMessage(ClientMessageType::HANDSHAKE_INIT, "Hello, I am " + Utils::getHostname());
|
||||
} else {
|
||||
Utils::error("Client connect failed: " + ec.message());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Utils::error("Client resolve failed: " + ec.message());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sendMessage(ClientMessageType type, const std::string& data = "") {
|
||||
if (!mConnected) {
|
||||
Utils::error("Cannot send message, client not connected.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHandler) {
|
||||
asio::post(mHandler->socket().get_executor(), [self = shared_from_this(), type, data]() {
|
||||
self->mHandler->sendMessage(type, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
if (mConnected && mHandler) {
|
||||
mHandler->sendMessage(ClientMessageType::GRACEFUL_DISCONNECT, "Goodbye");
|
||||
|
||||
asio::error_code ec;
|
||||
|
||||
mHandler->socket().shutdown(tcp::socket::shutdown_both, ec);
|
||||
if (ec) {
|
||||
Utils::error("Error during socket shutdown: " + ec.message());
|
||||
}
|
||||
|
||||
mHandler->socket().close(ec);
|
||||
if (ec) {
|
||||
Utils::error("Error during socket close: " + ec.message());
|
||||
}
|
||||
|
||||
mConnected = false;
|
||||
Utils::log("Client disconnected.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void mHandleMessage(ServerMessageType type, const std::string& data) {
|
||||
switch (type) {
|
||||
case ServerMessageType::HANDSHAKE_IDENTIFY:
|
||||
Utils::log("Received server identity: " + data);
|
||||
//std::memcpy(mServerPublicKey, data.data(), std::min(data.size(), sizeof(mServerPublicKey)));
|
||||
break;
|
||||
case ServerMessageType::GRACEFUL_DISCONNECT:
|
||||
Utils::log("Server is disconnecting: " + data);
|
||||
if (mConnected) { // Prevent Recursion
|
||||
disconnect();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Utils::log("Received unknown message type from server.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool mConnected = false;
|
||||
tcp::resolver mResolver;
|
||||
tcp::socket mSocket;
|
||||
std::shared_ptr<MessageHandler> mHandler;
|
||||
std::string mHost, mPort;
|
||||
uint8_t mServerPublicKey[32]; // Assuming 256-bit public key
|
||||
};
|
||||
}
|
||||
27
include/columnlynx/common/libsodium_wrapper.hpp
Normal file
27
include/columnlynx/common/libsodium_wrapper.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// libsodium_wrapper.hpp - Libsodium Wrapper for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sodium.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <columnlynx/common/utils.hpp>
|
||||
|
||||
namespace ColumnLynx::Utils {
|
||||
|
||||
class LibSodiumWrapper {
|
||||
public:
|
||||
LibSodiumWrapper();
|
||||
|
||||
uint8_t* getPublicKey();
|
||||
uint8_t* getPrivateKey();
|
||||
uint8_t generateRandomAESKey();
|
||||
|
||||
private:
|
||||
uint8_t mPublicKey[crypto_kx_PUBLICKEYBYTES];
|
||||
uint8_t mPrivateKey[crypto_kx_SECRETKEYBYTES];
|
||||
};
|
||||
}
|
||||
19
include/columnlynx/common/net/tcp/net_helper.hpp
Normal file
19
include/columnlynx/common/net/tcp/net_helper.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// net_helper.hpp - Network Helper Functions for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
#include <asio/asio.hpp>
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
class NetHelper {
|
||||
public:
|
||||
inline static bool isExpectedDisconnect(const asio::error_code& ec) {
|
||||
using asio::error::operation_aborted;
|
||||
using asio::error::bad_descriptor;
|
||||
using asio::error::eof;
|
||||
|
||||
return ec == operation_aborted || ec == bad_descriptor || ec == eof;
|
||||
}
|
||||
};
|
||||
}
|
||||
44
include/columnlynx/common/net/tcp/tcp_message_handler.hpp
Normal file
44
include/columnlynx/common/net/tcp/tcp_message_handler.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// tcp_message_handler.hpp - TCP Message Handler for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <asio/asio.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_type.hpp>
|
||||
#include <columnlynx/common/utils.hpp>
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
class MessageHandler : public std::enable_shared_from_this<MessageHandler> {
|
||||
public:
|
||||
MessageHandler(asio::ip::tcp::socket socket)
|
||||
: mSocket(std::move(socket)) {}
|
||||
|
||||
asio::ip::tcp::socket &socket() { return mSocket; }
|
||||
|
||||
void start();
|
||||
void sendMessage(AnyMessageType type, const std::string &payload = "");
|
||||
void onMessage(std::function<void(AnyMessageType, std::string)> callback);
|
||||
void onDisconnect(std::function<void(const asio::error_code&)> callback) {
|
||||
mOnDisconnect = std::move(callback);
|
||||
}
|
||||
|
||||
static AnyMessageType decodeMessageType(uint8_t code);
|
||||
static uint8_t toUint8(const AnyMessageType& type);
|
||||
|
||||
private:
|
||||
void mReadHeader();
|
||||
void mReadBody(uint16_t length);
|
||||
|
||||
asio::ip::tcp::socket mSocket;
|
||||
AnyMessageType mCurrentType = ServerMessageType::KILL_CONNECTION; // Doesn't matter initial value
|
||||
std::array<uint8_t, 3> mHeader{}; // [type][lenHigh][lenLow]
|
||||
std::vector<uint8_t> mBody;
|
||||
std::function<void(AnyMessageType, std::string)> mOnMessage;
|
||||
std::function<void(asio::error_code&)> mOnDisconnect;
|
||||
};
|
||||
}
|
||||
32
include/columnlynx/common/net/tcp/tcp_message_type.hpp
Normal file
32
include/columnlynx/common/net/tcp/tcp_message_type.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// tcp_message_type.hpp - TCP Message Types for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
enum class ServerMessageType : uint8_t { // Server to Client
|
||||
HANDSHAKE_IDENTIFY = 0x02, // Send server identity (public key, server name, etc)
|
||||
HANDSHAKE_CHALLENGE_RESPONSE = 0x04, // Response to client's challenge
|
||||
HANDSHAKE_EXCHANGE_KEY = 0x06, // If accepted, send encrypted AES key and session ID
|
||||
|
||||
GRACEFUL_DISCONNECT = 0xFE, // Notify client of impending disconnection
|
||||
KILL_CONNECTION = 0xFF, // Forecefully terminate the connection (with cleanup if possible), reserved for unrecoverable errors
|
||||
};
|
||||
|
||||
enum class ClientMessageType : uint8_t { // Client to Server
|
||||
HANDSHAKE_INIT = 0xA1, // Request connection
|
||||
HANDSHAKE_CHALLENGE = 0xA3, // Challenge ownership of private key
|
||||
HANDSHAKE_CONFIRM = 0xA5, // Accept or reject identity, can kill the connection
|
||||
HANDSHAKE_EXCHANGE_KEY_CONFIRM = 0xA7, // Confirm receipt of AES key and session ID
|
||||
|
||||
GRACEFUL_DISCONNECT = 0xFE, // Notify server of impending disconnection
|
||||
KILL_CONNECTION = 0xFF, // Forecefully terminate the connection (with cleanup if possible), reserved for unrecoverable errors
|
||||
};
|
||||
|
||||
// Make a variant type for either message type
|
||||
using AnyMessageType = std::variant<ServerMessageType, ClientMessageType>;
|
||||
}
|
||||
214
include/columnlynx/common/panic_handler.hpp
Normal file
214
include/columnlynx/common/panic_handler.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// panic_handler.hpp - Panic Handler for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <psapi.h>
|
||||
#include <processthreadsapi.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
namespace ColumnLynx::Utils {
|
||||
class PanicHandler {
|
||||
public:
|
||||
static void init() {
|
||||
std::set_terminate(terminateHandler);
|
||||
std::signal(SIGSEGV, signalHandler);
|
||||
std::signal(SIGABRT, signalHandler);
|
||||
std::signal(SIGFPE, signalHandler);
|
||||
std::signal(SIGILL, signalHandler);
|
||||
std::signal(SIGTERM, signalHandler);
|
||||
}
|
||||
|
||||
private:
|
||||
static void signalHandler(int signal) {
|
||||
std::string reason;
|
||||
switch (signal) {
|
||||
case SIGSEGV: reason = "Segmentation Fault"; break;
|
||||
case SIGABRT: reason = "Abort (SIGABRT)"; break;
|
||||
case SIGFPE: reason = "Floating Point Exception"; break;
|
||||
case SIGILL: reason = "Illegal Instruction"; break;
|
||||
case SIGTERM: reason = "Termination Signal"; break;
|
||||
default: reason = "Unknown Fatal Signal"; break;
|
||||
}
|
||||
panic(reason);
|
||||
std::_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void terminateHandler() {
|
||||
if (auto eptr = std::current_exception()) {
|
||||
try {
|
||||
std::rethrow_exception(eptr);
|
||||
} catch (const std::exception& e) {
|
||||
panic(std::string("Unhandled exception: ") + e.what());
|
||||
} catch (...) {
|
||||
panic("Unhandled non-standard exception");
|
||||
}
|
||||
} else {
|
||||
panic("Unknown termination cause");
|
||||
}
|
||||
std::_Exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void dumpStack(std::ostream& out) {
|
||||
#ifdef _WIN32
|
||||
void* stack[64];
|
||||
HANDLE process = GetCurrentProcess();
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
|
||||
USHORT frames = CaptureStackBackTrace(0, 64, stack, NULL);
|
||||
SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256, 1);
|
||||
symbol->MaxNameLen = 255;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
|
||||
#if DEBUG || _DEBUG
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
DWORD displacement = 0;
|
||||
#endif
|
||||
|
||||
for (USHORT i = 0; i < frames; i++) {
|
||||
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
||||
out << i << ": " << symbol->Name << " - 0x"
|
||||
<< std::hex << symbol->Address << std::dec;
|
||||
|
||||
#if DEBUG || _DEBUG
|
||||
// Try to resolve file and line info if available
|
||||
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &displacement, &line)) {
|
||||
out << " (" << line.FileName << ":" << line.LineNumber << ")";
|
||||
}
|
||||
#endif
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
#else
|
||||
void* buffer[50];
|
||||
int nptrs = backtrace(buffer, 50);
|
||||
char** strings = backtrace_symbols(buffer, nptrs);
|
||||
|
||||
// --- NEW: Detect base address for PIE binaries ---
|
||||
uintptr_t base_addr = 0;
|
||||
{
|
||||
FILE* maps = fopen("/proc/self/maps", "r");
|
||||
if (maps) {
|
||||
char line[256];
|
||||
if (fgets(line, sizeof(line), maps)) {
|
||||
// First line usually looks like: "55b6d71a0000-55b6d71c0000 r--p ..."
|
||||
sscanf(line, "%lx-", &base_addr);
|
||||
}
|
||||
fclose(maps);
|
||||
}
|
||||
}
|
||||
|
||||
if (strings) {
|
||||
for (int i = 0; i < nptrs; ++i) {
|
||||
out << i << ": " << strings[i] << "\n";
|
||||
#if DEBUG || _DEBUG
|
||||
// Adjust address for PIE executables
|
||||
uintptr_t addr = (uintptr_t)buffer[i] - base_addr;
|
||||
|
||||
// Use addr2line to resolve file and line number
|
||||
char cmd[512];
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"addr2line -e /proc/%d/exe %p 2>/dev/null",
|
||||
getpid(), (void*)addr);
|
||||
|
||||
FILE* fp = popen(cmd, "r");
|
||||
if (fp) {
|
||||
char line[256];
|
||||
if (fgets(line, sizeof(line), fp)) {
|
||||
// addr2line adds a newline already, no need to trim
|
||||
out << " " << line;
|
||||
} else {
|
||||
out << " ??\n";
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
free(strings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dumpSystemInfo(std::ostream& out) {
|
||||
out << "---- System Info ----\n";
|
||||
#ifdef _WIN32
|
||||
out << "Platform: Windows\n";
|
||||
out << "Process ID: " << GetCurrentProcessId() << "\n";
|
||||
|
||||
char cwd[MAX_PATH];
|
||||
GetCurrentDirectoryA(MAX_PATH, cwd);
|
||||
out << "Working Dir: " << cwd << "\n";
|
||||
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(memInfo);
|
||||
GlobalMemoryStatusEx(&memInfo);
|
||||
out << "Memory Load: " << memInfo.dwMemoryLoad << "%\n";
|
||||
out << "Total Phys: " << memInfo.ullTotalPhys / (1024*1024) << " MB\n";
|
||||
out << "Avail Phys: " << memInfo.ullAvailPhys / (1024*1024) << " MB\n";
|
||||
#else
|
||||
struct utsname unameData;
|
||||
uname(&unameData);
|
||||
out << "Platform: " << unameData.sysname << " " << unameData.release
|
||||
<< " (" << unameData.machine << ")\n";
|
||||
out << "Process ID: " << getpid() << "\n";
|
||||
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof(cwd)))
|
||||
out << "Working Dir: " << cwd << "\n";
|
||||
|
||||
struct rusage usage;
|
||||
getrusage(RUSAGE_SELF, &usage);
|
||||
out << "Memory usage: " << usage.ru_maxrss << " KB\n";
|
||||
#endif
|
||||
out << "----------------------\n";
|
||||
}
|
||||
|
||||
//Panic the main thread and instantly halt execution. This produces a stack trace dump. Do not use by itself, throw an error instead.
|
||||
static void panic(const std::string& reason) {
|
||||
std::cerr << "\n***\033[31m MAIN THREAD PANIC! \033[0m***\n";
|
||||
std::cerr << "Reason: " << reason << "\n";
|
||||
std::cerr << "Dumping panic trace...\n";
|
||||
|
||||
std::ofstream dump("panic_dump.txt", std::ios::trunc);
|
||||
if (dump.is_open()) {
|
||||
dump << "==== PANIC DUMP ====\n";
|
||||
dump << "Time: " << currentTime() << "\n";
|
||||
dump << "Reason: " << reason << "\n";
|
||||
dumpSystemInfo(dump);
|
||||
dump << "---- Stack Trace ----\n";
|
||||
dumpStack(dump);
|
||||
dump << "====================\n\n";
|
||||
}
|
||||
|
||||
std::cerr << "Panic trace written to panic_dump.txt\n";
|
||||
}
|
||||
|
||||
static std::string currentTime() {
|
||||
std::time_t t = std::time(nullptr);
|
||||
char buf[64];
|
||||
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
}
|
||||
25
include/columnlynx/common/utils.hpp
Normal file
25
include/columnlynx/common/utils.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// utils.hpp - Utility functions for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace ColumnLynx::Utils {
|
||||
void log(const std::string &msg);
|
||||
void warn(const std::string &msg);
|
||||
void error(const std::string &msg);
|
||||
|
||||
std::string getHostname();
|
||||
std::string getVersion();
|
||||
unsigned short serverPort();
|
||||
};
|
||||
101
include/columnlynx/server/net/tcp/tcp_connection.hpp
Normal file
101
include/columnlynx/server/net/tcp/tcp_connection.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// tcp_connection.hpp - TCP Connection for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <cstdint>
|
||||
#include <asio/asio.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_type.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_handler.hpp>
|
||||
#include <columnlynx/common/utils.hpp>
|
||||
#include <columnlynx/common/libsodium_wrapper.hpp>
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
class TCPConnection : public std::enable_shared_from_this<TCPConnection> {
|
||||
public:
|
||||
using pointer = std::shared_ptr<TCPConnection>;
|
||||
|
||||
static pointer create(
|
||||
asio::ip::tcp::socket socket,
|
||||
Utils::LibSodiumWrapper *libsodium,
|
||||
std::function<void(pointer)> onDisconnect)
|
||||
{
|
||||
auto conn = pointer(new TCPConnection(std::move(socket), libsodium));
|
||||
conn->mOnDisconnect = std::move(onDisconnect);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void start() {
|
||||
mHandler->onMessage([this](AnyMessageType type, const std::string& data) {
|
||||
mHandleMessage(static_cast<ClientMessageType>(MessageHandler::toUint8(type)), data);
|
||||
});
|
||||
|
||||
mHandler->onDisconnect([this](const asio::error_code& ec) {
|
||||
Utils::log("Client disconnected: " + mHandler->socket().remote_endpoint().address().to_string() + " - " + ec.message());
|
||||
disconnect();
|
||||
});
|
||||
|
||||
mHandler->start();
|
||||
|
||||
// Placeholder for message handling setup
|
||||
Utils::log("Client connected: " + mHandler->socket().remote_endpoint().address().to_string());
|
||||
}
|
||||
|
||||
void sendMessage(ServerMessageType type, const std::string& data = "") {
|
||||
if (mHandler) {
|
||||
mHandler->sendMessage(type, data);
|
||||
}
|
||||
}
|
||||
|
||||
void setDisconnectCallback(std::function<void(std::shared_ptr<TCPConnection>)> cb) {
|
||||
mOnDisconnect = std::move(cb);
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
std::string ip = mHandler->socket().remote_endpoint().address().to_string();
|
||||
|
||||
asio::error_code ec;
|
||||
mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
mHandler->socket().close(ec);
|
||||
|
||||
Utils::log("Closed connection to " + ip);
|
||||
|
||||
if (mOnDisconnect) {
|
||||
mOnDisconnect(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TCPConnection(asio::ip::tcp::socket socket, Utils::LibSodiumWrapper *libsodium)
|
||||
: mHandler(std::make_shared<MessageHandler>(std::move(socket))), mLibSodiumWrapper(libsodium) {}
|
||||
|
||||
void mHandleMessage(ClientMessageType type, const std::string& data) {
|
||||
std::string reqAddr = mHandler->socket().remote_endpoint().address().to_string();
|
||||
|
||||
switch (type) {
|
||||
case ClientMessageType::HANDSHAKE_INIT: {
|
||||
Utils::log("Received HANDSHAKE_INIT from " + reqAddr + ": " + data);
|
||||
mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, std::string(reinterpret_cast<const char*>(mLibSodiumWrapper->getPublicKey())));
|
||||
break;
|
||||
}
|
||||
case ClientMessageType::GRACEFUL_DISCONNECT: {
|
||||
Utils::log("Received GRACEFUL_DISCONNECT from " + reqAddr + ": " + data);
|
||||
disconnect();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Utils::warn("Unhandled message type from " + reqAddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageHandler> mHandler;
|
||||
std::function<void(std::shared_ptr<TCPConnection>)> mOnDisconnect;
|
||||
Utils::LibSodiumWrapper *mLibSodiumWrapper;
|
||||
};
|
||||
}
|
||||
38
include/columnlynx/server/net/tcp/tcp_server.hpp
Normal file
38
include/columnlynx/server/net/tcp/tcp_server.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// tcp_server.hpp - TCP Server for ColumnLynx
|
||||
// Copyright (C) 2025 DcruBro
|
||||
// Distributed under the GPLv3 license. See LICENSE for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <asio/asio.hpp>
|
||||
#include <columnlynx/common/net/tcp/tcp_message_type.hpp>
|
||||
#include <columnlynx/common/utils.hpp>
|
||||
#include <columnlynx/server/net/tcp/tcp_connection.hpp>
|
||||
#include <columnlynx/common/libsodium_wrapper.hpp>
|
||||
|
||||
namespace ColumnLynx::Net::TCP {
|
||||
|
||||
class TCPServer {
|
||||
public:
|
||||
TCPServer(asio::io_context& ioContext, uint16_t port, Utils::LibSodiumWrapper* sodiumWrapper)
|
||||
: mIoContext(ioContext), mAcceptor(ioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)), mSodiumWrapper(sodiumWrapper)
|
||||
{
|
||||
Utils::log("Started TCP server on port " + std::to_string(port));
|
||||
mStartAccept();
|
||||
}
|
||||
|
||||
private:
|
||||
void mStartAccept();
|
||||
asio::io_context &mIoContext;
|
||||
asio::ip::tcp::acceptor mAcceptor;
|
||||
std::unordered_set<TCPConnection::pointer> mClients;
|
||||
Utils::LibSodiumWrapper *mSodiumWrapper;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user