Browse Source

chatgui-android: auto_sockets=android — сокеты из конфига, лёгкое обновление без полного рестарта

- auto_sockets три-режимный: off/system/android; android берёт сокеты из [server] конфига
- auto_socket_reload_config: reconcile сокетов (remove→create) по имени+addr:port, сохранение портов
- etcp_create_server_socket вынесен из init_sockets; stcp_server_list_find_by_sock
- instance_lite_update_sockets + JNI nativeUpdateSockets + NativeLib.updateSockets
- NetworkChangeMonitor: updateSockets вместо restart; порты сохраняются при смене сети
v2
evgeny 3 weeks ago
parent
commit
5b31a765c2
  1. 4
      src/config_parser.c
  2. 8
      src/config_parser.h
  3. 120
      src/transport_layer/auto_socket.c
  4. 6
      src/transport_layer/auto_socket.h
  5. 76
      src/transport_layer/etcp_connections.c
  6. 3
      src/transport_layer/etcp_connections.h
  7. 7
      src/transport_layer/stcp_link.c
  8. 1
      src/transport_layer/stcp_link.h
  9. 1
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt
  10. 6
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt
  11. 4
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/NetworkChangeMonitor.kt
  12. 16
      tools/chatgui-android/jni_bridge/android_jni_bridge.c
  13. 4
      tools/chatgui-android/jni_bridge/android_jni_bridge.h
  14. 51
      tools/chatgui-android/libutun_lite/instance_lite.c
  15. 5
      tools/chatgui-android/libutun_lite/instance_lite.h

4
src/config_parser.c

@ -407,7 +407,9 @@ static int parse_global(const char *key, const char *value, struct global_config
return 0; return 0;
} }
if (strcmp(key, "auto_sockets") == 0) { if (strcmp(key, "auto_sockets") == 0) {
global->auto_sockets = strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0; if (strcasecmp(value, "android") == 0) global->auto_sockets = CFG_AUTO_SOCKETS_ANDROID;
else if (strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0) global->auto_sockets = CFG_AUTO_SOCKETS_SYSTEM;
else global->auto_sockets = CFG_AUTO_SOCKETS_OFF;
return 0; return 0;
} }
if (strcmp(key, "auto_socket_skip_no_default_route") == 0) { if (strcmp(key, "auto_socket_skip_no_default_route") == 0) {

8
src/config_parser.h

@ -29,6 +29,12 @@ struct IP {
#define CFG_SERVER_TYPE_PRIVATE 3 #define CFG_SERVER_TYPE_PRIVATE 3
#define CFG_SERVER_TYPE_LOCAL 4 #define CFG_SERVER_TYPE_LOCAL 4
/* Режимы auto_sockets: OFF — ручные [server]; SYSTEM — авто-сокеты из системных
* интерфейсов; ANDROID — авто-сокеты из [server] секций конфига (генерирует Kotlin). */
#define CFG_AUTO_SOCKETS_OFF 0
#define CFG_AUTO_SOCKETS_SYSTEM 1
#define CFG_AUTO_SOCKETS_ANDROID 2
#define CFG_IPV6_MODE_NONE 0 // normal explicit address #define CFG_IPV6_MODE_NONE 0 // normal explicit address
#define CFG_IPV6_MODE_TEMPORARY 1 // temporary IPv6 from interface #define CFG_IPV6_MODE_TEMPORARY 1 // temporary IPv6 from interface
#define CFG_IPV6_MODE_PERMANENT 2 // permanent IPv6 from interface #define CFG_IPV6_MODE_PERMANENT 2 // permanent IPv6 from interface
@ -189,7 +195,7 @@ struct global_config {
// TCP proxy server (exit node) configuration ([tcp_proxy_server] section) // TCP proxy server (exit node) configuration ([tcp_proxy_server] section)
int tcp_proxy_server_enabled; int tcp_proxy_server_enabled;
int auto_sockets; // 1 = auto-create sockets from interfaces, skip [server] int auto_sockets; // CFG_AUTO_SOCKETS_*: OFF/SYSTEM/ANDROID (см. константы ниже)
int auto_socket_skip_no_default_route; // 1 = в авто-режиме не создавать сокеты на интерфейсах без default-маршрута int auto_socket_skip_no_default_route; // 1 = в авто-режиме не создавать сокеты на интерфейсах без default-маршрута
int tcp_recv_buf; // SO_RCVBUF для TCP сокетов (0=default ОС) int tcp_recv_buf; // SO_RCVBUF для TCP сокетов (0=default ОС)
int tcp_proxy_server_fib; // FreeBSD SO_SETFIB, 0=не менять int tcp_proxy_server_fib; // FreeBSD SO_SETFIB, 0=не менять

120
src/transport_layer/auto_socket.c

@ -144,6 +144,7 @@ struct auto_sock_iface {
struct AUTO_SOCKET { struct AUTO_SOCKET {
struct UTUN_INSTANCE* instance; // родительский utun instance struct UTUN_INSTANCE* instance; // родительский utun instance
struct auto_sock_iface* ifaces; // связный список интерфейсов struct auto_sock_iface* ifaces; // связный список интерфейсов
uint8_t mode; // CFG_AUTO_SOCKETS_SYSTEM или CFG_AUTO_SOCKETS_ANDROID
uint8_t v4_usable; // есть ли сейчас рабочий v4-сокет с реальным IP uint8_t v4_usable; // есть ли сейчас рабочий v4-сокет с реальным IP
uint8_t v6_usable; // есть ли сейчас рабочий v6-сокет с реальным IP uint8_t v6_usable; // есть ли сейчас рабочий v6-сокет с реальным IP
uint8_t v4_addr_changed; // v4-адрес или тип изменился с прошлой проверки uint8_t v4_addr_changed; // v4-адрес или тип изменился с прошлой проверки
@ -167,6 +168,7 @@ static void remove_iface_sockets(struct AUTO_SOCKET* as, uint32_t ifindex);
static int scan_and_classify_iface(uint32_t ifindex, const char* ifname, int* out_has_v4, int* out_has_v6, uint8_t* out_v4_type, uint8_t* out_v6_type); static int scan_and_classify_iface(uint32_t ifindex, const char* ifname, int* out_has_v4, int* out_has_v6, uint8_t* out_v4_type, uint8_t* out_v6_type);
static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname); static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname);
static void auto_socket_scan_all(struct AUTO_SOCKET* as); static void auto_socket_scan_all(struct AUTO_SOCKET* as);
static void auto_socket_reconcile_config(struct AUTO_SOCKET* as);
/* ─── DB helpers (port persistence) ─── /* ─── DB helpers (port persistence) ───
* Сохраняют порт сокета в SQLite чтобы переиспользовать его при перезапуске utun. * Сохраняют порт сокета в SQLite чтобы переиспользовать его при перезапуске utun.
@ -1318,14 +1320,23 @@ static void auto_socket_destroy_monitor(struct AUTO_SOCKET* as) { (void)as; }
*/ */
int auto_socket_init(struct UTUN_INSTANCE* inst) { int auto_socket_init(struct UTUN_INSTANCE* inst) {
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] instance is NULL"); return -1; } if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] instance is NULL"); return -1; }
if (!inst->config || !inst->config->global.auto_sockets) return 0; if (!inst->config || inst->config->global.auto_sockets == CFG_AUTO_SOCKETS_OFF) return 0;
if (inst->auto_socket_state) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] already initialized"); return -1; } if (inst->auto_socket_state) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] already initialized"); return -1; }
struct AUTO_SOCKET* as = u_calloc(1, sizeof(*as)); struct AUTO_SOCKET* as = u_calloc(1, sizeof(*as));
if (!as) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] alloc failed"); return -1; } if (!as) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] alloc failed"); return -1; }
as->instance = inst; as->instance = inst;
as->mode = (uint8_t)inst->config->global.auto_sockets;
inst->auto_socket_state = as; inst->auto_socket_state = as;
/* Android-режим: источник сокетов — [server] секции конфига (генерирует Kotlin).
* Системный мониторинг и таблица портов в БД не нужны. */
if (as->mode == CFG_AUTO_SOCKETS_ANDROID) {
auto_socket_reconcile_config(as);
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] initialized (android mode)");
return 0;
}
if (inst->topo_sqlite_db) { if (inst->topo_sqlite_db) {
sqlite3_exec(inst->topo_sqlite_db, sqlite3_exec(inst->topo_sqlite_db,
"CREATE TABLE IF NOT EXISTS auto_socket_ports (" "CREATE TABLE IF NOT EXISTS auto_socket_ports ("
@ -1375,5 +1386,112 @@ static void auto_socket_scan_all_trampoline(void* arg) {
void auto_socket_on_network_change(struct UTUN_INSTANCE* inst) { void auto_socket_on_network_change(struct UTUN_INSTANCE* inst) {
if (!inst || !inst->auto_socket_state) return; if (!inst || !inst->auto_socket_state) return;
struct AUTO_SOCKET* as = (struct AUTO_SOCKET*)inst->auto_socket_state; struct AUTO_SOCKET* as = (struct AUTO_SOCKET*)inst->auto_socket_state;
/* Android-режим: обновление идёт только через auto_socket_reload_config (Kotlin → JNI). */
if (as->mode == CFG_AUTO_SOCKETS_ANDROID) return;
uasync_post(as->instance->ua, auto_socket_scan_all_trampoline, as); uasync_post(as->instance->ua, auto_socket_scan_all_trampoline, as);
} }
/* ═══════════ Android-режим: reconcile сокетов из [server] секций конфига ═══════════ */
/* Сравнивает CFG_SERVER с существующим ETCP_SOCKET: имя, адрес+порт, тип, транспорт. */
static int server_matches_socket(const struct CFG_SERVER* server, const struct ETCP_SOCKET* sock) {
if (!server || !sock) return 0;
if (strcmp(server->name, sock->name) != 0) return 0;
if (server->transport != (uint8_t)sock->is_tcp) return 0;
if (server->type != sock->type) return 0;
const struct sockaddr_storage* a = &server->ip;
const struct sockaddr_storage* b = sock->local_addr.ss_family ? &sock->local_addr : &sock->interface_addr;
if (a->ss_family != b->ss_family) return 0;
if (a->ss_family == AF_INET) {
const struct sockaddr_in* ia = (const struct sockaddr_in*)a;
const struct sockaddr_in* ib = (const struct sockaddr_in*)b;
return ia->sin_addr.s_addr == ib->sin_addr.s_addr && ia->sin_port == ib->sin_port;
}
if (a->ss_family == AF_INET6) {
const struct sockaddr_in6* ia = (const struct sockaddr_in6*)a;
const struct sockaddr_in6* ib = (const struct sockaddr_in6*)b;
return memcmp(&ia->sin6_addr, &ib->sin6_addr, 16) == 0 && ia->sin6_port == ib->sin6_port;
}
return 0;
}
/* Удаляет один сокет (UDP или TCP) и все его линки. */
static void remove_config_socket(struct AUTO_SOCKET* as, struct ETCP_SOCKET* sock) {
if (!sock) return;
ncd_remove_socket_links(as->instance, sock);
if (sock->is_tcp) {
struct stcp_server* tsrv = stcp_server_list_find_by_sock(as->instance, sock);
if (tsrv) {
stcp_server_list_remove(as->instance, tsrv);
stcp_link_server_destroy(tsrv);
}
tcp_socket_remove(sock);
} else {
etcp_socket_remove(sock);
}
}
/* Сверяет inst->config->servers с inst->etcp_sockets и устраняет расхождения:
* удаляет сокеты, которых нет/которые изменились (проход 1), создаёт отсутствующие (проход 2). */
static void auto_socket_reconcile_config(struct AUTO_SOCKET* as) {
struct UTUN_INSTANCE* inst = as->instance;
if (!inst || !inst->config) return;
/* Проход 1: удалить сокеты, которых нет в новом списке, либо которые изменились. */
struct ETCP_SOCKET* sock = inst->etcp_sockets;
while (sock) {
struct ETCP_SOCKET* next = sock->next;
const struct CFG_SERVER* srv = NULL;
for (const struct CFG_SERVER* s = inst->config->servers; s; s = s->next)
if (strcmp(s->name, sock->name) == 0) { srv = s; break; }
ip_str_t old_addr = sockaddr_storage_to_str(sock->local_addr.ss_family ? &sock->local_addr : &sock->interface_addr);
if (!srv) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] removing socket %s (%s) addr=%s (no longer in config)",
sock->name, sock->is_tcp ? "tcp" : "udp", old_addr.str);
remove_config_socket(as, sock);
} else if (!server_matches_socket(srv, sock)) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] socket %s changed: %s → %s — rebuild",
sock->name, old_addr.str, sockaddr_storage_to_str(&srv->ip).str);
remove_config_socket(as, sock);
}
sock = next;
}
/* Проход 2: создать сокеты, которых ещё нет. */
for (struct CFG_SERVER* s = inst->config->servers; s; s = s->next) {
int exists = 0;
for (struct ETCP_SOCKET* es = inst->etcp_sockets; es; es = es->next)
if (strcmp(es->name, s->name) == 0) { exists = 1; break; }
if (exists) continue;
struct ETCP_SOCKET* created = NULL;
if (etcp_create_server_socket(inst, s, &created) == 0 && created) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] created socket %s (%s) addr=%s",
s->name, s->transport ? "tcp" : "udp", sockaddr_storage_to_str(&s->ip).str);
ncd_add_socket_links(inst, created);
}
}
auto_socket_post_sockets_changed(as);
}
void auto_socket_reload_config(struct UTUN_INSTANCE* inst, struct utun_config* new_cfg) {
if (!new_cfg) return;
if (!inst || !inst->auto_socket_state) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] reload_config: invalid args"); free_config(new_cfg); return; }
struct AUTO_SOCKET* as = (struct AUTO_SOCKET*)inst->auto_socket_state;
if (as->mode != CFG_AUTO_SOCKETS_ANDROID) { DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] reload_config: not in android mode"); free_config(new_cfg); return; }
/* Сводка до/после: сколько серверов было → стало. */
int old_count = 0; for (struct CFG_SERVER* s = inst->config->servers; s; s = s->next) old_count++;
int new_count = 0; for (struct CFG_SERVER* s = new_cfg->servers; s; s = s->next) new_count++;
/* Переносим новый список серверов в inst->config, старый освобождаем. */
struct CFG_SERVER* old = inst->config->servers;
inst->config->servers = new_cfg->servers;
new_cfg->servers = NULL;
while (old) { struct CFG_SERVER* n = old->next; u_free(old); old = n; }
free_config(new_cfg);
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] reload_config: %d → %d servers", old_count, new_count);
auto_socket_reconcile_config(as);
}

6
src/transport_layer/auto_socket.h

@ -38,6 +38,7 @@ extern "C" {
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_SOCKET; struct ETCP_SOCKET;
struct utun_config;
/* Внутреннее состояние модуля — хранится в inst->auto_socket_state. /* Внутреннее состояние модуля — хранится в inst->auto_socket_state.
* Содержит связный список отслеживаемых интерфейсов (ifaces) и платформенные * Содержит связный список отслеживаемых интерфейсов (ifaces) и платформенные
@ -58,6 +59,11 @@ void auto_socket_destroy(struct UTUN_INSTANCE* inst);
* Откладывает полное сканирование (auto_socket_scan_all) в uasync-поток. */ * Откладывает полное сканирование (auto_socket_scan_all) в uasync-поток. */
void auto_socket_on_network_change(struct UTUN_INSTANCE* inst); void auto_socket_on_network_change(struct UTUN_INSTANCE* inst);
/* Android-режим (auto_sockets=android): обновить сокеты из [server] секций нового
* конфига. new_cfg — уже распарсенный конфиг; его список servers переносится в
* inst->config, остальное освобождается. Должен вызываться в uasync-потоке. */
void auto_socket_reload_config(struct UTUN_INSTANCE* inst, struct utun_config* new_cfg);
/* ─── Platform-agnostic API: управление сокетами на конкретном интерфейсе ─── */ /* ─── Platform-agnostic API: управление сокетами на конкретном интерфейсе ─── */
/* /*

76
src/transport_layer/etcp_connections.c

@ -2333,6 +2333,41 @@ int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
// Initialize only sockets (servers for incoming connections) // Initialize only sockets (servers for incoming connections)
// Called before topo_group_init() to populate etcp_sockets for nodeinfo // Called before topo_group_init() to populate etcp_sockets for nodeinfo
// Returns: 0 = all OK, 1 = partial success (some sockets failed), -1 = fatal error (no sockets) // Returns: 0 = all OK, 1 = partial success (some sockets failed), -1 = fatal error (no sockets)
/* Создаёт один серверный сокет из CFG_SERVER: UDP (etcp_socket_add) или TCP
* (tcp_socket_add + stcp_server_listen + stcp_server_list_add).
* Линки к соединениям не добавляет — это забота вызывающего.
* Возвращает 0 при успехе и *out_sock = созданный сокет, -1 при ошибке. */
int etcp_create_server_socket(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server, struct ETCP_SOCKET** out_sock) {
if (!instance || !server || !out_sock) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_create_server_socket: invalid args"); return -1; }
*out_sock = NULL;
if (server->transport) {
uint16_t port = 0;
if (server->ip.ss_family == AF_INET) port = ntohs(((struct sockaddr_in*)&server->ip)->sin_port);
else if (server->ip.ss_family == AF_INET6) port = ntohs(((struct sockaddr_in6*)&server->ip)->sin6_port);
struct ETCP_SOCKET* ts = tcp_socket_add(instance, server);
if (!ts) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "tcp_socket_add failed for %s", server->name); return -1; }
struct stcp_link_config scfg = {.inst = instance, .listen_family = server->ip.ss_family};
struct stcp_server* tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, ts);
if (!tsrv) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create TCP server for %s", server->name);
tcp_socket_remove(ts);
return -1;
}
stcp_server_list_add(instance, tsrv);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server %s on port %u", server->name, port);
*out_sock = ts;
return 0;
}
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, server);
if (!e_sock) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name); return -1; }
*out_sock = e_sock;
return 0;
}
int init_sockets(struct UTUN_INSTANCE* instance) { int init_sockets(struct UTUN_INSTANCE* instance) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!instance || !instance->config) return -1; if (!instance || !instance->config) return -1;
@ -2401,52 +2436,27 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
} }
} }
// TCP transport: create stcp_server instead of UDP socket struct ETCP_SOCKET* sock = NULL;
if (server->transport) { if (etcp_create_server_socket(instance, server, &sock) != 0) {
uint16_t port = 0; fail_count++;
if (server->ip.ss_family == AF_INET) port = ntohs(((struct sockaddr_in*)&server->ip)->sin_port);
else if (server->ip.ss_family == AF_INET6) port = ntohs(((struct sockaddr_in6*)&server->ip)->sin6_port);
struct ETCP_SOCKET* ts = tcp_socket_add(instance, server);
if (!ts) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "tcp_socket_add failed for %s", server->name); fail_count++; server = server->next; continue; }
struct stcp_link_config scfg = {.inst = instance, .listen_family = server->ip.ss_family};
struct stcp_server *tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, ts);
if (!tsrv) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create TCP server for %s", server->name);
tcp_socket_remove(ts);
fail_count++;
} else {
stcp_server_list_add(instance, tsrv);
success_count++;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server %s on port %u", server->name, port);
}
server = server->next; server = server->next;
continue; continue;
} }
if (sock && !server->transport && default_ip != 0) {
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, server); sock->local_defaultroute_ip = default_ip;
if (e_sock && default_ip != 0) {
e_sock->local_defaultroute_ip = default_ip;
if (server->ip.ss_family == AF_INET) { if (server->ip.ss_family == AF_INET) {
struct in_addr addr; struct in_addr addr;
addr.s_addr = default_ip; addr.s_addr = default_ip;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Server %s type=%s ip=%s", server->name, server_type_str(server->type), ip_to_str(&addr, AF_INET).str); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Server %s type=%s ip=%s", server->name, server_type_str(server->type), ip_to_str(&addr, AF_INET).str);
} }
} }
if (e_sock && have_default_ip6) { if (sock && !server->transport && have_default_ip6) {
memcpy(e_sock->local_defaultroute_ip6, default_ip6, 16); memcpy(sock->local_defaultroute_ip6, default_ip6, 16);
}
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name);
fail_count++;
server = server->next;
continue;
} }
success_count++; success_count++;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized server %s on %s (links: %d)", DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized server %s on %s (links: %d)",
server->name, sockaddr_storage_to_str(&server->ip).str, queue_entry_count(e_sock->links_queue)); server->name, sockaddr_storage_to_str(&server->ip).str, queue_entry_count(sock->links_queue));
server = server->next; server = server->next;
} }

3
src/transport_layer/etcp_connections.h

@ -348,6 +348,9 @@ void etcp_socket_remove(struct ETCP_SOCKET* conn);
struct ETCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server); struct ETCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server);
void tcp_socket_remove(struct ETCP_SOCKET* sock); void tcp_socket_remove(struct ETCP_SOCKET* sock);
// Создаёт один серверный сокет (UDP или TCP) из CFG_SERVER. Линки не добавляет.
int etcp_create_server_socket(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server, struct ETCP_SOCKET** out_sock);
struct stcp_link; struct stcp_link;
void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock); void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock);

7
src/transport_layer/stcp_link.c

@ -420,3 +420,10 @@ int stcp_server_list_count(struct UTUN_INSTANCE *inst) {
for (struct stcp_server *s = inst->stcp_servers; s; s = s->next) n++; for (struct stcp_server *s = inst->stcp_servers; s; s = s->next) n++;
return n; return n;
} }
struct stcp_server *stcp_server_list_find_by_sock(struct UTUN_INSTANCE *inst, const struct ETCP_SOCKET *sock) {
if (!inst || !sock) return NULL;
for (struct stcp_server *s = inst->stcp_servers; s; s = s->next)
if (s->tcp_sock == sock) return s;
return NULL;
}

1
src/transport_layer/stcp_link.h

@ -39,6 +39,7 @@ void stcp_server_list_add(struct UTUN_INSTANCE *inst, struct stcp_server *srv);
void stcp_server_list_remove(struct UTUN_INSTANCE *inst, struct stcp_server *srv); void stcp_server_list_remove(struct UTUN_INSTANCE *inst, struct stcp_server *srv);
void stcp_server_list_destroy_all(struct UTUN_INSTANCE *inst); void stcp_server_list_destroy_all(struct UTUN_INSTANCE *inst);
int stcp_server_list_count(struct UTUN_INSTANCE *inst); int stcp_server_list_count(struct UTUN_INSTANCE *inst);
struct stcp_server *stcp_server_list_find_by_sock(struct UTUN_INSTANCE *inst, const struct ETCP_SOCKET *sock);
// ====== TCP client link ====== // ====== TCP client link ======

1
tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt

@ -333,6 +333,7 @@ class ConfigProvider(private val context: Context) {
appendLine("db_path=$dbDir") appendLine("db_path=$dbDir")
appendLine("db_sync_enabled=1") appendLine("db_sync_enabled=1")
appendLine("chatserver_enabled=1") appendLine("chatserver_enabled=1")
appendLine("auto_sockets=android")
appendLine() appendLine()
for (srv in servers) appendServerSection(this, srv, false) for (srv in servers) appendServerSection(this, srv, false)
for (srv in commented) appendServerSection(this, srv, true) for (srv in commented) appendServerSection(this, srv, true)

6
tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt

@ -44,6 +44,11 @@ object NativeLib {
return nativeRestart(configText) return nativeRestart(configText)
} }
/** Лёгкое обновление сокетов (auto_sockets=android) без полного рестарта ядра */
fun updateSockets(configText: String): Boolean {
return nativeUpdateSockets(configText)
}
fun isReady(): Boolean = nativeIsRunning() fun isReady(): Boolean = nativeIsRunning()
fun getMyNodeId(): Long = nativeGetMyNodeId() fun getMyNodeId(): Long = nativeGetMyNodeId()
@ -156,6 +161,7 @@ object NativeLib {
private external fun nativeRequestInviteCandidates(channelId: String) private external fun nativeRequestInviteCandidates(channelId: String)
private external fun nativeRequestInviteLink(channelId: String, targetNodeId: Long) private external fun nativeRequestInviteLink(channelId: String, targetNodeId: Long)
private external fun nativeRestart(configText: String): Boolean private external fun nativeRestart(configText: String): Boolean
private external fun nativeUpdateSockets(configText: String): Boolean
private external fun nativePing() private external fun nativePing()
private external fun nativeIsResponsive(): Boolean private external fun nativeIsResponsive(): Boolean
private external fun nativeIsRunning(): Boolean private external fun nativeIsRunning(): Boolean

4
tools/chatgui-android/app/src/main/java/com/utun/chat/data/NetworkChangeMonitor.kt

@ -69,9 +69,7 @@ object NetworkChangeMonitor {
val nowStr = now.joinToString(", ") { (n, ip) -> "$n=$ip" } val nowStr = now.joinToString(", ") { (n, ip) -> "$n=$ip" }
LogManager.addLog("INFO", "NET", "network change: [$prevStr] -> [$nowStr]") LogManager.addLog("INFO", "NET", "network change: [$prevStr] -> [$nowStr]")
NativeLib.notifyNetworkActive() NativeLib.notifyNetworkActive()
val path = provider.getString("db.path") ?: "" NativeLib.updateSockets(provider.buildConfigText())
val port = provider.getInt("control.port")
NativeLib.restart(path, port, provider.buildConfigText())
} finally { } finally {
restarting = false restarting = false
} }

16
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -1014,6 +1014,11 @@ void utun_bridge_restart(const char* config_text) {
bridge_log(BLEV_ERROR, "bridge restart: instance not ready after 5s"); bridge_log(BLEV_ERROR, "bridge restart: instance not ready after 5s");
} }
void utun_bridge_update_sockets(const char* config_text) {
bridge_log(BLEV_INFO, "bridge update_sockets: posting to instance");
instance_lite_update_sockets(config_text);
}
void utun_bridge_ping(void) { void utun_bridge_ping(void) {
instance_lite_ping(); instance_lite_ping();
} }
@ -1519,6 +1524,17 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRestart(
return JNI_TRUE; return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeUpdateSockets(
JNIEnv* env, jobject thiz, jstring configText) {
(void)thiz;
if (!configText) return JNI_FALSE;
const char* cfg = (*env)->GetStringUTFChars(env, configText, NULL);
if (!cfg) return JNI_FALSE;
utun_bridge_update_sockets(cfg);
(*env)->ReleaseStringUTFChars(env, configText, cfg);
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeIsResponsive( JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeIsResponsive(
JNIEnv* env, jobject thiz) { JNIEnv* env, jobject thiz) {
(void)env; (void)thiz; (void)env; (void)thiz;

4
tools/chatgui-android/jni_bridge/android_jni_bridge.h

@ -70,6 +70,10 @@ uint64_t utun_bridge_get_my_node_id(void);
void utun_bridge_restart(const char* config_text); void utun_bridge_restart(const char* config_text);
/* ── Лёгкое обновление сокетов (auto_sockets=android) ── */
void utun_bridge_update_sockets(const char* config_text);
/* ── Health check ── */ /* ── Health check ── */
void utun_bridge_ping(void); void utun_bridge_ping(void);

51
tools/chatgui-android/libutun_lite/instance_lite.c

@ -24,6 +24,7 @@
#include "debug_config.h" #include "debug_config.h"
#include "transport_layer/etcp_connections.h" #include "transport_layer/etcp_connections.h"
#include "transport_layer/etcp.h" #include "transport_layer/etcp.h"
#include "transport_layer/auto_socket.h"
#include "../jni_bridge/android_udp_log.h" #include "../jni_bridge/android_udp_log.h"
#include "../jni_bridge/android_jni_bridge.h" #include "../jni_bridge/android_jni_bridge.h"
#include "standby.h" #include "standby.h"
@ -499,9 +500,9 @@ void instance_lite_stop(void) {
g_thread_exited = 0; g_thread_exited = 0;
} }
/* Логирует текущие активные сокеты (имя=ip:port(протокол)) — состояние ДО рестарта. */ /* Логирует текущие активные сокеты (имя=ip:port(протокол)) — состояние ДО изменения. */
static void log_active_sockets(struct UTUN_INSTANCE* inst, const char* label) { static void log_active_sockets(struct UTUN_INSTANCE* inst, const char* label) {
if (!inst) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "restart: %s: (no instance)", label); return; } if (!inst) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "%s: (no instance)", label); return; }
char buf[768] = ""; char buf[768] = "";
int off = 0; int off = 0;
for (struct ETCP_SOCKET* s = inst->etcp_sockets; s; s = s->next) { for (struct ETCP_SOCKET* s = inst->etcp_sockets; s; s = s->next) {
@ -511,10 +512,10 @@ static void log_active_sockets(struct UTUN_INSTANCE* inst, const char* label) {
off += snprintf(buf + off, sizeof(buf) - (size_t)off, " %s=%s(%s)", off += snprintf(buf + off, sizeof(buf) - (size_t)off, " %s=%s(%s)",
s->name, addr, s->is_tcp ? "TCP" : "UDP"); s->name, addr, s->is_tcp ? "TCP" : "UDP");
} }
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "restart: %s:%s", label, buf[0] ? buf : " (none)"); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "%s:%s", label, buf[0] ? buf : " (none)");
} }
/* Логирует [server:...]/addr= секции нового конфига — состояние ПОСЛЕ рестарта. */ /* Логирует [server:...]/addr= секции нового конфига — состояние ПОСЛЕ изменения. */
static void log_config_servers(const char* cfg, const char* label) { static void log_config_servers(const char* cfg, const char* label) {
char buf[768] = ""; char buf[768] = "";
int off = 0; int off = 0;
@ -539,7 +540,7 @@ static void log_config_servers(const char* cfg, const char* label) {
while (*p && *p != '\n') p++; while (*p && *p != '\n') p++;
if (*p == '\n') p++; if (*p == '\n') p++;
} }
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "restart: %s:%s", label, buf[0] ? buf : " (none)"); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "%s:%s", label, buf[0] ? buf : " (none)");
} }
void instance_lite_restart(const char* new_config_text) { void instance_lite_restart(const char* new_config_text) {
@ -549,8 +550,8 @@ void instance_lite_restart(const char* new_config_text) {
int running = __atomic_load_n(&g_running, __ATOMIC_ACQUIRE); int running = __atomic_load_n(&g_running, __ATOMIC_ACQUIRE);
/* Диагностика смены сети: какие сокеты/адреса были → какие будут */ /* Диагностика смены сети: какие сокеты/адреса были → какие будут */
log_active_sockets(inst, "OLD sockets"); log_active_sockets(inst, "restart OLD sockets");
log_config_servers(new_config_text, "NEW config"); log_config_servers(new_config_text, "restart NEW config");
if (!ua || !inst || !running) { if (!ua || !inst || !running) {
IL_LOGI("restart: instance dead, doing hard restart via stop+start"); IL_LOGI("restart: instance dead, doing hard restart via stop+start");
@ -568,6 +569,42 @@ void instance_lite_restart(const char* new_config_text) {
uasync_wakeup(ua); uasync_wakeup(ua);
} }
/* ── Лёгкое обновление сокетов (auto_sockets=android) ── */
static void update_sockets_trampoline(void* arg) {
char* cfg_text = (char*)arg;
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
if (!inst) {
IL_LOGE("update_sockets: instance gone in trampoline — dropping update");
u_free(cfg_text);
return;
}
struct utun_config* cfg = parse_config_from_buf(cfg_text, strlen(cfg_text), "android-update");
u_free(cfg_text);
if (!cfg) { IL_LOGE("update_sockets: parse_config_from_buf failed"); return; }
auto_socket_reload_config(inst, cfg); /* cfg владеет auto_socket: servers переносятся, остальное освобождается */
}
void instance_lite_update_sockets(const char* config_text) {
if (!config_text) return;
struct UASYNC* ua = (struct UASYNC*)__atomic_load_n(&g_ua, __ATOMIC_ACQUIRE);
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)__atomic_load_n(&g_inst, __ATOMIC_ACQUIRE);
int running = __atomic_load_n(&g_running, __ATOMIC_ACQUIRE);
/* Диагностика смены сети: какие сокеты/адреса были → какие будут (без полного рестарта). */
log_active_sockets(inst, "update OLD sockets");
log_config_servers(config_text, "update NEW config");
if (!ua || !inst || !running) {
IL_LOGE("update_sockets: instance not running (ua=%p inst=%p running=%d) — update dropped (no fallback restart)",
(void*)ua, (void*)inst, running);
return;
}
char* copy = u_strdup(config_text);
if (!copy) { IL_LOGE("update_sockets: u_strdup failed"); return; }
uasync_post(ua, update_sockets_trampoline, copy);
}
/* ── Health check ping ── */ /* ── Health check ping ── */
static volatile int g_ping_id = 0; static volatile int g_ping_id = 0;

5
tools/chatgui-android/libutun_lite/instance_lite.h

@ -45,6 +45,11 @@ void instance_lite_regenerate_keys(void);
* Posts to uasync thread — restart happens asynchronously. */ * Posts to uasync thread — restart happens asynchronously. */
void instance_lite_restart(const char* new_config_text); void instance_lite_restart(const char* new_config_text);
/* Лёгкое обновление сокетов (auto_sockets=android): перечитывает [server] секции
* из нового конфига и сверяет сокеты без полного рестарта. Posts to uasync thread.
* Если ядро не запущено — только логирует ошибку и дропает вызов. */
void instance_lite_update_sockets(const char* config_text);
/* Health check: posts a ping to uasync, returns responsiveness after delay */ /* Health check: posts a ping to uasync, returns responsiveness after delay */
void instance_lite_ping(void); void instance_lite_ping(void);
int instance_lite_is_responsive(void); int instance_lite_is_responsive(void);

Loading…
Cancel
Save