21 Commits

Author SHA1 Message Date
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
8923f45356 Add routeset to win32 2025-12-29 19:33:57 +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
d5bf741650 Test Fix double SIGTERM 2025-12-29 19:05:25 +01:00
ae507c3fb9 Test Fix panic on disconnect 2025-12-29 19:02:22 +01:00
072fb69a4a test3 2025-12-29 18:30:54 +01:00
6031d9655a test3 2025-12-29 18:29:42 +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
5 changed files with 48 additions and 38 deletions

View File

@@ -186,7 +186,7 @@ namespace ColumnLynx::Utils {
// 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 << "\n***\033[31m MASTER THREAD PANIC! \033[0m***\n";
std::cerr << "Reason: " << reason << "\n";
std::cerr << "Dumping panic trace...\n";

View File

@@ -76,5 +76,6 @@ namespace ColumnLynx::Net::TCP {
std::chrono::steady_clock::time_point mLastHeartbeatReceived;
std::chrono::steady_clock::time_point mLastHeartbeatSent;
int mMissedHeartbeats = 0;
std::string mRemoteIP; // Cached remote IP to avoid calling remote_endpoint() on closed sockets
};
}

View File

@@ -327,7 +327,15 @@ namespace ColumnLynx::Net {
);
system(cmd);
#elif defined(_WIN32)
char cmd[256];
char cmd[512];
// Remove any persistent routes associated with this interface
snprintf(cmd, sizeof(cmd),
"netsh routing ip delete persistentroute all name=\"%s\"",
mIfName.c_str()
);
system(cmd);
// Reset to DHCP
snprintf(cmd, sizeof(cmd),
"netsh interface ip set address name=\"%s\" dhcp",
mIfName.c_str()
@@ -420,7 +428,11 @@ namespace ColumnLynx::Net {
uint32_t maskInt = (prefixLen == 0) ? 0 : (0xFFFFFFFF << (32 - prefixLen));
mask = ipv4ToString(maskInt);
char cmd[256];
// Calculate network address from IP and mask
uint32_t networkInt = (clientIP & maskInt);
std::string network = ipv4ToString(networkInt);
char cmd[512];
// 1. Set the static IP + mask + gateway
snprintf(cmd, sizeof(cmd),
@@ -436,6 +448,14 @@ namespace ColumnLynx::Net {
);
system(cmd);
// 3. Add route for the VPN network to go through the TUN interface
// This is critical: tells Windows to send packets destined for the server/network through the TUN interface
snprintf(cmd, sizeof(cmd),
"netsh routing ip add persistentroute dest=%s/%d name=\"%s\" nexthopcfg=%s",
network.c_str(), prefixLen, mIfName.c_str(), gw.c_str()
);
system(cmd);
return true;
#else
return false;

View File

@@ -4,6 +4,8 @@
#include <asio.hpp>
#include <iostream>
#include <thread>
#include <chrono>
#include <columnlynx/common/utils.hpp>
#include <columnlynx/common/panic_handler.hpp>
#include <columnlynx/server/net/tcp/tcp_server.hpp>
@@ -23,22 +25,7 @@ using namespace ColumnLynx;
volatile sig_atomic_t done = 0;
void signalHandler(int signum) {
if (signum == SIGINT || signum == SIGTERM) {
log("Received termination signal. Shutting down server gracefully.");
done = 1;
}
}
int main(int argc, char** argv) {
// Capture SIGINT and SIGTERM for graceful shutdown
#if !defined(_WIN32)
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = signalHandler;
sigaction(SIGINT, &action, nullptr);
sigaction(SIGTERM, &action, nullptr);
#endif
cxxopts::Options options("columnlynx_server", "ColumnLynx Server Application");
@@ -69,20 +56,15 @@ int main(int argc, char** argv) {
log("ColumnLynx Server, Version " + getVersion());
log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details.");
#if defined(__WIN32__)
//WintunInitialize();
#endif
std::unordered_map<std::string, std::string> config = Utils::getConfigMap(optionsObj["config"].as<std::string>());
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>(optionsObj["interface"].as<std::string>());
log("Using virtual interface: " + tun->getName());
// Get network configuration from config file
std::unordered_map<std::string, std::string> config = Utils::getConfigMap(optionsObj["config"].as<std::string>());
std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0";
uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24;
uint32_t baseIP = VirtualInterface::stringToIpv4(networkString);
uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1
// Configure the server's TUN interface
tun->configureIP(serverIP, serverIP, subnetMask, 1420);
log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + "/" + std::to_string(subnetMask));
// Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory)
std::shared_ptr<LibSodiumWrapper> sodiumWrapper = std::make_shared<LibSodiumWrapper>();
@@ -135,6 +117,8 @@ int main(int argc, char** argv) {
while (!done) {
auto packet = tun->readPacket();
if (packet.empty()) {
// Small sleep to avoid busy-waiting and to allow signal processing
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}

View File

@@ -6,21 +6,28 @@
namespace ColumnLynx::Net::TCP {
void TCPConnection::start() {
try {
// Cache the remote IP early to avoid calling remote_endpoint() on closed sockets later
mRemoteIP = mHandler->socket().remote_endpoint().address().to_string();
} catch (const std::exception& e) {
mRemoteIP = "unknown";
Utils::warn("Failed to get remote endpoint: " + std::string(e.what()));
}
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) {
// Peer has closed; finalize locally without sending RST
std::string ip = mHandler->socket().remote_endpoint().address().to_string();
Utils::log("Client disconnected: " + ip + " - " + ec.message());
Utils::log("Client disconnected: " + mRemoteIP + " - " + ec.message());
asio::error_code ec2;
mHandler->socket().close(ec2);
SessionRegistry::getInstance().erase(mConnectionSessionID);
SessionRegistry::getInstance().deallocIP(mConnectionSessionID);
Utils::log("Closed connection to " + ip);
Utils::log("Closed connection to " + mRemoteIP);
if (mOnDisconnect) {
mOnDisconnect(shared_from_this());
@@ -31,7 +38,7 @@ namespace ColumnLynx::Net::TCP {
mStartHeartbeat();
// Placeholder for message handling setup
Utils::log("Client connected: " + mHandler->socket().remote_endpoint().address().to_string());
Utils::log("Client connected: " + mRemoteIP);
}
void TCPConnection::sendMessage(ServerMessageType type, const std::string& data) {
@@ -45,8 +52,6 @@ namespace ColumnLynx::Net::TCP {
}
void TCPConnection::disconnect(bool echo) {
std::string ip = mHandler->socket().remote_endpoint().address().to_string();
if (echo) {
mHandler->sendMessage(ServerMessageType::GRACEFUL_DISCONNECT, "Server initiated disconnect.");
}
@@ -58,7 +63,7 @@ namespace ColumnLynx::Net::TCP {
Utils::error("Error during socket shutdown: " + ec.message());
}
// Do not close immediately; final cleanup happens in onDisconnect
Utils::log("Initiated graceful disconnect (half-close) to " + ip);
Utils::log("Initiated graceful disconnect (half-close) to " + mRemoteIP);
}
uint64_t TCPConnection::getSessionID() const {
@@ -102,7 +107,7 @@ namespace ColumnLynx::Net::TCP {
}
void TCPConnection::mHandleMessage(ClientMessageType type, const std::string& data) {
std::string reqAddr = mHandler->socket().remote_endpoint().address().to_string();
std::string& reqAddr = mRemoteIP;
switch (type) {
case ClientMessageType::HANDSHAKE_INIT: {