// utun_node.cpp — embedded uTun node running in a dedicated thread #include "utun_node.h" #include // === 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" #include "chat_sync.h" #include "chat_core.h" #include "gui_bridge.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::setDebugFile(const QString& path) { m_debugFile = path; } void UtunNode::setDbPath(const QString& path) { m_dbPath = path; } 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); if (!m_debugFile.isEmpty()) { debug_enable_file_output(m_debugFile.toUtf8().constData(), 0); g_debug_config.thread_marker = 2; } 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; /* set ua for gui_bridge before init, so GUI can post */ gui_bridge_set_uasync(ua); 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); /* Initialize chat_core (DB) and chat_sync */ chat_core_init(m_instance, m_dbPath.toUtf8().constData()); chat_sync_init(m_instance, NULL); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chat_core + chat_sync initialized"); while (!m_stop) { uasync_poll(ua, 100); } etcp_router_bind(m_instance, ETCP_RT_ID_CHAT, nullptr); chat_sync_destroy(m_instance); chat_core_destroy(m_instance); utun_instance_destroy(m_instance); m_instance = nullptr; uasync_destroy(ua, 0); g_currentNode = nullptr; QMetaObject::invokeMethod(this, [this] { emit stopped(); }); }