diff --git a/src/chat/chat_channel.c b/src/chat/chat_channel.c index 39c61cf7..d88af7e5 100644 --- a/src/chat/chat_channel.c +++ b/src/chat/chat_channel.c @@ -62,8 +62,8 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) { if (!sa) sa = s->local_addr.ss_family ? &s->local_addr : NULL; if (sa && sa->ss_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)sa; - if (pos + 8 > max_buf_sz) break; - buf[pos++] = 4; buf[pos++] = s->sock_id; + if (pos + 9 > max_buf_sz) break; + buf[pos++] = 4; buf[pos++] = s->sock_id; buf[pos++] = 1; // proto=1 (UDP) memcpy(buf + pos, &sin->sin_addr, 4); pos += 4; uint16_t port = ntohs(sin->sin_port); buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); @@ -73,8 +73,8 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) { CC_ID, s->sock_id, ip[0], ip[1], ip[2], ip[3], port); } else if (sa && sa->ss_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; - if (pos + 20 > max_buf_sz) break; - buf[pos++] = 6; buf[pos++] = s->sock_id; + if (pos + 21 > max_buf_sz) break; + buf[pos++] = 6; buf[pos++] = s->sock_id; buf[pos++] = 1; // proto=1 (UDP) memcpy(buf + pos, &sin6->sin6_addr, 16); pos += 16; uint16_t port = ntohs(sin6->sin6_port); buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); @@ -90,8 +90,8 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) { struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr; if (addr && addr->ss_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)addr; - if (pos + 8 > max_buf_sz) break; - buf[pos++] = 4; buf[pos++] = ts->sock_id; + if (pos + 9 > max_buf_sz) break; + buf[pos++] = 4; buf[pos++] = ts->sock_id; buf[pos++] = 2; // proto=2 (TCP) memcpy(buf + pos, &sin->sin_addr, 4); pos += 4; uint16_t port = ntohs(sin->sin_port); buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); @@ -101,8 +101,8 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) { CC_ID, ts->sock_id, ip[0], ip[1], ip[2], ip[3], port); } else if (addr && addr->ss_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; - if (pos + 20 > max_buf_sz) break; - buf[pos++] = 6; buf[pos++] = ts->sock_id; + if (pos + 21 > max_buf_sz) break; + buf[pos++] = 6; buf[pos++] = ts->sock_id; buf[pos++] = 2; // proto=2 (TCP) memcpy(buf + pos, &sin6->sin6_addr, 16); pos += 16; uint16_t port = ntohs(sin6->sin6_port); buf[pos++] = (uint8_t)(port >> 8); buf[pos++] = (uint8_t)(port & 0xFF); diff --git a/src/chat/chat_event.c b/src/chat/chat_event.c index 2b3803ca..e2d963aa 100644 --- a/src/chat/chat_event.c +++ b/src/chat/chat_event.c @@ -22,8 +22,11 @@ void chat_event_post(int type, const uint8_t* data, int len) { [4]="CHANNEL_UPDATED", [5]="MEMBERS_CHANGED", [6]="MY_NODE_ID", [7]="AUTO_CONNECT_STATUS", [8]="CHANNEL_PEERS_ONLINE", [9]="DB_READY", [10]="STATUS_REFRESH", [11]="KEYS_GENERATED", + [12]="SERVICE_STARTED", [13]="SERVICE_STOPPED", + [14]="ATTACHMENT_DOWNLOADED", [15]="DOWNLOAD_PROGRESS", + [16]="NODEINFO_UPDATED", [17]="LINK_STATUS_CHANGED", }; - const char* n = (type >= 1 && type <= 11) ? names[type] : "?"; + const char* n = (type >= 1 && type <= 17) ? names[type] : "?"; DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "chat_event: %s(%d) data=%d bytes", n, type, len); if (data && len > 0) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DEBUG, " ", data, len); } diff --git a/src/chat/chat_event.h b/src/chat/chat_event.h index 30713d55..9e299590 100644 --- a/src/chat/chat_event.h +++ b/src/chat/chat_event.h @@ -34,7 +34,8 @@ extern "C" { #define CHAT_EVT_SERVICE_STOPPED 13 /* data: none */ #define CHAT_EVT_ATTACHMENT_DOWNLOADED 14 /* [ch_id_len:1][ch_id:var][msg_id:8] */ #define CHAT_EVT_DOWNLOAD_PROGRESS 15 /* [ch_id_len:1][ch_id:var][msg_id:8][blocks_done:4][num_blocks:4] */ -#define CHAT_EVT_NODEINFO_UPDATED 16 /* [group_id:8][node_id:8][conn_presence:1][conn_up:1][best_rtt:2] */ +#define CHAT_EVT_NODEINFO_UPDATED 16 /* [node_id:8][conn_presence:1][conn_up:1][best_rtt:2] */ +#define CHAT_EVT_LINK_STATUS_CHANGED 17 /* [peer_id:8][link_id:1][state:1][status:1][links_up:1][initialized:1] */ typedef void (*chat_event_handler_fn)(int type, const uint8_t* data, int len); diff --git a/src/chat/chat_sync.c b/src/chat/chat_sync.c index ad284b75..ad72bf73 100644 --- a/src/chat/chat_sync.c +++ b/src/chat/chat_sync.c @@ -967,7 +967,7 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, struct sockaddr_in* sin = (struct sockaddr_in*)&lk->remote_addr; uint8_t ip[4]; memcpy(ip, &sin->sin_addr, 4); uint16_t port = ntohs(sin->sin_port); char sql[256]; snprintf(sql, sizeof(sql), - "INSERT OR REPLACE INTO node_addresses(node_id,family,address,port,addr_type,socket_id) VALUES(?,4,?,?,0,?)"); + "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id) VALUES(?,4,1,?,?,0,?)"); sqlite3_stmt* as = NULL; if (sqlite3_prepare_v2(vdb, sql, -1, &as, NULL) == SQLITE_OK) { sqlite3_bind_int64(as, 1, (sqlite3_int64)peer); @@ -1108,14 +1108,15 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer, for (uint8_t i = 0; i < addr_cnt && p + 2 <= pl + len; i++) { uint8_t fm = *p++; uint8_t sid = *p++; + uint8_t proto = *p++; int ip_len = (fm == 4) ? 4 : 16; if (p + ip_len + 2 > pl + len) break; const uint8_t* ip = p; p += ip_len; uint16_t port = ((uint16_t)p[0] << 8) | p[1]; p += 2; char sql[256]; snprintf(sql, sizeof(sql), - "INSERT OR REPLACE INTO node_addresses(node_id,family,address,port,addr_type,socket_id)" - " VALUES(?,?,?,?,0,?)"); + "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" + " VALUES(?,?,?,?,?,0,?)"); sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) { sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); @@ -1268,23 +1269,25 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer, if (p + 2 > pl + len) break; uint8_t fm = *p++; uint8_t sid = *p++; + uint8_t proto = *p++; int ip_len = (fm == 4) ? 4 : 16; if (p + ip_len + 2 > pl + len) break; const uint8_t* ip = p; p += ip_len; uint16_t port = ((uint16_t)p[0] << 8) | p[1]; p += 2; char sql[256]; snprintf(sql, sizeof(sql), - "INSERT OR REPLACE INTO node_addresses(node_id,family,address,port,addr_type,socket_id)" - " VALUES(?,?,?,?,0,?)"); - sqlite3_stmt* stmt = NULL; - if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) { - sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); - sqlite3_bind_int(stmt, 2, fm); - sqlite3_bind_blob(stmt, 3, ip, ip_len, SQLITE_STATIC); - sqlite3_bind_int(stmt, 4, (int)port); - sqlite3_bind_int(stmt, 5, (int)sid); - sqlite3_step(stmt); sqlite3_finalize(stmt); - } + "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" + " VALUES(?,?,?,?,?,0,?)"); + sqlite3_stmt* stmt = NULL; + if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) { + sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); + sqlite3_bind_int(stmt, 2, fm); + sqlite3_bind_int(stmt, 3, (int)proto); + sqlite3_bind_blob(stmt, 4, ip, ip_len, SQLITE_STATIC); + sqlite3_bind_int(stmt, 5, (int)port); + sqlite3_bind_int(stmt, 6, (int)sid); + sqlite3_step(stmt); sqlite3_finalize(stmt); + } if (fm == 4) DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] WELCOME INSERT node=0x%016llx sock=%d %d.%d.%d.%d:%d", CS_ID, (unsigned long long)node_id, sid, ip[0], ip[1], ip[2], ip[3], port); @@ -1380,21 +1383,23 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer, if (p + 2 > pl + len) break; uint8_t fm = *p++; uint8_t sid = *p++; + uint8_t proto = *p++; int ip_len = (fm == 4) ? 4 : 16; if (p + ip_len + 2 > pl + len) break; const uint8_t* ip = p; p += ip_len; uint16_t port = ((uint16_t)p[0] << 8) | p[1]; p += 2; char sql[256]; snprintf(sql, sizeof(sql), - "INSERT OR REPLACE INTO node_addresses(node_id,family,address,port,addr_type,socket_id)" - " VALUES(?,?,?,?,0,?)"); + "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" + " VALUES(?,?,?,?,?,0,?)"); sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) { sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); sqlite3_bind_int(stmt, 2, fm); - sqlite3_bind_blob(stmt, 3, ip, ip_len, SQLITE_STATIC); - sqlite3_bind_int(stmt, 4, (int)port); - sqlite3_bind_int(stmt, 5, (int)sid); + sqlite3_bind_int(stmt, 3, (int)proto); + sqlite3_bind_blob(stmt, 4, ip, ip_len, SQLITE_STATIC); + sqlite3_bind_int(stmt, 5, (int)port); + sqlite3_bind_int(stmt, 6, (int)sid); sqlite3_step(stmt); sqlite3_finalize(stmt); } if (fm == 4) diff --git a/src/chat/member_sync.c b/src/chat/member_sync.c index c8afa237..906dcf19 100644 --- a/src/chat/member_sync.c +++ b/src/chat/member_sync.c @@ -532,26 +532,28 @@ int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id, sqlite3_stmt* as = NULL; sqlite3_prepare_v2(db, "INSERT INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" - " VALUES(?,?,1,?,?,0,?)", -1, &as, NULL); + " VALUES(?,?,?,?,?,0,?)", -1, &as, NULL); if (as) { const uint8_t* p = addrs_data; int written = 0; for (int i = 0; i < addr_count; i++) { uint8_t fam = *p++; uint8_t sid = *p++; + uint8_t proto = *p++; int ip_sz = fam == 4 ? 4 : 16; sqlite3_bind_int64(as, 1, (sqlite3_int64)member_id); sqlite3_bind_int(as, 2, fam); - sqlite3_bind_blob(as, 3, p, ip_sz, SQLITE_STATIC); + sqlite3_bind_int(as, 3, (int)proto); + sqlite3_bind_blob(as, 4, p, ip_sz, SQLITE_STATIC); p += ip_sz; uint16_t port = ((uint16_t)p[0] << 8) | p[1]; p += 2; - sqlite3_bind_int(as, 4, port); - sqlite3_bind_int(as, 5, (int)sid); + sqlite3_bind_int(as, 5, port); + sqlite3_bind_int(as, 6, (int)sid); sqlite3_step(as); sqlite3_reset(as); if (fam == 4) { - const uint8_t* ip = p - 6; /* p advanced by ip_sz(4) + port(2) */ - DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] member_sync_put INSERT node=0x%016llx sock=%d %d.%d.%d.%d:%d", - MS_ID, (unsigned long long)member_id, sid, ip[0], ip[1], ip[2], ip[3], port); + const uint8_t* ip = p - 7; /* p advanced by ip_sz(4) + port(2) - back to start of ip */ + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] member_sync_put INSERT node=0x%016llx proto=%d sock=%d %d.%d.%d.%d:%d", + MS_ID, (unsigned long long)member_id, proto, sid, ip[0], ip[1], ip[2], ip[3], port); } else { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] member_sync_put INSERT v6 node=0x%016llx sock=%d port=%d", MS_ID, (unsigned long long)member_id, sid, port); diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 3297b84c..f76a2de0 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -618,6 +618,9 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR else if (ts->interface_addr.ss_family == AF_INET6 || ts->local_addr.ss_family == AF_INET6) { tcp6_count++; addr6_count++; } ts = ts->next; } } + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "counts: udp_v4s=%d udp_v4a=%d udp_v6s=%d udp_v6a=%d tcp4=%d tcp6=%d v4a_total=%d v6a_total=%d", + sock_count, addr_count - tcp4_count, sock6_count, addr6_count - tcp6_count, + tcp4_count, tcp6_count, addr_count, addr6_count); int changed = 1; uint8_t old_ver = 0; if (group->local_node) { @@ -646,6 +649,13 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR if (memcmp(oni->ed25519_public_key, group->ed25519_public_key, SC_PUBKEY_SIZE) != 0) changed = 1; } } + if (!changed && group->local_node) { + struct TOPO_NODE* oni2 = topo_node_registry_find(instance->topo_groups, group->local_node->node_id); + int old_v4a = oni2 ? topo_list_count((struct _topo_head*)oni2->v4_addrs) : -1; + int old_v6a = oni2 ? topo_list_count((struct _topo_head*)oni2->v6_addrs) : -1; + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "nodeinfo unchanged: old_v4a=%d new_v4a=%d old_v6a=%d new_v6a=%d tcp4=%d tcp6=%d", + old_v4a, addr_count, old_v6a, addr6_count, tcp4_count, tcp6_count); + } if (changed) { if (group->local_node) { diff --git a/src/transport_layer/etcp_api.c b/src/transport_layer/etcp_api.c index e36c3da0..61667416 100644 --- a/src/transport_layer/etcp_api.c +++ b/src/transport_layer/etcp_api.c @@ -4,6 +4,7 @@ */ #include "etcp_api.h" #include "etcp.h" +#include "etcp_connections.h" #include "pkt_normalizer.h" #include "utun_instance.h" #include "../lib/debug_config.h" @@ -62,6 +63,37 @@ static void etcp_inst_cbk_remove_chain(struct etcp_inst_cbk_entry** head, etcp_i void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg) { if (inst) etcp_inst_cbk_add_chain(&inst->new_conn_cbks, fn, arg); } void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg) { if (inst) etcp_inst_cbk_remove_chain(&inst->new_conn_cbks, fn, arg); } +// Link status callback chain +static void etcp_link_status_cbk_add_chain(struct etcp_link_status_cbk_entry** head, etcp_link_status_cbk_fn fn, void* arg) { + if (!head || !fn) return; + struct etcp_link_status_cbk_entry* e = u_malloc(sizeof(struct etcp_link_status_cbk_entry)); + if (!e) return; + e->fn = fn; e->arg = arg; e->next = *head; + *head = e; +} +static void etcp_link_status_cbk_remove_chain(struct etcp_link_status_cbk_entry** head, etcp_link_status_cbk_fn fn, void* arg) { + if (!head || !fn) return; + struct etcp_link_status_cbk_entry** p = head; + while (*p) { + if ((*p)->fn == fn && (*p)->arg == arg) { + struct etcp_link_status_cbk_entry* rm = *p; + *p = rm->next; u_free(rm); return; + } + p = &(*p)->next; + } +} +void etcp_add_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg) { if (inst) etcp_link_status_cbk_add_chain(&inst->link_status_cbks, fn, arg); } +void etcp_remove_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg) { if (inst) etcp_link_status_cbk_remove_chain(&inst->link_status_cbks, fn, arg); } + +void etcp_fire_link_status_cbk(struct ETCP_LINK* link, int old_state, int old_status) { + if (!link || !link->etcp || !link->etcp->instance) return; + struct etcp_link_status_cbk_entry* cbe = link->etcp->instance->link_status_cbks; + while (cbe) { + cbe->fn(link->etcp, link, old_state, old_status, cbe->arg); + cbe = cbe->next; + } +} + static void etcp_status_cbk_add_chain(struct etcp_status_cbk_entry** head, etcp_conn_status_fn fn, void* arg) { if (!head || !fn) return; struct etcp_status_cbk_entry* e = u_malloc(sizeof(struct etcp_status_cbk_entry)); diff --git a/src/transport_layer/etcp_api.h b/src/transport_layer/etcp_api.h index 56f7b482..ac0cc793 100644 --- a/src/transport_layer/etcp_api.h +++ b/src/transport_layer/etcp_api.h @@ -54,6 +54,7 @@ extern "C" { // Forward declarations struct ETCP_CONN; +struct ETCP_LINK; struct ETCP_SOCKET; struct UTUN_INSTANCE; @@ -133,6 +134,20 @@ void etcp_conn_update_cbk_mask(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg); void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_inst_cbk_fn fn, void* arg); +// ---- Link status change callback (instance-level) ---- +typedef void (*etcp_link_status_cbk_fn)(struct ETCP_CONN* conn, struct ETCP_LINK* link, + int old_state, int old_status, void* arg); + +struct etcp_link_status_cbk_entry { + etcp_link_status_cbk_fn fn; + void* arg; + struct etcp_link_status_cbk_entry* next; +}; + +void etcp_add_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg); +void etcp_remove_link_status_cbk(struct UTUN_INSTANCE* inst, etcp_link_status_cbk_fn fn, void* arg); +void etcp_fire_link_status_cbk(struct ETCP_LINK* link, int old_state, int old_status); + // ---- Background connection initialization ---- #define ETCP_CONNECT_EARLY 1 #define ETCP_CONNECT_LATE 2 diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index f0a39551..f46e99eb 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -2190,7 +2190,8 @@ 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 stcp_link_config scfg = {.ua = instance->ua, .my_keys = &instance->my_keys, .inst = instance}; + 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); if (!tsrv) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create TCP server for %s", server->name); diff --git a/src/transport_layer/stcp_link.c b/src/transport_layer/stcp_link.c index ba1fbc6d..b6baf1f7 100644 --- a/src/transport_layer/stcp_link.c +++ b/src/transport_layer/stcp_link.c @@ -120,7 +120,7 @@ struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t po ss->cfg = *cfg; ss->on_link = on_link; ss->on_link_arg = arg; - ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss, NULL, NULL); + ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss, NULL, NULL, cfg->listen_family); if (!ss->srv) { u_free(ss); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "port=%u", port); return ss; diff --git a/src/transport_layer/stcp_link.h b/src/transport_layer/stcp_link.h index 0e8aeb45..7e70f55c 100644 --- a/src/transport_layer/stcp_link.h +++ b/src/transport_layer/stcp_link.h @@ -27,6 +27,7 @@ struct stcp_link_config { int peer_pubkey_mode; // 0=binary, 1=hex const struct sockaddr_storage *remote_addr; // адрес пира (клиент) uint16_t remote_port; // порт пира (клиент) + int listen_family; // AF_INET или AF_INET6 для сервера (0 = v4) }; // ====== TCP server ====== diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index 7095885a..2799e80b 100644 --- a/src/transport_layer/stcp_server.c +++ b/src/transport_layer/stcp_server.c @@ -345,7 +345,8 @@ static void server_accept_cb(socket_t listen_sock, void *arg) { struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, struct SC_MYKEYS *keys, stcp_connect_cb connect_cb, void *arg, - stcp_close_cb close_cb, void *close_arg) { + stcp_close_cb close_cb, void *close_arg, + int family) { if (!ua || !keys || !connect_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid args"); return NULL; } struct stcp_server *srv = u_calloc(1, sizeof(struct stcp_server)); if (!srv) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "calloc failed"); return NULL; } @@ -356,28 +357,43 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, srv->close_cb = close_cb; srv->close_arg = close_arg; - srv->listen_sock = socket(AF_INET6, SOCK_STREAM, 0); + int use_family = (family == AF_INET6) ? AF_INET6 : AF_INET; + srv->listen_sock = socket(use_family, SOCK_STREAM, 0); if (srv->listen_sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socket failed"); u_free(srv); return NULL; } socket_set_nonblocking(srv->listen_sock); int reuse = 1; setsockopt(srv->listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)); - int v6only = 0; - setsockopt(srv->listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&v6only, sizeof(v6only)); - struct sockaddr_in6 addr; - memset(&addr, 0, sizeof(addr)); - addr.sin6_family = AF_INET6; - addr.sin6_addr = in6addr_any; - addr.sin6_port = htons(port); - if (bind(srv->listen_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "bind port=%u failed err=%d", port, socket_get_error()); - socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL; + if (use_family == AF_INET6) { + int v6only = 1; + setsockopt(srv->listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&v6only, sizeof(v6only)); + } + if (use_family == AF_INET) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + if (bind(srv->listen_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "bind port=%u (v4) failed err=%d", port, socket_get_error()); + socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL; + } + } else { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = htons(port); + if (bind(srv->listen_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "bind port=%u (v6) failed err=%d", port, socket_get_error()); + socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL; + } } if (listen(srv->listen_sock, 16) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "listen failed err=%d", socket_get_error()); socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL; } srv->listen_id = uasync_add_socket_t(ua, srv->listen_sock, server_accept_cb, NULL, NULL, srv); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server: listening on port %u", port); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server: listening on port %u family=%s", port, use_family == AF_INET ? "v4" : "v6"); return srv; } diff --git a/src/transport_layer/stcp_server.h b/src/transport_layer/stcp_server.h index 8b1d076a..c34e7acc 100644 --- a/src/transport_layer/stcp_server.h +++ b/src/transport_layer/stcp_server.h @@ -15,7 +15,8 @@ typedef void (*stcp_close_cb)(struct stcp_conn *conn, int err, void *arg); struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, struct SC_MYKEYS *keys, stcp_connect_cb connect_cb, void *arg, - stcp_close_cb close_cb, void *close_arg); + stcp_close_cb close_cb, void *close_arg, + int family); void stcp_server_destroy(struct stcp_server *srv); diff --git a/src/utun_instance.c b/src/utun_instance.c index 232f42b9..6cf7bc99 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -193,13 +193,10 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u } int socket_result = init_sockets(instance); instance->socket_init_status = socket_result; - if (socket_result < 0) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets"); + if (socket_result != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Socket initialization failed: result=%d", socket_result); return -1; } - if (socket_result == 1) { - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Partial socket initialization - some servers failed to bind"); - } instance->nat_det = nat_detection_create(instance); if (!instance->nat_det) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT detection creation failed"); } diff --git a/tests/test_stcp.c b/tests/test_stcp.c index 69442ad9..f02451c0 100644 --- a/tests/test_stcp.c +++ b/tests/test_stcp.c @@ -100,7 +100,7 @@ static int test1_sizes(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 1; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); size_t sizes[] = {0, 1, 16, 17, 255, 256, 1000, 65535}; @@ -140,7 +140,7 @@ static int test2_many(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 2; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int sent = 0, ticks = 0; @@ -178,7 +178,7 @@ static int test3_wrong_key(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 3; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); struct SC_MYKEYS rogue; TASSERT(sc_generate_keypair(&rogue) == SC_OK); @@ -206,7 +206,7 @@ static int test4_close(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 4; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int closed = 0, ticks = 0; @@ -253,7 +253,7 @@ static int test5_multi(void) { memset(srvp, 0, sizeof(srvp)); memset(clip, 0, sizeof(clip)); g_multi_peers = srvp; g_multi_idx = 0; g_multi_max = NCLI; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, multi_connect_cb, NULL, NULL, NULL); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, multi_connect_cb, NULL, NULL, NULL, AF_INET); TASSERT(ss); struct stcp_client *clients[NCLI] = {0}; for (int i = 0; i < NCLI; i++) { @@ -305,7 +305,7 @@ static int test6_interleaved(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 6; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int round = 0, ticks = 0; diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt index 588b3a3a..7db80c1a 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt @@ -7,6 +7,7 @@ import java.nio.ByteBuffer import java.nio.ByteOrder const val EVT_NODEINFO_UPDATED = 16 +const val EVT_LINK_STATUS_CHANGED = 17 data class NodeStatus( val nodeId: Long = 0, diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt index 21d5e7e5..c401377a 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt @@ -30,8 +30,6 @@ import com.utun.chat.data.MemberAddr import com.utun.chat.data.MemberLinks import com.utun.chat.data.LinkStatus import com.utun.chat.viewmodel.ChatViewModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -52,10 +50,7 @@ fun MemberListScreen( LaunchedEffect(selectedMember?.nodeId) { val nodeId = selectedMember?.nodeId if (nodeId == null) { viewModel.refreshMemberLinks(0); return@LaunchedEffect } - while (isActive) { - viewModel.refreshMemberLinks(nodeId) - delay(1000) - } + viewModel.refreshMemberLinks(nodeId) } Column(modifier = Modifier.fillMaxSize().navigationBarsPadding()) { diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt index 37e3b71b..2cb63ce8 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt @@ -196,6 +196,13 @@ class ChatViewModel : ViewModel() { val cur = _currentChannel.value if (cur != null) refreshMembers(cur.id) } + 17 -> { /* LINK_STATUS_CHANGED: [peer_id:8][link_id:1][state:1][status:1][links_up:1][initialized:1] */ + if (data == null || data.size < 13) return + val buf = java.nio.ByteBuffer.wrap(data).order(java.nio.ByteOrder.LITTLE_ENDIAN) + val nodeId = buf.getLong() + val detail = _selectedMember.value + if (detail != null && detail.nodeId == nodeId) refreshMemberLinks(nodeId) + } } } diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index c3a821fa..fa9da598 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/tools/chatgui-android/libutun_lite/instance_lite.c @@ -23,6 +23,7 @@ #include "mem.h" #include "debug_config.h" #include "transport_layer/etcp_connections.h" +#include "transport_layer/etcp.h" #include "../jni_bridge/android_udp_log.h" #include #include @@ -151,6 +152,20 @@ static void nodeinfo_event_cb(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* g_event_handler(CHAT_EVT_NODEINFO_UPDATED, data, sizeof(data)); } +static void on_link_status_changed(struct ETCP_CONN* conn, struct ETCP_LINK* link, + int old_state, int old_status, void* arg) { + (void)old_state; (void)old_status; (void)arg; + if (!g_event_handler || !conn || conn->peer_node_id == 0) return; + uint8_t data[13]; + memcpy(data, &conn->peer_node_id, 8); + data[8] = link->local_link_id; + data[9] = link->link_state; + data[10] = link->link_status; + data[11] = conn->links_up; + data[12] = conn->initialized ? 1 : 0; + g_event_handler(CHAT_EVT_LINK_STATUS_CHANGED, data, sizeof(data)); +} + /* ── Crash handler ── */ static volatile int g_signal_caught = 0; @@ -314,7 +329,8 @@ static void* instance_thread(void* arg) { chat_core_sync_my_addresses(); if (g_inst->config->global.name[0]) chat_core_update_my_name(g_inst->config->global.name); etcp_router_bind(g_inst, ETCP_RT_ID_CHAT_SYNC, NULL); - utun_add_nodeinfo_cbk(g_inst, nodeinfo_event_cb, NULL); + utun_add_nodeinfo_cbk(g_inst, nodeinfo_event_cb, NULL); + etcp_add_link_status_cbk(g_inst, on_link_status_changed, NULL); chat_event_post(CHAT_EVT_KEYS_GENERATED, (const uint8_t*)config->global.my_public_key_hex, 64); uasync_set_timeout(g_ua, 100000, NULL, heartbeat_cb, "hb"); diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp index 260aaf89..0196e096 100644 --- a/tools/chatgui/transport/utun_node.cpp +++ b/tools/chatgui/transport/utun_node.cpp @@ -396,6 +396,7 @@ void UtunNode::runLoop() { /* Initialize chat_core (DB) and chat_sync (channel/message P2P sync) */ chat_core_init(m_instance, QString(m_dbPath + "/chats.db").toUtf8().constData()); chat_sync_init(m_instance); + chat_core_sync_my_addresses(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chat_core + chat_sync initialized"); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "utun_node: entering poll loop");