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; порты сохраняются при смене сети
This commit is contained in:
1 parent
c474c37284
commit
5b31a765c2
15 files changed
+266
-46
No files matched your search
+3
-1
@@ -407,7 +407,9 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
return 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;
|
||||
}
|
||||
if (strcmp(key, "auto_socket_skip_no_default_route") == 0) {
|
||||
|
||||
+7
-1
@@ -29,6 +29,12 @@ struct IP {
|
||||
#define CFG_SERVER_TYPE_PRIVATE 3
|
||||
#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_TEMPORARY 1 // temporary 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)
|
||||
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 tcp_recv_buf; // SO_RCVBUF для TCP сокетов (0=default ОС)
|
||||
int tcp_proxy_server_fib; // FreeBSD SO_SETFIB, 0=не менять
|
||||
|
||||
@@ -144,6 +144,7 @@ struct auto_sock_iface {
|
||||
struct AUTO_SOCKET {
|
||||
struct UTUN_INSTANCE* instance; // родительский utun instance
|
||||
struct auto_sock_iface* ifaces; // связный список интерфейсов
|
||||
uint8_t mode; // CFG_AUTO_SOCKETS_SYSTEM или CFG_AUTO_SOCKETS_ANDROID
|
||||
uint8_t v4_usable; // есть ли сейчас рабочий v4-сокет с реальным IP
|
||||
uint8_t v6_usable; // есть ли сейчас рабочий v6-сокет с реальным IP
|
||||
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 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_reconcile_config(struct AUTO_SOCKET* as);
|
||||
|
||||
/* ─── DB helpers (port persistence) ───
|
||||
* Сохраняют порт сокета в 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) {
|
||||
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; }
|
||||
|
||||
struct AUTO_SOCKET* as = u_calloc(1, sizeof(*as));
|
||||
if (!as) { DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] alloc failed"); return -1; }
|
||||
as->instance = inst;
|
||||
as->mode = (uint8_t)inst->config->global.auto_sockets;
|
||||
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) {
|
||||
sqlite3_exec(inst->topo_sqlite_db,
|
||||
"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) {
|
||||
if (!inst || !inst->auto_socket_state) return;
|
||||
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);
|
||||
}
|
||||
|
||||
/* ═══════════ 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);
|
||||
}
|
||||
@@ -38,6 +38,7 @@ extern "C" {
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct ETCP_SOCKET;
|
||||
struct utun_config;
|
||||
|
||||
/* Внутреннее состояние модуля — хранится в inst->auto_socket_state.
|
||||
* Содержит связный список отслеживаемых интерфейсов (ifaces) и платформенные
|
||||
@@ -58,6 +59,11 @@ void auto_socket_destroy(struct UTUN_INSTANCE* inst);
|
||||
* Откладывает полное сканирование (auto_socket_scan_all) в uasync-поток. */
|
||||
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: управление сокетами на конкретном интерфейсе ─── */
|
||||
|
||||
/*
|
||||
|
||||
@@ -2333,6 +2333,41 @@ int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
// Initialize only sockets (servers for incoming connections)
|
||||
// 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)
|
||||
/* Создаёт один серверный сокет из 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) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
|
||||
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
|
||||
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); 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);
|
||||
}
|
||||
struct ETCP_SOCKET* sock = NULL;
|
||||
if (etcp_create_server_socket(instance, server, &sock) != 0) {
|
||||
fail_count++;
|
||||
server = server->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, server);
|
||||
if (e_sock && default_ip != 0) {
|
||||
e_sock->local_defaultroute_ip = default_ip;
|
||||
if (sock && !server->transport && default_ip != 0) {
|
||||
sock->local_defaultroute_ip = default_ip;
|
||||
if (server->ip.ss_family == AF_INET) {
|
||||
struct in_addr addr;
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (e_sock && have_default_ip6) {
|
||||
memcpy(e_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;
|
||||
if (sock && !server->transport && have_default_ip6) {
|
||||
memcpy(sock->local_defaultroute_ip6, default_ip6, 16);
|
||||
}
|
||||
success_count++;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
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;
|
||||
void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock);
|
||||
|
||||
|
||||
@@ -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++;
|
||||
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;
|
||||
}
|
||||
@@ -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_destroy_all(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 ======
|
||||
|
||||
|
||||
Reference in new issue
Block a user