Browse Source
- UtunNode runs uTun instance in a dedicated std::thread - no TUN, no msg_transport — direct etcp_route_send/recv calls - Thread-safe cross-thread communication via Qt signals - C callback (etcp_recv_fn) stores UtunNode* via thread_local - etcp_router_bind on service ID 0x11 for chat messages - uasync_poll(100ms) loop runs until stop() calledchatgui
3 changed files with 180 additions and 0 deletions
@ -0,0 +1,135 @@ |
|||||||
|
// utun_node.cpp — embedded uTun node running in a dedicated thread
|
||||||
|
#include "utun_node.h" |
||||||
|
#include <cinttypes> |
||||||
|
|
||||||
|
// === C headers ===
|
||||||
|
extern "C" { |
||||||
|
#include "utun_instance.h" |
||||||
|
#include "etcp_router.h" |
||||||
|
#include "etcp_api.h" |
||||||
|
#include "etcp.h" |
||||||
|
#include "config_parser.h" |
||||||
|
#include "config_updater.h" |
||||||
|
#include "control_server.h" |
||||||
|
#include "secure_channel.h" |
||||||
|
#include "../lib/u_async.h" |
||||||
|
#include "../lib/ll_queue.h" |
||||||
|
#include "../lib/memory_pool.h" |
||||||
|
#include "../lib/debug_config.h" |
||||||
|
#include "../lib/mem.h" |
||||||
|
} |
||||||
|
|
||||||
|
#define ETCP_RT_ID_CHAT 0x11 |
||||||
|
|
||||||
|
static thread_local UtunNode* g_currentNode = nullptr; |
||||||
|
|
||||||
|
UtunNode::UtunNode(QObject* parent) : QObject(parent) {} |
||||||
|
|
||||||
|
UtunNode::~UtunNode() { |
||||||
|
stop(); |
||||||
|
} |
||||||
|
|
||||||
|
bool UtunNode::start(const QString& configPath) { |
||||||
|
if (m_running) return false; |
||||||
|
m_configPath = configPath; |
||||||
|
m_stop = false; |
||||||
|
m_running = true; |
||||||
|
m_thread = std::thread(&UtunNode::runLoop, this); |
||||||
|
|
||||||
|
for (int i = 0; i < 50 && m_running && !m_instance; ++i) |
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
||||||
|
|
||||||
|
if (!m_instance) { |
||||||
|
m_stop = true; |
||||||
|
if (m_thread.joinable()) m_thread.join(); |
||||||
|
m_running = false; |
||||||
|
return false; |
||||||
|
} |
||||||
|
emit started(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void UtunNode::stop() { |
||||||
|
if (!m_running) return; |
||||||
|
m_stop = true; |
||||||
|
if (m_thread.joinable()) m_thread.join(); |
||||||
|
m_running = false; |
||||||
|
} |
||||||
|
|
||||||
|
void UtunNode::send(uint64_t dstNodeId, const QByteArray& data) { |
||||||
|
if (!m_instance) return; |
||||||
|
struct ll_entry* entry = queue_entry_new(data.size()); |
||||||
|
if (!entry) return; |
||||||
|
memcpy(entry->data, data.constData(), data.size()); |
||||||
|
etcp_route_send(m_instance, dstNodeId, entry, 0); |
||||||
|
} |
||||||
|
|
||||||
|
QString UtunNode::nodeIdHex() const { |
||||||
|
if (!m_instance) return {}; |
||||||
|
char buf[32]; |
||||||
|
snprintf(buf, sizeof(buf), "%016" PRIx64, m_instance->node_id); |
||||||
|
return QString(buf); |
||||||
|
} |
||||||
|
|
||||||
|
QString UtunNode::pubKeyHex() const { |
||||||
|
if (!m_instance || !m_instance->config) return {}; |
||||||
|
return QString::fromLatin1(m_instance->config->global.my_public_key_hex, 64); |
||||||
|
} |
||||||
|
|
||||||
|
void UtunNode::recvCallback(struct ETCP_CONN* conn, struct ll_entry* entry) { |
||||||
|
if (!g_currentNode || !entry) return; |
||||||
|
uint64_t src = conn ? conn->peer_node_id : 0; |
||||||
|
QByteArray data((const char*)entry->data, (int)(entry->len ? entry->len : 0)); |
||||||
|
QMetaObject::invokeMethod(g_currentNode, [=] { |
||||||
|
emit g_currentNode->messageReceived(src, data); |
||||||
|
}, Qt::QueuedConnection); |
||||||
|
} |
||||||
|
|
||||||
|
void UtunNode::runLoop() { |
||||||
|
g_currentNode = this; |
||||||
|
|
||||||
|
debug_config_init(); |
||||||
|
debug_set_level(DEBUG_LEVEL_ERROR); |
||||||
|
debug_enable_function_name(0); |
||||||
|
|
||||||
|
utun_instance_set_tun_init_enabled(0); |
||||||
|
|
||||||
|
struct UASYNC* ua = uasync_create(); |
||||||
|
if (!ua) { |
||||||
|
QMetaObject::invokeMethod(this, [this] { emit error("uasync_create failed"); }); |
||||||
|
g_currentNode = nullptr; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
m_instance = utun_instance_create(ua, m_configPath.toUtf8().constData()); |
||||||
|
if (!m_instance) { |
||||||
|
QMetaObject::invokeMethod(this, [this] { emit error("utun_instance_create failed"); }); |
||||||
|
uasync_destroy(ua, 0); |
||||||
|
g_currentNode = nullptr; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
m_instance->test_user_ptr = this; |
||||||
|
|
||||||
|
if (utun_instance_init(m_instance) != 0) { |
||||||
|
QMetaObject::invokeMethod(this, [this] { emit error("utun_instance_init failed"); }); |
||||||
|
utun_instance_destroy(m_instance); |
||||||
|
m_instance = nullptr; |
||||||
|
uasync_destroy(ua, 0); |
||||||
|
g_currentNode = nullptr; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
etcp_router_bind(m_instance, ETCP_RT_ID_CHAT, recvCallback); |
||||||
|
|
||||||
|
while (!m_stop) { |
||||||
|
uasync_poll(ua, 100); |
||||||
|
} |
||||||
|
|
||||||
|
etcp_router_bind(m_instance, ETCP_RT_ID_CHAT, nullptr); |
||||||
|
utun_instance_destroy(m_instance); |
||||||
|
m_instance = nullptr; |
||||||
|
uasync_destroy(ua, 0); |
||||||
|
g_currentNode = nullptr; |
||||||
|
QMetaObject::invokeMethod(this, [this] { emit stopped(); }); |
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
// utun_node.h — C++ wrapper for embedded uTun node (runs in dedicated thread)
|
||||||
|
#ifndef UTUN_NODE_H |
||||||
|
#define UTUN_NODE_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QByteArray> |
||||||
|
#include <QString> |
||||||
|
#include <atomic> |
||||||
|
#include <thread> |
||||||
|
|
||||||
|
struct UTUN_INSTANCE; |
||||||
|
|
||||||
|
class UtunNode : public QObject { |
||||||
|
Q_OBJECT |
||||||
|
public: |
||||||
|
explicit UtunNode(QObject* parent = nullptr); |
||||||
|
~UtunNode(); |
||||||
|
|
||||||
|
bool start(const QString& configPath); |
||||||
|
void stop(); |
||||||
|
bool isRunning() const { return m_running; } |
||||||
|
|
||||||
|
void send(uint64_t dstNodeId, const QByteArray& data); |
||||||
|
|
||||||
|
QString nodeIdHex() const; |
||||||
|
QString pubKeyHex() const; |
||||||
|
|
||||||
|
signals: |
||||||
|
void started(); |
||||||
|
void stopped(); |
||||||
|
void error(const QString& msg); |
||||||
|
void messageReceived(uint64_t src, const QByteArray& data); |
||||||
|
|
||||||
|
private: |
||||||
|
void runLoop(); |
||||||
|
static void recvCallback(struct ETCP_CONN* conn, struct ll_entry* entry); |
||||||
|
|
||||||
|
UTUN_INSTANCE* m_instance = nullptr; |
||||||
|
std::thread m_thread; |
||||||
|
std::atomic<bool> m_running{false}; |
||||||
|
std::atomic<bool> m_stop{false}; |
||||||
|
QString m_configPath; |
||||||
|
}; |
||||||
|
#endif // UTUN_NODE_H
|
||||||
Loading…
Reference in new issue