fix: NCD UAF, conn_mgr invite protocol, node_id derivation, link reuse
node_conn_direct: - ncd_init_cb: skip UP when links_up==0, ncd_up_cb delivers when link comes up - close/force_close: handle_count safety checks, callback clearing - open/open_node case 2: create links on reused dead conn - ncd_create_links: skip duplicate link on same conn (stale->etcp==conn) - set_callback: optional callback change on live handle conn_mgr: - cm_ncd_callback: DOWN only delivers event (handle stays alive) - cm_ncd_callback: TIMEOUT force_close BEFORE cm_deliver_event - cm_deliver_event: read handle fields to locals before callback (reentrany-safe) - INVITE_INFO_REQ/RESP: add duplicate cmd byte for direct-send protocol - cm_handle_invite_info_resp: search invite by group_id in channel conn_mgr Android: - node_id computed by sc_derive_node_id_from_pubkey in C, not Kotlin - utun_bridge_join_channel: recompute node_id, log mismatch - invite_link_c: compute nodeId from pubkey via sc_derive_node_id_from_pubkey - NativeLib.deriveNodeId, InviteLink.kt uses C function
This commit is contained in:
1 parent
4b07d59043
commit
740ed2a93a
10 files changed
+351
-53
No files matched your search
@@ -62,15 +62,6 @@ data class InviteData(
|
||||
object InviteLink {
|
||||
private const val PREFIX = "utun://"
|
||||
|
||||
fun deriveNodeId(pubkey: ByteArray): Long {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
val hash = md.digest(pubkey)
|
||||
var id = 0L
|
||||
for (i in 0 until 8)
|
||||
id = (id shl 8) or (hash[i].toLong() and 0xFF)
|
||||
return id and 0x7FFFFFFFFFFFFFFFL
|
||||
}
|
||||
|
||||
fun decode(link: String): InviteData? {
|
||||
LogManager.addLog("DEBUG", "INVITE", "decode: ${link.take(80)}")
|
||||
if (!link.startsWith(PREFIX)) {
|
||||
@@ -140,7 +131,7 @@ object InviteLink {
|
||||
}
|
||||
|
||||
if (pubkey == null || pubkey.size != INVITE_PUBKEY_SIZE) return null
|
||||
val nodeId = deriveNodeId(pubkey)
|
||||
val nodeId = NativeLib.deriveNodeId(pubkey)
|
||||
val pkHex = pubkey.take(8).joinToString("") { "%02x".format(it) }
|
||||
LogManager.addLog("INFO", "INVITE", "decode: ch=0x%016x node=0x%016x pk=%s... addrs=%d".format(
|
||||
chId, nodeId, pkHex, addrs.size))
|
||||
|
||||
@@ -7,7 +7,6 @@ interface ChatEventCallback {
|
||||
}
|
||||
|
||||
object NativeLib {
|
||||
private var running = false
|
||||
|
||||
init {
|
||||
System.loadLibrary("utun")
|
||||
@@ -24,16 +23,13 @@ object NativeLib {
|
||||
|
||||
/** Start full uTun instance from INI config text */
|
||||
fun start(configText: String): Boolean {
|
||||
if (running) return true
|
||||
val ok = nativeStart(configText)
|
||||
if (ok) running = true
|
||||
return ok
|
||||
if (isReady()) return true
|
||||
return nativeStart(configText)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
if (!running) return
|
||||
if (!isReady()) return
|
||||
nativeStop()
|
||||
running = false
|
||||
}
|
||||
|
||||
fun setEventCallback(cb: ChatEventCallback) {
|
||||
@@ -42,16 +38,13 @@ object NativeLib {
|
||||
})
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
nativeDestroy()
|
||||
running = false
|
||||
}
|
||||
fun destroy() { nativeDestroy() }
|
||||
|
||||
fun restart(dbPath: String, controlPort: Int, configText: String): Boolean {
|
||||
return nativeRestart(configText)
|
||||
}
|
||||
|
||||
fun isReady(): Boolean = running
|
||||
fun isReady(): Boolean = nativeIsRunning()
|
||||
|
||||
fun getMyNodeId(): Long = nativeGetMyNodeId()
|
||||
|
||||
@@ -66,6 +59,7 @@ object NativeLib {
|
||||
nativeJoinChannel(channelId, nodeId, pubkey, addrs, addrCount)
|
||||
LogManager.addLog("INFO", "JNI", "joinChannel: native returned")
|
||||
}
|
||||
fun deriveNodeId(pubkey: ByteArray): Long = nativeDeriveNodeId(pubkey)
|
||||
fun connectChannel(channelId: String) { nativeConnectChannel(channelId) }
|
||||
fun getChannels(): String = nativeGetChannels() ?: "[]"
|
||||
fun getMessages(channelId: String, limit: Int = 50): String = nativeGetMessages(channelId, limit) ?: "[]"
|
||||
@@ -124,6 +118,7 @@ object NativeLib {
|
||||
private external fun nativeCreateChannel(name: String, channelId: String)
|
||||
private external fun nativeConnectNode(address: String, port: Int, pubkeyHex: String)
|
||||
private external fun nativeJoinChannel(channelId: Long, nodeId: Long, pubkey: ByteArray, addrs: ByteArray, addrCount: Int)
|
||||
private external fun nativeDeriveNodeId(pubkey: ByteArray): Long
|
||||
private external fun nativeConnectChannel(channelId: String)
|
||||
private external fun nativeGetChannels(): String?
|
||||
private external fun nativeGetMessages(channelId: String, limit: Int): String?
|
||||
@@ -138,6 +133,7 @@ object NativeLib {
|
||||
private external fun nativeRestart(configText: String): Boolean
|
||||
private external fun nativePing()
|
||||
private external fun nativeIsResponsive(): Boolean
|
||||
private external fun nativeIsRunning(): Boolean
|
||||
|
||||
/* ── Channel members JNI ── */
|
||||
private external fun nativeGetChannelMembers(channelId: String): String?
|
||||
|
||||
@@ -95,7 +95,7 @@ class ChatViewModel : ViewModel() {
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
delay(3000)
|
||||
if (!dbReady || !NativeLib.isReady()) continue
|
||||
if (!dbReady || !NativeLib.isReady()) { _isStuck.value = true; continue }
|
||||
NativeLib.ping()
|
||||
delay(100)
|
||||
_isStuck.value = !NativeLib.isResponsive()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "../../../src/utun_instance.h"
|
||||
#include "../../../src/transport_layer/etcp.h"
|
||||
#include "../../../src/transport_layer/etcp_connections.h"
|
||||
#include "../../../src/transport_layer/secure_channel.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -215,14 +216,18 @@ void utun_bridge_join_channel(uint64_t channel_id, uint64_t node_id,
|
||||
const uint8_t* pubkey_bin,
|
||||
const uint8_t* addrs_data, int addr_count,
|
||||
int addrs_data_len) {
|
||||
uint64_t real_node_id = sc_derive_node_id_from_pubkey(pubkey_bin);
|
||||
if (real_node_id != node_id)
|
||||
bridge_log(BLEV_WARN, "join: node_id Kotlin=0x%016llx != C=0x%016llx — using C value",
|
||||
(unsigned long long)node_id, (unsigned long long)real_node_id);
|
||||
bridge_log(BLEV_INFO, "join ch=%llu node=0x%016llx addrs=%d len=%d",
|
||||
(unsigned long long)channel_id, (unsigned long long)node_id, addr_count, addrs_data_len);
|
||||
(unsigned long long)channel_id, (unsigned long long)real_node_id, addr_count, addrs_data_len);
|
||||
if (!pubkey_bin || !addrs_data || addr_count <= 0) {
|
||||
bridge_log(BLEV_ERROR, "join invalid args");
|
||||
utun_bridge_event(6, "{\"node_id\":0,\"result\":-7}"); /* CHAT_EVT_CONNECT_RESULT */
|
||||
return;
|
||||
}
|
||||
chat_sync_connect_from_invite(channel_id, node_id, pubkey_bin, addrs_data, addr_count, addrs_data_len);
|
||||
chat_sync_connect_from_invite(channel_id, real_node_id, pubkey_bin, addrs_data, addr_count, addrs_data_len);
|
||||
}
|
||||
|
||||
void utun_bridge_connect_channel(const char* channel_id) {
|
||||
@@ -1131,6 +1136,20 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeJoinChannel(
|
||||
(*env)->ReleaseByteArrayElements(env, pubkey, (jbyte*)pk, JNI_ABORT);
|
||||
}
|
||||
|
||||
/* ── nativeDeriveNodeId ── */
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_com_utun_chat_data_NativeLib_nativeDeriveNodeId(
|
||||
JNIEnv* env, jobject thiz, jbyteArray pubkey) {
|
||||
(void)thiz;
|
||||
if (!pubkey) return 0;
|
||||
jsize pk_len = (*env)->GetArrayLength(env, pubkey);
|
||||
if (pk_len < 32) return 0;
|
||||
uint8_t* pk = (uint8_t*)(*env)->GetByteArrayElements(env, pubkey, NULL);
|
||||
uint64_t node_id = sc_derive_node_id_from_pubkey(pk);
|
||||
(*env)->ReleaseByteArrayElements(env, pubkey, (jbyte*)pk, JNI_ABORT);
|
||||
return (jlong)node_id;
|
||||
}
|
||||
|
||||
/* ── nativeSetEventCallback(eventCb) ── */
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetEventCallback(
|
||||
@@ -1233,6 +1252,12 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeIsResponsive(
|
||||
return utun_bridge_is_responsive() ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeIsRunning(
|
||||
JNIEnv* env, jobject thiz) {
|
||||
(void)env; (void)thiz;
|
||||
return instance_lite_is_running() ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativePing(
|
||||
JNIEnv* env, jobject thiz) {
|
||||
(void)env; (void)thiz;
|
||||
|
||||
@@ -309,6 +309,7 @@ static void restart_trampoline(void* arg) {
|
||||
if (!new_config) {
|
||||
IL_LOGE("restart: parse_config failed");
|
||||
g_running = 0;
|
||||
g_inst = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,12 +343,14 @@ static void restart_trampoline(void* arg) {
|
||||
} else {
|
||||
IL_LOGE("restart: SQLite reopen failed rc=%d", rc);
|
||||
g_running = 0;
|
||||
g_inst = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (utun_instance_init(g_inst) != 0) {
|
||||
IL_LOGE("restart: utun_instance_init failed");
|
||||
g_running = 0;
|
||||
g_inst = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,7 +363,13 @@ static void restart_trampoline(void* arg) {
|
||||
}
|
||||
|
||||
void instance_lite_restart(const char* new_config_text) {
|
||||
if (!g_ua || !g_inst || !new_config_text) return;
|
||||
if (!new_config_text) return;
|
||||
if (!g_ua || !g_inst || !g_running) {
|
||||
IL_LOGI("restart: instance dead, doing hard restart via stop+start");
|
||||
if (g_inst) instance_lite_stop();
|
||||
instance_lite_start(new_config_text);
|
||||
return;
|
||||
}
|
||||
IL_LOGI("restart: posting to uasync");
|
||||
char* copy = u_strdup(new_config_text);
|
||||
if (!copy) return;
|
||||
@@ -384,11 +393,13 @@ void instance_lite_ping(void) {
|
||||
}
|
||||
|
||||
int instance_lite_is_responsive(void) {
|
||||
if (!g_ua || !g_inst || !g_running) return 0;
|
||||
if (g_ping_id == 0) return 0;
|
||||
return (g_pong_id == g_ping_id) ? 1 : 0;
|
||||
}
|
||||
|
||||
int instance_lite_is_running(void) {
|
||||
return g_inst != NULL ? 1 : 0;
|
||||
return (g_inst != NULL && g_running) ? 1 : 0;
|
||||
}
|
||||
|
||||
struct UASYNC* instance_lite_get_uasync(void) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../../../src/transport_layer/secure_channel.h"
|
||||
|
||||
#define INVITE_PREFIX "utun://"
|
||||
#define INVITE_PREFIX_LEN 7
|
||||
|
||||
@@ -81,7 +83,6 @@ int invite_link_decode(const char* link, size_t link_len, struct InviteDataC* ou
|
||||
for (int i = 0; i < 8; i++)
|
||||
chId = (chId << 8) | raw[off++];
|
||||
out->channelId = chId;
|
||||
out->nodeId = 0;
|
||||
memset(out->pubkey, 0, INVITE_PUBKEY_SIZE);
|
||||
out->addrCount = 0;
|
||||
|
||||
@@ -99,6 +100,7 @@ int invite_link_decode(const char* link, size_t link_len, struct InviteDataC* ou
|
||||
}
|
||||
if (first_block) {
|
||||
memcpy(out->pubkey, raw + off, INVITE_PUBKEY_SIZE);
|
||||
out->nodeId = sc_derive_node_id_from_pubkey(out->pubkey);
|
||||
first_block = 0;
|
||||
}
|
||||
off += 32;
|
||||
|
||||
Reference in new issue
Block a user