Browse Source

unify TCP_SOCKET into ETCP_SOCKET with is_tcp flag; add local+remote addr to member detail links with arrow display

topo_upd
evgeny 2 months ago
parent
commit
544c77efff
  1. 2
      src/chat/chat_channel.c
  2. 2
      src/chat/chat_headless_control.c
  3. 2
      src/chat/chat_profile.c
  4. 45
      src/chat/chat_status.c
  5. 6
      src/routing_layer/topo_node.c
  6. 65
      src/transport_layer/auto_socket.c
  7. 28
      src/transport_layer/etcp_connections.c
  8. 61
      src/transport_layer/etcp_connections.h
  9. 8
      src/transport_layer/stcp_link.c
  10. 5
      src/transport_layer/stcp_link.h
  11. 5
      src/utun_instance.h
  12. 6
      tests/test_etcp_link_stress.c
  13. 2
      tests/test_invite_group_create.c
  14. 22
      tests/test_stcp_link.c
  15. 66
      tools/chatgui-android/jni_bridge/android_jni_bridge.c
  16. 93
      tools/chatgui/src/accountlist.cpp
  17. 3
      tools/chatgui/src/accountlist.h
  18. 2
      tools/chatgui/transport/gui_bridge.h
  19. 2
      tools/chatgui/transport/utun_node.cpp

2
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) {

2
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;

2
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++;

45
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;

6
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; }

65
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);

28
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);
}

61
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)

8
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);

5
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) */

5
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)

6
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);

2
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++;

22
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);

66
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;

93
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<span style='color:%2;'>●</span> %3%4 %5")
.arg(prefix, linkColor, proto, scRole, a.ifname));
lines.append(QString("&nbsp;&nbsp;%1:<span style='color:#FF9800;'>%2</span>")
.arg(addrStr(a.addr.constData(), a.family)).arg(a.port));
} else {
lines.append(QString("%1<span style='color:%2;'>●</span> %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("<span style='color:%1;'>●</span> %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("<span style='color:%1;'>●</span> %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("<span style='color:#9E9E9E;'>●</span> %1 %2:%3")
.arg(proto, addrStr(a.addr.constData(), a.family)).arg(a.port));
}
}

3
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;

2
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

2
tools/chatgui/transport/utun_node.cpp

@ -217,7 +217,7 @@ QList<NodeAddr> 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) {

Loading…
Cancel
Save