diff --git a/src/chat/chat_channel.c b/src/chat/chat_channel.c
index ece80447..1e9999dc 100644
--- a/src/chat/chat_channel.c
+++ b/src/chat/chat_channel.c
@@ -86,7 +86,7 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) {
s = s->next;
}
- { struct TCP_SOCKET* ts = g_cc.inst->tcp_sockets;
+ { struct ETCP_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) {
diff --git a/src/chat/chat_headless_control.c b/src/chat/chat_headless_control.c
index ed01cc0e..be915d5c 100644
--- a/src/chat/chat_headless_control.c
+++ b/src/chat/chat_headless_control.c
@@ -352,7 +352,7 @@ static void hc_handle_invite(struct headless_client* cli, int id, const char* js
}
s = s->next;
}
- struct TCP_SOCKET* ts = g_hc.inst->tcp_sockets;
+ struct ETCP_SOCKET* ts = g_hc.inst->tcp_sockets;
while (ts && inv.addrCount < INVITE_ADDR_MAX) {
if (ts->interface_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&ts->interface_addr;
diff --git a/src/chat/chat_profile.c b/src/chat/chat_profile.c
index 1538d53e..3fba0058 100644
--- a/src/chat/chat_profile.c
+++ b/src/chat/chat_profile.c
@@ -161,7 +161,7 @@ void chat_core_sync_my_addresses(void) {
"INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)"
" VALUES(?,?,2,?,?,?,?)", -1, &tcp_ins, NULL);
if (tcp_ins) {
- struct TCP_SOCKET* ts = g_cc.inst->tcp_sockets;
+ struct ETCP_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++;
diff --git a/src/chat/chat_status.c b/src/chat/chat_status.c
index 689b7c9c..4181e424 100644
--- a/src/chat/chat_status.c
+++ b/src/chat/chat_status.c
@@ -301,15 +301,17 @@ void chat_core_collect_conn_metrics_trampoline(void* arg) {
}
/* ── Live-снапшот для панели деталей участника ──
- * packet: [node_id:8][flags:1][own_tcp_active:2][link_count:1](link:25B)*[sock_count:1](sock:48B)*
+ * packet: [node_id:8][flags:1][own_tcp_active:2][link_count:1](link:43B)*[sock_count:1](sock:48B)*
* flags: bit0=conn_present bit1=conn_up(links_up) bit2=conn_initialized
- * link: [is_tcp:1][link_state:1][link_status:1][tcp_ready:1][is_server:1][family:1][addr_len:1][addr:16][port:2]
+ * link: [is_tcp:1][link_state:1][link_status:1][tcp_ready:1][is_server:1]
+ * [local_family:1][local_addr:16][local_port:2]
+ * [remote_family:1][remote_addr:16][remote_port:2]
* sock: [sock_id:4][port:2][link_count:4][ifname:38]
* Собирается в worker-потоке — читает живые структуры UTUN_INSTANCE безопасно. */
#define MEMBER_DETAIL_MAX_LINKS 16
#define MEMBER_DETAIL_MAX_SOCKS 16
-#define MEMBER_DETAIL_LINK_SIZE 25
+#define MEMBER_DETAIL_LINK_SIZE 43
#define MEMBER_DETAIL_SOCK_SIZE 48
/* Повторяет Qt: name.lastIndexOf("_v") → name.mid(3, idx - 3) */
@@ -365,7 +367,7 @@ static void collect_member_detail(uint64_t node_id) {
uint8_t sock_count = 0;
for (struct ETCP_SOCKET* s = inst->etcp_sockets; s && sock_count < MEMBER_DETAIL_MAX_SOCKS; s = s->next)
sock_count++;
- for (struct TCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count < MEMBER_DETAIL_MAX_SOCKS; ts = ts->next)
+ for (struct ETCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count < MEMBER_DETAIL_MAX_SOCKS; ts = ts->next)
sock_count++;
size_t buf_sz = 12 + (size_t)link_count * MEMBER_DETAIL_LINK_SIZE
@@ -385,19 +387,28 @@ static void collect_member_detail(uint64_t node_id) {
*p++ = lk->link_status;
*p++ = (lk->is_tcp && lk->tcp_link && stcp_link_is_ready(lk->tcp_link)) ? 1 : 0;
*p++ = lk->is_server;
- uint8_t family = 0, addr_len = 0; uint8_t addr[16] = {0}; uint16_t port = 0;
- const struct sockaddr_storage* sa = &lk->remote_addr;
- if (sa->ss_family == AF_INET) {
- struct sockaddr_in* sin = (struct sockaddr_in*)sa;
- family = 4; addr_len = 4; memcpy(addr, &sin->sin_addr, 4); port = ntohs(sin->sin_port);
- } else if (sa->ss_family == AF_INET6) {
- struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
- family = 6; addr_len = 16; memcpy(addr, &sin6->sin6_addr, 16); port = ntohs(sin6->sin6_port);
+
+ { uint8_t lf = 0; uint8_t la[16] = {0}; uint16_t lp = 0;
+ if (lk->conn) {
+ const struct sockaddr_storage* lsa = &lk->conn->local_addr;
+ if (lsa->ss_family == AF_INET) {
+ lf = 4; memcpy(la, &((const struct sockaddr_in*)lsa)->sin_addr, 4); lp = ntohs(((const struct sockaddr_in*)lsa)->sin_port);
+ } else if (lsa->ss_family == AF_INET6) {
+ lf = 6; memcpy(la, &((const struct sockaddr_in6*)lsa)->sin6_addr, 16); lp = ntohs(((const struct sockaddr_in6*)lsa)->sin6_port);
+ }
+ }
+ *p++ = lf; memcpy(p, la, 16); p += 16; memcpy(p, &lp, 2); p += 2;
+ }
+
+ { uint8_t rf = 0; uint8_t ra[16] = {0}; uint16_t rp = 0;
+ const struct sockaddr_storage* sa = &lk->remote_addr;
+ if (sa->ss_family == AF_INET) {
+ rf = 4; memcpy(ra, &((const struct sockaddr_in*)sa)->sin_addr, 4); rp = ntohs(((const struct sockaddr_in*)sa)->sin_port);
+ } else if (sa->ss_family == AF_INET6) {
+ rf = 6; memcpy(ra, &((const struct sockaddr_in6*)sa)->sin6_addr, 16); rp = ntohs(((const struct sockaddr_in6*)sa)->sin6_port);
+ }
+ *p++ = rf; memcpy(p, ra, 16); p += 16; memcpy(p, &rp, 2); p += 2;
}
- *p++ = family;
- *p++ = addr_len;
- memcpy(p, addr, 16); p += 16;
- memcpy(p, &port, 2); p += 2;
}
*p++ = sock_count;
@@ -414,7 +425,7 @@ static void collect_member_detail(uint64_t node_id) {
memcpy(p, &lc, 4); p += 4;
memcpy(p, ifname, 38); p += 38;
}
- for (struct TCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count; ts = ts->next, sock_count--) {
+ for (struct ETCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count; ts = ts->next, sock_count--) {
uint32_t sid = ts->sock_id;
uint16_t sp = 0;
const struct sockaddr_storage* sa = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c
index 891286ca..563f447c 100644
--- a/src/routing_layer/topo_node.c
+++ b/src/routing_layer/topo_node.c
@@ -683,7 +683,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
else if (e_sock->local_addr.ss_family == AF_INET6) { sock6_count++; addr6_count++; }
e_sock = e_sock->next;
}
- { struct TCP_SOCKET* ts = instance->tcp_sockets;
+ { struct ETCP_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++; }
@@ -800,7 +800,7 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
}
e_sock = e_sock->next;
}
- { struct TCP_SOCKET* ts = instance->tcp_sockets;
+ { struct ETCP_SOCKET* ts = instance->tcp_sockets;
int tcp_iter_cnt = 0;
while (ts) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: tcp_iter[%d] ts=%p type=%d fam=%d next=%p",
tcp_iter_cnt, (void*)ts, ts->type, ts->local_addr.ss_family, (void*)ts->next);
@@ -907,7 +907,7 @@ void topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) {
}
e_sock = e_sock->next;
}
- { struct TCP_SOCKET* ts = instance->tcp_sockets;
+ { struct ETCP_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; }
diff --git a/src/transport_layer/auto_socket.c b/src/transport_layer/auto_socket.c
index 3c2ba8eb..64934973 100644
--- a/src/transport_layer/auto_socket.c
+++ b/src/transport_layer/auto_socket.c
@@ -282,19 +282,6 @@ static struct stcp_server* create_iface_tcp_socket(struct AUTO_SOCKET* as, uint3
for (int attempt = 0; attempt < max_att; attempt++) {
uint16_t port = (attempt == 0 && saved_port) ? saved_port : random_port();
- struct stcp_server* tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, inst);
- if (!tsrv) {
- if (attempt == 0 && saved_port)
- DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP reuse port failed if=%s fam=%d port=%u — trying random", ifname, family, port);
- else
- DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP socket create failed if=%s fam=%d port=%u att=%d err=%d",
- ifname, family, port, attempt + 1, socket_get_error());
- if (socket_get_error() != EADDRINUSE) return NULL;
- continue;
- }
-
- stcp_server_list_add(inst, tsrv);
-
struct CFG_SERVER server;
memset(&server, 0, sizeof(server));
snprintf(server.name, sizeof(server.name), "as_%s_%s_tcp", ifname, family == AF_INET6 ? "v6" : "v4");
@@ -303,27 +290,37 @@ static struct stcp_server* create_iface_tcp_socket(struct AUTO_SOCKET* as, uint3
server.transport = 1;
if (family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&server.ip;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = INADDR_ANY;
- sin->sin_port = htons(port);
+ sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = htons(port);
} else {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server.ip;
- sin6->sin6_family = AF_INET6;
- sin6->sin6_addr = in6addr_any;
- sin6->sin6_port = htons(port);
+ sin6->sin6_family = AF_INET6; sin6->sin6_addr = in6addr_any; sin6->sin6_port = htons(port);
}
- struct TCP_SOCKET* ts = tcp_socket_add(inst, &server);
- { const struct sockaddr_storage* ia = (ts && ts->interface_addr.ss_family) ? &ts->interface_addr : &ts->local_addr;
- if (!ts || (ia->ss_family == AF_INET && ((struct sockaddr_in*)ia)->sin_addr.s_addr == INADDR_ANY) ||
- (ia->ss_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)ia)->sin6_addr))) {
+ struct ETCP_SOCKET* ts = tcp_socket_add(inst, &server);
+ if (!ts) { DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP tcp_socket_add failed if=%s fam=%d port=%u", ifname, family, port); continue; }
+
+ { const struct sockaddr_storage* ia = (ts->interface_addr.ss_family) ? &ts->interface_addr : &ts->local_addr;
+ if ((ia->ss_family == AF_INET && ((const struct sockaddr_in*)ia)->sin_addr.s_addr == INADDR_ANY) ||
+ (ia->ss_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&((const struct sockaddr_in6*)ia)->sin6_addr))) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] TCP socket %s has null addr — removing", server.name);
- if (ts) tcp_socket_remove(ts);
- stcp_server_list_remove(inst, tsrv);
- stcp_link_server_destroy(tsrv); continue;
+ tcp_socket_remove(ts); continue;
}
}
+ struct stcp_server* tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, ts);
+ if (!tsrv) {
+ if (attempt == 0 && saved_port)
+ DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP reuse port failed if=%s fam=%d port=%u — trying random", ifname, family, port);
+ else
+ DEBUG_WARN(DEBUG_CATEGORY_AS, "[as] TCP socket create failed if=%s fam=%d port=%u att=%d err=%d",
+ ifname, family, port, attempt + 1, socket_get_error());
+ tcp_socket_remove(ts);
+ if (socket_get_error() != EADDRINUSE) return NULL;
+ continue;
+ }
+
+ stcp_server_list_add(inst, tsrv);
+
save_port_to_db(as, ifname, family, AS_PROTO_TCP, port);
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] TCP socket created: if=%s(%u) type=%d port=%u", ifname, ifindex, type, port);
@@ -413,12 +410,12 @@ static void remove_iface_sockets(struct AUTO_SOCKET* as, uint32_t ifindex) {
if (ifa->v4_tcp) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] removing v4 TCP socket ifidx=%u", ifindex);
{ uint16_t v4_port = load_port_from_db(as, ifname, AF_INET, AS_PROTO_TCP);
- struct TCP_SOCKET** tsp = &as->instance->tcp_sockets;
+ struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) {
uint16_t tsp_port = 0;
const struct sockaddr_storage* addr = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (addr->ss_family == AF_INET) tsp_port = ntohs(((struct sockaddr_in*)addr)->sin_port);
- if (tsp_port == v4_port && v4_port > 0) { struct TCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; }
+ if (tsp_port == v4_port && v4_port > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; }
tsp = &(*tsp)->next;
}
}
@@ -429,12 +426,12 @@ static void remove_iface_sockets(struct AUTO_SOCKET* as, uint32_t ifindex) {
if (ifa->v6_tcp) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] removing v6 TCP socket ifidx=%u", ifindex);
{ uint16_t v6_port = load_port_from_db(as, ifname, AF_INET6, AS_PROTO_TCP);
- struct TCP_SOCKET** tsp = &as->instance->tcp_sockets;
+ struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) {
uint16_t tsp_port = 0;
const struct sockaddr_storage* addr = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (addr->ss_family == AF_INET6) tsp_port = ntohs(((struct sockaddr_in6*)addr)->sin6_port);
- if (tsp_port == v6_port && v6_port > 0) { struct TCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; }
+ if (tsp_port == v6_port && v6_port > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; }
tsp = &(*tsp)->next;
}
}
@@ -711,10 +708,10 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v4 TCP removed: %s ifidx=%u", ifname, ifindex);
{ char buf[IFNAMSIZ]; const char* nm = if_indextoname(ifindex, buf) ? buf : ifname;
uint16_t p = load_port_from_db(as, nm, AF_INET, AS_PROTO_TCP);
- struct TCP_SOCKET** tsp = &as->instance->tcp_sockets;
+ struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) { uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (a->ss_family == AF_INET) tp = ntohs(((struct sockaddr_in*)a)->sin_port);
- if (tp == p && p > 0) { struct TCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
+ if (tp == p && p > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
}
stcp_server_list_remove(as->instance, srv);
stcp_link_server_destroy(srv);
@@ -734,10 +731,10 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v6 TCP removed: %s ifidx=%u", ifname, ifindex);
{ char buf[IFNAMSIZ]; const char* nm = if_indextoname(ifindex, buf) ? buf : ifname;
uint16_t p = load_port_from_db(as, nm, AF_INET6, AS_PROTO_TCP);
- struct TCP_SOCKET** tsp = &as->instance->tcp_sockets;
+ struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) { uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (a->ss_family == AF_INET6) tp = ntohs(((struct sockaddr_in6*)a)->sin6_port);
- if (tp == p && p > 0) { struct TCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
+ if (tp == p && p > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
}
stcp_server_list_remove(as->instance, srv);
stcp_link_server_destroy(srv);
diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c
index 359b9781..e723bea8 100644
--- a/src/transport_layer/etcp_connections.c
+++ b/src/transport_layer/etcp_connections.c
@@ -44,8 +44,8 @@ static void tcp_link_close_cb(struct stcp_link *sl, int err, void *arg);
void etcp_link_enter_ready_tcp(struct ETCP_LINK *link);
// TCP server: on new incoming connection → create ETCP_LINK + enter ready
-void tcp_server_on_link(struct stcp_link *link, void *arg) {
- struct UTUN_INSTANCE *inst = (struct UTUN_INSTANCE *)arg;
+void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock) {
+ struct UTUN_INSTANCE *inst = tcp_sock->instance;
const uint8_t *pubkey = stcp_link_get_peer_pubkey(link);
if (!pubkey) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "tcp_server_on_link: no peer pubkey"); return; }
uint64_t node_id = sc_derive_node_id_from_pubkey(pubkey);
@@ -86,9 +86,9 @@ void tcp_server_on_link(struct stcp_link *link, void *arg) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "tcp_server_on_link: new ETCP_CONN peer=0x%016llx", (unsigned long long)node_id);
}
- struct ETCP_LINK *tlink = etcp_link_new(conn, NULL, NULL, 1);
+ const struct sockaddr_storage* ra = stcp_link_get_remote_addr(link);
+ struct ETCP_LINK *tlink = etcp_link_new(conn, tcp_sock, ra, 1);
if (!tlink) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "tcp_server_on_link: etcp_link_new failed"); return; }
- tlink->is_tcp = 1;
tlink->tcp_link = link;
tlink->is_server = 1;
@@ -848,12 +848,14 @@ void etcp_socket_remove(struct ETCP_SOCKET* conn) {
/* ── TCP socket management ── */
-struct TCP_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) {
if (!instance || !server) return NULL;
- struct TCP_SOCKET* ts = u_calloc(1, sizeof(struct TCP_SOCKET));
+ struct ETCP_SOCKET* ts = u_calloc(1, sizeof(struct ETCP_SOCKET));
if (!ts) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_socket_add: alloc failed"); return NULL; }
ts->instance = instance;
+ ts->is_tcp = 1;
+ ts->fd = SOCKET_INVALID;
ts->type = server->type;
if (server->name && server->name[0]) {
strncpy(ts->name, server->name, MAX_CONN_NAME_LEN - 1);
@@ -933,10 +935,10 @@ struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SER
return ts;
}
-void tcp_socket_remove(struct TCP_SOCKET* sock) {
+void tcp_socket_remove(struct ETCP_SOCKET* sock) {
if (!sock || !sock->instance) return;
struct UTUN_INSTANCE* inst = sock->instance;
- struct TCP_SOCKET** pp = &inst->tcp_sockets;
+ struct ETCP_SOCKET** pp = &inst->tcp_sockets;
while (*pp && *pp != sock) pp = &(*pp)->next;
if (*pp) *pp = sock->next;
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "tcp_socket_remove: %s sock_id=%u", sock->name, sock->sock_id);
@@ -947,7 +949,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!etcp) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "null etcp"); return NULL; }
if (etcp->state == 2) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] link_new on deleted conn", etcp->log_name); return NULL; }
- int is_tcp = (conn == NULL);
+ int is_tcp = (conn == NULL) || conn->is_tcp;
struct ETCP_LINK* link = u_calloc(1, sizeof(struct ETCP_LINK));
if (!link) {
@@ -2428,15 +2430,19 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
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 = {.ua = instance->ua, .my_keys = &instance->my_keys, .inst = instance,
.listen_family = server->ip.ss_family};
- struct stcp_server *tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, instance);
+ 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);
- tcp_socket_add(instance, server);
success_count++;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server %s on port %u", server->name, port);
}
diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h
index 44708053..9cf4480d 100644
--- a/src/transport_layer/etcp_connections.h
+++ b/src/transport_layer/etcp_connections.h
@@ -132,25 +132,29 @@ struct link_queue_entry {
struct ETCP_SOCKET {
struct ETCP_SOCKET* next; // Linked list для всех соединений
struct UTUN_INSTANCE* instance;
- char name[MAX_CONN_NAME_LEN]; // Socket name from config (e.g., "lan1" from [server: lan1])
- socket_t fd; // UDP socket (cross-platform)
- struct sockaddr_storage local_addr; // Локальный адрес
- int mtu; // MTU для этого сокета
-
- int errorcode;
- size_t pkt_format_errors;
-
- void* socket_id; // Socket ID from uasync_add_socket
- uint8_t type; // CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE
- uint8_t sock_id; // unique socket id (0-255) for NAT matching
- uint8_t nat_type; // NAT_TYPE_* (detected by server)
- uint32_t local_defaultroute_ip; // auto-detected IPv4 for public servers (network byte order)
- uint8_t local_defaultroute_ip6[16]; // auto-detected IPv6 for public servers
- uint8_t only_local; // 1 = only local connections, no forwarding
- uint32_t netif_index; // индекс интерфейса для sin6_scope_id (0 = не задан)
- struct sockaddr_storage interface_addr; // адрес интерфейса: если bind на интерфейс - его IP, если 0.0.0.0 без интерфейса - IP интерфейса через который идет default route, иначе - адрес из конфига
- struct sockaddr_storage nat_addr; // NAT адрес (network byte order), ss_family=0 = не определён
- struct ll_queue* links_queue; // хеш-очередь линков, ключ = link_queue_entry.key (19 байт)
+ char name[MAX_CONN_NAME_LEN];
+ socket_t fd; // UDP socket / TCP listen socket
+ int mtu;
+
+ uint8_t type; // CFG_SERVER_TYPE_*
+ uint8_t sock_id; // unique id (0-255) for NAT matching
+ uint8_t is_tcp; // 1=TCP (STCP), 0=UDP
+ uint8_t only_local; // 1 = only local, no forwarding (UDP)
+ uint32_t netif_index; // для sin6_scope_id
+ struct sockaddr_storage local_addr;
+ struct sockaddr_storage interface_addr;
+ struct sockaddr_storage nat_addr;
+
+ /* ── uasync handle (UDP read_cb / TCP accept_cb) ── */
+ void* socket_id;
+
+ /* ── UDP-only ── */
+ struct ll_queue* links_queue; // hash-очередь линков
+ int errorcode;
+ size_t pkt_format_errors;
+ uint8_t nat_type; // NAT_TYPE_*
+ uint32_t local_defaultroute_ip; // auto-detected IPv4 (network byte order)
+ uint8_t local_defaultroute_ip6[16];
};
// NAT check status
@@ -326,23 +330,12 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
// удаляет сокет и освобождает ресурсы (грохает все его подключения и сокет)
void etcp_socket_remove(struct ETCP_SOCKET* conn);
-// TCP socket descriptor (analogous to ETCP_SOCKET for UDP)
-struct TCP_SOCKET {
- struct TCP_SOCKET* next;
- struct UTUN_INSTANCE* instance;
- char name[MAX_CONN_NAME_LEN];
- uint8_t sock_id;
- uint8_t type; // CFG_SERVER_TYPE_*
- struct sockaddr_storage local_addr;
- struct sockaddr_storage interface_addr;
- struct sockaddr_storage nat_addr;
-};
-
-struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server);
-void tcp_socket_remove(struct TCP_SOCKET* sock);
+// TCP socket functions (uses unified ETCP_SOCKET with is_tcp=1)
+struct ETCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server);
+void tcp_socket_remove(struct ETCP_SOCKET* sock);
struct stcp_link;
-void tcp_server_on_link(struct stcp_link *link, void *arg);
+void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock);
// connection functions
// создает новый канал связи для etcp подключения (ETCP_CONN)
diff --git a/src/transport_layer/stcp_link.c b/src/transport_layer/stcp_link.c
index c4d888dc..834eb13a 100644
--- a/src/transport_layer/stcp_link.c
+++ b/src/transport_layer/stcp_link.c
@@ -20,7 +20,7 @@ struct stcp_server {
struct stcp_server *srv; // stcp_server from stcp_server.h
struct stcp_link_config cfg;
stcp_server_on_link_cb on_link;
- void *on_link_arg;
+ struct ETCP_SOCKET *tcp_sock;
};
struct stcp_link {
@@ -128,7 +128,7 @@ static void server_accept_cb(struct stcp_conn *conn, void *arg) {
stcp_conn_set_tx_queue(conn, link->tx_queue);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: server accepted connection");
- if (ss->on_link) ss->on_link(link, ss->on_link_arg);
+ if (ss->on_link) ss->on_link(link, ss->tcp_sock);
}
// ====== client connect → link ======
@@ -161,13 +161,13 @@ static void client_ready_cb(struct stcp_conn *conn, void *arg) {
// ====== API ======
struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t port,
- stcp_server_on_link_cb on_link, void *arg) {
+ stcp_server_on_link_cb on_link, struct ETCP_SOCKET *tcp_sock) {
if (!cfg || !cfg->ua || !cfg->my_keys) return NULL;
struct stcp_server *ss = u_calloc(1, sizeof(struct stcp_server));
if (!ss) return NULL;
ss->cfg = *cfg;
ss->on_link = on_link;
- ss->on_link_arg = arg;
+ ss->tcp_sock = tcp_sock;
ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, cfg->inst ? cfg->inst->my_ed25519_pubkey : NULL, cfg->inst, server_accept_cb, ss, NULL, NULL, cfg->listen_family);
if (!ss->srv) { u_free(ss); return NULL; }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "port=%u", port);
diff --git a/src/transport_layer/stcp_link.h b/src/transport_layer/stcp_link.h
index f2de1dad..bcf9a561 100644
--- a/src/transport_layer/stcp_link.h
+++ b/src/transport_layer/stcp_link.h
@@ -16,6 +16,7 @@ struct stcp_link; // opaque
struct stcp_server; // opaque
struct ETCP_CONN;
struct ETCP_LINK;
+struct ETCP_SOCKET;
struct UTUN_INSTANCE;
// ====== Link config ======
@@ -35,10 +36,10 @@ struct stcp_link_config {
// ====== TCP server ======
-typedef void (*stcp_server_on_link_cb)(struct stcp_link *link, void *arg);
+typedef void (*stcp_server_on_link_cb)(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock);
struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t port,
- stcp_server_on_link_cb on_link, void *arg);
+ stcp_server_on_link_cb on_link, struct ETCP_SOCKET *tcp_sock);
void stcp_link_server_destroy(struct stcp_server *srv);
/* Server list management (multiple servers per instance) */
diff --git a/src/utun_instance.h b/src/utun_instance.h
index 63f9df78..eeebc667 100644
--- a/src/utun_instance.h
+++ b/src/utun_instance.h
@@ -37,7 +37,6 @@ struct ROUTE_TABLE;
struct ETCP_CONN;
typedef void (*etcp_new_conn_fn)(struct ETCP_CONN* conn, void* arg);
struct ETCP_SOCKET;
-struct TCP_SOCKET;
struct NODE_CONN_DIRECT;
struct tun_if;
struct ETCP_BINDINGS;
@@ -136,8 +135,8 @@ struct UTUN_INSTANCE {
struct memory_pool* ack_pool;
// Active sockets (UDP)
- struct ETCP_SOCKET* etcp_sockets;// linked-list
- struct TCP_SOCKET* tcp_sockets; // linked-list [TCP-only]
+ struct ETCP_SOCKET* etcp_sockets;// linked-list (UDP + TCP is_tcp=1)
+ struct ETCP_SOCKET* tcp_sockets; // linked-list (TCP, filtered by is_tcp=1)
struct stcp_server *stcp_servers; // TCP servers linked list (via stcp_link.c)
void* socket_monitor; // SOCKET_MONITOR* (opaque, transport_layer/socket_monitor.c)
void* auto_socket_state; // AUTO_SOCKET* (opaque, transport_layer/auto_socket.c)
diff --git a/tests/test_etcp_link_stress.c b/tests/test_etcp_link_stress.c
index 5f993978..8d6e6c93 100644
--- a/tests/test_etcp_link_stress.c
+++ b/tests/test_etcp_link_stress.c
@@ -60,7 +60,7 @@ struct extra_info {
int type;
int port;
struct ETCP_SOCKET* udp_sock;
- struct TCP_SOCKET* tcp_sock;
+ struct ETCP_SOCKET* tcp_sock;
struct stcp_server* tcp_srv;
struct ETCP_LINK* client_link;
uint64_t create_tb;
@@ -293,11 +293,11 @@ static int add_tcp_pair(struct ETCP_CONN* conn) {
cfg.type = CFG_SERVER_TYPE_PUBLIC; cfg.transport = 1;
memcpy(&cfg.ip, &sin, sizeof(sin));
- struct TCP_SOCKET* ts = tcp_socket_add(server_instance, &cfg);
+ struct ETCP_SOCKET* ts = tcp_socket_add(server_instance, &cfg);
if (!ts) { release_tcp_port(port); return -1; }
struct stcp_link_config scfg = {.ua = ua, .my_keys = &server_instance->my_keys, .inst = server_instance, .listen_family = AF_INET};
- struct stcp_server* srv = stcp_server_listen(&scfg, port, tcp_server_on_link, server_instance);
+ struct stcp_server* srv = stcp_server_listen(&scfg, port, (stcp_server_on_link_cb)tcp_server_on_link, ts);
if (!srv) { tcp_socket_remove(ts); release_tcp_port(port); return -1; }
stcp_server_list_add(server_instance, srv);
diff --git a/tests/test_invite_group_create.c b/tests/test_invite_group_create.c
index 4c4e75a7..41dbdfd9 100644
--- a/tests/test_invite_group_create.c
+++ b/tests/test_invite_group_create.c
@@ -88,7 +88,7 @@ int main(void) {
/* ── Add a TCP socket (simulating Android STCP server) ── */
{
- struct TCP_SOCKET* ts = u_calloc(1, sizeof(*ts));
+ struct ETCP_SOCKET* ts = u_calloc(1, sizeof(*ts));
ts->instance = a;
ts->type = CFG_SERVER_TYPE_PUBLIC;
ts->sock_id = (uint8_t)a->next_socket_id++;
diff --git a/tests/test_stcp_link.c b/tests/test_stcp_link.c
index ac7835ed..19779933 100644
--- a/tests/test_stcp_link.c
+++ b/tests/test_stcp_link.c
@@ -33,9 +33,9 @@ static void recv_cb(struct ll_queue *q, void *arg) {
queue_resume_callback(q);
}
-static void on_srv_link(struct stcp_link *link, void *arg) {
- struct stcp_link **out = (struct stcp_link **)arg;
- *out = link;
+static struct stcp_link *g_on_srv_link = NULL;
+static void on_srv_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock) {
+ (void)tcp_sock; g_on_srv_link = link;
}
static void on_cli_ready(struct stcp_link *link, void *arg) {
@@ -48,8 +48,8 @@ static int test1_basic(void) {
struct UASYNC *ua = uasync_create(); TASSERT(ua);
struct stcp_link_config cfg = {.ua = ua, .my_keys = &s_keys, .inst = NULL};
- struct stcp_link *srv_link = NULL;
- struct stcp_server *ss = stcp_server_listen(&cfg, BASE_PORT + 1, on_srv_link, &srv_link);
+ g_on_srv_link = NULL;
+ struct stcp_server *ss = stcp_server_listen(&cfg, BASE_PORT + 1, (stcp_server_on_link_cb)on_srv_link, NULL);
TASSERT(ss);
struct sockaddr_in raddr; memset(&raddr, 0, sizeof(raddr));
@@ -63,8 +63,8 @@ static int test1_basic(void) {
stcp_link_set_on_ready(cli_link, on_cli_ready, &cli_ready);
int ticks = 0;
- while ((!srv_link || !cli_ready) && ticks < 5000) { uasync_poll(ua, 10); ticks++; }
- TASSERT(srv_link != NULL); TASSERT(cli_ready);
+ while ((!g_on_srv_link || !cli_ready) && ticks < 5000) { uasync_poll(ua, 10); ticks++; }
+ TASSERT(g_on_srv_link != NULL); TASSERT(cli_ready);
// Verify send works (no crash, data goes through TCP)
const char *msg = "hello link!";
@@ -81,8 +81,8 @@ static int test2_wrong_key(void) {
struct UASYNC *ua = uasync_create(); TASSERT(ua);
struct stcp_link_config cfg = {.ua = ua, .my_keys = &s_keys, .inst = NULL};
- struct stcp_link *srv_link = NULL;
- struct stcp_server *ss = stcp_server_listen(&cfg, BASE_PORT + 2, on_srv_link, &srv_link);
+ g_on_srv_link = NULL;
+ struct stcp_server *ss = stcp_server_listen(&cfg, BASE_PORT + 2, (stcp_server_on_link_cb)on_srv_link, NULL);
TASSERT(ss);
struct SC_MYKEYS rogue; TASSERT(sc_generate_keypair(&rogue) == SC_OK);
@@ -95,8 +95,8 @@ static int test2_wrong_key(void) {
struct stcp_link *cli_link = stcp_link_connect(&cli_cfg); TASSERT(cli_link);
int ticks = 0;
- while (!srv_link && ticks < 2000) { uasync_poll(ua, 10); ticks++; }
- TASSERT(!srv_link); // should NOT connect with wrong key
+ while (!g_on_srv_link && ticks < 2000) { uasync_poll(ua, 10); ticks++; }
+ TASSERT(!g_on_srv_link); // should NOT connect with wrong key
stcp_link_close(cli_link);
stcp_link_server_destroy(ss);
diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c
index 4d80736e..248b21d4 100644
--- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c
+++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c
@@ -247,7 +247,7 @@ static int bridge_collect_invite_addrs(struct InviteAddrC* out, int max_cnt) {
}
s = s->next;
}
- { struct TCP_SOCKET* ts = g_cc.inst->tcp_sockets;
+ { struct ETCP_SOCKET* ts = g_cc.inst->tcp_sockets;
while (ts) {
struct sockaddr_storage* sa = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (sa && sa->ss_family == AF_INET && cnt < max_cnt) {
@@ -1012,50 +1012,32 @@ char* utun_bridge_get_member_links_json(uint64_t node_id) {
int first = 1;
for (struct ETCP_LINK* lk = conn->links; lk; lk = lk->next) {
if (pos >= (int)sizeof(buf) - 100) break;
- const struct sockaddr_storage* sa = &lk->remote_addr;
- char ip[INET6_ADDRSTRLEN] = "";
- int fm = 0;
- uint16_t po = 0;
- if (sa->ss_family == AF_INET) {
- struct sockaddr_in* sin = (struct sockaddr_in*)sa;
- inet_ntop(AF_INET, &sin->sin_addr, ip, sizeof(ip));
- fm = 4; po = ntohs(sin->sin_port);
- } else if (sa->ss_family == AF_INET6) {
- struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
- inet_ntop(AF_INET6, &sin6->sin6_addr, ip, sizeof(ip));
- fm = 6; po = ntohs(sin6->sin6_port);
- } else continue;
+ char la[INET6_ADDRSTRLEN] = ""; int lf = 0; uint16_t lp = 0;
+ if (lk->conn) {
+ const struct sockaddr_storage* lsa = &lk->conn->local_addr;
+ if (lsa->ss_family == AF_INET) {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)lsa)->sin_addr, la, sizeof(la)); lf = 4; lp = ntohs(((struct sockaddr_in*)lsa)->sin_port);
+ } else if (lsa->ss_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)lsa)->sin6_addr, la, sizeof(la)); lf = 6; lp = ntohs(((struct sockaddr_in6*)lsa)->sin6_port);
+ }
+ }
+
+ char ra[INET6_ADDRSTRLEN] = ""; int rf = 0; uint16_t rp = 0;
+ int st = lk->link_state, ls = lk->link_status;
+ if (lk->is_tcp && lk->tcp_link && stcp_link_is_ready(lk->tcp_link)) { st = 3; ls = 1; }
+ const struct sockaddr_storage* rsa = &lk->remote_addr;
+
+ if (rsa->ss_family == AF_INET) {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)rsa)->sin_addr, ra, sizeof(ra)); rf = 4; rp = ntohs(((struct sockaddr_in*)rsa)->sin_port);
+ } else if (rsa->ss_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)rsa)->sin6_addr, ra, sizeof(ra)); rf = 6; rp = ntohs(((struct sockaddr_in6*)rsa)->sin6_port);
+ }
- if (!ip[0]) continue;
- const char* sep = first ? "" : ","; first = 0;
- pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos,
- "%s{\"fm\":%d,\"ad\":\"%s\",\"po\":%d,\"st\":%d,\"ls\":%d,\"is\":%d}",
- sep, fm, ip, (int)po, lk->link_state, lk->link_status, lk->is_server);
- }
- for (struct ETCP_LINK* lk2 = conn->links; lk2; lk2 = lk2->next) {
- if (!lk2->is_tcp || !lk2->tcp_link) continue;
- if (pos >= (int)sizeof(buf) - 100) break;
- struct stcp_link* tcp = lk2->tcp_link;
- const struct sockaddr_storage* sa = stcp_link_get_remote_addr(tcp);
- if (!sa) continue;
- char ip[INET6_ADDRSTRLEN] = "";
- int fm = 0; uint16_t po = 0;
- if (sa->ss_family == AF_INET) {
- struct sockaddr_in* sin = (struct sockaddr_in*)sa;
- inet_ntop(AF_INET, &sin->sin_addr, ip, sizeof(ip));
- fm = 4; po = ntohs(sin->sin_port);
- } else if (sa->ss_family == AF_INET6) {
- struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
- inet_ntop(AF_INET6, &sin6->sin6_addr, ip, sizeof(ip));
- fm = 6; po = ntohs(sin6->sin6_port);
- } else continue;
- if (!ip[0]) continue;
- int ready = stcp_link_is_ready(tcp);
const char* sep = first ? "" : ","; first = 0;
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos,
- "%s{\"fm\":%d,\"ad\":\"%s\",\"po\":%d,\"st\":%d,\"ls\":%d,\"is\":%d}",
- sep, fm, ip, (int)po, ready ? 3 : 1, ready ? 1 : 0, lk2->is_server);
+ "%s{\"lf\":%d,\"la\":\"%s\",\"lp\":%d,\"rf\":%d,\"ra\":\"%s\",\"rp\":%d,\"st\":%d,\"ls\":%d,\"is\":%d}",
+ sep, lf, la, (int)lp, rf, ra, (int)rp, st, ls, lk->is_server);
}
if (pos < (int)sizeof(buf))
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "]}");
@@ -1108,7 +1090,7 @@ char* utun_bridge_get_local_sockets_json(void) {
bridge_log(BLEV_INFO, "getLocalSockets: tcp_sockets=%p etcp_sockets=%p conns=%d links=%d",
(void*)inst->tcp_sockets, (void*)inst->etcp_sockets, active_conns, active_links);
/* TCP sockets */
- { struct TCP_SOCKET* ts = inst->tcp_sockets;
+ { struct ETCP_SOCKET* ts = inst->tcp_sockets;
while (ts) {
char ip_str[INET6_ADDRSTRLEN] = ""; int port = 0;
const struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
diff --git a/tools/chatgui/src/accountlist.cpp b/tools/chatgui/src/accountlist.cpp
index de604755..26c8165e 100644
--- a/tools/chatgui/src/accountlist.cpp
+++ b/tools/chatgui/src/accountlist.cpp
@@ -258,10 +258,12 @@ void AccountList::onMemberDetailData(uint64_t nodeId, const uint8_t* data, int l
lk.linkStatus = p[2];
lk.tcpReady = p[3] != 0;
lk.isServer = p[4] != 0;
- lk.family = p[5];
- int addrLen = p[6];
- lk.addr = QByteArray((const char*)p + 7, qMin(addrLen, 16));
- memcpy(&lk.port, p + 23, 2);
+ lk.localFamily = p[5];
+ lk.localAddr = QByteArray((const char*)p + 6, 16);
+ memcpy(&lk.localPort, p + 22, 2);
+ lk.family = p[24];
+ lk.addr = QByteArray((const char*)p + 25, 16);
+ memcpy(&lk.port, p + 41, 2);
p += MEMBER_DETAIL_LINK_SIZE;
m_snapLinks.append(lk);
}
@@ -314,65 +316,40 @@ void AccountList::updateDetailDisplay() {
bool isSelf = m_db ? (m_detailNodeId == m_db->myNodeId()) : false;
- for (const auto& a : m_detailAddrs) {
- QString proto;
- switch (a.protocol) { case 1: proto = "UDP"; break; case 2: proto = "TCP"; break; default: proto = "UDP/TCP"; break; }
- QString linkColor = "#9E9E9E"; QString scRole;
- if (m_snapConnPresent) {
- for (const auto& lk : m_snapLinks) {
- if (lk.family == a.family && lk.port == a.port
- && lk.addr == a.addr) {
- linkColor = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
- scRole = lk.isServer ? " S" : " C";
- break;
- }
- }
- if (linkColor == "#9E9E9E" && a.protocol == 2) {
- for (const auto& lk : m_snapLinks) {
- if (lk.isTcp && lk.tcpReady) { linkColor = "#4CAF50"; scRole = lk.isServer ? " S" : " C"; break; }
- }
- }
+ if (isSelf) {
+ for (const auto& a : m_detailAddrs) {
+ QString proto;
+ switch (a.protocol) { case 1: proto = "UDP"; break; case 2: proto = "TCP"; break; default: proto = "UDP/TCP"; break; }
+ int lc = 0;
+ if (a.protocol == 1) { for (const auto& s : m_snapSocks) { if (s.port == a.port) { lc = s.linkCount; break; } } }
+ else if (a.protocol == 2) lc = m_snapOwnTcpActive;
+ QString prefix = lc > 0 ? QString("[%1] ").arg(lc) : "";
+ if (!a.ifname.isEmpty())
+ lines.append(QString("%1● %2 %3").arg(prefix, proto, a.ifname));
+ else
+ lines.append(QString("%1● %2:%3:%4").arg(prefix, proto, addrStr(a.addr.constData(), a.family)).arg(a.port));
}
-
- QString prefix;
- if (isSelf) {
- int linkCount = 0;
- if (a.protocol == 1) {
- for (const auto& s : m_snapSocks) {
- if (s.port == a.port) { linkCount = s.linkCount; break; }
- }
- } else if (a.protocol == 2) {
- linkCount = m_snapOwnTcpActive;
- }
- if (linkCount > 0) prefix = QString("[%1] ").arg(linkCount);
- }
-
- if (!a.ifname.isEmpty()) {
- lines.append(QString("%1● %3%4 %5")
- .arg(prefix, linkColor, proto, scRole, a.ifname));
- lines.append(QString(" %1:%2")
- .arg(addrStr(a.addr.constData(), a.family)).arg(a.port));
- } else {
- lines.append(QString("%1● %3%4:%5:%6")
- .arg(prefix, linkColor, proto, scRole, addrStr(a.addr.constData(), a.family)).arg(a.port));
+ } else {
+ /* all live links with arrows */
+ for (const auto& lk : m_snapLinks) {
+ QString proto = lk.isTcp ? "TCP" : "UDP";
+ QString lc = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
+ QString left = QString("%1:%2").arg(addrStr(lk.localAddr.constData(), lk.localFamily)).arg(lk.localPort);
+ QString right = QString("%1:%2").arg(addrStr(lk.addr.constData(), lk.family)).arg(lk.port);
+ lines.append(QString("● %2 %3 → %4")
+ .arg(lc, proto, left, right));
}
- }
-
- /* Show snapshot links that are not in DB (non-self) */
- if (m_snapConnPresent && m_snapLinks.size() > 0) {
- for (int li = 0; li < m_snapLinks.size(); li++) {
- const auto& lk = m_snapLinks[li];
+ /* DB addresses without live link */
+ for (const auto& a : m_detailAddrs) {
bool matched = false;
- for (const auto& a : m_detailAddrs) {
- if (lk.family == a.family && lk.port == a.port
- && lk.addr == a.addr) { matched = true; break; }
+ for (const auto& lk : m_snapLinks) {
+ if (lk.family == a.family && lk.port == a.port && lk.addr == a.addr) { matched = true; break; }
}
if (matched) continue;
- QString proto = lk.isTcp ? "TCP" : "UDP";
- QString sc = lk.isServer ? " S" : " C";
- QString lc = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
- lines.append(QString("● %2%3:%4:%5 [link]")
- .arg(lc, proto, sc, addrStr(lk.addr.constData(), lk.family)).arg(lk.port));
+ QString proto;
+ switch (a.protocol) { case 1: proto = "UDP"; break; case 2: proto = "TCP"; break; default: proto = "UDP/TCP"; break; }
+ lines.append(QString("● %1 %2:%3")
+ .arg(proto, addrStr(a.addr.constData(), a.family)).arg(a.port));
}
}
diff --git a/tools/chatgui/src/accountlist.h b/tools/chatgui/src/accountlist.h
index 302a7fce..4ffa15e1 100644
--- a/tools/chatgui/src/accountlist.h
+++ b/tools/chatgui/src/accountlist.h
@@ -27,6 +27,9 @@ struct MemberLinkSnap {
int linkStatus;
bool tcpReady;
bool isServer;
+ int localFamily;
+ QByteArray localAddr;
+ uint16_t localPort;
int family;
QByteArray addr;
uint16_t port;
diff --git a/tools/chatgui/transport/gui_bridge.h b/tools/chatgui/transport/gui_bridge.h
index 8aaf45c4..b18cb4d7 100644
--- a/tools/chatgui/transport/gui_bridge.h
+++ b/tools/chatgui/transport/gui_bridge.h
@@ -36,7 +36,7 @@ struct TOPO_GROUP_NODE;
#define GUI_EVT_MEMBER_REMOVED 25 /* data: [ch_id_len:1][ch_id:var][node_id:8] — мембер удалён из канала */
/* Размеры элементов пакета GUI_EVT_MEMBER_DETAIL (должны совпадать с chat_status.c) */
-#define MEMBER_DETAIL_LINK_SIZE 25
+#define MEMBER_DETAIL_LINK_SIZE 43
#define MEMBER_DETAIL_SOCK_SIZE 48
#define MEMBER_DETAIL_MAX_LINKS 16
#define MEMBER_DETAIL_MAX_SOCKS 16
diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp
index b1bd0a83..fc1735ee 100644
--- a/tools/chatgui/transport/utun_node.cpp
+++ b/tools/chatgui/transport/utun_node.cpp
@@ -217,7 +217,7 @@ QList UtunNode::getInviteAddresses(DbManager* db) {
/* 3) TCP from local TCP sockets */
{
- struct TCP_SOCKET* ts = m_instance->tcp_sockets;
+ struct ETCP_SOCKET* ts = m_instance->tcp_sockets;
while (ts) {
struct sockaddr_storage* sa = &ts->interface_addr;
if (sa->ss_family == AF_INET) {