diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index ffc855a6..f9f9bb4d 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/src/routing_layer/conn_mgr_core.c @@ -531,13 +531,18 @@ static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* e /* v4 */ if (db_loaded) { for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { - if (!(a->protocol & TOPO_PROTO_UDP)) continue; - struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; - while (s) { if (s->local_addr.ss_family == AF_INET) { - struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family=AF_INET; - memcpy(&sin.sin_addr.s_addr,a->addr,4); sin.sin_port=htons(a->port); - struct sockaddr_storage sa; memcpy(&sa,&sin,sizeof(sin)); - if (etcp_link_new(conn,s,&sa,0)) any=1; } s=s->next; } + struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family=AF_INET; + memcpy(&sin.sin_addr.s_addr,a->addr,4); sin.sin_port=htons(a->port); + struct sockaddr_storage sa; memcpy(&sa,&sin,sizeof(sin)); + if (a->protocol & TOPO_PROTO_TCP) { + struct ETCP_LINK *tlink = etcp_link_new(conn, NULL, NULL, 0); + if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); any = 1; } + } + if (a->protocol & TOPO_PROTO_UDP) { + struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets; + while (s) { if (s->local_addr.ss_family == AF_INET) { + if (etcp_link_new(conn,s,&sa,0)) any=1; } s=s->next; } + } } } else { uint8_t prio[]={TOPO_ADDR_NAT,TOPO_ADDR_INTERFACE}; @@ -556,10 +561,23 @@ static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* e } /* v6 */ for (const struct TOPO_ADDR6* a6=ni->v6_addrs;a6;a6=a6->next) { - if (!(a6->protocol&TOPO_PROTO_UDP)) continue; - uint8_t tc=cm_classify_v6_addr(a6->addr); if(tc==CM_V6_OTH) continue; - struct ETCP_SOCKET* s=entry->mgr->instance->etcp_sockets; - while(s){uint8_t sc=cm_sock_v6_classify(s);if(sc==CM_V6_ANY||sc==tc){cm_add_v6_link(conn,a6->addr,a6->port,s);any=1;} s=s->next;} + int is_ll = (a6->addr[0] == 0xfe && (a6->addr[1] & 0xc0) == 0x80); + if (a6->protocol & TOPO_PROTO_TCP) { + struct sockaddr_in6 sin6; memset(&sin6,0,sizeof(sin6)); sin6.sin6_family=AF_INET6; + memcpy(&sin6.sin6_addr,a6->addr,16); sin6.sin6_port=htons(a6->port); + if (is_ll) { struct ETCP_SOCKET* sv=entry->mgr->instance->etcp_sockets; + while(sv){if(sv->local_addr.ss_family==AF_INET6&&sv->netif_index) + {sin6.sin6_scope_id=sv->netif_index;break;} sv=sv->next;} + } + struct sockaddr_storage sa; memcpy(&sa,&sin6,sizeof(sin6)); + struct ETCP_LINK *tlink = etcp_link_new(conn, NULL, NULL, 0); + if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a6->port); any = 1; } + } + if (a6->protocol & TOPO_PROTO_UDP) { + uint8_t tc=cm_classify_v6_addr(a6->addr); if(tc==CM_V6_OTH) continue; + struct ETCP_SOCKET* s=entry->mgr->instance->etcp_sockets; + while(s){uint8_t sc=cm_sock_v6_classify(s);if(sc==CM_V6_ANY||sc==tc){cm_add_v6_link(conn,a6->addr,a6->port,s);any=1;} s=s->next;} + } } if (any) return; diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index aa458e05..03c84627 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -623,8 +623,8 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr { sqlite3_stmt* s = NULL; if (sqlite3_prepare_v2(db, - "SELECT address,port,addr_type,socket_id FROM node_addresses" - " WHERE node_id=? AND family=4 AND protocol=1 ORDER BY addr_type", + "SELECT address,port,addr_type,socket_id,protocol FROM node_addresses" + " WHERE node_id=? AND family=4 ORDER BY addr_type", -1, &s, NULL) == SQLITE_OK) { sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id); while (sqlite3_step(s) == SQLITE_ROW) { @@ -638,8 +638,9 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr struct TOPO_ADDR4* a4 = memory_pool_alloc(groups->v4_addr_pool); if (!a4) continue; memset(a4, 0, sizeof(*a4)); + int proto = sqlite3_column_int(s, 4); memcpy(a4->addr, addr, 4); a4->port = (uint16_t)port; - a4->type = type; a4->socket_id = sock_id; a4->protocol = TOPO_PROTO_UDP; + a4->type = type; a4->socket_id = sock_id; a4->protocol = (uint8_t)proto; if (!v4_head) v4_head = v4_tail = a4; else { v4_tail->next = a4; v4_tail = a4; } } @@ -651,8 +652,8 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr { sqlite3_stmt* s = NULL; if (sqlite3_prepare_v2(db, - "SELECT address,port,addr_type,socket_id FROM node_addresses" - " WHERE node_id=? AND family=6 AND protocol=1 ORDER BY addr_type", + "SELECT address,port,addr_type,socket_id,protocol FROM node_addresses" + " WHERE node_id=? AND family=6 ORDER BY addr_type", -1, &s, NULL) == SQLITE_OK) { sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id); while (sqlite3_step(s) == SQLITE_ROW) { @@ -666,8 +667,9 @@ struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* gr struct TOPO_ADDR6* a6 = memory_pool_alloc(groups->v6_addr_pool); if (!a6) continue; memset(a6, 0, sizeof(*a6)); + int proto6 = sqlite3_column_int(s, 4); memcpy(a6->addr, addr, 16); a6->port = (uint16_t)port; - a6->type = type; a6->socket_id = sock_id; a6->protocol = TOPO_PROTO_UDP; + a6->type = type; a6->socket_id = sock_id; a6->protocol = (uint8_t)proto6; if (!v6_head) v6_head = v6_tail = a6; else { v6_tail->next = a6; v6_tail = a6; } } diff --git a/src/transport_layer/auto_socket.c b/src/transport_layer/auto_socket.c index 475540ed..429e2157 100644 --- a/src/transport_layer/auto_socket.c +++ b/src/transport_layer/auto_socket.c @@ -132,7 +132,7 @@ struct AUTO_SOCKET { /* ─── forward declarations ─── */ static int create_iface_udp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname, int family, uint8_t type); -static int create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname, int family, uint8_t type); +static struct stcp_server* create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname, int family, uint8_t type); 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); @@ -273,7 +273,7 @@ static int create_iface_udp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, con return -1; } -static int create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname, int family, uint8_t type) { +static struct stcp_server* create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, const char* ifname, int family, uint8_t type) { struct UTUN_INSTANCE* inst = as->instance; uint16_t saved_port = load_port_from_db(as, ifname, family, AS_PROTO_TCP); @@ -296,7 +296,7 @@ static int create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, con 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 -1; + if (socket_get_error() != EADDRINUSE) return NULL; continue; } @@ -334,11 +334,11 @@ static int create_iface_tcp_socket(struct AUTO_SOCKET* as, uint32_t ifindex, con 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); - return 0; + return tsrv; } - DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] failed to create TCP socket after %d attempts if=%s fam=%d", max_att, ifname, family); - return -1; + DEBUG_ERROR(DEBUG_CATEGORY_AS, "[as] failed to create TCP socket after %d attempts if=%s fam=%d", max_att, ifname, family); + return NULL; } int auto_socket_add_interface(struct UTUN_INSTANCE* inst, uint32_t ifindex, @@ -365,9 +365,8 @@ int auto_socket_add_interface(struct UTUN_INSTANCE* inst, uint32_t ifindex, struct ETCP_SOCKET* s = inst->etcp_sockets; while (s) { if (s->netif_index == ifindex && s->local_addr.ss_family == AF_INET && !ifa->v4_udp) { ifa->v4_udp = s; break; } s = s->next; } } - if (create_iface_tcp_socket(as, ifindex, ifname, AF_INET, v4_type) == 0) { - added++; - } + { struct stcp_server* tsrv = create_iface_tcp_socket(as, ifindex, ifname, AF_INET, v4_type); + if (tsrv) { added++; ifa->v4_tcp = tsrv; } } } if (has_v6) { if (create_iface_udp_socket(as, ifindex, ifname, AF_INET6, v6_type) == 0) { @@ -375,9 +374,8 @@ int auto_socket_add_interface(struct UTUN_INSTANCE* inst, uint32_t ifindex, struct ETCP_SOCKET* s = inst->etcp_sockets; while (s) { if (s->netif_index == ifindex && s->local_addr.ss_family == AF_INET6 && !ifa->v6_udp) { ifa->v6_udp = s; break; } s = s->next; } } - if (create_iface_tcp_socket(as, ifindex, ifname, AF_INET6, v6_type) == 0) { - added++; - } + { struct stcp_server* tsrv = create_iface_tcp_socket(as, ifindex, ifname, AF_INET6, v6_type); + if (tsrv) { added++; ifa->v6_tcp = tsrv; } } } if (added > 0) { @@ -713,7 +711,7 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char struct stcp_server* srv = ifa ? ifa->v4_tcp : NULL; if (has && !srv) { if (!ifa) ifa = reconcile_get_ifa(as, ifindex); - if (ifa) { create_iface_tcp_socket(as, ifindex, ifname, AF_INET, v4_need_type); ifa->v4_tcp = NULL; /* find below */ changed = 1; } + if (ifa) { struct stcp_server* tsrv = create_iface_tcp_socket(as, ifindex, ifname, AF_INET, v4_need_type); ifa->v4_tcp = tsrv; if (tsrv) changed = 1; } } else if (!has && srv) { 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; @@ -735,7 +733,7 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char struct stcp_server* srv = ifa ? ifa->v6_tcp : NULL; if (has && !srv) { if (!ifa) ifa = reconcile_get_ifa(as, ifindex); - if (ifa) { create_iface_tcp_socket(as, ifindex, ifname, AF_INET6, v6_need_type); ifa->v6_tcp = NULL; changed = 1; } + if (ifa) { struct stcp_server* tsrv = create_iface_tcp_socket(as, ifindex, ifname, AF_INET6, v6_need_type); ifa->v6_tcp = tsrv; if (tsrv) changed = 1; } } else if (!has && srv) { 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; diff --git a/src/transport_layer/auto_socket.h b/src/transport_layer/auto_socket.h index 7f7d6150..d4a6e8b2 100644 --- a/src/transport_layer/auto_socket.h +++ b/src/transport_layer/auto_socket.h @@ -32,6 +32,10 @@ struct ETCP_SOCKET; int auto_socket_init(struct UTUN_INSTANCE* inst); void auto_socket_destroy(struct UTUN_INSTANCE* inst); +/* Platform notification: call from any thread when network interfaces change. + * Posts auto_socket_scan_all() to uasync thread. Safe for JNI/ConnectivityManager callbacks. */ +void auto_socket_on_network_change(struct UTUN_INSTANCE* inst); + /* ─── Platform-agnostic API: управление сокетами на конкретном интерфейсе ─── */ /* diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index 3b8a43c2..ebcc3eef 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -44,7 +44,7 @@ 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 -static void tcp_server_on_link(struct stcp_link *link, void *arg) { +void tcp_server_on_link(struct stcp_link *link, void *arg) { struct UTUN_INSTANCE *inst = (struct UTUN_INSTANCE *)arg; 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; } @@ -180,7 +180,9 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset, uint8_t c // padding int wire_ov_init = WIRE_OVERHEAD(link->remote_addr.ss_family); - int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; + int pad_range = (int)link->handshake_maxsize - (int)link->handshake_minsize; + if (pad_range <= 0) pad_range = 1; + int s = rand() % pad_range + link->handshake_minsize; int s_max = (int)(link->mtu) - wire_ov_init; if (s > s_max) s = s_max; if (s < 0) s = 0; @@ -830,6 +832,15 @@ struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SER struct sockaddr_in* sin = (struct sockaddr_in*)&server->ip; memcpy(&ts->local_addr, sin, sizeof(*sin)); ts->interface_addr = ts->local_addr; + if (sin->sin_addr.s_addr == 0 && server->netif_index > 0) { + uint32_t if_ip = get_interface_ip_by_index(server->netif_index); + if (if_ip != 0) { + struct sockaddr_in* if_sin = (struct sockaddr_in*)&ts->interface_addr; + if_sin->sin_family = AF_INET; + if_sin->sin_addr.s_addr = if_ip; + if_sin->sin_port = sin->sin_port; + } + } if (server->type == CFG_SERVER_TYPE_PUBLIC && sin->sin_addr.s_addr == 0) { struct sockaddr_storage remote; memset(&remote, 0, sizeof(remote)); struct sockaddr_in* rs = (struct sockaddr_in*)&remote; @@ -845,6 +856,16 @@ struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SER struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server->ip; memcpy(&ts->local_addr, sin6, sizeof(*sin6)); ts->interface_addr = ts->local_addr; + if (memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)) == 0 && server->netif_index > 0) { + uint8_t v6addr[16]; + int got = get_interface_ipv6_addr_nl(server->netif_index, 1, v6addr); + if (got != 0) got = get_interface_ipv6_addr_nl(server->netif_index, 0, v6addr); + if (got == 0) { + struct sockaddr_in6* if6 = (struct sockaddr_in6*)&ts->interface_addr; + if6->sin6_family = AF_INET6; memcpy(&if6->sin6_addr, v6addr, 16); + if6->sin6_port = sin6->sin6_port; + } + } if (server->type == CFG_SERVER_TYPE_PUBLIC && memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)) == 0) { uint16_t v6if = (server->netif_index > 0) ? server->netif_index : get_default_route_netif_index(AF_INET6); if (v6if > 0) { @@ -905,7 +926,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn link->is_tcp = (uint8_t)is_tcp; int mtu; mtu = conn ? conn->mtu : STCP_MAX_MSG_SIZE; - if (is_tcp) { mtu = STCP_MAX_MSG_SIZE; link->mtu_local = mtu; link->mtu = mtu; link->inflight_lim_bytes = mtu * 4; } + if (is_tcp) { mtu = STCP_MAX_MSG_SIZE; link->mtu_local = mtu; link->mtu = mtu; link->inflight_lim_bytes = mtu * 4; link->handshake_minsize = 100; link->handshake_maxsize = mtu; } else { if (mtu == 0) mtu = 1500; if (mtu > PACKET_DATA_MAX_MTU) mtu = PACKET_DATA_MAX_MTU; link->mtu_local = mtu; link->mtu = mtu; link->inflight_lim_bytes = mtu * 4; link->handshake_minsize = 100; link->handshake_maxsize = mtu; } /* mtu init moved above */ etcp_update_mtu(etcp); @@ -1603,7 +1624,9 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk // padding int wire_ov = WIRE_OVERHEAD(link->remote_addr.ss_family); int max_data = link->mtu - wire_ov; - int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; + int pad_range = (int)link->handshake_maxsize - (int)link->handshake_minsize; + if (pad_range <= 0) pad_range = 1; + int s = rand() % pad_range + link->handshake_minsize; if (s > (int)(link->mtu)) s = (int)(link->mtu); if (s < 0) s = 0; diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index bfb379fe..1e98a195 100644 --- a/src/transport_layer/etcp_connections.h +++ b/src/transport_layer/etcp_connections.h @@ -342,6 +342,9 @@ struct TCP_SOCKET { struct TCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server); void tcp_socket_remove(struct TCP_SOCKET* sock); +struct stcp_link; +void tcp_server_on_link(struct stcp_link *link, void *arg); + // connection functions // создает новый канал связи для etcp подключения (ETCP_CONN) struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server); diff --git a/src/transport_layer/stcp.c b/src/transport_layer/stcp.c index 52a77da8..21a5e90c 100644 --- a/src/transport_layer/stcp.c +++ b/src/transport_layer/stcp.c @@ -27,7 +27,7 @@ void stcp_conn_set_on_close(struct stcp_conn *c, void (*cb)(struct stcp_conn *co void stcp_conn_free(struct stcp_conn *c) { if (!c) return; - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_conn_free: c=%p sock=%d sock_id=%p recv_buf=%p state=%d allocated=%d", + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_conn_free: c=%p sock=%d sock_id=%p recv_buf=%p state=%d allocated=%d", (void*)c, (int)c->sock, (void*)c->socket_id, (void*)c->recv_buf, (int)c->state, (int)c->allocated); if (c->socket_id) { uasync_remove_socket_t(c->ua, c->sock); c->socket_id = NULL; } if (c->sock != SOCKET_INVALID) { socket_close_wrapper(c->sock); c->sock = SOCKET_INVALID; } diff --git a/src/transport_layer/stcp_client.c b/src/transport_layer/stcp_client.c index bab942eb..54073ed1 100644 --- a/src/transport_layer/stcp_client.c +++ b/src/transport_layer/stcp_client.c @@ -38,7 +38,7 @@ static int client_derive_session(struct stcp_conn *c, const uint8_t *peer_pubkey if (sc_set_peer_public_key(&sc, peer_pubkey, SC_PEER_PUBKEY_BIN) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "client ECDH failed"); return -1; } memcpy(c->session_key, sc.session_key, SC_SESSION_KEY_SIZE); memcpy(c->peer_pubkey, peer_pubkey, SC_PUBKEY_SIZE); c->peer_pubkey_set = 1; - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_client session_key", c->session_key, 16); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_client session_key", c->session_key, 16); if (sc_stream_init(&sc, &c->stream_send, STCP_STREAM_CLIENT_SEND) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "client stream_send init failed"); return -1; } if (sc_stream_init(&sc, &c->stream_recv, STCP_STREAM_SERVER_SEND) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "client stream_recv init failed"); return -1; } return 0; @@ -69,7 +69,7 @@ static void client_send_handshake(struct stcp_conn *c, const uint8_t *server_pub } static void process_server_response(struct stcp_conn *c) { - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_client process_srv_resp recv_buf", c->recv_buf, c->recv_buf_len); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_client process_srv_resp recv_buf", c->recv_buf, c->recv_buf_len); if (!c->hs_key_processed) { const uint8_t *salt = c->recv_buf; const uint8_t *enc_pubkey = salt + SC_PUBKEY_ENC_SALT_SIZE; @@ -83,10 +83,10 @@ static void process_server_response(struct stcp_conn *c) { } uint8_t enc_hs[STCP_HS_ENC_SERVER]; memcpy(enc_hs, c->recv_buf + SC_PUBKEY_ENC_SIZE, STCP_HS_ENC_SERVER); - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_client enc_hs BEFORE xor", enc_hs, STCP_HS_ENC_SERVER); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_client enc_hs BEFORE xor", enc_hs, STCP_HS_ENC_SERVER); size_t hs_data_len; if (stcp_frame_decrypt(enc_hs, STCP_HS_ENC_SERVER, &c->stream_recv, &hs_data_len)) { client_do_close(c, 3); return; } - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_client enc_hs AFTER xor", enc_hs, STCP_HS_ENC_SERVER); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_client enc_hs AFTER xor", enc_hs, STCP_HS_ENC_SERVER); memcpy(c->peer_ed25519_pubkey, enc_hs, SC_PUBKEY_SIZE); c->peer_ed25519_set = 1; uint8_t status = enc_hs[32]; uint16_t padding_size = (uint16_t)enc_hs[33] | ((uint16_t)enc_hs[34] << 8); @@ -106,7 +106,7 @@ static void finish_server_response(struct stcp_conn *c) { } static void stcp_conn_process_recv(struct stcp_conn *c) { - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: process_recv entry state=%d sock=%d recv_buf=%p len=%zu cap=%zu", + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "stcp_client: process_recv entry state=%d sock=%d recv_buf=%p len=%zu cap=%zu", c->state, (int)c->sock, (void*)c->recv_buf, c->recv_buf_len, c->recv_buf_cap); if (!c->recv_buf && c->recv_buf_len > 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_client: process_recv NULL recv_buf with len=%zu — closing", c->recv_buf_len); @@ -136,8 +136,6 @@ static void stcp_conn_process_recv(struct stcp_conn *c) { if (e) { e->dgram = u_malloc(data_len); if (e->dgram) { if (data_len) memcpy(e->dgram, enc_data, data_len); e->len = (uint16_t)data_len; queue_data_put(c->rx_queue, e); } else { queue_entry_free(e); } } if (c->state != STCP_STATE_DATA) return; } - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: memmove recv_buf=%p src=%p(+%zu) len=%zu (total=%zu recv_buf_len=%zu)", - (void*)c->recv_buf, (void*)(c->recv_buf + total), total, c->recv_buf_len - total, total, c->recv_buf_len); if ((uintptr_t)c->recv_buf < 4096) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_client: suspicious recv_buf=%p (addr < 4K) — closing", (void*)c->recv_buf); client_do_close(c, EFAULT); return; @@ -154,10 +152,10 @@ static void stcp_conn_process_recv(struct stcp_conn *c) { static void client_conn_read_cb(socket_t sock, void *arg) { struct stcp_conn *c = (struct stcp_conn *)arg; if (c->state == STCP_STATE_CLOSED || c->state == STCP_STATE_ERROR) { - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: read_cb SKIP state=%d sock=%d c=%p recv_buf=%p", c->state, (int)sock, (void*)c, (void*)c->recv_buf); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: read_cb SKIP state=%d sock=%d c=%p recv_buf=%p", c->state, (int)sock, (void*)c, (void*)c->recv_buf); return; } - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: read_cb state=%d sock=%d c=%p recv_buf=%p len=%zu cap=%zu", + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "stcp_client: read_cb state=%d sock=%d c=%p recv_buf=%p len=%zu cap=%zu", c->state, (int)sock, (void*)c, (void*)c->recv_buf, c->recv_buf_len, c->recv_buf_cap); if (!c->recv_buf) { c->recv_buf_cap = STCP_RECV_BUF_INIT; c->recv_buf = u_malloc(c->recv_buf_cap); if (!c->recv_buf) { client_do_close(c, ENOMEM); return; } } if (c->recv_buf_len + 4096 > c->recv_buf_cap) { @@ -208,14 +206,14 @@ static void client_do_close(struct stcp_conn *c, int err) { int prev = c->state; c->state = STCP_STATE_CLOSED; DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: closed is_server=%d err=%d prev_state=%d", c->is_server, err, prev); - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: do_close sock=%d sock_id=%p recv_buf=%p len=%zu cap=%zu send_buf=%p on_close=%p", + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: do_close sock=%d sock_id=%p recv_buf=%p len=%zu cap=%zu send_buf=%p on_close=%p", (int)c->sock, (void*)c->socket_id, (void*)c->recv_buf, c->recv_buf_len, c->recv_buf_cap, (void*)c->send_buf, (void*)c->on_close); if (c->socket_id) { uasync_remove_socket_t(c->ua, c->sock); c->socket_id = NULL; } if (c->sock != SOCKET_INVALID) { socket_close_wrapper(c->sock); c->sock = SOCKET_INVALID; } - if (c->recv_buf) { void *p = c->recv_buf; u_free(c->recv_buf); c->recv_buf = NULL; c->recv_buf_len = 0; c->recv_buf_cap = 0; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: do_close u_free recv_buf=%p", p); } - if (c->send_buf) { void *p = c->send_buf; u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: do_close u_free send_buf=%p", p); } + if (c->recv_buf) { void *p = c->recv_buf; u_free(c->recv_buf); c->recv_buf = NULL; c->recv_buf_len = 0; c->recv_buf_cap = 0; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: do_close u_free recv_buf=%p", p); } + if (c->send_buf) { void *p = c->send_buf; u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: do_close u_free send_buf=%p", p); } stcp_pending_clear(c); - if (c->on_close) { void (*cb)(struct stcp_conn*, int, void*) = c->on_close; c->on_close = NULL; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: do_close calling on_close=%p", (void*)cb); cb(c, err, c->close_arg); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_client: do_close on_close returned"); } + if (c->on_close) { void (*cb)(struct stcp_conn*, int, void*) = c->on_close; c->on_close = NULL; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: do_close calling on_close=%p", (void*)cb); cb(c, err, c->close_arg); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: do_close on_close returned"); } } struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uint16_t port, diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index a797b5ca..a5a9d44f 100644 --- a/src/transport_layer/stcp_server.c +++ b/src/transport_layer/stcp_server.c @@ -44,7 +44,7 @@ static int derive_session_and_streams(struct stcp_conn *c, const uint8_t *peer_p return -1; } memcpy(c->session_key, sc.session_key, SC_SESSION_KEY_SIZE); - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_server session_key", c->session_key, 16); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server session_key", c->session_key, 16); if (sc_stream_init(&sc, &c->stream_send, STCP_STREAM_SERVER_SEND) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stream_send init failed"); return -1; @@ -101,17 +101,17 @@ static void finish_client_handshake(struct stcp_conn *c) { uint8_t *enc_dst = resp + SC_PUBKEY_ENC_SIZE; memcpy(enc_dst, plain_hs, 35); enc_dst[35] = (uint8_t)(crc >> 0); enc_dst[36] = (uint8_t)(crc >> 8); enc_dst[37] = (uint8_t)(crc >> 16); enc_dst[38] = (uint8_t)(crc >> 24); - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_server hs_resp BEFORE xor", enc_dst, STCP_HS_ENC_SERVER); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp BEFORE xor", enc_dst, STCP_HS_ENC_SERVER); if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_SERVER) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "encrypt failed"); u_free(resp); stcp_conn_do_close(c, 3); return; } - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_server hs_resp AFTER xor", enc_dst, STCP_HS_ENC_SERVER); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp AFTER xor", enc_dst, STCP_HS_ENC_SERVER); for (int i = 0; i < padding; i++) resp[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + i] = (uint8_t)(salt2[0] ^ i); c->state = STCP_STATE_DATA; DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: handshake OK, entering DATA state"); - log_dump(DEBUG_LEVEL_ERROR, DEBUG_CATEGORY_DEBUG, "stcp_server FULL RESP", resp, total_resp); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_server FULL RESP", resp, total_resp); int r = stcp_try_send(c, resp, total_resp); if (r < 0) { u_free(resp); stcp_conn_do_close(c, 3); return; } if (c->on_ready) c->on_ready(c, c->ready_arg); @@ -120,10 +120,10 @@ static void finish_client_handshake(struct stcp_conn *c) { static void server_conn_read_cb(socket_t sock, void *arg) { struct stcp_conn *c = (struct stcp_conn *)arg; if (c->state == STCP_STATE_CLOSED || c->state == STCP_STATE_ERROR) { - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: read_cb SKIP state=%d sock=%d c=%p recv_buf=%p", c->state, (int)sock, (void*)c, (void*)c->recv_buf); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: read_cb SKIP state=%d sock=%d c=%p recv_buf=%p", c->state, (int)sock, (void*)c, (void*)c->recv_buf); return; } - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: read_cb state=%d sock=%d c=%p recv_buf=%p len=%zu cap=%zu", + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "stcp_server: read_cb state=%d sock=%d c=%p recv_buf=%p len=%zu cap=%zu", c->state, (int)sock, (void*)c, (void*)c->recv_buf, c->recv_buf_len, c->recv_buf_cap); if (c->recv_buf_cap == 0) { @@ -152,7 +152,7 @@ static void server_conn_read_cb(socket_t sock, void *arg) { } static void stcp_conn_process_recv(struct stcp_conn *c) { - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: process_recv entry state=%d sock=%d recv_buf=%p len=%zu cap=%zu", + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "stcp_server: process_recv entry state=%d sock=%d recv_buf=%p len=%zu cap=%zu", c->state, (int)c->sock, (void*)c->recv_buf, c->recv_buf_len, c->recv_buf_cap); if (!c->recv_buf && c->recv_buf_len > 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server: process_recv NULL recv_buf with len=%zu — closing", c->recv_buf_len); @@ -191,8 +191,6 @@ static void stcp_conn_process_recv(struct stcp_conn *c) { else { queue_entry_free(e); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "rx malloc(%zu) failed", data_len); } } else { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "queue_entry_new failed"); } } - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: memmove recv_buf=%p src=%p(+%zu) len=%zu (total=%zu recv_buf_len=%zu)", - (void*)c->recv_buf, (void*)(c->recv_buf + total), total, c->recv_buf_len - total, total, c->recv_buf_len); if ((uintptr_t)c->recv_buf < 4096) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_server: suspicious recv_buf=%p (addr < 4K) — closing", (void*)c->recv_buf); stcp_conn_do_close(c, EFAULT); return; @@ -228,13 +226,13 @@ static void stcp_conn_do_close(struct stcp_conn *c, int err) { c->state = STCP_STATE_CLOSED; if (c->socket_id) { uasync_remove_socket_t(c->ua, c->sock); c->socket_id = NULL; } if (c->sock != SOCKET_INVALID) { socket_close_wrapper(c->sock); c->sock = SOCKET_INVALID; } - if (c->recv_buf) { void *p = c->recv_buf; u_free(c->recv_buf); c->recv_buf = NULL; c->recv_buf_len = 0; c->recv_buf_cap = 0; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: do_close u_free recv_buf=%p", p); } - if (c->send_buf) { void *p = c->send_buf; u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: do_close u_free send_buf=%p", p); } + if (c->recv_buf) { void *p = c->recv_buf; u_free(c->recv_buf); c->recv_buf = NULL; c->recv_buf_len = 0; c->recv_buf_cap = 0; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: do_close u_free recv_buf=%p", p); } + if (c->send_buf) { void *p = c->send_buf; u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: do_close u_free send_buf=%p", p); } stcp_pending_clear(c); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_conn closed is_server=%d prev_state=%d err=%d sock=%d", c->is_server, prev, err, (int)c->sock); - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: do_close sock=%d sock_id=%p recv_buf was=%p on_close=%p", + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: do_close sock=%d sock_id=%p recv_buf was=%p on_close=%p", (int)c->sock, (void*)c->socket_id, (void*)c->recv_buf, (void*)c->on_close); - if (c->on_close) { void (*cb)(struct stcp_conn*, int, void*) = c->on_close; c->on_close = NULL; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: do_close calling on_close=%p", (void*)cb); cb(c, err, c->close_arg); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_server: do_close on_close returned"); } + if (c->on_close) { void (*cb)(struct stcp_conn*, int, void*) = c->on_close; c->on_close = NULL; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: do_close calling on_close=%p", (void*)cb); cb(c, err, c->close_arg); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_server: do_close on_close returned"); } else if (prev == STCP_STATE_HS_SERVER_WAIT) { sc_stream_cleanup(&c->stream_send); sc_stream_cleanup(&c->stream_recv); if (c->allocated) uasync_call_soon(c->ua, c, stcp_conn_deferred_free); } } diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 85432d2a..1fbb0c00 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -22,6 +22,7 @@ #include "../../../src/utun_instance.h" #include "../../../src/transport_layer/etcp.h" #include "../../../src/transport_layer/etcp_connections.h" +#include "../../../src/transport_layer/auto_socket.h" #include "../../../src/transport_layer/secure_channel.h" #include #include @@ -1060,6 +1061,8 @@ char* utun_bridge_get_local_sockets_json(void) { json[0] = '['; int first = 1; + 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; while (ts) { @@ -1074,7 +1077,7 @@ char* utun_bridge_get_local_sockets_json(void) { port = ntohs(sin6->sin6_port); inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, sizeof(ip_str)); } - int state = active_conns > 0 ? 2 : 0; + int state = 2; /* bound and listening */ const char* sep = first ? "" : ","; first = 0; size_t needed = snprintf(NULL, 0, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"TCP\",\"st\":%d,\"lk\":%d}", sep, ts->sock_id, ts->name, ip_str, port, state, active_links); @@ -1101,7 +1104,7 @@ char* utun_bridge_get_local_sockets_json(void) { inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, sizeof(ip_str)); } int lk = es->links_queue ? queue_entry_count(es->links_queue) : 0; - int state = lk > 0 ? 2 : 0; + int state = 2; /* bound and listening */ const char* sep = first ? "" : ","; first = 0; size_t needed = snprintf(NULL, 0, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"UDP\",\"st\":%d,\"lk\":%d}", sep, es->sock_id, es->name, ip_str, port, state, lk); @@ -1117,6 +1120,12 @@ char* utun_bridge_get_local_sockets_json(void) { return json; } +void utun_bridge_on_network_change(void) { + if (!g_cc.inst) return; + auto_socket_on_network_change(g_cc.inst); + bridge_log(BLEV_DEBUG, "network change notified to auto_socket"); +} + void utun_bridge_restart(const char* config_text) { bridge_log(BLEV_INFO, "bridge restart: posting to instance"); #ifdef __ANDROID__ @@ -1914,4 +1923,12 @@ JNIEXPORT jstring JNICALL Java_com_utun_chat_data_NativeLib_nativeGetLocalSocket return result; } +/* ── Network change notification ── */ + +JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeOnNetworkChanged( + JNIEnv* env, jobject thiz) { + (void)env; (void)thiz; + utun_bridge_on_network_change(); +} + #endif /* __ANDROID__ */ diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.h b/tools/chatgui-android/jni_bridge/android_jni_bridge.h index ccbab7a5..7f6fa4bc 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.h +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.h @@ -129,6 +129,10 @@ char* utun_bridge_get_member_links_json(uint64_t node_id); char* utun_bridge_get_local_sockets_json(void); +/* ── Network change notification (ConnectivityManager → auto_socket) ── */ + +void utun_bridge_on_network_change(void); + #ifdef __cplusplus } #endif diff --git a/tools/chatgui/src/accountlist.cpp b/tools/chatgui/src/accountlist.cpp index 0e18107a..4753e48d 100644 --- a/tools/chatgui/src/accountlist.cpp +++ b/tools/chatgui/src/accountlist.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "../../../lib/platform_compat.h" extern "C" { @@ -16,6 +17,8 @@ extern "C" { #include "etcp.h" #include "etcp_connections.h" #include "../lib/socket_compat.h" + #include "../lib/ll_queue.h" + #include "stcp_link.h" } QString AccountList::addrStr(const void* addr, int family) { @@ -211,6 +214,30 @@ void AccountList::updateDetailDisplay() { struct ETCP_CONN* conn = nullptr; if (m_inst) conn = instance_find_conn(m_inst, m_detailNodeId); + bool isSelf = m_db ? (m_detailNodeId == m_db->myNodeId()) : false; + QSet matchedLinks; + + /* Count active TCP connections for own node (shared across all TCP sockets) */ + int ownTcpActiveLinks = 0; + if (isSelf && m_inst) { + if (m_inst->connections) { + for (struct ll_entry* e = m_inst->connections->head; e; e = e->next) { + struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; + if (ce && ce->conn && ce->conn->initialized) { + for (struct ETCP_LINK* lk = ce->conn->links; lk; lk = lk->next) { + if (lk->is_tcp) { ownTcpActiveLinks++; break; } + } + } + } + } + if (m_inst->tcp_connections) { + for (struct ll_entry* e = m_inst->tcp_connections->head; e; e = e->next) { + struct tcp_conn_entry* te = (struct tcp_conn_entry*)e->data; + if (te && te->etcp_conn && te->etcp_conn->initialized) ownTcpActiveLinks++; + } + } + } + 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; } @@ -231,6 +258,7 @@ void AccountList::updateDetailDisplay() { } if (lfm == a.family && lpo == (uint16_t)a.port && lip == addrStr(a.addr.constData(), a.family)) { linkColor = (lk->link_state == 3 && lk->link_status == 1) ? "#4CAF50" : "#E53935"; + matchedLinks.insert(lk); break; } } @@ -243,8 +271,51 @@ void AccountList::updateDetailDisplay() { } } } - lines.append(QString("● %2:%3:%4") - .arg(linkColor, proto, addrStr(a.addr.constData(), a.family)).arg(a.port)); + + QString prefix; + if (isSelf && m_inst) { + int linkCount = 0; + if (a.protocol == 1) { + for (struct ETCP_SOCKET* sock = m_inst->etcp_sockets; sock; sock = sock->next) { + uint16_t sp = 0; + const struct sockaddr_storage* addr = sock->interface_addr.ss_family ? &sock->interface_addr : &sock->local_addr; + if (addr->ss_family == AF_INET) sp = ntohs(((const struct sockaddr_in*)addr)->sin_port); + else if (addr->ss_family == AF_INET6) sp = ntohs(((const struct sockaddr_in6*)addr)->sin6_port); + if (sp == (uint16_t)a.port) { + linkCount = sock->links_queue ? queue_entry_count(sock->links_queue) : 0; + break; + } + } + } else if (a.protocol == 2) { + linkCount = ownTcpActiveLinks; + } + if (linkCount > 0) prefix = QString("[%1] ").arg(linkCount); + } + + lines.append(QString("%1● %3:%4:%5") + .arg(prefix, linkColor, proto, addrStr(a.addr.constData(), a.family)).arg(a.port)); + } + + /* Show ETCP links that are not in DB (non-self) */ + if (conn) { + for (struct ETCP_LINK* lk = conn->links; lk; lk = lk->next) { + if (matchedLinks.contains(lk)) continue; + const struct sockaddr_storage* sa = &lk->remote_addr; + char ipbuf[INET6_ADDRSTRLEN]; QString lip; uint16_t lpo = 0; + if (sa->ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)sa; + inet_ntop(AF_INET, &sin->sin_addr, ipbuf, sizeof(ipbuf)); + lpo = ntohs(sin->sin_port); lip = QString::fromLocal8Bit(ipbuf); + } else if (sa->ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; + inet_ntop(AF_INET6, &sin6->sin6_addr, ipbuf, sizeof(ipbuf)); + lpo = ntohs(sin6->sin6_port); lip = QString::fromLocal8Bit(ipbuf); + } else { continue; } + QString proto = lk->is_tcp ? "TCP" : "UDP"; + QString lc = (lk->link_state == 3 && lk->link_status == 1) ? "#4CAF50" : "#E53935"; + lines.append(QString("● %2:%3:%4 [link]") + .arg(lc, proto, lip).arg(lpo)); + } } m_detailText->setHtml(lines.join("
"));