From ddf696bcf691154801154d322fb4ce0b5fe69d03 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 3 Aug 2026 13:57:42 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20TCP=20socket=20addresses=20?= =?UTF-8?q?=E2=80=94=20sync=20to=20chat=20DB,=20topo=5Fnode,=20config=20fi?= =?UTF-8?q?le=20single=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - chat: collect_my_addrs extends to TCP + IPv6, chat_core_sync_my_addresses writes TCP addrs - topo_node: replace CFG_SERVER with TCP_SOCKET for TCP addresses, simplify NAT logic - etcp_connections: add IPv6 interface addr resolution for TCP sockets - Android: INI config file as single source of truth for servers, remove JSON/Gson - Android: filter temporary IPv6 addresses, fix restart with socket cleanup - instance_lite: sync addresses after socket creation, proper restart cleanup --- src/chat/chat_channel.c | 62 +++++- src/chat/chat_core.c | 2 - src/chat/chat_profile.c | 58 +++++- src/routing_layer/topo_node.c | 126 ++++++------ src/transport_layer/etcp_connections.c | 34 ++- .../java/com/utun/chat/ChatApplication.kt | 4 +- .../java/com/utun/chat/data/ConfigProvider.kt | 194 +++++++++++------- .../utun/chat/ui/screens/SettingsScreen.kt | 4 +- .../libutun_lite/instance_lite.c | 15 ++ 9 files changed, 338 insertions(+), 161 deletions(-) diff --git a/src/chat/chat_channel.c b/src/chat/chat_channel.c index 6dcd5e7e..39c61cf7 100644 --- a/src/chat/chat_channel.c +++ b/src/chat/chat_channel.c @@ -18,6 +18,7 @@ #include "../../lib/ll_queue.h" #include "../../lib/platform_compat.h" #include "../transport_layer/etcp.h" +#include "../transport_layer/etcp_connections.h" #include #include @@ -52,24 +53,66 @@ static int channel_sign_join(const uint8_t* ch_x25519, const uint8_t* ch_ed25519 /* ─── сбор адресов текущего узла (для member_sync_put) ─── */ -static int collect_my_addrs(uint8_t* buf, int max_addrs) { - int count = 0; +static int collect_my_addrs(uint8_t* buf, int max_buf_sz) { + int pos = 0, count = 0; + struct ETCP_SOCKET* s = g_cc.inst->etcp_sockets; - while (s && count < max_addrs) { + while (s) { struct sockaddr_storage* sa = s->interface_addr.ss_family ? &s->interface_addr : NULL; if (!sa) sa = s->local_addr.ss_family ? &s->local_addr : NULL; if (sa && sa->ss_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)sa; - buf[count * 8] = 4; - buf[count * 8 + 1] = s->sock_id; - memcpy(buf + count * 8 + 2, &sin->sin_addr, 4); + if (pos + 8 > max_buf_sz) break; + buf[pos++] = 4; buf[pos++] = s->sock_id; + memcpy(buf + pos, &sin->sin_addr, 4); pos += 4; uint16_t port = ntohs(sin->sin_port); - buf[count * 8 + 6] = (uint8_t)(port >> 8); - buf[count * 8 + 7] = (uint8_t)(port & 0xFF); + buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); + count++; + uint8_t* ip = (uint8_t*)&sin->sin_addr; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] collect_my_addrs UDP sock=%d %d.%d.%d.%d:%d", + CC_ID, s->sock_id, ip[0], ip[1], ip[2], ip[3], port); + } else if (sa && sa->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; + if (pos + 20 > max_buf_sz) break; + buf[pos++] = 6; buf[pos++] = s->sock_id; + memcpy(buf + pos, &sin6->sin6_addr, 16); pos += 16; + uint16_t port = ntohs(sin6->sin6_port); + buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); count++; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] collect_my_addrs UDP v6 sock=%d port=%d", + CC_ID, s->sock_id, port); } s = s->next; } + + { struct TCP_SOCKET* ts = g_cc.inst->tcp_sockets; + while (ts) { + struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr; + if (addr && addr->ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)addr; + if (pos + 8 > max_buf_sz) break; + buf[pos++] = 4; buf[pos++] = ts->sock_id; + memcpy(buf + pos, &sin->sin_addr, 4); pos += 4; + uint16_t port = ntohs(sin->sin_port); + buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); + count++; + uint8_t* ip = (uint8_t*)&sin->sin_addr; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] collect_my_addrs TCP sock=%d %d.%d.%d.%d:%d", + CC_ID, ts->sock_id, ip[0], ip[1], ip[2], ip[3], port); + } else if (addr && addr->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; + if (pos + 20 > max_buf_sz) break; + buf[pos++] = 6; buf[pos++] = ts->sock_id; + memcpy(buf + pos, &sin6->sin6_addr, 16); pos += 16; + uint16_t port = ntohs(sin6->sin6_port); + buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); + count++; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] collect_my_addrs TCP v6 sock=%d port=%d", + CC_ID, ts->sock_id, port); + } + ts = ts->next; } + } + return count; } @@ -140,7 +183,7 @@ void chat_core_create_channel(struct chat_channel_create* req) { myid, g_cc.inst->my_keys.public_key, join_ts, g_cc.inst->my_ed25519_privkey, join_sig); - uint8_t my_addrs[256]; int my_addr_cnt = collect_my_addrs(my_addrs, 16); + uint8_t my_addrs[256]; int my_addr_cnt = collect_my_addrs(my_addrs, (int)sizeof(my_addrs)); char juser3[256]; snprintf(juser3, sizeof(juser3), "{\"name\":\"%s\"}", g_cc.inst->name[0] ? g_cc.inst->name : ""); @@ -151,6 +194,7 @@ void chat_core_create_channel(struct chat_channel_create* req) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_sync_put(self) FAILED ch=%s rc=%d", CC_ID, req->channel_id, mrc); } + chat_core_sync_my_addresses(); } chat_event_post(CHAT_EVT_CHANNEL_UPDATED, data, 1 + ch_id_len); diff --git a/src/chat/chat_core.c b/src/chat/chat_core.c index dcdcbf25..a01d6478 100644 --- a/src/chat/chat_core.c +++ b/src/chat/chat_core.c @@ -135,8 +135,6 @@ int chat_core_init(struct UTUN_INSTANCE* inst, const char* db_path) { g_cc.initialized = 1; - chat_core_sync_my_addresses(); - /* create media_files table */ media_index_init(g_cc.db); diff --git a/src/chat/chat_profile.c b/src/chat/chat_profile.c index 99c2a158..5540c84a 100644 --- a/src/chat/chat_profile.c +++ b/src/chat/chat_profile.c @@ -12,6 +12,7 @@ #include "../utun_instance.h" #include "../transport_layer/etcp.h" +#include "../transport_layer/etcp_connections.h" #include "../transport_layer/secure_channel.h" #include "../ntp_time.h" #include "../../lib/u_async.h" @@ -113,7 +114,7 @@ void chat_core_sync_my_addresses(void) { } struct ETCP_SOCKET* sock = g_cc.inst->etcp_sockets; - int sock_count = 0, written = 0; + int sock_count = 0, written = 0, tcp_written = 0; while (sock) { sock_count++; int sock_family = sock->local_addr.ss_family ? sock->local_addr.ss_family : 0; @@ -149,13 +150,56 @@ void chat_core_sync_my_addresses(void) { } sqlite3_finalize(ins); - if (sock_count == 0) - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] etcp_sockets is NULL — NO sockets, addresses NOT written!", CC_ID); - else if (written == 0) - DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] %d sockets found but 0 addresses written (all have no interface_addr nor local_addr)", CC_ID, sock_count); + { sqlite3_stmt* tcp_ins = NULL; + sqlite3_prepare_v2(g_cc.db, + "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" + " VALUES(?,?,2,?,?,0,?)", -1, &tcp_ins, NULL); + if (tcp_ins) { + struct TCP_SOCKET* ts = g_cc.inst->tcp_sockets; + int tcp_count = 0; + while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; } + tcp_count++; + struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr; + if (!addr || !addr->ss_family) { ts = ts->next; continue; } + if (addr->ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)addr; + sqlite3_bind_int64(tcp_ins, 1, (sqlite3_int64)g_cc.my_node_id); + sqlite3_bind_int(tcp_ins, 2, 4); + sqlite3_bind_blob(tcp_ins, 3, &sin->sin_addr, 4, SQLITE_STATIC); + sqlite3_bind_int(tcp_ins, 4, (int)ntohs(sin->sin_port)); + sqlite3_bind_int(tcp_ins, 5, (int)ts->sock_id); + sqlite3_step(tcp_ins); sqlite3_reset(tcp_ins); + uint8_t* ip = (uint8_t*)&sin->sin_addr; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] INSERT TCP my_node=0x%016llx sock=%d %d.%d.%d.%d:%d", + CC_ID, (unsigned long long)g_cc.my_node_id, ts->sock_id, ip[0], ip[1], ip[2], ip[3], (int)ntohs(sin->sin_port)); + tcp_written++; + } else if (addr->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; + sqlite3_bind_int64(tcp_ins, 1, (sqlite3_int64)g_cc.my_node_id); + sqlite3_bind_int(tcp_ins, 2, 6); + sqlite3_bind_blob(tcp_ins, 3, &sin6->sin6_addr, 16, SQLITE_STATIC); + sqlite3_bind_int(tcp_ins, 4, (int)ntohs(sin6->sin6_port)); + sqlite3_bind_int(tcp_ins, 5, (int)ts->sock_id); + sqlite3_step(tcp_ins); sqlite3_reset(tcp_ins); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] INSERT TCP v6 my_node=0x%016llx sock=%d port=%d", + CC_ID, (unsigned long long)g_cc.my_node_id, ts->sock_id, (int)ntohs(sin6->sin6_port)); + tcp_written++; + } + ts = ts->next; } + sqlite3_finalize(tcp_ins); + if (tcp_written > 0) + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] TCP DONE: %d addresses written, my_node=0x%016llx", + CC_ID, tcp_written, (unsigned long long)g_cc.my_node_id); + } + } + + if (sock_count == 0 && tcp_written == 0) + DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] no sockets found — NO addresses written!", CC_ID); + else if (written == 0 && tcp_written == 0) + DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] %d sockets found but 0 addresses written", CC_ID, sock_count); else - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] DONE: %d of %d sockets written, my_node=0x%016llx", - CC_ID, written, sock_count, (unsigned long long)g_cc.my_node_id); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] DONE: %d UDP + %d TCP = %d total, my_node=0x%016llx", + CC_ID, written, tcp_written, written + tcp_written, (unsigned long long)g_cc.my_node_id); sqlite3_stmt* chk = NULL; sqlite3_prepare_v2(g_cc.db, "SELECT COUNT(*) FROM node_addresses WHERE node_id=? AND addr_type=0", -1, &chk, NULL); diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 012e2940..3297b84c 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -14,6 +14,7 @@ #include "route_lib.h" #include "etcp_debug.h" #include "topo_node_sqlite.h" +#include "../transport_layer/etcp_connections.h" #include "../lib/u_async.h" void topo_node_ref(struct TOPO_NODE* ni) { @@ -603,26 +604,19 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR if (group->group_type == TOPO_GROUP_TYPE_CHAT) { vc = 0; vc6 = 0; } int sock_count = 0, addr_count = 0, sock6_count = 0, addr6_count = 0; + int tcp4_count = 0, tcp6_count = 0; struct ETCP_SOCKET* e_sock = instance->etcp_sockets; while (e_sock) { if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; } - if (e_sock->local_addr.ss_family == AF_INET) { - sock_count++; addr_count++; - if (e_sock->nat_addr.ss_family == AF_INET) { - struct sockaddr_in* ns = (struct sockaddr_in*)&e_sock->nat_addr; - struct sockaddr_in* ls = (struct sockaddr_in*)&e_sock->local_addr; - struct sockaddr_in* ifs = (struct sockaddr_in*)&e_sock->interface_addr; - uint32_t ref_ip = ls->sin_addr.s_addr ? ls->sin_addr.s_addr : ifs->sin_addr.s_addr; - uint16_t ref_port = ntohs(ls->sin_addr.s_addr ? ls->sin_port : ifs->sin_port); - if (ns->sin_addr.s_addr != 0 && e_sock->nat_type != NAT_VERIFIED_STRICT && (ns->sin_addr.s_addr != ref_ip || ntohs(ns->sin_port) != ref_port)) - addr_count++; - } - if (e_sock->nat_type == NAT_VERIFIED_DIRECT || ((e_sock->type == CFG_SERVER_TYPE_PUBLIC || e_sock->type == CFG_SERVER_TYPE_UNKNOWN) && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) addr_count++; - } else if (e_sock->local_addr.ss_family == AF_INET6) { sock6_count++; addr6_count++; } + if (e_sock->local_addr.ss_family == AF_INET) { sock_count++; addr_count++; } + else if (e_sock->local_addr.ss_family == AF_INET6) { sock6_count++; addr6_count++; } e_sock = e_sock->next; } - { struct CFG_SERVER* srv = instance->config->servers; - while (srv) { if (srv->transport) { if (srv->ip.ss_family == AF_INET) addr_count++; else if (srv->ip.ss_family == AF_INET6) addr6_count++; } srv = srv->next; } + { struct TCP_SOCKET* ts = instance->tcp_sockets; + while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; } + if (ts->interface_addr.ss_family == AF_INET || ts->local_addr.ss_family == AF_INET) { tcp4_count++; addr_count++; } + else if (ts->interface_addr.ss_family == AF_INET6 || ts->local_addr.ss_family == AF_INET6) { tcp6_count++; addr6_count++; } + ts = ts->next; } } int changed = 1; uint8_t old_ver = 0; @@ -693,25 +687,19 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR struct sockaddr_in* local_sin = (struct sockaddr_in*)&e_sock->local_addr; struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr; - int use_local = (local_sin->sin_addr.s_addr != 0); - uint32_t ref_ip = use_local ? local_sin->sin_addr.s_addr : if_sin->sin_addr.s_addr; - uint16_t ref_port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); int nat_verified = (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0 && e_sock->nat_type != NAT_VERIFIED_STRICT); - int nat_differs = (nat_verified && (nat_sin->sin_addr.s_addr != ref_ip || ntohs(nat_sin->sin_port) != ref_port)); - { struct TOPO_ADDR4* a = memory_pool_alloc(group->instance->topo_groups->v4_addr_pool); - memcpy(a->addr, use_local ? &local_sin->sin_addr.s_addr : &if_sin->sin_addr.s_addr, 4); - a->port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); - a->type = (!nat_differs && nat_verified) ? TOPO_ADDR_NAT : TOPO_ADDR_INTERFACE; - a->socket_id = (a->type == TOPO_ADDR_NAT) ? (e_sock->sock_id | 1) : e_sock->sock_id; + if (nat_verified) { + memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port); + a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id | 1; + } else { + int use_local = (local_sin->sin_addr.s_addr != 0); + memcpy(a->addr, use_local ? &local_sin->sin_addr.s_addr : &if_sin->sin_addr.s_addr, 4); + a->port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); + a->type = TOPO_ADDR_INTERFACE; a->socket_id = e_sock->sock_id; + } a->protocol = TOPO_PROTO_UDP; a->next = ni->v4_addrs; ni->v4_addrs = a; } - if (nat_differs) { - struct TOPO_ADDR4* a = memory_pool_alloc(group->instance->topo_groups->v4_addr_pool); - memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port); - a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id | 1; a->protocol = TOPO_PROTO_UDP; - a->next = ni->v4_addrs; ni->v4_addrs = a; - } } else if (e_sock->local_addr.ss_family == AF_INET6) { { struct TOPO_SOCKMETA6* sm6 = memory_pool_alloc(group->instance->topo_groups->v6_sock_meta_pool); sm6->id = e_sock->sock_id; sm6->config_type = e_sock->type; sm6->nat_type = e_sock->nat_type; @@ -724,20 +712,24 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR } e_sock = e_sock->next; } - { struct CFG_SERVER* srv = instance->config->servers; - while (srv) { if (srv->transport && srv->ip.ss_family == AF_INET) { - struct sockaddr_in* tcp_sin = (struct sockaddr_in*)&srv->ip; - struct TOPO_ADDR4* a = memory_pool_alloc(group->instance->topo_groups->v4_addr_pool); - memcpy(a->addr, &tcp_sin->sin_addr.s_addr, 4); a->port = ntohs(tcp_sin->sin_port); - a->type = TOPO_ADDR_INTERFACE; a->socket_id = 0; a->protocol = TOPO_PROTO_TCP; - a->next = ni->v4_addrs; ni->v4_addrs = a; - } else if (srv->transport && srv->ip.ss_family == AF_INET6) { - struct sockaddr_in6* tcp_sin6 = (struct sockaddr_in6*)&srv->ip; - struct TOPO_ADDR6* a6 = memory_pool_alloc(group->instance->topo_groups->v6_addr_pool); - memcpy(a6->addr, &tcp_sin6->sin6_addr, 16); a6->port = ntohs(tcp_sin6->sin6_port); - a6->type = TOPO_ADDR_INTERFACE; a6->socket_id = 0; a6->protocol = TOPO_PROTO_TCP; - a6->next = ni->v6_addrs; ni->v6_addrs = a6; - } srv = srv->next; } + { struct TCP_SOCKET* ts = instance->tcp_sockets; + while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; } + struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr; + if (!addr || !addr->ss_family) { ts = ts->next; continue; } + if (addr->ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)addr; + { struct TOPO_ADDR4* a = memory_pool_alloc(group->instance->topo_groups->v4_addr_pool); + memcpy(a->addr, &sin->sin_addr.s_addr, 4); a->port = ntohs(sin->sin_port); + a->type = TOPO_ADDR_INTERFACE; a->socket_id = ts->sock_id; a->protocol = TOPO_PROTO_TCP; + a->next = ni->v4_addrs; ni->v4_addrs = a; } + } else if (addr->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; + { struct TOPO_ADDR6* a6 = memory_pool_alloc(group->instance->topo_groups->v6_addr_pool); + memcpy(a6->addr, &sin6->sin6_addr, 16); a6->port = ntohs(sin6->sin6_port); + a6->type = TOPO_ADDR_INTERFACE; a6->socket_id = ts->sock_id; a6->protocol = TOPO_PROTO_TCP; + a6->next = ni->v6_addrs; ni->v6_addrs = a6; } + } + ts = ts->next; } } if (group->group_type != TOPO_GROUP_TYPE_CHAT && (vc || vc6)) { @@ -758,8 +750,8 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR lq->subnets = r; } - DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: v4s=%d v4a=%d v6s=%d v6a=%d v4sub=%d v6sub=%d ver=%d", - sock_count, addr_count, sock6_count, addr6_count, vc, vc6, ni->ver); + DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: v4s=%d v4a=%d v6s=%d v6a=%d tcp4=%d tcp6=%d v4sub=%d v6sub=%d ver=%d", + sock_count, addr_count, sock6_count, addr6_count, tcp4_count, tcp6_count, vc, vc6, ni->ver); if (group->group_type != TOPO_GROUP_TYPE_CHAT && instance->rt && !route_insert(instance->rt, lq)) DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "failed to insert local routes"); } else { @@ -793,24 +785,19 @@ void topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) { struct sockaddr_in* local_sin = (struct sockaddr_in*)&e_sock->local_addr; struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr; - int use_local = (local_sin->sin_addr.s_addr != 0); - uint32_t ref_ip = use_local ? local_sin->sin_addr.s_addr : if_sin->sin_addr.s_addr; - uint16_t ref_port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); int nat_verified = (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0 && e_sock->nat_type != NAT_VERIFIED_STRICT); - int nat_differs = (nat_verified && (nat_sin->sin_addr.s_addr != ref_ip || ntohs(nat_sin->sin_port) != ref_port)); { struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool); - memcpy(a->addr, use_local ? &local_sin->sin_addr.s_addr : &if_sin->sin_addr.s_addr, 4); - a->port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); - a->type = (!nat_differs && nat_verified) ? TOPO_ADDR_NAT : TOPO_ADDR_INTERFACE; - a->socket_id = (a->type == TOPO_ADDR_NAT) ? (e_sock->sock_id | 1) : e_sock->sock_id; + if (nat_verified) { + memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port); + a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id | 1; + } else { + int use_local = (local_sin->sin_addr.s_addr != 0); + memcpy(a->addr, use_local ? &local_sin->sin_addr.s_addr : &if_sin->sin_addr.s_addr, 4); + a->port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port); + a->type = TOPO_ADDR_INTERFACE; a->socket_id = e_sock->sock_id; + } a->protocol = TOPO_PROTO_UDP; a->next = ni->v4_addrs; ni->v4_addrs = a; } - if (nat_differs) { - struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool); - memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port); - a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id | 1; a->protocol = TOPO_PROTO_UDP; - a->next = ni->v4_addrs; ni->v4_addrs = a; - } } else if (e_sock->local_addr.ss_family == AF_INET6) { { struct TOPO_SOCKMETA6* sm6 = memory_pool_alloc(instance->topo_groups->v6_sock_meta_pool); sm6->id = e_sock->sock_id; sm6->config_type = e_sock->type; sm6->nat_type = e_sock->nat_type; @@ -823,6 +810,25 @@ void topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) { } e_sock = e_sock->next; } + { struct TCP_SOCKET* ts = instance->tcp_sockets; + while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; } + struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr; + if (!addr || !addr->ss_family) { ts = ts->next; continue; } + if (addr->ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)addr; + { struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool); + memcpy(a->addr, &sin->sin_addr.s_addr, 4); a->port = ntohs(sin->sin_port); + a->type = TOPO_ADDR_INTERFACE; a->socket_id = ts->sock_id; a->protocol = TOPO_PROTO_TCP; + a->next = ni->v4_addrs; ni->v4_addrs = a; } + } else if (addr->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; + { struct TOPO_ADDR6* a6 = memory_pool_alloc(instance->topo_groups->v6_addr_pool); + memcpy(a6->addr, &sin6->sin6_addr, 16); a6->port = ntohs(sin6->sin6_port); + a6->type = TOPO_ADDR_INTERFACE; a6->socket_id = ts->sock_id; a6->protocol = TOPO_PROTO_TCP; + a6->next = ni->v6_addrs; ni->v6_addrs = a6; } + } + ts = ts->next; } + } ni->ver = (ni->ver % 255) + 1; DEBUG_INFO(DEBUG_CATEGORY_BGP, "my addresses updated, new ver=%d", ni->ver); diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index 100694c7..47932f3e 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -784,18 +784,46 @@ struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SER rs->sin_family = AF_INET; rs->sin_port = htons(53); inet_pton(AF_INET, "8.8.8.8", &rs->sin_addr); struct sockaddr_storage local; - if (get_outgoing_local_ip(&server->ip, &remote, &local) == 0) + if (get_outgoing_local_ip(&server->ip, &remote, &local) == 0) { ts->interface_addr = local; + ((struct sockaddr_in*)&ts->interface_addr)->sin_port = sin->sin_port; + } } } else if (server->ip.ss_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server->ip; memcpy(&ts->local_addr, sin6, sizeof(*sin6)); ts->interface_addr = ts->local_addr; + if (server->type == CFG_SERVER_TYPE_PUBLIC && memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)) == 0) { + uint16_t v6if = (server->netif_index > 0) ? server->netif_index : get_default_route_netif_index(AF_INET6); + if (v6if > 0) { + uint8_t v6addr[16]; + int got = get_interface_ipv6_addr_nl(v6if, 1, v6addr); + if (got != 0) got = get_interface_ipv6_addr_nl(v6if, 0, v6addr); + if (got == 0) { + struct sockaddr_in6* if6 = (struct sockaddr_in6*)&ts->interface_addr; + if6->sin6_family = AF_INET6; memcpy(&if6->sin6_addr, v6addr, 16); + } + } + if (ts->interface_addr.ss_family == 0) { + struct sockaddr_storage remote; memset(&remote, 0, sizeof(remote)); + struct sockaddr_in6* rs6 = (struct sockaddr_in6*)&remote; + rs6->sin6_family = AF_INET6; rs6->sin6_port = htons(53); + inet_pton(AF_INET6, "2001:4860:4860::8888", &rs6->sin6_addr); + if (get_outgoing_local_ip(&server->ip, &remote, &ts->interface_addr) == 0) + ((struct sockaddr_in6*)&ts->interface_addr)->sin6_port = sin6->sin6_port; + else + ts->interface_addr = ts->local_addr; + } + } } ts->next = instance->tcp_sockets; instance->tcp_sockets = ts; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "tcp_socket_add: %s type=%d sock_id=%u", ts->name, ts->type, ts->sock_id); + { char loc_str[64] = "none", if_str[64] = "none"; + if (ts->local_addr.ss_family) snprintf(loc_str, sizeof(loc_str), "%s", sockaddr_storage_to_str(&ts->local_addr).str); + if (ts->interface_addr.ss_family) snprintf(if_str, sizeof(if_str), "%s", sockaddr_storage_to_str(&ts->interface_addr).str); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "tcp_socket_add: %s type=%d sock_id=%u local=%s iface=%s", + ts->name, ts->type, ts->sock_id, loc_str, if_str); } return ts; } @@ -1406,8 +1434,6 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk if (g) { topo_group_update_my_nodeinfo(g->instance, g); if (g->local_node && g->senders_list) { - struct TOPO_NODE* vni = topo_node_registry_find(g->instance->topo_groups, g->local_node->node_id); - if (vni) vni->ver = (vni->ver % 255) + 1; struct ll_entry* se = g->senders_list->head; while (se) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data; diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ChatApplication.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ChatApplication.kt index 937aba5f..8a039f93 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ChatApplication.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ChatApplication.kt @@ -35,10 +35,8 @@ class ChatApplication : Application() { /* First launch config */ if (configProvider.isFirstLaunch()) { kotlinx.coroutines.runBlocking { - LogManager.addLog("INFO", "APP", "First launch — configuring") - configProvider.detectInterfaces() + LogManager.addLog("INFO", "APP", "First launch — marking launched") configProvider.markLaunched() - LogManager.addLog("INFO", "APP", "First launch done, ${configProvider.loadServers().size} interfaces detected") } } } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt index da356d2e..59271b9f 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt @@ -7,8 +7,6 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.first @@ -19,6 +17,7 @@ import java.net.InetAddress import java.net.NetworkInterface import java.io.File import java.util.UUID +import java.util.concurrent.ThreadLocalRandom private val Context.dataStore by preferencesDataStore("utun_config") @@ -35,7 +34,6 @@ class ConfigProvider(private val context: Context) { private val debugCategories = stringPreferencesKey("debug_categories") private val logUdpIp = stringPreferencesKey("log_udp_ip") private val logUdpPort = intPreferencesKey("log_udp_port") - private val serversJson = stringPreferencesKey("servers") private val chatStorageAutoload = intPreferencesKey("chat_storage_autoload") private val chatStorageAutoloadMaxsizeMb = intPreferencesKey("chat_storage_autoload_maxsize_mb") private val chatStorageMaxsizeGb = intPreferencesKey("chat_storage_maxsize_gb") @@ -46,9 +44,6 @@ class ConfigProvider(private val context: Context) { private val chatMediaDownloadMaxPeers = intPreferencesKey("chat_media_download_max_peers") private val firstLaunchDone = booleanPreferencesKey("first_launch_done") - private val gson = Gson() - private val random = java.util.concurrent.ThreadLocalRandom.current() - private fun keysFilePath(): String { val dir = runBlocking { context.dataStore.data.first()[dbPath] } ?: context.filesDir.absolutePath return "$dir/keys.conf" @@ -95,19 +90,6 @@ class ConfigProvider(private val context: Context) { fun getString(key: String): String? = runBlocking { when { - key.startsWith("servers.") -> { - val parts = key.removePrefix("servers.").split(".", limit = 2) - val idx = parts[0].toIntOrNull() ?: return@runBlocking null - val field = parts.getOrNull(1) ?: return@runBlocking null - val list = loadServers() - val srv = list.getOrNull(idx) ?: return@runBlocking null - when (field) { - "bindIp" -> srv.bindIp - "protocol" -> srv.protocol - else -> null - } - } - key == "servers.count" -> loadServers().size.toString() key == "node.private_key" -> readKeysFromFile()?.second ?: context.dataStore.data.first()[nodePrivateKey] key == "node.public_key" -> readKeysFromFile()?.first ?: context.dataStore.data.first()[nodePublicKey] key == "node.name" -> context.dataStore.data.first()[nodeName] ?: getDeviceName() @@ -123,18 +105,6 @@ class ConfigProvider(private val context: Context) { fun getInt(key: String): Int = runBlocking { when { - key.startsWith("servers.") -> { - val parts = key.removePrefix("servers.").split(".", limit = 2) - val idx = parts[0].toIntOrNull() ?: return@runBlocking 0 - val field = parts.getOrNull(1) ?: return@runBlocking 0 - val list = loadServers() - val srv = list.getOrNull(idx) ?: return@runBlocking 0 - when (field) { - "port" -> srv.port - else -> 0 - } - } - key == "servers.count" -> loadServers().size key == "node.listen_port" -> context.dataStore.data.first()[nodeListenPort] ?: 12345 key == "control.port" -> context.dataStore.data.first()[controlPort] ?: 9999 key == "log_udp.port" -> context.dataStore.data.first()[logUdpPort] ?: 9999 @@ -191,6 +161,20 @@ class ConfigProvider(private val context: Context) { // fc00::/7 → fc00-fdff, 2000::/3 → 2000-3fff return hi in 0xFC00..0xFDFF || hi in 0x2000..0x3FFF } + + // 0x20 = IFA_F_TEMPORARY в /proc/net/if_inet6 + fun isTemporaryV6(ifName: String, ip: String): Boolean { + val hexAddr = ip.split("%")[0].replace(":", "").uppercase().padStart(32, '0') + return try { + File("/proc/net/if_inet6").useLines { lines -> + lines.any { line -> + val parts = line.trim().split("\\s+".toRegex(), limit = 6) + parts.size >= 6 && parts[0] == hexAddr && parts[5] == ifName && + parts[4].toIntOrNull(16)?.let { (it and 0x20) != 0 } == true + } + } + } catch (_: Exception) { false } + } } /* ── First launch ── */ @@ -207,6 +191,7 @@ class ConfigProvider(private val context: Context) { suspend fun detectInterfaces(): List { val servers = mutableListOf() + val seen = mutableSetOf() try { val nis = NetworkInterface.getNetworkInterfaces() while (nis.hasMoreElements()) { @@ -216,70 +201,90 @@ class ConfigProvider(private val context: Context) { val addr = a.address val ip = when { addr is Inet4Address -> addr.hostAddress - addr is Inet6Address && isUsableV6(addr) -> addr.hostAddress?.let { it.split("%")[0] } + addr is Inet6Address && isUsableV6(addr) && !isTemporaryV6(ni.name, addr.hostAddress ?: "") -> addr.hostAddress?.let { it.split("%")[0] } else -> null } ?: continue + val key = "${ni.name}/${if (ip.contains(":")) "v6" else "v4"}" + if (!seen.add(key)) continue servers.add(ServerEntry( id = UUID.randomUUID().toString(), ifName = ni.name, bindIp = ip, protocol = "tcp", - port = random.nextInt(2000, 10000) // 2000-9999 + port = ThreadLocalRandom.current().nextInt(2000, 10000) )) } } } catch (e: Exception) { LogManager.addLog("ERROR", "CFG", "detectInterfaces failed: ${e.message}") } - saveServers(servers) return servers } - suspend fun saveServers(servers: List) { - val json = gson.toJson(servers) - context.dataStore.edit { prefs -> prefs[serversJson] = json } - } + /* ── Config file as single source of truth for servers ── */ - fun loadServers(): List = runBlocking { - val json = context.dataStore.data.first()[serversJson] ?: "[]" - try { - gson.fromJson(json, object : TypeToken>() {}.type) ?: emptyList() - } catch (e: Exception) { - emptyList() - } + fun configFilePath(): String { + val dir = getString("db.path") ?: context.filesDir.absolutePath + return "$dir/utun.cfg" } - /** Return all currently available network interface names (for dropdown) */ - fun getAvailableInterfaces(): List> = runBlocking { - val list = mutableListOf>() + fun loadServersFromConfig(): List { + val file = File(configFilePath()) + if (!file.exists()) return emptyList() + data class RawSrv(val ifName: String, val family: String, val port: Int, val protocol: String) + val raw = mutableListOf() try { - val nis = NetworkInterface.getNetworkInterfaces() - while (nis.hasMoreElements()) { - val ni = nis.nextElement() - if (!ni.isUp || ni.isLoopback) continue - for (a in ni.interfaceAddresses) { - val addr = a.address - val ip = when { - addr is Inet4Address -> addr.hostAddress - addr is Inet6Address && isUsableV6(addr) -> addr.hostAddress?.let { it.split("%")[0] } - else -> null - } ?: continue - list.add(ni.name to ip) + val lines = file.readLines() + var ifName = ""; var family = ""; var addrPort = 0; var proto = "tcp"; var sectionOpen = false + for (line in lines) { + val trimmed = line.trim() + if (trimmed.startsWith("[server:") && trimmed.endsWith("]")) { + if (sectionOpen && ifName.isNotEmpty() && addrPort > 0) + raw.add(RawSrv(ifName, family, addrPort, proto)) + val section = trimmed.removeSurrounding("[", "]").removePrefix("server:") + val lastUnderscore = section.lastIndexOf('_') + if (lastUnderscore > 0) { + ifName = section.substring(0, lastUnderscore) + family = section.substring(lastUnderscore + 1) + } else { ifName = ""; family = "" } + addrPort = 0; proto = "tcp"; sectionOpen = true + } else if (trimmed.startsWith("addr=")) { + val addr = trimmed.removePrefix("addr=") + val colonIdx = addr.lastIndexOf(':') + if (colonIdx >= 0) addrPort = addr.substring(colonIdx + 1).toIntOrNull() ?: 0 + } else if (trimmed.startsWith("transport=")) { + proto = if (trimmed.removePrefix("transport=") == "udp") "udp" else "tcp" } } - } catch (_: Exception) {} - if (list.isEmpty()) list.add("lo" to "127.0.0.1") - list + if (sectionOpen && ifName.isNotEmpty() && addrPort > 0) + raw.add(RawSrv(ifName, family, addrPort, proto)) + } catch (_: Exception) { return emptyList() } + + val liveIps = getAvailableInterfaces() + return raw.map { rs -> + val isV6 = rs.family == "v6" + val realIp = liveIps.firstOrNull { (ifn, ip) -> + ifn == rs.ifName && (if (isV6) ip.contains(":") else !ip.contains(":")) + }?.second + ServerEntry(UUID.randomUUID().toString(), rs.ifName, + realIp ?: if (isV6) "::" else "0.0.0.0", rs.protocol, rs.port) + } } - /** Build full INI config text for NativeLib.start() */ - fun buildConfigText(): String { + fun saveServersToConfig(servers: List) { + val text = buildConfigFromServers(servers) + File(configFilePath()).writeText(text) + LogManager.addLog("INFO", "CFG", "saveServersToConfig: ${servers.size} servers written" + + servers.joinToString(", ") { "${it.ifName}=${it.protocol}" }) + } + + /** Build complete INI text from server list (without touching config file) */ + private fun buildConfigFromServers(servers: List): String { val pubKey = getString("node.public_key") ?: "" val privKey = getString("node.private_key") ?: "" val nodeName = getString("node.name") ?: getDeviceName() - val listenPort = getInt("node.listen_port") - val dbDir = getString("db.path") ?: context.filesDir.absolutePath - return buildString { + val dbDir = getString("db.path") ?: context.filesDir.absolutePath + return buildString { appendLine("[global]") if (nodeName.isNotBlank()) appendLine("my_node_name=$nodeName") if (pubKey.length == 64) appendLine("my_public_key=$pubKey") @@ -287,12 +292,15 @@ class ConfigProvider(private val context: Context) { appendLine("db_path=$dbDir") appendLine("db_sync_enabled=1") appendLine() - appendLine("[server:main]") - appendLine("addr=0.0.0.0:$listenPort") - appendLine() - appendLine("[server:main6]") - appendLine("addr=[::]:$listenPort") - appendLine() + for (srv in servers) { + val family = if (srv.bindIp.contains(":")) "v6" else "v4" + appendLine("[server:${srv.ifName}_$family]") + appendLine("addr=${if (family == "v6") "[::]" else "0.0.0.0"}:${srv.port}") + appendLine("type=public") + appendLine("transport=${srv.protocol}") + if (family == "v6") appendLine("netif=${srv.ifName}") + appendLine() + } appendLine("[allowed_keys]") appendLine("allow_all=yes") appendLine() @@ -329,4 +337,42 @@ class ConfigProvider(private val context: Context) { } } } + + /** Return all currently available network interface names (for dropdown) */ + fun getAvailableInterfaces(): List> = runBlocking { + val list = mutableListOf>() + try { + val nis = NetworkInterface.getNetworkInterfaces() + while (nis.hasMoreElements()) { + val ni = nis.nextElement() + if (!ni.isUp || ni.isLoopback) continue + for (a in ni.interfaceAddresses) { + val addr = a.address + val ip = when { + addr is Inet4Address -> addr.hostAddress + addr is Inet6Address && isUsableV6(addr) && !isTemporaryV6(ni.name, addr.hostAddress ?: "") -> addr.hostAddress?.let { it.split("%")[0] } + else -> null + } ?: continue + list.add(ni.name to ip) + } + } + } catch (_: Exception) {} + if (list.isEmpty()) list.add("lo" to "127.0.0.1") + list + } + + /** Build full INI config text for NativeLib.start() */ + fun buildConfigText(): String { + val cfgFile = File(configFilePath()) + val existing = if (cfgFile.exists()) loadServersFromConfig() else emptyList() + LogManager.addLog("INFO", "CFG", "buildConfigText: exists=${cfgFile.exists()} srv=${existing.size}" + + existing.joinToString(", ") { "${it.ifName}=${it.protocol}" }) + if (!cfgFile.exists() || existing.isEmpty()) { + val detected = runBlocking { detectInterfaces() } + LogManager.addLog("WARN", "CFG", "buildConfigText: auto-detecting ${detected.size} interfaces") + saveServersToConfig(detected) + return buildConfigFromServers(detected) + } + return buildConfigFromServers(existing) + } } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt index 35bb4d0c..f730b59a 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/SettingsScreen.kt @@ -123,11 +123,11 @@ private fun GeneralSettingsTab(vm: ChatViewModel, dirty: Boolean, onDirty: (Bool if (logUdpIp.isNotEmpty() && port > 0) NativeLib.setUdpLogTarget(logUdpIp, port) val stored = provider.getString("debug.categories") ?: "" catLevels = if (stored.isNotEmpty()) stored.split(",").associate { it.split("=", limit = 2).let { (k, v) -> k to v } } else emptyMap() - servers = provider.loadServers() + servers = provider.loadServersFromConfig() availableIfaces = provider.getAvailableInterfaces() } - val save = { s: List -> onDirty(true); servers = s; scope.launch { provider.saveServers(s) } } + val save = { s: List -> onDirty(true); servers = s; provider.saveServersToConfig(s) } val setDebug = { level: String -> onDirty(true); NativeLib.setDebugLevel("", level) scope.launch { provider.setValue("debug.console_level", level) }; debugLevel = level diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index f912e487..f9766749 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -21,6 +21,8 @@ #include "sqlite3.h" #include "mem.h" #include "debug_config.h" +#include "transport_layer/etcp_connections.h" +#include "transport_layer/stcp_link.h" #include "../jni_bridge/android_udp_log.h" #include #include @@ -207,6 +209,9 @@ static void* instance_thread(void* arg) { return NULL; } + /* sockets are now created — sync my addresses to DB */ + chat_core_sync_my_addresses(); + /* Bind chat sync via etcp_router */ etcp_router_bind(g_inst, ETCP_RT_ID_CHAT_SYNC, NULL); @@ -347,6 +352,14 @@ static void restart_trampoline(void* arg) { return; } + /* Clean up old sockets so init_connections reads new config */ + while (g_inst->etcp_sockets) etcp_socket_remove(g_inst->etcp_sockets); + g_inst->etcp_sockets = NULL; + while (g_inst->tcp_sockets) tcp_socket_remove(g_inst->tcp_sockets); + g_inst->tcp_sockets = NULL; + if (g_inst->stcp_server) { stcp_link_server_destroy(g_inst->stcp_server); g_inst->stcp_server = NULL; } + g_inst->socket_init_status = 0; + if (utun_instance_init(g_inst) != 0) { IL_LOGE("restart: utun_instance_init failed"); g_running = 0; @@ -354,6 +367,8 @@ static void restart_trampoline(void* arg) { return; } + chat_core_sync_my_addresses(); + if (g_inst->config->global.name[0]) { chat_core_update_my_name(g_inst->config->global.name); IL_LOGI("restart: my_name set to '%s'", g_inst->config->global.name);