17 Commits

Author SHA1 Message Date
31cb3d0a02 Merge branch 'dev' into beta 2026-02-13 13:05:59 +01:00
0299b03d9c Merge branch 'dev' into beta - Version 1.1.0
This version introduces protocol version 2:
 - UDP Headers stripped to 16 Bytes, allowing more throughput
 - Nonce is derived, but unique every packet - allows faster sending
2026-02-10 19:33:56 +01:00
204f89006f Merge branch 'dev' into beta 2026-01-18 19:54:59 +01:00
57d260976c Merge branch 'dev' into beta 2026-01-18 19:47:48 +01:00
4fa26d51d0 Merge branch 'dev' into beta 2026-01-11 20:32:28 +01:00
e1118ccafe Merge branch 'dev' into beta 2026-01-01 17:21:15 +01:00
00f72e1a64 Merge branch 'dev' into beta - Version 1.0.0 2026-01-01 16:32:59 +01:00
3cd99243ad Version 1.0.0 2026-01-01 16:32:14 +01:00
8f536abe77 Merge branch 'dev' into beta - Version 1.0.0 2026-01-01 16:23:37 +01:00
3eadd41a00 Merge branch 'dev' into beta 2025-12-29 20:28:15 +01:00
714aa52f98 Merge branch 'dev' into beta 2025-12-29 19:06:59 +01:00
a2ecc589f8 Merge branch 'dev' into beta - Version b0.1 2025-12-08 17:37:44 +01:00
640a751f9b Merge branch 'dev' into beta - Alpha 0.6
This version adds dynamic IP assignment based on config.
2025-12-02 18:46:28 +01:00
a08dba5b59 Merge branch 'dev' into beta
This is the merge of version a0.5 into beta.
This version adds general authentication of the client and server, and control of connection via key whitelisting.
Also added loading of keypairs via a config file system.
2025-11-28 19:27:15 +01:00
4ba59fb23f Merge pull request 'First working alpha, version a0.4.' (#6) from dev into beta
Reviewed-on: #6
2025-11-18 20:07:30 +00:00
9e5e728438 Merge pull request 'Add legal clarification' (#3) from dev into beta
Reviewed-on: #3
2025-11-10 15:58:18 +00:00
d20bee9e60 Merge pull request 'Update license' (#1) from dev into beta
Reviewed-on: #1
2025-11-10 15:15:10 +00:00
9 changed files with 50 additions and 91 deletions

View File

@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16)
# If MAJOR is 0, and MINOR > 0, Version is BETA
project(ColumnLynx
VERSION 1.1.1
VERSION 1.1.0
LANGUAGES CXX
)

View File

@@ -11,7 +11,6 @@
#include <columnlynx/common/utils.hpp>
#include <columnlynx/common/libsodium_wrapper.hpp>
#include <array>
#include <atomic>
#include <algorithm>
#include <vector>
#include <unordered_map>
@@ -90,8 +89,8 @@ namespace ColumnLynx::Net::TCP {
// TODO: Move ptrs to smart ptrs
std::atomic<bool> mConnected{false};
std::atomic<bool> mHandshakeComplete{false};
bool mConnected = false;
bool mHandshakeComplete = false;
tcp::resolver mResolver;
tcp::socket mSocket;
std::shared_ptr<MessageHandler> mHandler;

View File

@@ -39,6 +39,6 @@ namespace ColumnLynx::Net::TCP {
std::array<uint8_t, 3> mHeader{}; // [type][lenHigh][lenLow]
std::vector<uint8_t> mBody;
std::function<void(AnyMessageType, std::string)> mOnMessage;
std::function<void(const asio::error_code&)> mOnDisconnect;
std::function<void(asio::error_code&)> mOnDisconnect;
};
}

View File

@@ -14,25 +14,19 @@ namespace ColumnLynx::Net::TCP {
asio::async_connect(mSocket, endpoints,
[this, self](asio::error_code ec, const tcp::endpoint&) {
if (!ec) {
mConnected.store(true, std::memory_order_relaxed);
mConnected = true;
Utils::log("Client connected.");
mHandler = std::make_shared<MessageHandler>(std::move(mSocket));
mHandler->onMessage([weakSelf = weak_from_this()](AnyMessageType type, const std::string& data) {
if (auto self = weakSelf.lock()) {
self->mHandleMessage(static_cast<ServerMessageType>(MessageHandler::toUint8(type)), data);
}
mHandler->onMessage([this](AnyMessageType type, const std::string& data) {
mHandleMessage(static_cast<ServerMessageType>(MessageHandler::toUint8(type)), data);
});
// Close only after peer FIN to avoid RSTs
mHandler->onDisconnect([weakSelf = weak_from_this()](const asio::error_code& ec) {
auto self = weakSelf.lock();
if (!self) {
return;
}
mHandler->onDisconnect([this](const asio::error_code& ec) {
asio::error_code ec2;
if (self->mHandler) {
self->mHandler->socket().close(ec2);
if (mHandler) {
mHandler->socket().close(ec2);
}
self->mConnected.store(false, std::memory_order_relaxed);
mConnected = false;
Utils::log(std::string("Server disconnected: ") + ec.message());
});
mHandler->start();
@@ -80,7 +74,7 @@ namespace ColumnLynx::Net::TCP {
}
void TCPClient::sendMessage(ClientMessageType type, const std::string& data) {
if (!mConnected.load(std::memory_order_relaxed)) {
if (!mConnected) {
Utils::error("Cannot send message, client not connected.");
return;
}
@@ -93,7 +87,7 @@ namespace ColumnLynx::Net::TCP {
}
void TCPClient::disconnect(bool echo) {
if (mConnected.load(std::memory_order_relaxed) && mHandler) {
if (mConnected && mHandler) {
if (echo) {
mHandler->sendMessage(ClientMessageType::GRACEFUL_DISCONNECT, "Goodbye");
}
@@ -113,17 +107,17 @@ namespace ColumnLynx::Net::TCP {
}
bool TCPClient::isHandshakeComplete() const {
return mHandshakeComplete.load(std::memory_order_relaxed);
return mHandshakeComplete;
}
bool TCPClient::isConnected() const {
return mConnected.load(std::memory_order_relaxed);
return mConnected;
}
void TCPClient::mStartHeartbeat() {
auto self = shared_from_this();
mHeartbeatTimer.expires_after(std::chrono::seconds(5));
mHeartbeatTimer.async_wait([self](const asio::error_code& ec) {
mHeartbeatTimer.async_wait([this, self](const asio::error_code& ec) {
if (ec == asio::error::operation_aborted) {
return; // Timer was cancelled
}
@@ -136,11 +130,9 @@ namespace ColumnLynx::Net::TCP {
// Close sockets forcefully, server is dead
asio::error_code ec;
if (self->mHandler) {
self->mHandler->socket().shutdown(tcp::socket::shutdown_both, ec);
self->mHandler->socket().close(ec);
}
self->mConnected.store(false, std::memory_order_relaxed);
mHandler->socket().shutdown(tcp::socket::shutdown_both, ec);
mHandler->socket().close(ec);
mConnected = false;
ClientSession::getInstance().setAESKey({}); // Clear AES key with all zeros
ClientSession::getInstance().setSessionID(0);
@@ -269,7 +261,7 @@ namespace ColumnLynx::Net::TCP {
mTun->configureIP(clientIP, serverIP, prefixLen, mtu);
}
mHandshakeComplete.store(true, std::memory_order_relaxed);
mHandshakeComplete = true;
}
break;
@@ -284,13 +276,13 @@ namespace ColumnLynx::Net::TCP {
break;
case ServerMessageType::GRACEFUL_DISCONNECT:
Utils::log("Server is disconnecting: " + data);
if (mConnected.load(std::memory_order_relaxed)) { // Prevent Recursion
if (mConnected) { // Prevent Recursion
disconnect(false);
}
break;
case ServerMessageType::KILL_CONNECTION:
Utils::warn("Server is killing the connection: " + data);
if (mConnected.load(std::memory_order_relaxed)) {
if (mConnected) {
disconnect(false);
}
break;

View File

@@ -85,15 +85,11 @@ namespace ColumnLynx::Net {
void SessionRegistry::lockIP(uint32_t sessionID, uint32_t ip) {
std::unique_lock lock(mMutex);
mSessionIPs[sessionID] = ip;
auto it = mSessions.find(sessionID);
if (it == mSessions.end() || !it->second) {
Utils::warn("SessionRegistry::lockIP called for unknown session " + std::to_string(sessionID));
mSessionIPs.erase(sessionID);
return;
}
mIPSessions[ip] = it->second;
/*if (mIPSessions.find(sessionID) == mIPSessions.end()) {
Utils::debug("yikes");
}*/
mIPSessions[ip] = mSessions.find(sessionID)->second;
}
void SessionRegistry::deallocIP(uint32_t sessionID) {

View File

@@ -5,7 +5,6 @@
#include <columnlynx/common/net/tcp/tcp_message_handler.hpp>
#include <columnlynx/common/net/tcp/net_helper.hpp>
#include <columnlynx/common/utils.hpp>
#include <memory>
namespace ColumnLynx::Net::TCP {
void MessageHandler::start() {
@@ -18,17 +17,17 @@ namespace ColumnLynx::Net::TCP {
return static_cast<uint8_t>(type);
}, type);
auto data = std::make_shared<std::vector<uint8_t>>();
data->push_back(typeByte);
std::vector<uint8_t> data;
data.push_back(typeByte);
uint16_t length = payload.size();
data->push_back(length >> 8);
data->push_back(length & 0xFF);
data.push_back(length >> 8);
data.push_back(length & 0xFF);
data->insert(data->end(), payload.begin(), payload.end());
data.insert(data.end(), payload.begin(), payload.end());
auto self = shared_from_this();
asio::async_write(mSocket, asio::buffer(*data),
[self, data](asio::error_code ec, std::size_t) {
asio::async_write(mSocket, asio::buffer(data),
[self](asio::error_code ec, std::size_t) {
if (ec) {
Utils::error("Send failed: " + ec.message());
}

View File

@@ -85,7 +85,7 @@ namespace ColumnLynx::Utils {
}
std::string getVersion() {
return "1.1.1";
return "1.1.0";
}
unsigned short serverPort() {

View File

@@ -6,7 +6,6 @@
#include <iostream>
#include <thread>
#include <chrono>
#include <cstring>
#include <columnlynx/common/utils.hpp>
#include <columnlynx/common/panic_handler.hpp>
#include <columnlynx/server/net/tcp/tcp_server.hpp>
@@ -173,24 +172,9 @@ int main(int argc, char** argv) {
continue;
}
if (packet.size() < 20) {
Utils::warn("TUN: Dropping packet smaller than IPv4 header (" + std::to_string(packet.size()) + " bytes)");
continue;
}
const uint8_t* ip = packet.data();
uint8_t ipVersion = (ip[0] >> 4);
if (ipVersion != 4) {
Utils::debug("TUN: Non-IPv4 packet received (version=" + std::to_string(ipVersion) + "), skipping server IPv4 routing path.");
continue;
}
uint32_t srcIPNet = 0;
uint32_t dstIPNet = 0;
std::memcpy(&srcIPNet, ip + 12, sizeof(srcIPNet)); // IPv4 source address offset
std::memcpy(&dstIPNet, ip + 16, sizeof(dstIPNet)); // IPv4 destination address offset
uint32_t srcIP = ntohl(srcIPNet);
uint32_t dstIP = ntohl(dstIPNet);
uint32_t srcIP = ntohl(*(uint32_t*)(ip + 12)); // IPv4 source address offset
uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address offset
// First, check if destination IP is a registered client (e.g., server responding to client or client-to-client)
auto dstSession = SessionRegistry::getInstance().getByIP(dstIP);

View File

@@ -14,31 +14,23 @@ namespace ColumnLynx::Net::TCP {
Utils::warn("Failed to get remote endpoint: " + std::string(e.what()));
}
mHandler->onMessage([weakSelf = weak_from_this()](AnyMessageType type, const std::string& data) {
if (auto self = weakSelf.lock()) {
self->mHandleMessage(static_cast<ClientMessageType>(MessageHandler::toUint8(type)), data);
}
mHandler->onMessage([this](AnyMessageType type, const std::string& data) {
mHandleMessage(static_cast<ClientMessageType>(MessageHandler::toUint8(type)), data);
});
mHandler->onDisconnect([weakSelf = weak_from_this()](const asio::error_code& ec) {
auto self = weakSelf.lock();
if (!self) {
return;
}
mHandler->onDisconnect([this](const asio::error_code& ec) {
// Peer has closed; finalize locally without sending RST
Utils::log("Client disconnected: " + self->mRemoteIP + " - " + ec.message());
Utils::log("Client disconnected: " + mRemoteIP + " - " + ec.message());
asio::error_code ec2;
if (self->mHandler) {
self->mHandler->socket().close(ec2);
}
mHandler->socket().close(ec2);
SessionRegistry::getInstance().erase(self->mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(self->mConnectionSessionID);
SessionRegistry::getInstance().erase(mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(mConnectionSessionID);
Utils::log("Closed connection to " + self->mRemoteIP);
Utils::log("Closed connection to " + mRemoteIP);
if (self->mOnDisconnect) {
self->mOnDisconnect(self);
if (mOnDisconnect) {
mOnDisconnect(shared_from_this());
}
});
@@ -85,7 +77,7 @@ namespace ColumnLynx::Net::TCP {
void TCPConnection::mStartHeartbeat() {
auto self = shared_from_this();
mHeartbeatTimer.expires_after(std::chrono::seconds(5));
mHeartbeatTimer.async_wait([self](const asio::error_code& ec) {
mHeartbeatTimer.async_wait([this, self](const asio::error_code& ec) {
if (ec == asio::error::operation_aborted) {
return; // Timer was cancelled
}
@@ -98,13 +90,10 @@ namespace ColumnLynx::Net::TCP {
// Remove socket forcefully, client is dead
asio::error_code ec;
if (self->mHandler) {
self->mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
self->mHandler->socket().close(ec);
}
mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
mHandler->socket().close(ec);
SessionRegistry::getInstance().erase(self->mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(self->mConnectionSessionID);
return;
}