Compare commits
12 Commits
766f878a8d
...
auth_test
| Author | SHA1 | Date | |
|---|---|---|---|
| d2242ebbc7 | |||
| 84c00b7bcb | |||
| 7b757c814c | |||
|
|
e3df3cd0a7 | ||
|
|
f776f1fdd1 | ||
|
|
de3ec98363 | ||
| 022debfa5b | |||
| a78b98ac56 | |||
| 09806c3c0f | |||
| ff81bfed31 | |||
| 2343fdd1e2 | |||
| 3ad98b8403 |
@@ -6,16 +6,20 @@ cmake_minimum_required(VERSION 3.16)
|
||||
# If MAJOR is 0, and MINOR > 0, Version is BETA
|
||||
|
||||
project(ColumnLynx
|
||||
VERSION 0.0.3
|
||||
VERSION 0.0.5
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# General C++ setup
|
||||
# ---------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
#add_compile_options(${CMAKE_CXX_FLAGS_DEBUG})
|
||||
|
||||
add_compile_definitions(DEBUG=1) # TODO: Forcing for now, add dymanic based on compile flags later
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <columnlynx/common/net/protocol_structs.hpp>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
|
||||
@@ -42,19 +43,46 @@ namespace ColumnLynx::Net::TCP {
|
||||
mLastHeartbeatReceived(std::chrono::steady_clock::now()),
|
||||
mLastHeartbeatSent(std::chrono::steady_clock::now()),
|
||||
mTun(tun)
|
||||
{}
|
||||
{
|
||||
// Preload the config map
|
||||
mRawClientConfig = Utils::getConfigMap("client_config");
|
||||
|
||||
if (!mRawClientConfig.empty()) {
|
||||
Utils::debug("Loading the keys");
|
||||
|
||||
PrivateKey sk;
|
||||
PublicKey pk;
|
||||
std::copy_n(Utils::hexStringToBytes(mRawClientConfig.find("CLIENT_PRIVATE_KEY")->second).begin(), sk.size(), sk.begin()); // This is extremely stupid, but the C++ compiler has forced my hand (I would've just used to_array, but fucking asio decls)
|
||||
std::copy_n(Utils::hexStringToBytes(mRawClientConfig.find("CLIENT_PUBLIC_KEY")->second).begin(), pk.size(), pk.begin());
|
||||
|
||||
mLibSodiumWrapper->setKeys(pk, sk);
|
||||
|
||||
Utils::debug("Newly-Loaded Public Key: " + Utils::bytesToHexString(mLibSodiumWrapper->getPublicKey(), 32));
|
||||
Utils::debug("Newly-Loaded Private Key: " + Utils::bytesToHexString(mLibSodiumWrapper->getPrivateKey(), 64));
|
||||
Utils::debug("Public Encryption Key: " + Utils::bytesToHexString(mLibSodiumWrapper->getXPublicKey(), 32));
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the TCP Client and initiaties the handshake
|
||||
void start();
|
||||
// Sends a TCP message to the server
|
||||
void sendMessage(ClientMessageType type, const std::string& data = "");
|
||||
// Attempt to gracefully disconnect from the server
|
||||
void disconnect(bool echo = true);
|
||||
|
||||
// Get the handshake status
|
||||
bool isHandshakeComplete() const;
|
||||
// Get the connection status
|
||||
bool isConnected() const;
|
||||
|
||||
private:
|
||||
// Start the heartbeat routine
|
||||
void mStartHeartbeat();
|
||||
// Handle an incoming TCP message
|
||||
void mHandleMessage(ServerMessageType type, const std::string& data);
|
||||
|
||||
// TODO: Move ptrs to smart ptrs
|
||||
|
||||
bool mConnected = false;
|
||||
bool mHandshakeComplete = false;
|
||||
tcp::resolver mResolver;
|
||||
@@ -76,5 +104,6 @@ namespace ColumnLynx::Net::TCP {
|
||||
bool mIsHostDomain;
|
||||
Protocol::TunConfig mTunConfig;
|
||||
std::shared_ptr<VirtualInterface> mTun = nullptr;
|
||||
std::unordered_map<std::string, std::string> mRawClientConfig;
|
||||
};
|
||||
}
|
||||
@@ -25,12 +25,17 @@ namespace ColumnLynx::Net::UDP {
|
||||
mStartReceive();
|
||||
}
|
||||
|
||||
// Start the UDP client
|
||||
void start();
|
||||
// Send a UDP message
|
||||
void sendMessage(const std::string& data = "");
|
||||
// Stop the UDP client
|
||||
void stop();
|
||||
|
||||
private:
|
||||
// Start the UDP listener routine
|
||||
void mStartReceive();
|
||||
// Handle an incoming UDP message
|
||||
void mHandlePacket(std::size_t bytes);
|
||||
|
||||
asio::ip::udp::socket mSocket;
|
||||
|
||||
@@ -35,27 +35,35 @@ namespace ColumnLynx::Utils {
|
||||
public:
|
||||
LibSodiumWrapper();
|
||||
|
||||
// These are pretty self-explanatory
|
||||
|
||||
uint8_t* getPublicKey();
|
||||
uint8_t* getPrivateKey();
|
||||
uint8_t* getXPublicKey() { return mXPublicKey.data(); }
|
||||
uint8_t* getXPrivateKey() { return mXPrivateKey.data(); }
|
||||
|
||||
// Set the Asymmetric signing keypair. This also regenerates the corresponding encryption keypair; Dangerous!
|
||||
void setKeys(PublicKey pk, PrivateKey sk) {
|
||||
mPublicKey = pk;
|
||||
mPrivateKey = sk;
|
||||
|
||||
// Convert to Curve25519 keys for encryption
|
||||
crypto_sign_ed25519_pk_to_curve25519(mXPublicKey.data(), mPublicKey.data());
|
||||
crypto_sign_ed25519_sk_to_curve25519(mXPrivateKey.data(), mPrivateKey.data());
|
||||
}
|
||||
|
||||
// Helper section
|
||||
|
||||
// Generates a random 256-bit (32-byte) array
|
||||
static std::array<uint8_t, 32> generateRandom256Bit();
|
||||
|
||||
// Sign a message with the stored private key
|
||||
static inline Signature signMessage(const uint8_t* msg, size_t len, const PrivateKey& sk) {
|
||||
Signature sig{};
|
||||
crypto_sign_detached(sig.data(), nullptr, msg, len, sk.data());
|
||||
return sig;
|
||||
}
|
||||
|
||||
static inline bool verifyMessage(const uint8_t* msg, size_t len,
|
||||
const Signature& sig, const PublicKey& pk) {
|
||||
return crypto_sign_verify_detached(sig.data(), msg, len, pk.data()) == 0;
|
||||
}
|
||||
|
||||
// Overloads for std::string / std::array
|
||||
static inline Signature signMessage(const std::string& msg, const PrivateKey& sk) {
|
||||
return signMessage(reinterpret_cast<const uint8_t*>(msg.data()), msg.size(), sk);
|
||||
@@ -72,6 +80,11 @@ namespace ColumnLynx::Utils {
|
||||
return sig;
|
||||
}
|
||||
|
||||
// Verify a message with a given public key
|
||||
static inline bool verifyMessage(const uint8_t* msg, size_t len, const Signature& sig, const PublicKey& pk) {
|
||||
return crypto_sign_verify_detached(sig.data(), msg, len, pk.data()) == 0;
|
||||
}
|
||||
|
||||
static inline bool verifyMessage(const std::string& msg, const Signature& sig, const PublicKey& pk) {
|
||||
return verifyMessage(reinterpret_cast<const uint8_t*>(msg.data()), msg.size(), sig, pk);
|
||||
}
|
||||
@@ -86,7 +99,7 @@ namespace ColumnLynx::Utils {
|
||||
return crypto_sign_verify_detached(sig.data(), msg, len, pk_raw) == 0;
|
||||
}
|
||||
|
||||
// Encrypt with ChaCha20-Poly1305 (returns ciphertext as bytes)
|
||||
// Encrypt symmetrically with ChaCha20-Poly1305; returns ciphertext as bytes
|
||||
static inline std::vector<uint8_t> encryptMessage(
|
||||
const uint8_t* plaintext, size_t len,
|
||||
const SymmetricKey& key, const Nonce& nonce,
|
||||
@@ -109,7 +122,7 @@ namespace ColumnLynx::Utils {
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
// Decrypt with ChaCha20-Poly1305 (returns plaintext as bytes)
|
||||
// Decrypt symmetrically with ChaCha20-Poly1305; Returns plaintext as bytes
|
||||
static inline std::vector<uint8_t> decryptMessage(
|
||||
const uint8_t* ciphertext, size_t len,
|
||||
const SymmetricKey& key, const Nonce& nonce,
|
||||
@@ -135,12 +148,14 @@ namespace ColumnLynx::Utils {
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
// Returns a random nonce
|
||||
static inline Nonce generateNonce() {
|
||||
Nonce n{};
|
||||
randombytes_buf(n.data(), n.size());
|
||||
return n;
|
||||
}
|
||||
|
||||
// Encrypt message asymmetrically; Returns ciphertext as bytes
|
||||
static inline std::vector<uint8_t> encryptAsymmetric(
|
||||
const uint8_t* plaintext, size_t len,
|
||||
const AsymNonce& nonce,
|
||||
@@ -161,6 +176,7 @@ namespace ColumnLynx::Utils {
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
// Decrypt message asymmetrically; Returns plaintext as bytes
|
||||
static inline std::vector<uint8_t> decryptAsymmetric(
|
||||
const uint8_t* ciphertext, size_t len,
|
||||
const AsymNonce& nonce,
|
||||
@@ -184,6 +200,7 @@ namespace ColumnLynx::Utils {
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
// Verify a public key (certificate) against system-installed CAs
|
||||
static inline bool verifyCertificateWithSystemCAs(const std::vector<uint8_t>& cert_der) {
|
||||
// Parse DER-encoded certificate
|
||||
const unsigned char* p = cert_der.data();
|
||||
@@ -225,6 +242,7 @@ namespace ColumnLynx::Utils {
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
// Extract the hostnames (Subject Alternative Names and Common Names) out of a public key (certificate)
|
||||
static inline std::vector<std::string> getCertificateHostname(const std::vector<uint8_t>& cert_der) {
|
||||
std::vector<std::string> names;
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
|
||||
namespace ColumnLynx::Net {
|
||||
struct SessionState {
|
||||
SymmetricKey aesKey; // Immutable after creation
|
||||
SymmetricKey aesKey; // Agreed-upon AES-256 kes for that session; Immutable after creation
|
||||
std::atomic<uint64_t> send_ctr{0}; // Per-direction counters
|
||||
std::atomic<uint64_t> recv_ctr{0};
|
||||
asio::ip::udp::endpoint udpEndpoint;
|
||||
std::atomic<uint64_t> sendCounter{0};
|
||||
std::chrono::steady_clock::time_point created = std::chrono::steady_clock::now();
|
||||
std::chrono::steady_clock::time_point expires{};
|
||||
uint32_t clientTunIP;
|
||||
uint32_t serverTunIP;
|
||||
uint64_t sessionID;
|
||||
std::atomic<uint64_t> recv_ctr{0}; // Per-direction counters
|
||||
asio::ip::udp::endpoint udpEndpoint; // Deducted IP + Port of that session client
|
||||
std::atomic<uint64_t> sendCounter{0}; // Counter of sent messages
|
||||
std::chrono::steady_clock::time_point created = std::chrono::steady_clock::now(); // Time created
|
||||
std::chrono::steady_clock::time_point expires{}; // Time of expiry
|
||||
uint32_t clientTunIP; // Assigned IP
|
||||
uint32_t serverTunIP; // Server IP
|
||||
uint64_t sessionID; // Session ID
|
||||
Nonce base_nonce{};
|
||||
|
||||
~SessionState() { sodium_memzero(aesKey.data(), aesKey.size()); }
|
||||
@@ -36,6 +36,7 @@ namespace ColumnLynx::Net {
|
||||
expires = created + ttl;
|
||||
}
|
||||
|
||||
// Set the UDP endpoint
|
||||
void setUDPEndpoint(const asio::ip::udp::endpoint& ep) {
|
||||
udpEndpoint = ep;
|
||||
}
|
||||
@@ -43,28 +44,31 @@ namespace ColumnLynx::Net {
|
||||
|
||||
class SessionRegistry {
|
||||
public:
|
||||
// Return a reference to the Session Registry instance
|
||||
static SessionRegistry& getInstance() { static SessionRegistry instance; return instance; }
|
||||
|
||||
// Insert or replace
|
||||
// Insert or replace a session entry
|
||||
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
|
||||
// Lookup a session entry by session ID
|
||||
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
|
||||
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
|
||||
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);
|
||||
@@ -72,7 +76,7 @@ namespace ColumnLynx::Net {
|
||||
return snap;
|
||||
}
|
||||
|
||||
// Remove
|
||||
// Remove a session by ID
|
||||
void erase(uint64_t sessionID) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mSessions.erase(sessionID);
|
||||
@@ -99,6 +103,7 @@ namespace ColumnLynx::Net {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of registered sessions
|
||||
int size() const {
|
||||
std::shared_lock lock(mMutex);
|
||||
return static_cast<int>(mSessions.size());
|
||||
@@ -106,6 +111,7 @@ namespace ColumnLynx::Net {
|
||||
|
||||
// IP management (simple for /24 subnet)
|
||||
|
||||
// Get the lowest available IPv4 address; Returns 0 if none available
|
||||
uint32_t getFirstAvailableIP() const {
|
||||
std::shared_lock lock(mMutex);
|
||||
uint32_t baseIP = 0x0A0A0002; // 10.10.0.2
|
||||
@@ -116,14 +122,18 @@ namespace ColumnLynx::Net {
|
||||
if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) {
|
||||
return candidateIP;
|
||||
}
|
||||
|
||||
return 0; // Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <array>
|
||||
|
||||
namespace ColumnLynx::Net::UDP {
|
||||
// @deprecated
|
||||
// Shared between server and client
|
||||
enum class MessageType : uint8_t {
|
||||
PING = 0x01,
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace ColumnLynx::Net {
|
||||
const std::string& getName() const;
|
||||
int getFd() const; // For ASIO integration (on POSIX)
|
||||
|
||||
static inline std::string ipToString(uint32_t ip) {
|
||||
static inline std::string ipv4ToString(uint32_t ip) {
|
||||
struct in_addr addr;
|
||||
addr.s_addr = ip; // expected in network byte order
|
||||
addr.s_addr = htonl(ip);
|
||||
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
if (!inet_ntop(AF_INET, &addr, buf, sizeof(buf)))
|
||||
@@ -60,6 +60,12 @@ namespace ColumnLynx::Net {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) {
|
||||
if (prefixLen == 0) return 0;
|
||||
uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF;
|
||||
return htonl(mask); // convert to network byte order
|
||||
}
|
||||
|
||||
private:
|
||||
bool mApplyLinuxIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu);
|
||||
bool mApplyMacOSIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu);
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace ColumnLynx::Utils {
|
||||
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.
|
||||
// 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";
|
||||
@@ -204,6 +204,7 @@ namespace ColumnLynx::Utils {
|
||||
std::cerr << "Panic trace written to panic_dump.txt\n";
|
||||
}
|
||||
|
||||
// Gets the current time
|
||||
static std::string currentTime() {
|
||||
std::time_t t = std::time(nullptr);
|
||||
char buf[64];
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -17,17 +23,27 @@
|
||||
#endif
|
||||
|
||||
namespace ColumnLynx::Utils {
|
||||
// General log function. Use for logging important information.
|
||||
void log(const std::string &msg);
|
||||
// General warning function. Use for logging important warnings.
|
||||
void warn(const std::string &msg);
|
||||
// General error function. Use for logging failures and general errors.
|
||||
void error(const std::string &msg);
|
||||
// Debug log function. Use for logging non-important information. These will not print unless the binary is compiled with DEBUG=1
|
||||
void debug(const std::string &msg);
|
||||
|
||||
// Returns the hostname of the running platform.
|
||||
std::string getHostname();
|
||||
// Returns the version of the running release.
|
||||
std::string getVersion();
|
||||
unsigned short serverPort();
|
||||
unsigned char protocolVersion();
|
||||
std::vector<std::string> getWhitelistedKeys();
|
||||
|
||||
// Raw byte to hex string conversion helper
|
||||
std::string bytesToHexString(const uint8_t* bytes, size_t length);
|
||||
// Hex string to raw byte conversion helper
|
||||
std::vector<uint8_t> hexStringToBytes(const std::string& hex);
|
||||
|
||||
// uint8_t to raw string conversion helper
|
||||
template <size_t N>
|
||||
@@ -38,4 +54,28 @@ namespace ColumnLynx::Utils {
|
||||
inline std::string uint8ArrayToString(const uint8_t* data, size_t length) {
|
||||
return std::string(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
inline constexpr uint64_t cbswap64(uint64_t x) {
|
||||
return ((x & 0x00000000000000FFULL) << 56) |
|
||||
((x & 0x000000000000FF00ULL) << 40) |
|
||||
((x & 0x0000000000FF0000ULL) << 24) |
|
||||
((x & 0x00000000FF000000ULL) << 8) |
|
||||
((x & 0x000000FF00000000ULL) >> 8) |
|
||||
((x & 0x0000FF0000000000ULL) >> 24) |
|
||||
((x & 0x00FF000000000000ULL) >> 40) |
|
||||
((x & 0xFF00000000000000ULL) >> 56);
|
||||
}
|
||||
|
||||
// host -> big-endian (for little-endian hosts) - 64 bit
|
||||
inline constexpr uint64_t chtobe64(uint64_t x) {
|
||||
return cbswap64(x);
|
||||
}
|
||||
|
||||
// big-endian -> host (for little-endian hosts) - 64 bit
|
||||
inline constexpr uint64_t cbe64toh(uint64_t x) {
|
||||
return cbswap64(x);
|
||||
}
|
||||
|
||||
// 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);
|
||||
};
|
||||
@@ -33,12 +33,18 @@ namespace ColumnLynx::Net::TCP {
|
||||
return conn;
|
||||
}
|
||||
|
||||
// Start a TCP Connection (Handler for an incoming connection)
|
||||
void start();
|
||||
// Send a message to the TCP client
|
||||
void sendMessage(ServerMessageType type, const std::string& data = "");
|
||||
// Set callback for disconnects
|
||||
void setDisconnectCallback(std::function<void(std::shared_ptr<TCPConnection>)> cb);
|
||||
// Disconnect the client
|
||||
void disconnect();
|
||||
|
||||
// Get the assigned session ID
|
||||
uint64_t getSessionID() const;
|
||||
// Get the assigned AES key; You should probably access this via the Session Registry instead
|
||||
std::array<uint8_t, 32> getAESKey() const;
|
||||
|
||||
private:
|
||||
@@ -51,7 +57,9 @@ namespace ColumnLynx::Net::TCP {
|
||||
mLastHeartbeatSent(std::chrono::steady_clock::now())
|
||||
{}
|
||||
|
||||
// Start the heartbeat routine
|
||||
void mStartHeartbeat();
|
||||
// Handle an incoming TCP message
|
||||
void mHandleMessage(ClientMessageType type, const std::string& data);
|
||||
|
||||
std::shared_ptr<MessageHandler> mHandler;
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace ColumnLynx::Net::TCP {
|
||||
mSodiumWrapper(sodiumWrapper),
|
||||
mHostRunning(hostRunning)
|
||||
{
|
||||
// Preload the config map
|
||||
mRawServerConfig = Utils::getConfigMap("server_config");
|
||||
|
||||
asio::error_code ec;
|
||||
|
||||
if (!ipv4Only) {
|
||||
@@ -59,15 +62,19 @@ namespace ColumnLynx::Net::TCP {
|
||||
mStartAccept();
|
||||
}
|
||||
|
||||
// Stop the TCP Server
|
||||
void stop();
|
||||
|
||||
private:
|
||||
// Start accepting clients via TCP
|
||||
void mStartAccept();
|
||||
|
||||
asio::io_context &mIoContext;
|
||||
asio::ip::tcp::acceptor mAcceptor;
|
||||
std::unordered_set<TCPConnection::pointer> mClients;
|
||||
Utils::LibSodiumWrapper *mSodiumWrapper;
|
||||
bool* mHostRunning;
|
||||
std::unordered_map<std::string, std::string> mRawServerConfig;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -42,13 +42,18 @@ namespace ColumnLynx::Net::UDP {
|
||||
mStartReceive();
|
||||
}
|
||||
|
||||
// Stop the UDP server
|
||||
void stop();
|
||||
|
||||
// Send UDP data to an endpoint; Fetched via the Session Registry
|
||||
void sendData(const uint64_t sessionID, const std::string& data);
|
||||
|
||||
private:
|
||||
// Start receiving UDP data
|
||||
void mStartReceive();
|
||||
// Handle an incoming UDP packet
|
||||
void mHandlePacket(std::size_t bytes);
|
||||
|
||||
asio::ip::udp::socket mSocket;
|
||||
asio::ip::udp::endpoint mRemoteEndpoint;
|
||||
std::array<uint8_t, 2048> mRecvBuffer; // Adjust size as needed
|
||||
|
||||
@@ -49,6 +49,9 @@ int main(int argc, char** argv) {
|
||||
auto result = options.parse(argc, argv);
|
||||
if (result.count("help")) {
|
||||
std::cout << options.help() << std::endl;
|
||||
std::cout << "This software is licensed under the GPLv2-only license OR the GPLv3 license.\n";
|
||||
std::cout << "Copyright (C) 2025, The ColumnLynx Contributors.\n";
|
||||
std::cout << "This software is provided under ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -63,10 +66,12 @@ int main(int argc, char** argv) {
|
||||
WintunInitialize();
|
||||
#endif
|
||||
|
||||
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>("utun0");
|
||||
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>("utun1");
|
||||
log("Using virtual interface: " + tun->getName());
|
||||
|
||||
LibSodiumWrapper sodiumWrapper = LibSodiumWrapper();
|
||||
debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper.getPublicKey(), 32));
|
||||
debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper.getPrivateKey(), 64));
|
||||
|
||||
std::array<uint8_t, 32> aesKey = {0}; // Defualt zeroed state until modified by handshake
|
||||
uint64_t sessionID = 0;
|
||||
|
||||
@@ -28,14 +28,18 @@ namespace ColumnLynx::Net::TCP {
|
||||
// Check if hostname or IPv4/IPv6
|
||||
sockaddr_in addr4{};
|
||||
sockaddr_in6 addr6{};
|
||||
self->mIsHostDomain = inet_pton(AF_INET, mHost.c_str(), (void*)(&addr4)) != 1 && inet_pton(AF_INET6, mHost.c_str(), (void*)(&addr6)) != 1;
|
||||
self->mIsHostDomain = inet_pton(AF_INET, mHost.c_str(), (void*)(&addr4)) != 1 && inet_pton(AF_INET6, mHost.c_str(), (void*)(&addr6)) != 1; // Voodoo black magic
|
||||
|
||||
std::vector<uint8_t> payload;
|
||||
payload.reserve(1 + crypto_box_PUBLICKEYBYTES);
|
||||
payload.push_back(Utils::protocolVersion());
|
||||
payload.insert(payload.end(),
|
||||
/*payload.insert(payload.end(),
|
||||
mLibSodiumWrapper->getXPublicKey(),
|
||||
mLibSodiumWrapper->getXPublicKey() + crypto_box_PUBLICKEYBYTES
|
||||
);*/
|
||||
payload.insert(payload.end(),
|
||||
mLibSodiumWrapper->getPublicKey(),
|
||||
mLibSodiumWrapper->getPublicKey() + crypto_sign_PUBLICKEYBYTES
|
||||
);
|
||||
|
||||
mHandler->sendMessage(ClientMessageType::HANDSHAKE_INIT, Utils::uint8ArrayToString(payload.data(), payload.size()));
|
||||
@@ -247,6 +251,9 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
std::memcpy(&mConnectionSessionID, decrypted.data(), sizeof(mConnectionSessionID));
|
||||
std::memcpy(&mTunConfig, decrypted.data() + sizeof(mConnectionSessionID), sizeof(Protocol::TunConfig));
|
||||
|
||||
mConnectionSessionID = Utils::cbe64toh(mConnectionSessionID);
|
||||
|
||||
Utils::log("Connection established with Session ID: " + std::to_string(mConnectionSessionID));
|
||||
|
||||
if (mSessionIDRef) { // Copy to the global reference
|
||||
|
||||
@@ -22,9 +22,12 @@ namespace ColumnLynx::Net::UDP {
|
||||
return;
|
||||
}
|
||||
|
||||
//Utils::debug("Using AES key: " + Utils::bytesToHexString(mAesKeyRef->data(), 32));
|
||||
|
||||
auto encryptedPayload = Utils::LibSodiumWrapper::encryptMessage(
|
||||
reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||
*mAesKeyRef, hdr.nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&mSessionIDRef), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
std::vector<uint8_t> packet;
|
||||
@@ -41,7 +44,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
packet.insert(packet.end(), encryptedPayload.begin(), encryptedPayload.end());
|
||||
|
||||
mSocket.send_to(asio::buffer(packet), mRemoteEndpoint);
|
||||
Utils::log("Sent UDP packet of size " + std::to_string(packet.size()));
|
||||
Utils::debug("Sent UDP packet of size " + std::to_string(packet.size()));
|
||||
}
|
||||
|
||||
void UDPClient::stop() {
|
||||
@@ -100,6 +103,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
|
||||
std::vector<uint8_t> plaintext = Utils::LibSodiumWrapper::decryptMessage(
|
||||
ciphertext.data(), ciphertext.size(), *mAesKeyRef, hdr.nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&mSessionIDRef), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
if (plaintext.empty()) {
|
||||
@@ -107,7 +111,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::log("UDP Client received packet from " + mRemoteEndpoint.address().to_string() + " - Packet size: " + std::to_string(bytes));
|
||||
Utils::debug("UDP Client received packet from " + mRemoteEndpoint.address().to_string() + " - Packet size: " + std::to_string(bytes));
|
||||
|
||||
// Write to TUN
|
||||
if (mTunRef) {
|
||||
|
||||
@@ -6,15 +6,27 @@
|
||||
|
||||
namespace ColumnLynx::Utils {
|
||||
void log(const std::string &msg) {
|
||||
std::cout << "[LOG] " << msg << std::endl;
|
||||
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::cout << "\033[0m[" << std::to_string(now) << " LOG] " << msg << std::endl;
|
||||
}
|
||||
|
||||
void warn(const std::string &msg) {
|
||||
std::cerr << "[WARN] " << msg << std::endl;
|
||||
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::cerr << "\033[33m[" << std::to_string(now) << " WARN] " << msg << "\033[0m" << std::endl;
|
||||
}
|
||||
|
||||
void error(const std::string &msg) {
|
||||
std::cerr << "[ERROR] " << msg << std::endl;
|
||||
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::cerr << "\033[31m[" << std::to_string(now) << " ERROR] " << msg << "\033[0m" << std::endl;
|
||||
}
|
||||
|
||||
void debug(const std::string &msg) {
|
||||
#if DEBUG || _DEBUG
|
||||
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::cerr << "\033[95m[" << std::to_string(now) << " DEBUG] " << msg << "\033[0m" << std::endl;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getHostname() {
|
||||
@@ -37,7 +49,7 @@ namespace ColumnLynx::Utils {
|
||||
}
|
||||
|
||||
std::string getVersion() {
|
||||
return "a0.3";
|
||||
return "a0.5";
|
||||
}
|
||||
|
||||
unsigned short serverPort() {
|
||||
@@ -61,4 +73,78 @@ namespace ColumnLynx::Utils {
|
||||
|
||||
return hexString;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> hexStringToBytes(const std::string& hex) {
|
||||
// TODO: recover from errors
|
||||
|
||||
if (hex.length() % 2 != 0) {
|
||||
throw std::invalid_argument("Hex string must have even length");
|
||||
}
|
||||
|
||||
auto hexValue = [](char c) -> uint8_t {
|
||||
if ('0' <= c && c <= '9') return c - '0';
|
||||
if ('A' <= c && c <= 'F') return c - 'A' + 10;
|
||||
if ('a' <= c && c <= 'f') return c - 'a' + 10;
|
||||
throw std::invalid_argument("Invalid hex character");
|
||||
};
|
||||
|
||||
size_t len = hex.length();
|
||||
std::vector<uint8_t> bytes;
|
||||
bytes.reserve(len / 2);
|
||||
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
uint8_t high = hexValue(hex[i]);
|
||||
uint8_t low = hexValue(hex[i + 1]);
|
||||
bytes.push_back((high << 4) | low);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<std::string> getWhitelistedKeys() {
|
||||
// Currently re-reads the file every time, should be fine.
|
||||
// Advantage of it is that you don't need to reload the server binary after adding/removing keys. Disadvantage is re-reading the file every time.
|
||||
// I might redo this part.
|
||||
|
||||
std::vector<std::string> out;
|
||||
|
||||
std::ifstream file("whitelisted_keys"); // TODO: This is hardcoded for now, make dynamic
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
out.push_back(line);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> getConfigMap(std::string path) {
|
||||
// TODO: Currently re-reads every time.
|
||||
std::vector<std::string> readLines;
|
||||
|
||||
std::ifstream file(path);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
readLines.push_back(line);
|
||||
}
|
||||
|
||||
// Parse them into the struct
|
||||
std::unordered_map<std::string, std::string> config;
|
||||
char delimiter = '=';
|
||||
|
||||
for (std::string str : readLines) {
|
||||
std::stringstream ss(str);
|
||||
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
||||
std::getline(ss, key, delimiter);
|
||||
std::getline(ss, val, delimiter);
|
||||
|
||||
config.insert({ key, val });
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
|
||||
// This is all fucking voodoo dark magic.
|
||||
|
||||
namespace ColumnLynx::Net {
|
||||
// ------------------------------ Constructor ------------------------------
|
||||
VirtualInterface::VirtualInterface(const std::string& ifName)
|
||||
@@ -42,8 +44,11 @@ namespace ColumnLynx::Net {
|
||||
sc.sc_id = ctlInfo.ctl_id;
|
||||
sc.sc_unit = 0; // utun0 (0 = auto-assign)
|
||||
|
||||
if (connect(mFd, (struct sockaddr*)&sc, sizeof(sc)) < 0)
|
||||
if (connect(mFd, (struct sockaddr*)&sc, sizeof(sc)) < 0) {
|
||||
if (errno == EPERM)
|
||||
throw std::runtime_error("connect(AF_SYS_CONTROL) failed: Insufficient permissions (try running as root)");
|
||||
throw std::runtime_error("connect(AF_SYS_CONTROL) failed: " + std::string(strerror(errno)));
|
||||
}
|
||||
|
||||
// Retrieve actual utun device name
|
||||
struct sockaddr_storage addr;
|
||||
@@ -158,8 +163,8 @@ namespace ColumnLynx::Net {
|
||||
{
|
||||
char cmd[512];
|
||||
|
||||
std::string ipStr = ipToString(clientIP);
|
||||
std::string peerStr = ipToString(serverIP);
|
||||
std::string ipStr = ipv4ToString(clientIP);
|
||||
std::string peerStr = ipv4ToString(serverIP);
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"ip addr add %s/%d peer %s dev %s",
|
||||
@@ -181,14 +186,17 @@ namespace ColumnLynx::Net {
|
||||
{
|
||||
char cmd[512];
|
||||
|
||||
std::string ipStr = ipToString(clientIP);
|
||||
std::string peerStr = ipToString(serverIP);
|
||||
std::string ipStr = ipv4ToString(clientIP);
|
||||
std::string peerStr = ipv4ToString(serverIP);
|
||||
|
||||
// Set netmask (/24 CIDR temporarily with raw command, improve later)
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"ifconfig utun0 %s %s mtu %d up",
|
||||
"ifconfig utun0 %s %s mtu %d netmask 255.255.255.0 up",
|
||||
ipStr.c_str(), peerStr.c_str(), mtu);
|
||||
system(cmd);
|
||||
|
||||
Utils::log("Executed command: " + std::string(cmd));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -200,8 +208,8 @@ namespace ColumnLynx::Net {
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char ip[32], gw[32];
|
||||
strcpy(ip, ipToString(clientIP).c_str());
|
||||
strcpy(gw, ipToString(serverIP).c_str());
|
||||
strcpy(ip, ipv4ToString(clientIP).c_str());
|
||||
strcpy(gw, ipv4ToString(serverIP).c_str());
|
||||
|
||||
char cmd[256];
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
|
||||
@@ -41,7 +41,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
options.add_options()
|
||||
("h,help", "Print help")
|
||||
("4,ipv4-only", "Force IPv4 only operation", cxxopts::value<bool>()->default_value("false"));
|
||||
("4,ipv4-only", "Force IPv4 only operation", cxxopts::value<bool>()->default_value("false"))
|
||||
("c,config", "Specify config file location", cxxopts::value<std::string>()->default_value("config.json"));
|
||||
|
||||
PanicHandler::init();
|
||||
|
||||
@@ -49,10 +50,14 @@ int main(int argc, char** argv) {
|
||||
auto result = options.parse(argc, argv);
|
||||
if (result.count("help")) {
|
||||
std::cout << options.help() << std::endl;
|
||||
std::cout << "This software is licensed under the GPLv2-only license OR the GPLv3 license.\n";
|
||||
std::cout << "Copyright (C) 2025, The ColumnLynx Contributors.\n";
|
||||
std::cout << "This software is provided under ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ipv4Only = result["ipv4-only"].as<bool>();
|
||||
std::string configPath = result["config"].as<std::string>();
|
||||
|
||||
log("ColumnLynx Server, Version " + getVersion());
|
||||
log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details.");
|
||||
@@ -107,7 +112,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
auto session = SessionRegistry::getInstance().getByIP(dstIP);
|
||||
if (!session) {
|
||||
Utils::warn("TUN: No session found for destination IP " + VirtualInterface::ipToString(dstIP));
|
||||
Utils::warn("TUN: No session found for destination IP " + VirtualInterface::ipv4ToString(dstIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,26 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
Utils::log("Client protocol version " + std::to_string(clientProtoVer) + " accepted from " + reqAddr + ".");
|
||||
|
||||
std::memcpy(mConnectionPublicKey.data(), data.data() + 1, std::min(data.size() - 1, sizeof(mConnectionPublicKey))); // Store the client's public key (for identification)
|
||||
PublicKey signPk;
|
||||
std::memcpy(signPk.data(), data.data() + 1, std::min(data.size() - 1, sizeof(signPk)));
|
||||
|
||||
// We can safely store this without further checking, the client will need to send the encrypted AES key in a way where they must possess the corresponding private key anyways.
|
||||
crypto_sign_ed25519_pk_to_curve25519(mConnectionPublicKey.data(), signPk.data()); // Store the client's public encryption key key (for identification)
|
||||
Utils::debug("Client " + reqAddr + " converted public encryption key: " + Utils::bytesToHexString(mConnectionPublicKey.data(), 32));
|
||||
|
||||
Utils::debug("Key attempted connect: " + Utils::bytesToHexString(signPk.data(), signPk.size()));
|
||||
|
||||
std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys();
|
||||
|
||||
if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(signPk.data(), signPk.size())) == whitelistedKeys.end()) {
|
||||
Utils::warn("Non-whitelisted client attempted to connect, terminating. Client IP: " + reqAddr);
|
||||
disconnect();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::debug("Client " + reqAddr + " passed authorized_keys");
|
||||
|
||||
mHandler->sendMessage(ServerMessageType::HANDSHAKE_IDENTIFY, Utils::uint8ArrayToString(mLibSodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES)); // This public key should always exist
|
||||
break;
|
||||
}
|
||||
@@ -173,12 +192,17 @@ namespace ColumnLynx::Net::TCP {
|
||||
// Make a Session ID
|
||||
randombytes_buf(&mConnectionSessionID, sizeof(mConnectionSessionID));
|
||||
|
||||
// TODO: Make the session ID little-endian for network transmission
|
||||
|
||||
// Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose)
|
||||
Nonce symNonce{}; // All zeros
|
||||
|
||||
uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP();
|
||||
|
||||
if (clientIP == 0) {
|
||||
Utils::warn("Out of available IPs! Disconnecting client " + reqAddr);
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
Protocol::TunConfig tunConfig{};
|
||||
tunConfig.version = Utils::protocolVersion();
|
||||
tunConfig.prefixLength = 24;
|
||||
@@ -190,8 +214,10 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP);
|
||||
|
||||
uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID);
|
||||
|
||||
std::vector<uint8_t> payload(sizeof(uint64_t) + sizeof(tunConfig));
|
||||
std::memcpy(payload.data(), &mConnectionSessionID, sizeof(uint64_t));
|
||||
std::memcpy(payload.data(), &sessionIDNet, sizeof(uint64_t));
|
||||
std::memcpy(payload.data() + sizeof(uint64_t), &tunConfig, sizeof(tunConfig));
|
||||
|
||||
std::vector<uint8_t> encryptedPayload = Utils::LibSodiumWrapper::encryptMessage(
|
||||
@@ -202,7 +228,7 @@ namespace ColumnLynx::Net::TCP {
|
||||
mHandler->sendMessage(ServerMessageType::HANDSHAKE_EXCHANGE_KEY_CONFIRM, Utils::uint8ArrayToString(encryptedPayload.data(), encryptedPayload.size()));
|
||||
|
||||
// Add to session registry
|
||||
Utils::log("Handshake with " + reqAddr + " completed successfully. Session ID assigned.");
|
||||
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);
|
||||
SessionRegistry::getInstance().put(mConnectionSessionID, std::move(session));
|
||||
|
||||
|
||||
@@ -48,26 +48,30 @@ namespace ColumnLynx::Net::UDP {
|
||||
|
||||
// Decrypt the actual payload
|
||||
try {
|
||||
//Utils::debug("Using AES key " + Utils::bytesToHexString(session->aesKey.data(), 32));
|
||||
|
||||
auto plaintext = Utils::LibSodiumWrapper::decryptMessage(
|
||||
encryptedPayload.data(), encryptedPayload.size(),
|
||||
session->aesKey,
|
||||
hdr->nonce,
|
||||
"udp-data"
|
||||
hdr->nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&sessionID), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
Utils::debug("Passed decryption");
|
||||
|
||||
const_cast<SessionState*>(session.get())->setUDPEndpoint(mRemoteEndpoint); // Update endpoint after confirming decryption
|
||||
// Update recv counter
|
||||
const_cast<SessionState*>(session.get())->recv_ctr.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// For now, just log the decrypted payload
|
||||
std::string payloadStr(plaintext.begin(), plaintext.end());
|
||||
Utils::log("UDP: Received packet from " + mRemoteEndpoint.address().to_string() + " - Payload: " + payloadStr);
|
||||
Utils::debug("UDP: Received packet from " + mRemoteEndpoint.address().to_string() + " - Payload: " + payloadStr);
|
||||
|
||||
if (mTun) {
|
||||
mTun->writePacket(plaintext); // Send to virtual interface
|
||||
}
|
||||
} catch (...) {
|
||||
Utils::warn("UDP: Failed to decrypt payload from " + mRemoteEndpoint.address().to_string());
|
||||
} catch (const std::exception &ex) {
|
||||
Utils::warn("UDP: Failed to process payload from " + mRemoteEndpoint.address().to_string() + " Raw Error: '" + ex.what() + "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +97,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
auto encryptedPayload = Utils::LibSodiumWrapper::encryptMessage(
|
||||
reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||
session->aesKey, hdr.nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&sessionID), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
std::vector<uint8_t> packet;
|
||||
@@ -109,7 +114,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
|
||||
// Send packet
|
||||
mSocket.send_to(asio::buffer(packet), endpoint);
|
||||
Utils::log("UDP: Sent packet of size " + std::to_string(packet.size()) + " to " + std::to_string(sessionID) + " (" + endpoint.address().to_string() + ":" + std::to_string(endpoint.port()) + ")");
|
||||
Utils::debug("UDP: Sent packet of size " + std::to_string(packet.size()) + " to " + std::to_string(sessionID) + " (" + endpoint.address().to_string() + ":" + std::to_string(endpoint.port()) + ")");
|
||||
}
|
||||
|
||||
void UDPServer::stop() {
|
||||
|
||||
Reference in New Issue
Block a user