20 Commits

Author SHA1 Message Date
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
b50b594d68 Added support for macOS-specific kernel stuff. 2025-12-08 17:01:41 +01:00
842752cd88 Version b0.1 (Beta 0.1) - Refactor some stuff and move session_registry to .cpp file 2025-12-04 15:48:18 +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
668b96e8e0 Alpha 0.6 - Dynamic IPs 2025-12-02 18:45:50 +01:00
8ed90a3bc8 Alpha 0.6 - Dynamic IPs 2025-12-02 18:44:57 +01:00
4f4a0fd385 Wipe tuns before adding 2025-12-02 17:23:02 +01:00
3bda0b2ec4 Test3 2025-12-02 17:11:03 +01:00
210f9f2436 Test2 2025-12-02 17:08:40 +01:00
265954d700 Test - fixed session ip locking 2025-12-02 17:00:13 +01:00
0ba78d72ed Test - Updated map check 2025-12-02 16:30:56 +01:00
eb7f4930e2 Test dynamic IPv4 + Subnet masks 2025-12-02 16:25:42 +01:00
4dbde290c2 Update README and IPv6 preparations 2025-12-02 01:18:06 +01:00
15d13b6f04 Converted some raw pointers to smart pointers 2025-12-01 21:13:46 +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
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
9f52bdd54c Merge pull request 'beta' (#4) from beta into master
Reviewed-on: #4
2025-11-10 15:58:29 +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
20 changed files with 363 additions and 150 deletions

View File

@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16)
# If MAJOR is 0, and MINOR > 0, Version is BETA # If MAJOR is 0, and MINOR > 0, Version is BETA
project(ColumnLynx project(ColumnLynx
VERSION 0.0.5 VERSION 0.1.0
LANGUAGES CXX LANGUAGES CXX
) )
@@ -19,7 +19,9 @@ set(CMAKE_CXX_EXTENSIONS OFF)
#set(CMAKE_CXX_FLAGS_DEBUG "-g") #set(CMAKE_CXX_FLAGS_DEBUG "-g")
#add_compile_options(${CMAKE_CXX_FLAGS_DEBUG}) #add_compile_options(${CMAKE_CXX_FLAGS_DEBUG})
add_compile_definitions(DEBUG=1) # TODO: Forcing for now, add dymanic based on compile flags later if(DEBUG)
add_compile_definitions(DEBUG=1) # TODO: Forcing for now, add dymanic based on compile flags later
endif()
include(FetchContent) include(FetchContent)
@@ -38,7 +40,7 @@ endif()
if(WIN32) if(WIN32)
add_compile_definitions(_WIN32_WINNT=0x0A00 NOMINMAX WIN32_LEAN_AND_MEAN) add_compile_definitions(_WIN32_WINNT=0x0A00 NOMINMAX WIN32_LEAN_AND_MEAN)
elseif(UNIX) elseif(UNIX)
add_compile_options(-Wall -Wextra -Wpedantic) add_compile_options(-Wall -Wextra -Wpedantic -O3)
add_link_options(-pthread) add_link_options(-pthread)
endif() endif()

View File

@@ -26,12 +26,16 @@ Configurating the server and client are are relatively easy. Currently (since th
- **SERVER_PUBLIC_KEY** (Hex String): The public key to be used - **SERVER_PUBLIC_KEY** (Hex String): The public key to be used
- **SERVER_PRIVATE_KEY** (Hex String): The private key to be used - **SERVER_PRIVATE_KEY** (Hex String): The private key to be used
- **NETWORK** (IPv4 Format): The network IPv4 to be used (Server Interface still needs to be configured manually)
- **SUBNET_MASK** (Integer): The subnet mask to be used (ensure proper length, it will not be checked)
**Example:** **Example:**
``` ```
SERVER_PUBLIC_KEY=787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B SERVER_PUBLIC_KEY=787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B
SERVER_PRIVATE_KEY=778604245F57B847E63BD85DE8208FF1A127FB559895195928C3987E246B77B8787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B SERVER_PRIVATE_KEY=778604245F57B847E63BD85DE8208FF1A127FB559895195928C3987E246B77B8787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B
NETWORK=10.10.0.0
SUBNET_MASK=24
``` ```
<hr></hr> <hr></hr>
@@ -76,6 +80,8 @@ ColumnLynx makes use of both **TCP** and **UDP**. **TCP** is used for the initia
It operates on port **48042** for both TCP and UDP. It operates on port **48042** for both TCP and UDP.
Generally, all transmission is done in **little-endian byte order**, since pretty much every single modern architecture uses it by default. The only exemption to this is the **transmission of IP addresses** (for the **Virtual Interface**), which is **big-endian**.
### Handshake Procedure ### Handshake Procedure
The handshake between the client and server is done over **TCP**. This is to ensure delivery without much hassle. The handshake between the client and server is done over **TCP**. This is to ensure delivery without much hassle.

View File

@@ -25,10 +25,10 @@ namespace ColumnLynx::Net::TCP {
TCPClient(asio::io_context& ioContext, TCPClient(asio::io_context& ioContext,
const std::string& host, const std::string& host,
const std::string& port, const std::string& port,
Utils::LibSodiumWrapper* sodiumWrapper, std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper,
std::array<uint8_t, 32>* aesKey, std::shared_ptr<std::array<uint8_t, 32>> aesKey,
uint64_t* sessionIDRef, std::shared_ptr<uint64_t> sessionIDRef,
bool* insecureMode, bool insecureMode,
std::shared_ptr<VirtualInterface> tun = nullptr) std::shared_ptr<VirtualInterface> tun = nullptr)
: :
mResolver(ioContext), mResolver(ioContext),
@@ -95,12 +95,12 @@ namespace ColumnLynx::Net::TCP {
std::string mHost, mPort; std::string mHost, mPort;
uint8_t mServerPublicKey[32]; // Assuming 256-bit public key uint8_t mServerPublicKey[32]; // Assuming 256-bit public key
std::array<uint8_t, 32> mSubmittedChallenge{}; std::array<uint8_t, 32> mSubmittedChallenge{};
Utils::LibSodiumWrapper* mLibSodiumWrapper; std::shared_ptr<Utils::LibSodiumWrapper> mLibSodiumWrapper;
uint64_t mConnectionSessionID; uint64_t mConnectionSessionID;
SymmetricKey mConnectionAESKey; SymmetricKey mConnectionAESKey;
std::array<uint8_t, 32>* mGlobalKeyRef; // Reference to global AES key std::shared_ptr<std::array<uint8_t, 32>> mGlobalKeyRef; // Reference to global AES key
uint64_t* mSessionIDRef; // Reference to global Session ID std::shared_ptr<uint64_t> mSessionIDRef; // Reference to global Session ID
bool* mInsecureMode; // Reference to insecure mode flag bool mInsecureMode; // Insecure mode flag
asio::steady_timer mHeartbeatTimer; asio::steady_timer mHeartbeatTimer;
std::chrono::steady_clock::time_point mLastHeartbeatReceived; std::chrono::steady_clock::time_point mLastHeartbeatReceived;
std::chrono::steady_clock::time_point mLastHeartbeatSent; std::chrono::steady_clock::time_point mLastHeartbeatSent;

View File

@@ -17,8 +17,8 @@ namespace ColumnLynx::Net::UDP {
UDPClient(asio::io_context& ioContext, UDPClient(asio::io_context& ioContext,
const std::string& host, const std::string& host,
const std::string& port, const std::string& port,
std::array<uint8_t, 32>* aesKeyRef, std::shared_ptr<std::array<uint8_t, 32>> aesKeyRef,
uint64_t* sessionIDRef, std::shared_ptr<uint64_t> sessionIDRef,
std::shared_ptr<VirtualInterface> tunRef = nullptr) std::shared_ptr<VirtualInterface> tunRef = nullptr)
: mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef), mTunRef(tunRef) : mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef), mTunRef(tunRef)
{ {
@@ -43,8 +43,8 @@ namespace ColumnLynx::Net::UDP {
asio::ip::udp::endpoint mRemoteEndpoint; asio::ip::udp::endpoint mRemoteEndpoint;
std::string mHost; std::string mHost;
std::string mPort; std::string mPort;
std::array<uint8_t, 32>* mAesKeyRef; std::shared_ptr<std::array<uint8_t, 32>> mAesKeyRef;
uint64_t* mSessionIDRef; std::shared_ptr<uint64_t> mSessionIDRef;
std::shared_ptr<VirtualInterface> mTunRef = nullptr; std::shared_ptr<VirtualInterface> mTunRef = nullptr;
std::array<uint8_t, 2048> mRecvBuffer; // Adjust size as needed std::array<uint8_t, 2048> mRecvBuffer; // Adjust size as needed
}; };

View File

@@ -8,7 +8,11 @@
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#include <array> #include <array>
#include <cmath>
#include <sodium.h> #include <sodium.h>
#include <mutex>
#include <atomic>
#include <asio.hpp>
#include <columnlynx/common/utils.hpp> #include <columnlynx/common/utils.hpp>
#include <columnlynx/common/libsodium_wrapper.hpp> #include <columnlynx/common/libsodium_wrapper.hpp>
@@ -48,96 +52,36 @@ namespace ColumnLynx::Net {
static SessionRegistry& getInstance() { static SessionRegistry instance; return instance; } static SessionRegistry& getInstance() { static SessionRegistry instance; return instance; }
// Insert or replace a session entry // Insert or replace a session entry
void put(uint64_t sessionID, std::shared_ptr<SessionState> state) { void put(uint64_t sessionID, std::shared_ptr<SessionState> state);
std::unique_lock lock(mMutex);
mSessions[sessionID] = std::move(state);
mIPSessions[mSessions[sessionID]->clientTunIP] = mSessions[sessionID];
}
// Lookup a session entry by session ID // Lookup a session entry by session ID
std::shared_ptr<const SessionState> get(uint64_t sessionID) const { std::shared_ptr<const SessionState> get(uint64_t sessionID) const;
std::shared_lock lock(mMutex);
auto it = mSessions.find(sessionID);
return (it == mSessions.end()) ? nullptr : it->second;
}
// Lookup a session entry by IPv4 // Lookup a session entry by IPv4
std::shared_ptr<const SessionState> getByIP(uint32_t ip) const { std::shared_ptr<const SessionState> getByIP(uint32_t ip) const;
std::shared_lock lock(mMutex);
auto it = mIPSessions.find(ip);
return (it == mIPSessions.end()) ? nullptr : it->second;
}
// Get a snapshot of the Session Registry // Get a snapshot of the Session Registry
std::unordered_map<uint64_t, std::shared_ptr<SessionState>> snapshot() const { std::unordered_map<uint64_t, std::shared_ptr<SessionState>> snapshot() const;
std::unordered_map<uint64_t, std::shared_ptr<SessionState>> snap;
std::shared_lock lock(mMutex);
snap = mSessions;
return snap;
}
// Remove a session by ID // Remove a session by ID
void erase(uint64_t sessionID) { void erase(uint64_t sessionID);
std::unique_lock lock(mMutex);
mSessions.erase(sessionID);
}
// Cleanup expired sessions // Cleanup expired sessions
void cleanupExpired() { void cleanupExpired();
std::unique_lock lock(mMutex);
auto now = std::chrono::steady_clock::now();
for (auto it = mSessions.begin(); it != mSessions.end(); ) {
if (it->second && it->second->expires <= now) {
it = mSessions.erase(it);
} else {
++it;
}
}
for (auto it = mIPSessions.begin(); it != mIPSessions.end(); ) {
if (it->second && it->second->expires <= now) {
it = mIPSessions.erase(it);
} else {
++it;
}
}
}
// Get the number of registered sessions // Get the number of registered sessions
int size() const { int size() const;
std::shared_lock lock(mMutex);
return static_cast<int>(mSessions.size());
}
// IP management (simple for /24 subnet) // IP management
// Get the lowest available IPv4 address; Returns 0 if none available // Get the lowest available IPv4 address; Returns 0 if none available
uint32_t getFirstAvailableIP() const { uint32_t getFirstAvailableIP(uint32_t baseIP, uint8_t mask) const;
std::shared_lock lock(mMutex);
uint32_t baseIP = 0x0A0A0002; // 10.10.0.2
// TODO: Expand to support larger subnets // Lock IP to session ID; Do NOT call before put() - You will segfault!
for (uint32_t offset = 0; offset < 254; offset++) { void lockIP(uint64_t sessionID, uint32_t ip);
uint32_t candidateIP = baseIP + offset;
if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) {
return candidateIP;
}
}
return 0; // Unavailable // Unlock IP from session ID
} void deallocIP(uint64_t sessionID);
// Lock an IP as assigned to a specific session
void lockIP(uint64_t sessionID, uint32_t ip) {
std::unique_lock lock(mMutex);
mSessionIPs[sessionID] = ip;
}
// Unlock the IP associated with a given session
void deallocIP(uint64_t sessionID) {
std::unique_lock lock(mMutex);
mSessionIPs.erase(sessionID);
}
private: private:
mutable std::shared_mutex mMutex; mutable std::shared_mutex mMutex;

View File

@@ -26,6 +26,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/poll.h>
#elif defined(_WIN32) #elif defined(_WIN32)
#include <windows.h> #include <windows.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
@@ -49,9 +50,13 @@ namespace ColumnLynx::Net {
const std::string& getName() const; const std::string& getName() const;
int getFd() const; // For ASIO integration (on POSIX) int getFd() const; // For ASIO integration (on POSIX)
static inline std::string ipv4ToString(uint32_t ip) { static inline std::string ipv4ToString(uint32_t ip, bool flip = true) {
struct in_addr addr; struct in_addr addr;
addr.s_addr = htonl(ip);
if (flip)
addr.s_addr = htonl(ip);
else
addr.s_addr = ip;
char buf[INET_ADDRSTRLEN]; char buf[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &addr, buf, sizeof(buf))) if (!inet_ntop(AF_INET, &addr, buf, sizeof(buf)))
@@ -60,6 +65,16 @@ namespace ColumnLynx::Net {
return std::string(buf); return std::string(buf);
} }
static inline uint32_t stringToIpv4(const std::string &ipStr) {
struct in_addr addr;
if (inet_pton(AF_INET, ipStr.c_str(), &addr) != 1) {
return 0; // "0.0.0.0"
}
return ntohl(addr.s_addr);
}
static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) { static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) {
if (prefixLen == 0) return 0; if (prefixLen == 0) return 0;
uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF; uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF;

View File

@@ -13,6 +13,8 @@
#include <fstream> #include <fstream>
#include <chrono> #include <chrono>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <algorithm>
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
@@ -22,6 +24,10 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
namespace ColumnLynx {
using IPv6Addr = std::array<uint8_t, 16>;
}
namespace ColumnLynx::Utils { namespace ColumnLynx::Utils {
// General log function. Use for logging important information. // General log function. Use for logging important information.
void log(const std::string &msg); void log(const std::string &msg);
@@ -76,6 +82,18 @@ namespace ColumnLynx::Utils {
return cbswap64(x); return cbswap64(x);
} }
template <typename T>
T cbswap128(const T& x) {
static_assert(sizeof(T) == 16, "cbswap128 requires a 128-bit type");
T out{};
const uint8_t* src = reinterpret_cast<const uint8_t*>(&x);
uint8_t* dst = reinterpret_cast<uint8_t*>(&out);
std::reverse_copy(src, src + 16, dst);
return out;
}
// Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually. // Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually.
std::unordered_map<std::string, std::string> getConfigMap(std::string path); std::unordered_map<std::string, std::string> getConfigMap(std::string path, std::vector<std::string> requiredKeys = {});
}; };

View File

@@ -17,6 +17,7 @@
#include <columnlynx/common/libsodium_wrapper.hpp> #include <columnlynx/common/libsodium_wrapper.hpp>
#include <columnlynx/common/net/session_registry.hpp> #include <columnlynx/common/net/session_registry.hpp>
#include <columnlynx/common/net/protocol_structs.hpp> #include <columnlynx/common/net/protocol_structs.hpp>
#include <columnlynx/common/net/virtual_interface.hpp>
namespace ColumnLynx::Net::TCP { namespace ColumnLynx::Net::TCP {
class TCPConnection : public std::enable_shared_from_this<TCPConnection> { class TCPConnection : public std::enable_shared_from_this<TCPConnection> {
@@ -25,10 +26,11 @@ namespace ColumnLynx::Net::TCP {
static pointer create( static pointer create(
asio::ip::tcp::socket socket, asio::ip::tcp::socket socket,
Utils::LibSodiumWrapper* sodiumWrapper, std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper,
std::unordered_map<std::string, std::string>* serverConfig,
std::function<void(pointer)> onDisconnect) std::function<void(pointer)> onDisconnect)
{ {
auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper)); auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper, serverConfig));
conn->mOnDisconnect = std::move(onDisconnect); conn->mOnDisconnect = std::move(onDisconnect);
return conn; return conn;
} }
@@ -48,10 +50,11 @@ namespace ColumnLynx::Net::TCP {
std::array<uint8_t, 32> getAESKey() const; std::array<uint8_t, 32> getAESKey() const;
private: private:
TCPConnection(asio::ip::tcp::socket socket, Utils::LibSodiumWrapper* sodiumWrapper) TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper, std::unordered_map<std::string, std::string>* serverConfig)
: :
mHandler(std::make_shared<MessageHandler>(std::move(socket))), mHandler(std::make_shared<MessageHandler>(std::move(socket))),
mLibSodiumWrapper(sodiumWrapper), mLibSodiumWrapper(sodiumWrapper),
mRawServerConfig(serverConfig),
mHeartbeatTimer(mHandler->socket().get_executor()), mHeartbeatTimer(mHandler->socket().get_executor()),
mLastHeartbeatReceived(std::chrono::steady_clock::now()), mLastHeartbeatReceived(std::chrono::steady_clock::now()),
mLastHeartbeatSent(std::chrono::steady_clock::now()) mLastHeartbeatSent(std::chrono::steady_clock::now())
@@ -64,7 +67,8 @@ namespace ColumnLynx::Net::TCP {
std::shared_ptr<MessageHandler> mHandler; std::shared_ptr<MessageHandler> mHandler;
std::function<void(std::shared_ptr<TCPConnection>)> mOnDisconnect; std::function<void(std::shared_ptr<TCPConnection>)> mOnDisconnect;
Utils::LibSodiumWrapper *mLibSodiumWrapper; std::shared_ptr<Utils::LibSodiumWrapper> mLibSodiumWrapper;
std::unordered_map<std::string, std::string>* mRawServerConfig;
std::array<uint8_t, 32> mConnectionAESKey; std::array<uint8_t, 32> mConnectionAESKey;
uint64_t mConnectionSessionID; uint64_t mConnectionSessionID;
AsymPublicKey mConnectionPublicKey; AsymPublicKey mConnectionPublicKey;

View File

@@ -24,15 +24,15 @@ namespace ColumnLynx::Net::TCP {
public: public:
TCPServer(asio::io_context& ioContext, TCPServer(asio::io_context& ioContext,
uint16_t port, uint16_t port,
Utils::LibSodiumWrapper* sodiumWrapper, std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper,
bool* hostRunning, bool ipv4Only = false) std::shared_ptr<bool> hostRunning, bool ipv4Only = false)
: mIoContext(ioContext), : mIoContext(ioContext),
mAcceptor(ioContext), mAcceptor(ioContext),
mSodiumWrapper(sodiumWrapper), mSodiumWrapper(sodiumWrapper),
mHostRunning(hostRunning) mHostRunning(hostRunning)
{ {
// Preload the config map // Preload the config map
mRawServerConfig = Utils::getConfigMap("server_config"); mRawServerConfig = Utils::getConfigMap("server_config", {"NETWORK", "SUBNET_MASK"});
asio::error_code ec; asio::error_code ec;
@@ -72,8 +72,8 @@ namespace ColumnLynx::Net::TCP {
asio::io_context &mIoContext; asio::io_context &mIoContext;
asio::ip::tcp::acceptor mAcceptor; asio::ip::tcp::acceptor mAcceptor;
std::unordered_set<TCPConnection::pointer> mClients; std::unordered_set<TCPConnection::pointer> mClients;
Utils::LibSodiumWrapper *mSodiumWrapper; std::shared_ptr<Utils::LibSodiumWrapper> mSodiumWrapper;
bool* mHostRunning; std::shared_ptr<bool> mHostRunning;
std::unordered_map<std::string, std::string> mRawServerConfig; std::unordered_map<std::string, std::string> mRawServerConfig;
}; };

View File

@@ -13,7 +13,7 @@
namespace ColumnLynx::Net::UDP { namespace ColumnLynx::Net::UDP {
class UDPServer { class UDPServer {
public: public:
UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning, bool ipv4Only = false, std::shared_ptr<VirtualInterface> tun = nullptr) UDPServer(asio::io_context& ioContext, uint16_t port, std::shared_ptr<bool> hostRunning, bool ipv4Only = false, std::shared_ptr<VirtualInterface> tun = nullptr)
: mSocket(ioContext), mHostRunning(hostRunning), mTun(tun) : mSocket(ioContext), mHostRunning(hostRunning), mTun(tun)
{ {
asio::error_code ec; asio::error_code ec;
@@ -56,8 +56,8 @@ namespace ColumnLynx::Net::UDP {
asio::ip::udp::socket mSocket; asio::ip::udp::socket mSocket;
asio::ip::udp::endpoint mRemoteEndpoint; asio::ip::udp::endpoint mRemoteEndpoint;
std::array<uint8_t, 2048> mRecvBuffer; // Adjust size as needed std::array<uint8_t, 2048> mRecvBuffer; // 2048 seems stable
bool* mHostRunning; std::shared_ptr<bool> mHostRunning;
std::shared_ptr<VirtualInterface> mTun; std::shared_ptr<VirtualInterface> mTun;
}; };
} }

View File

@@ -74,16 +74,17 @@ int main(int argc, char** argv) {
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>(optionsObj["interface"].as<std::string>()); std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>(optionsObj["interface"].as<std::string>());
log("Using virtual interface: " + tun->getName()); log("Using virtual interface: " + tun->getName());
LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); std::shared_ptr<LibSodiumWrapper> sodiumWrapper = std::make_shared<LibSodiumWrapper>();
debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper.getPublicKey(), 32)); debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper->getPublicKey(), 32));
debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper.getPrivateKey(), 64)); debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper->getPrivateKey(), 64));
std::array<uint8_t, 32> aesKey = {0}; // Defualt zeroed state until modified by handshake std::shared_ptr<std::array<uint8_t, 32>> aesKey = std::make_shared<std::array<uint8_t, 32>>();
uint64_t sessionID = 0; aesKey->fill(0); // Defualt zeroed state until modified by handshake
std::shared_ptr<uint64_t> sessionID = std::make_shared<uint64_t>(0);
asio::io_context io; asio::io_context io;
auto client = std::make_shared<ColumnLynx::Net::TCP::TCPClient>(io, host, port, &sodiumWrapper, &aesKey, &sessionID, &insecureMode, tun); auto client = std::make_shared<ColumnLynx::Net::TCP::TCPClient>(io, host, port, sodiumWrapper, aesKey, sessionID, insecureMode, tun);
auto udpClient = std::make_shared<ColumnLynx::Net::UDP::UDPClient>(io, host, port, &aesKey, &sessionID, tun); auto udpClient = std::make_shared<ColumnLynx::Net::UDP::UDPClient>(io, host, port, aesKey, sessionID, tun);
client->start(); client->start();
udpClient->start(); udpClient->start();
@@ -106,7 +107,7 @@ int main(int argc, char** argv) {
if (packet.empty()) { if (packet.empty()) {
continue; continue;
} }
udpClient->sendMessage(std::string(packet.begin(), packet.end())); udpClient->sendMessage(std::string(packet.begin(), packet.end()));
} }

View File

@@ -145,7 +145,7 @@ namespace ColumnLynx::Net::TCP {
// Verify pubkey against whitelisted_keys // Verify pubkey against whitelisted_keys
std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys(); std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys();
if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(mServerPublicKey, 32)) == whitelistedKeys.end()) { // Key verification is handled in later steps of the handshake if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(mServerPublicKey, 32)) == whitelistedKeys.end()) { // Key verification is handled in later steps of the handshake
if (!(*mInsecureMode)) { if (!mInsecureMode) {
Utils::error("Server public key not in whitelisted_keys. Terminating connection."); Utils::error("Server public key not in whitelisted_keys. Terminating connection.");
disconnect(); disconnect();
return; return;

View File

@@ -36,10 +36,9 @@ namespace ColumnLynx::Net::UDP {
reinterpret_cast<uint8_t*>(&hdr), reinterpret_cast<uint8_t*>(&hdr),
reinterpret_cast<uint8_t*>(&hdr) + sizeof(UDPPacketHeader) reinterpret_cast<uint8_t*>(&hdr) + sizeof(UDPPacketHeader)
); );
uint64_t sid = *mSessionIDRef;
packet.insert(packet.end(), packet.insert(packet.end(),
reinterpret_cast<uint8_t*>(&sid), reinterpret_cast<uint8_t*>(mSessionIDRef.get()),
reinterpret_cast<uint8_t*>(&sid) + sizeof(sid) reinterpret_cast<uint8_t*>(mSessionIDRef.get()) + sizeof(uint64_t)
); );
packet.insert(packet.end(), encryptedPayload.begin(), encryptedPayload.end()); packet.insert(packet.end(), encryptedPayload.begin(), encryptedPayload.end());
@@ -90,6 +89,11 @@ namespace ColumnLynx::Net::UDP {
uint64_t sessionID; uint64_t sessionID;
std::memcpy(&sessionID, mRecvBuffer.data() + sizeof(UDPPacketHeader), sizeof(uint64_t)); std::memcpy(&sessionID, mRecvBuffer.data() + sizeof(UDPPacketHeader), sizeof(uint64_t));
if (sessionID != *mSessionIDRef) {
Utils::warn("Got packet that isn't for me! Dropping!");
return;
}
// Decrypt payload // Decrypt payload
std::vector<uint8_t> ciphertext( std::vector<uint8_t> ciphertext(
mRecvBuffer.begin() + sizeof(UDPPacketHeader) + sizeof(uint64_t), mRecvBuffer.begin() + sizeof(UDPPacketHeader) + sizeof(uint64_t),

View File

@@ -0,0 +1,100 @@
// session_registry.cpp - Session Registry for ColumnLynx
// Copyright (C) 2025 DcruBro
// Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details.
#include <columnlynx/common/net/session_registry.hpp>
namespace ColumnLynx::Net {
void SessionRegistry::put(uint64_t sessionID, std::shared_ptr<SessionState> state) {
std::unique_lock lock(mMutex);
mSessions[sessionID] = std::move(state);
mIPSessions[mSessions[sessionID]->clientTunIP] = mSessions[sessionID];
}
std::shared_ptr<const SessionState> SessionRegistry::get(uint64_t sessionID) const {
std::shared_lock lock(mMutex);
auto it = mSessions.find(sessionID);
return (it == mSessions.end()) ? nullptr : it->second;
}
std::shared_ptr<const SessionState> SessionRegistry::getByIP(uint32_t ip) const {
std::shared_lock lock(mMutex);
auto it = mIPSessions.find(ip);
return (it == mIPSessions.end()) ? nullptr : it->second;
}
std::unordered_map<uint64_t, std::shared_ptr<SessionState>> SessionRegistry::snapshot() const {
std::unordered_map<uint64_t, std::shared_ptr<SessionState>> snap;
std::shared_lock lock(mMutex);
snap = mSessions;
return snap;
}
void SessionRegistry::erase(uint64_t sessionID) {
std::unique_lock lock(mMutex);
mSessions.erase(sessionID);
}
void SessionRegistry::cleanupExpired() {
std::unique_lock lock(mMutex);
auto now = std::chrono::steady_clock::now();
for (auto it = mSessions.begin(); it != mSessions.end(); ) {
if (it->second && it->second->expires <= now) {
it = mSessions.erase(it);
} else {
++it;
}
}
for (auto it = mIPSessions.begin(); it != mIPSessions.end(); ) {
if (it->second && it->second->expires <= now) {
it = mIPSessions.erase(it);
} else {
++it;
}
}
}
int SessionRegistry::size() const {
std::shared_lock lock(mMutex);
return static_cast<int>(mSessions.size());
}
uint32_t SessionRegistry::getFirstAvailableIP(uint32_t baseIP, uint8_t mask) const {
std::shared_lock lock(mMutex);
uint32_t hostCount = (1u << (32 - mask));
uint32_t firstHost = 2;
uint32_t lastHost = hostCount - 2;
for (uint32_t offset = firstHost; offset <= lastHost; offset++) {
uint32_t candidateIP = baseIP + offset;
if (mIPSessions.find(candidateIP) == mIPSessions.end()) {
return candidateIP;
}
}
return 0;
}
void SessionRegistry::lockIP(uint64_t sessionID, uint32_t ip) {
std::unique_lock lock(mMutex);
mSessionIPs[sessionID] = ip;
/*if (mIPSessions.find(sessionID) == mIPSessions.end()) {
Utils::debug("yikes");
}*/
mIPSessions[ip] = mSessions.find(sessionID)->second;
}
void SessionRegistry::deallocIP(uint64_t sessionID) {
std::unique_lock lock(mMutex);
auto it = mSessionIPs.find(sessionID);
if (it != mSessionIPs.end()) {
uint32_t ip = it->second;
mIPSessions.erase(ip);
mSessionIPs.erase(it);
}
}
}

View File

@@ -49,7 +49,7 @@ namespace ColumnLynx::Utils {
} }
std::string getVersion() { std::string getVersion() {
return "a0.5"; return "b0.1";
} }
unsigned short serverPort() { unsigned short serverPort() {
@@ -118,7 +118,7 @@ namespace ColumnLynx::Utils {
return out; return out;
} }
std::unordered_map<std::string, std::string> getConfigMap(std::string path) { std::unordered_map<std::string, std::string> getConfigMap(std::string path, std::vector<std::string> requiredKeys) {
// TODO: Currently re-reads every time. // TODO: Currently re-reads every time.
std::vector<std::string> readLines; std::vector<std::string> readLines;
@@ -145,6 +145,14 @@ namespace ColumnLynx::Utils {
config.insert({ key, val }); config.insert({ key, val });
} }
if (!requiredKeys.empty()) {
for (std::string x : requiredKeys) {
if (config.find(x) == config.end()) {
throw std::runtime_error("Config doesn't contain all required keys! (Missing: '" + x + "')");
}
}
}
return config; return config;
} }
} }

View File

@@ -28,6 +28,7 @@ namespace ColumnLynx::Net {
#elif defined(__APPLE__) #elif defined(__APPLE__)
// ---- macOS: UTUN (system control socket) ---- // ---- macOS: UTUN (system control socket) ----
// TL;DR: macOS doesn't really have a "device file" for TUN/TAP like Linux. Instead we have to request a "system control socket" from the kernel.
mFd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); mFd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (mFd < 0) if (mFd < 0)
throw std::runtime_error("socket(PF_SYSTEM) failed: " + std::string(strerror(errno))); throw std::runtime_error("socket(PF_SYSTEM) failed: " + std::string(strerror(errno)));
@@ -42,7 +43,7 @@ namespace ColumnLynx::Net {
sc.sc_family = AF_SYSTEM; sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL; sc.ss_sysaddr = AF_SYS_CONTROL;
sc.sc_id = ctlInfo.ctl_id; sc.sc_id = ctlInfo.ctl_id;
sc.sc_unit = 0; // lynx0 (0 = auto-assign) sc.sc_unit = 0; // 0 = auto-assign next utunX
if (connect(mFd, (struct sockaddr*)&sc, sizeof(sc)) < 0) { if (connect(mFd, (struct sockaddr*)&sc, sizeof(sc)) < 0) {
if (errno == EPERM) if (errno == EPERM)
@@ -50,16 +51,17 @@ namespace ColumnLynx::Net {
throw std::runtime_error("connect(AF_SYS_CONTROL) failed: " + std::string(strerror(errno))); throw std::runtime_error("connect(AF_SYS_CONTROL) failed: " + std::string(strerror(errno)));
} }
// Retrieve actual utun device name // Retrieve actual utun device name via UTUN_OPT_IFNAME
struct sockaddr_storage addr; char ifname[IFNAMSIZ];
socklen_t addrlen = sizeof(addr); socklen_t ifname_len = sizeof(ifname);
if (getsockname(mFd, (struct sockaddr*)&addr, &addrlen) == 0) { if (getsockopt(mFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, ifname, &ifname_len) == 0) {
const struct sockaddr_ctl* addr_ctl = (const struct sockaddr_ctl*)&addr; mIfName = ifname; // Update to actual assigned name
mIfName = "utun" + std::to_string(addr_ctl->sc_unit - 1);
} else { } else {
mIfName = "utunX"; mIfName = "utun0"; // Fallback (should not happen)
} }
Utils::log("VirtualInterface: opened macOS UTUN: " + mIfName);
#elif defined(_WIN32) #elif defined(_WIN32)
// ---- Windows: Wintun (WireGuard virtual adapter) ---- // ---- Windows: Wintun (WireGuard virtual adapter) ----
WINTUN_ADAPTER_HANDLE adapter = WINTUN_ADAPTER_HANDLE adapter =
@@ -95,24 +97,73 @@ namespace ColumnLynx::Net {
// ------------------------------ Read ------------------------------ // ------------------------------ Read ------------------------------
std::vector<uint8_t> VirtualInterface::readPacket() { std::vector<uint8_t> VirtualInterface::readPacket() {
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__)
// Linux TUN: blocking read is fine, unblocks on fd close / EINTR
std::vector<uint8_t> buf(4096); std::vector<uint8_t> buf(4096);
ssize_t n = read(mFd, buf.data(), buf.size()); ssize_t n = read(mFd, buf.data(), buf.size());
if (n < 0) { if (n < 0) {
if (errno == EINTR) { if (errno == EINTR) {
return {}; // Interrupted, return empty return {}; // Interrupted, just return empty
} }
throw std::runtime_error("read() failed: " + std::string(strerror(errno))); throw std::runtime_error("read() failed: " + std::string(strerror(errno)));
} }
buf.resize(n); buf.resize(n);
return buf; return buf;
#elif defined(__APPLE__)
// macOS utun: must poll, or read() can block forever
std::vector<uint8_t> buf(4096);
struct pollfd pfd;
pfd.fd = mFd;
pfd.events = POLLIN;
// timeout in ms; keep it small so shutdown is responsive
int ret = poll(&pfd, 1, 200);
if (ret == 0) {
// No data yet
return {};
}
if (ret < 0) {
if (errno == EINTR) {
return {}; // Interrupted by signal
}
throw std::runtime_error("poll() failed: " + std::string(strerror(errno)));
}
if (!(pfd.revents & POLLIN)) {
return {};
}
ssize_t n = read(mFd, buf.data(), buf.size());
if (n <= 0) {
// 0 or -1: treat as EOF or transient; you can decide how aggressive to be
return {};
}
if (n > 4) {
// Drop macOS UTUN header (4 bytes)
std::memmove(buf.data(), buf.data() + 4, n - 4);
buf.resize(n - 4);
} else {
return {};
}
return buf;
#elif defined(_WIN32) #elif defined(_WIN32)
WINTUN_PACKET* packet = WintunReceivePacket(mSession, nullptr); WINTUN_PACKET* packet = WintunReceivePacket(mSession, nullptr);
if (!packet) return {}; if (!packet) return {};
std::vector<uint8_t> buf(packet->Data, packet->Data + packet->Length); std::vector<uint8_t> buf(packet->Data, packet->Data + packet->Length);
WintunReleaseReceivePacket(mSession, packet); WintunReleaseReceivePacket(mSession, packet);
return buf; return buf;
#else #else
return {}; return {};
#endif #endif
@@ -120,16 +171,48 @@ namespace ColumnLynx::Net {
// ------------------------------ Write ------------------------------ // ------------------------------ Write ------------------------------
void VirtualInterface::writePacket(const std::vector<uint8_t>& packet) { void VirtualInterface::writePacket(const std::vector<uint8_t>& packet) {
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__)
// Linux TUN expects raw IP packet
ssize_t n = write(mFd, packet.data(), packet.size()); ssize_t n = write(mFd, packet.data(), packet.size());
if (n < 0) if (n < 0)
throw std::runtime_error("write() failed: " + std::string(strerror(errno))); throw std::runtime_error("write() failed: " + std::string(strerror(errno)));
#elif defined(__APPLE__)
if (packet.empty())
return;
// Detect IPv4 or IPv6
uint8_t version = packet[0] >> 4;
uint32_t af;
if (version == 4) {
af = htonl(AF_INET);
} else if (version == 6) {
af = htonl(AF_INET6);
} else {
throw std::runtime_error("writePacket(): unknown IP version");
}
// Prepend 4-byte AF header
std::vector<uint8_t> out(packet.size() + 4);
memcpy(out.data(), &af, 4);
memcpy(out.data() + 4, packet.data(), packet.size());
ssize_t n = write(mFd, out.data(), out.size());
if (n < 0)
throw std::runtime_error("utun write() failed: " + std::string(strerror(errno)));
#elif defined(_WIN32) #elif defined(_WIN32)
WINTUN_PACKET* tx = WintunAllocateSendPacket(mSession, (DWORD)packet.size()); WINTUN_PACKET* tx = WintunAllocateSendPacket(mSession, (DWORD)packet.size());
if (!tx) throw std::runtime_error("WintunAllocateSendPacket failed"); if (!tx)
throw std::runtime_error("WintunAllocateSendPacket failed");
memcpy(tx->Data, packet.data(), packet.size()); memcpy(tx->Data, packet.data(), packet.size());
WintunSendPacket(mSession, tx); WintunSendPacket(mSession, tx);
#endif #endif
} }
@@ -166,6 +249,13 @@ namespace ColumnLynx::Net {
std::string ipStr = ipv4ToString(clientIP); std::string ipStr = ipv4ToString(clientIP);
std::string peerStr = ipv4ToString(serverIP); std::string peerStr = ipv4ToString(serverIP);
// Wipe the current config
snprintf(cmd, sizeof(cmd),
"ip addr flush dev %s",
mIfName.c_str()
);
system(cmd);
snprintf(cmd, sizeof(cmd), snprintf(cmd, sizeof(cmd),
"ip addr add %s/%d peer %s dev %s", "ip addr add %s/%d peer %s dev %s",
ipStr.c_str(), prefixLen, peerStr.c_str(), mIfName.c_str()); ipStr.c_str(), prefixLen, peerStr.c_str(), mIfName.c_str());
@@ -188,12 +278,26 @@ namespace ColumnLynx::Net {
std::string ipStr = ipv4ToString(clientIP); std::string ipStr = ipv4ToString(clientIP);
std::string peerStr = ipv4ToString(serverIP); std::string peerStr = ipv4ToString(serverIP);
std::string prefixStr = ipv4ToString(prefixLen); std::string prefixStr = ipv4ToString(prefixLengthToNetmask(prefixLen), false);
Utils::debug("Prefix string: " + prefixStr);
// Set netmask (/24 CIDR temporarily with raw command, improve later) // Reset
snprintf(cmd, sizeof(cmd), snprintf(cmd, sizeof(cmd),
"ifconfig lynx0 %s %s mtu %d netmask %s up", "ifconfig %s inet 0.0.0.0 delete",
ipStr.c_str(), peerStr.c_str(), mtu, prefixStr.c_str()); mIfName.c_str()
);
system(cmd);
snprintf(cmd, sizeof(cmd),
"ifconfig %s inet6 :: delete",
mIfName.c_str()
);
system(cmd);
// Set
snprintf(cmd, sizeof(cmd),
"ifconfig %s inet %s %s mtu %d netmask %s up",
mIfName.c_str(), ipStr.c_str(), peerStr.c_str(), mtu, prefixStr.c_str());
system(cmd); system(cmd);
Utils::log("Executed command: " + std::string(cmd)); Utils::log("Executed command: " + std::string(cmd));

View File

@@ -77,7 +77,7 @@ int main(int argc, char** argv) {
log("Using virtual interface: " + tun->getName()); log("Using virtual interface: " + tun->getName());
// Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory)
LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); std::shared_ptr<LibSodiumWrapper> sodiumWrapper = std::make_shared<LibSodiumWrapper>();
auto itPubkey = config.find("SERVER_PUBLIC_KEY"); auto itPubkey = config.find("SERVER_PUBLIC_KEY");
auto itPrivkey = config.find("SERVER_PRIVATE_KEY"); auto itPrivkey = config.find("SERVER_PRIVATE_KEY");
@@ -91,27 +91,26 @@ int main(int argc, char** argv) {
std::copy_n(Utils::hexStringToBytes(itPrivkey->second).begin(), sk.size(), sk.begin()); std::copy_n(Utils::hexStringToBytes(itPrivkey->second).begin(), sk.size(), sk.begin());
std::copy_n(Utils::hexStringToBytes(itPubkey->second).begin(), pk.size(), pk.begin()); std::copy_n(Utils::hexStringToBytes(itPubkey->second).begin(), pk.size(), pk.begin());
sodiumWrapper.setKeys(pk, sk); sodiumWrapper->setKeys(pk, sk);
} else { } else {
warn("No keypair found in config file! Using random key."); warn("No keypair found in config file! Using random key.");
} }
log("Server public key: " + bytesToHexString(sodiumWrapper.getPublicKey(), crypto_sign_PUBLICKEYBYTES)); log("Server public key: " + bytesToHexString(sodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES));
//log("Server private key: " + bytesToHexString(sodiumWrapper.getPrivateKey(), crypto_sign_SECRETKEYBYTES)); // TEMP, remove later
bool hostRunning = true; std::shared_ptr<bool> hostRunning = std::make_shared<bool>(true);
asio::io_context io; asio::io_context io;
auto server = std::make_shared<TCPServer>(io, serverPort(), &sodiumWrapper, &hostRunning, ipv4Only); auto server = std::make_shared<TCPServer>(io, serverPort(), sodiumWrapper, hostRunning, ipv4Only);
auto udpServer = std::make_shared<UDPServer>(io, serverPort(), &hostRunning, ipv4Only, tun); auto udpServer = std::make_shared<UDPServer>(io, serverPort(), hostRunning, ipv4Only, tun);
asio::signal_set signals(io, SIGINT, SIGTERM); asio::signal_set signals(io, SIGINT, SIGTERM);
signals.async_wait([&](const std::error_code&, int) { signals.async_wait([&](const std::error_code&, int) {
log("Received termination signal. Shutting down server gracefully."); log("Received termination signal. Shutting down server gracefully.");
done = 1; done = 1;
asio::post(io, [&]() { asio::post(io, [&]() {
hostRunning = false; *hostRunning = false;
server->stop(); server->stop();
udpServer->stop(); udpServer->stop();
}); });
@@ -145,9 +144,6 @@ int main(int argc, char** argv) {
} }
log("Shutting down server..."); log("Shutting down server...");
/*hostRunning = false;
server->stop();
udpServer->stop();*/
io.stop(); io.stop();
if (ioThread.joinable()) { if (ioThread.joinable()) {

View File

@@ -202,7 +202,18 @@ namespace ColumnLynx::Net::TCP {
// Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose) // Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose)
Nonce symNonce{}; // All zeros Nonce symNonce{}; // All zeros
uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(); std::string networkString = mRawServerConfig->find("NETWORK")->second; // The load check guarantees that this value exists
uint8_t configMask = std::stoi(mRawServerConfig->find("SUBNET_MASK")->second); // Same deal here
uint32_t baseIP = Net::VirtualInterface::stringToIpv4(networkString);
if (baseIP == 0) {
Utils::warn("Your NETWORK value in the server configuration is malformed! I will not be able to accept connections! (Connection " + reqAddr + " was killed)");
disconnect();
return;
}
uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(baseIP, configMask);
if (clientIP == 0) { if (clientIP == 0) {
Utils::warn("Out of available IPs! Disconnecting client " + reqAddr); Utils::warn("Out of available IPs! Disconnecting client " + reqAddr);
@@ -214,13 +225,11 @@ namespace ColumnLynx::Net::TCP {
tunConfig.version = Utils::protocolVersion(); tunConfig.version = Utils::protocolVersion();
tunConfig.prefixLength = 24; tunConfig.prefixLength = 24;
tunConfig.mtu = 1420; tunConfig.mtu = 1420;
tunConfig.serverIP = htonl(0x0A0A0001); // 10.10.0.1 tunConfig.serverIP = htonl(baseIP + 1); // e.g. 10.10.0.1
tunConfig.clientIP = htonl(clientIP); // 10.10.0.X tunConfig.clientIP = htonl(clientIP); // e.g. 10.10.0.X
tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8 tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8
tunConfig.dns2 = 0; tunConfig.dns2 = 0;
SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP);
uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID); uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID);
std::vector<uint8_t> payload(sizeof(uint64_t) + sizeof(tunConfig)); std::vector<uint8_t> payload(sizeof(uint64_t) + sizeof(tunConfig));
@@ -238,6 +247,7 @@ namespace ColumnLynx::Net::TCP {
Utils::log("Handshake with " + reqAddr + " completed successfully. Session ID assigned (" + std::to_string(mConnectionSessionID) + ")."); Utils::log("Handshake with " + reqAddr + " completed successfully. Session ID assigned (" + std::to_string(mConnectionSessionID) + ").");
auto session = std::make_shared<SessionState>(mConnectionAESKey, std::chrono::hours(12), clientIP, htonl(0x0A0A0001), mConnectionSessionID); auto session = std::make_shared<SessionState>(mConnectionAESKey, std::chrono::hours(12), clientIP, htonl(0x0A0A0001), mConnectionSessionID);
SessionRegistry::getInstance().put(mConnectionSessionID, std::move(session)); SessionRegistry::getInstance().put(mConnectionSessionID, std::move(session));
SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP);
} catch (const std::exception& e) { } catch (const std::exception& e) {
Utils::error("Failed to decrypt HANDSHAKE_EXCHANGE_KEY from " + reqAddr + ": " + e.what()); Utils::error("Failed to decrypt HANDSHAKE_EXCHANGE_KEY from " + reqAddr + ": " + e.what());

View File

@@ -31,10 +31,11 @@ namespace ColumnLynx::Net::TCP {
mStartAccept(); mStartAccept();
return; return;
} }
auto client = TCPConnection::create( auto client = TCPConnection::create(
std::move(socket), std::move(socket),
mSodiumWrapper, mSodiumWrapper,
&mRawServerConfig,
[this](std::shared_ptr<TCPConnection> c) { [this](std::shared_ptr<TCPConnection> c) {
mClients.erase(c); mClients.erase(c);
Utils::log("Client removed."); Utils::log("Client removed.");