32 Commits

Author SHA1 Message Date
6484f8f5c6 Merge branch 'beta' 2026-02-13 13:06:18 +01:00
31cb3d0a02 Merge branch 'dev' into beta 2026-02-13 13:05:59 +01:00
918b80931e Merge branch '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:35:19 +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
07458c348b Merge branch 'beta' 2026-01-18 19:55:13 +01:00
204f89006f Merge branch 'dev' into beta 2026-01-18 19:54:59 +01:00
833629486e Merge branch 'beta'; Version 1.0.1 2026-01-18 19:48:02 +01:00
57d260976c Merge branch 'dev' into beta 2026-01-18 19:47:48 +01:00
cbfbcaa153 Merge branch 'beta' 2026-01-11 20:32:46 +01:00
4fa26d51d0 Merge branch 'dev' into beta 2026-01-11 20:32:28 +01:00
e101f5ddd6 Merge branch 'beta' - Version 1.0.0 2026-01-01 17:21:28 +01:00
e1118ccafe Merge branch 'dev' into beta 2026-01-01 17:21:15 +01:00
c715a43a10 Merge branch 'beta' - Version 1.0.0 2026-01-01 16:33:21 +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
f99036c523 Merge branch 'beta' 2025-12-29 20:28:34 +01:00
3eadd41a00 Merge branch 'dev' into beta 2025-12-29 20:28:15 +01:00
471224b043 Merge branch 'beta' - b0.3 2025-12-29 19:07:16 +01:00
714aa52f98 Merge branch 'dev' into beta 2025-12-29 19:06:59 +01:00
cb0f674c52 Merge branch 'beta' - Version b0.1
macOS Support
2025-12-08 17:38:05 +01:00
a2ecc589f8 Merge branch 'dev' into beta - Version b0.1 2025-12-08 17:37:44 +01:00
33bbd7cce6 Merge branch 'beta' - Alpha 0.6
This version adds Dynamic IP assignment based on config.
2025-12-02 18:47:58 +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
f9c5c56a1b Merge branch 'beta'
This is the merge of version a0.5 into master.
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:31:01 +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
17dd504a7a Merge pull request 'First working alpha, version a0.4' (#7) from beta into master
Reviewed-on: #7
2025-11-18 20:09:11 +00: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
9f52bdd54c Merge pull request 'beta' (#4) from beta into master
Reviewed-on: #4
2025-11-10 15:58:29 +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
29e90938c5 Merge pull request 'beta - Update License' (#2) from beta into master
Reviewed-on: #2
2025-11-10 15:15:31 +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

@@ -86,14 +86,10 @@ namespace ColumnLynx::Net {
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;
}