Browse Source

stcp: separate v4/v6 sockets, protocol byte in chat addr format, error on bind fail

- stcp_server_create: support AF_INET and AF_INET6 V6ONLY=1 (no dual-stack)
- Add listen_family to stcp_link_config
- init_sockets: return error (not partial) on any bind failure
- add chat_core_sync_my_addresses() call at chatgui startup
- collect_my_addrs: add proto byte (1=UDP, 2=TCP) to binary format
- chat_sync.c JOIN/WELCOME/PEER_UPSERT: read proto byte, write protocol column
- member_sync.c: read proto byte, dynamic protocol bind
- topo_node.c: debug logs for nodeinfo changed detection
- node_config.cpp: type=public for all servers
topo_upd
evgeny 2 months ago
parent
commit
8023f6ee70
  1. 16
      src/chat/chat_channel.c
  2. 5
      src/chat/chat_event.c
  3. 3
      src/chat/chat_event.h
  4. 43
      src/chat/chat_sync.c
  5. 16
      src/chat/member_sync.c
  6. 10
      src/routing_layer/topo_node.c
  7. 32
      src/transport_layer/etcp_api.c
  8. 15
      src/transport_layer/etcp_api.h
  9. 3
      src/transport_layer/etcp_connections.c
  10. 2
      src/transport_layer/stcp_link.c
  11. 1
      src/transport_layer/stcp_link.h
  12. 42
      src/transport_layer/stcp_server.c
  13. 3
      src/transport_layer/stcp_server.h
  14. 7
      src/utun_instance.c
  15. 12
      tests/test_stcp.c
  16. 1
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt
  17. 7
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt
  18. 7
      tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt
  19. 18
      tools/chatgui-android/libutun_lite/instance_lite.c
  20. 1
      tools/chatgui/transport/utun_node.cpp

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

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

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

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

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

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

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

15
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

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

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

1
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 ======

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

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

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

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

1
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,

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

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

18
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 <pthread.h>
#include <stdio.h>
@ -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");

1
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");

Loading…
Cancel
Save