|
|
|
|
@ -17,6 +17,7 @@
|
|
|
|
|
#include "../utun_instance.h" |
|
|
|
|
#include "../transport_layer/etcp_api.h" |
|
|
|
|
#include "../transport_layer/etcp.h" |
|
|
|
|
#include "../transport_layer/node_conn_direct.h" |
|
|
|
|
#include "../transport_layer/secure_channel.h" |
|
|
|
|
#include "../ntp_time.h" |
|
|
|
|
#include "../../lib/u_async.h" |
|
|
|
|
@ -73,6 +74,8 @@ struct chat_sync {
|
|
|
|
|
uint8_t pending_invite_is_inviter; /* 1 = inviter sends CHANNEL_INVITE on conn_up, 0 = joiner sends JOIN_INFO_REQ */ |
|
|
|
|
char pending_password[128]; /* пароль для JOIN_INFO_REQ */ |
|
|
|
|
uint64_t pending_join_key; /* join-ключ из invite-ссылки (джойнер) */ |
|
|
|
|
struct NODE_CONN_DIRECT* join_handle; /* транспорт протокола добавления, не членство */ |
|
|
|
|
void* join_transport_timer; |
|
|
|
|
struct join_pending* join_pending; /* connection-узел: ожидающие ready джойнеры */ |
|
|
|
|
/* Pending incoming invites (для ask/deny) */ |
|
|
|
|
uint64_t pending_inv_in_ch_id[4]; |
|
|
|
|
@ -87,6 +90,47 @@ struct chat_sync {
|
|
|
|
|
#define CS_INVITE_SEND_TIMEOUT_MS 3000 /* inviter waits for joiner to respond */ |
|
|
|
|
#define CS_SYNC_INTERVAL_MS 1000 |
|
|
|
|
|
|
|
|
|
static void cs_info_req_timeout_cb(void* arg); |
|
|
|
|
|
|
|
|
|
static void cs_join_connect_timeout(void* arg) { |
|
|
|
|
struct chat_sync* cs = arg; |
|
|
|
|
cs->join_transport_timer = NULL; |
|
|
|
|
cs_info_req_timeout_cb(cs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void cs_join_release_transport(struct chat_sync* cs) { |
|
|
|
|
if (cs->join_transport_timer) { |
|
|
|
|
uasync_cancel_timeout(cs->inst->ua, cs->join_transport_timer); cs->join_transport_timer = NULL; |
|
|
|
|
} |
|
|
|
|
struct NODE_CONN_DIRECT* handle = cs->join_handle; |
|
|
|
|
cs->join_handle = NULL; |
|
|
|
|
if (handle) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEMBER_SYNC, "join transport release peer=%016llx", |
|
|
|
|
(unsigned long long)node_conn_direct_node_id(handle)); |
|
|
|
|
node_conn_direct_close(handle); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int cs_join_hold_transport(struct chat_sync* cs, uint64_t node_id, struct TOPO_NODE* ni) { |
|
|
|
|
struct NODE_CONN_DIRECT* handle = NULL; |
|
|
|
|
int rc = ni ? node_conn_direct_open_node(cs->inst, node_id, NULL, NULL, &handle, ni, NULL) |
|
|
|
|
: node_conn_direct_open(cs->inst, node_id, NULL, NULL, &handle, NULL); |
|
|
|
|
if (rc == NCD_ERR) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "cannot acquire join transport peer=%016llx", (unsigned long long)node_id); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
cs->join_handle = handle; |
|
|
|
|
cs->join_transport_timer = uasync_set_timeout(cs->inst->ua, CS_INFO_REQ_TIMEOUT_MS * 10, |
|
|
|
|
cs, cs_join_connect_timeout, "join_transport_timeout"); |
|
|
|
|
if (!cs->join_transport_timer) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "cannot arm join transport timeout"); |
|
|
|
|
cs_join_release_transport(cs); return -1; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "join owns transport peer=%016llx", (unsigned long long)node_id); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const char* cs_msg_name(uint8_t type) { |
|
|
|
|
switch (type) { |
|
|
|
|
case CS_MSG_INIT_SYNC: return "INIT_SYNC"; |
|
|
|
|
@ -229,6 +273,13 @@ static void chat_sync_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: RECV %s from=%016llx ch=%s len=%zu", |
|
|
|
|
CS_ID, cs_msg_name(type), (unsigned long long)peer, ch_id, plen); |
|
|
|
|
|
|
|
|
|
if ((type == CS_MSG_JOIN_INFO_RESP || type == CS_MSG_JOIN_READY || type == CS_MSG_ERROR) && |
|
|
|
|
(cs->pending_invite_node_id != peer || cs->pending_invite_ch_id != group_id)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "ignore stale join response peer=%016llx group=%016llx type=%u", |
|
|
|
|
(unsigned long long)peer, (unsigned long long)group_id, type); |
|
|
|
|
u_free(entry->dgram); queue_entry_free(entry); return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (type) { |
|
|
|
|
case CS_MSG_JOIN_INFO_REQ: DEBUG_TRACE(DEBUG_CATEGORY_MEMBER_SYNC, "%s: → handle JOIN_INFO_REQ", CS_ID); cs_handle_join_info_req(cs, peer, ch_id, pl, plen); break; |
|
|
|
|
case CS_MSG_JOIN_INFO_RESP: DEBUG_TRACE(DEBUG_CATEGORY_MEMBER_SYNC, "%s: → handle JOIN_INFO_RESP", CS_ID); cs_handle_join_info_resp(cs, peer, ch_id, pl, plen); break; |
|
|
|
|
@ -236,10 +287,11 @@ static void chat_sync_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
|
|
|
|
case CS_MSG_JOIN_READY: DEBUG_TRACE(DEBUG_CATEGORY_MEMBER_SYNC, "%s: → handle JOIN_READY", CS_ID); cs_handle_join_ready(cs, peer, ch_id, pl, plen); break; |
|
|
|
|
case CS_MSG_ERROR: { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "%s: RECV ERROR from=%016llx ch=%s", CS_ID, (unsigned long long)peer, ch_id); |
|
|
|
|
if (cs->info_req_timer) { uasync_cancel_timeout(cs->inst->ua, cs->info_req_timer); cs->info_req_timer = NULL; } |
|
|
|
|
cs_cancel_proto_timers(cs); |
|
|
|
|
uint8_t err[20]; memcpy(err, &peer, 8); int r = -1; memcpy(err + 8, &r, 4); |
|
|
|
|
memcpy(err + 12, &cs->pending_invite_ch_id, 8); chat_event_post(cs->inst, CHAT_EVT_CONNECT_RESULT, err, 20); |
|
|
|
|
cs->pending_invite_node_id = 0; cs->pending_invite_ch_id = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case CS_MSG_CHANNEL_INVITE: cs_handle_channel_invite(cs, peer, ch_id, pl, plen); break; |
|
|
|
|
@ -267,6 +319,7 @@ static void cs_info_req_timeout_cb(void* arg) {
|
|
|
|
|
chat_event_post(cs->inst, CHAT_EVT_CONNECT_RESULT, err, 20); |
|
|
|
|
cs->pending_invite_node_id = 0; |
|
|
|
|
cs->pending_invite_ch_id = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void cs_join_timeout_cb(void* arg) { |
|
|
|
|
@ -283,6 +336,7 @@ static void cs_join_timeout_cb(void* arg) {
|
|
|
|
|
cs->pending_invite_node_id = 0; |
|
|
|
|
cs->pending_invite_ch_id = 0; |
|
|
|
|
cs->pending_invite_is_inviter = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void cs_invite_send_timeout_cb(void* arg) { |
|
|
|
|
@ -299,6 +353,7 @@ static void cs_invite_send_timeout_cb(void* arg) {
|
|
|
|
|
cs->pending_invite_node_id = 0; |
|
|
|
|
cs->pending_invite_ch_id = 0; |
|
|
|
|
cs->pending_invite_is_inviter = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ── helper: find active ETCP_CONN for node ── */ |
|
|
|
|
@ -518,7 +573,10 @@ static void _on_invite_sync_done(uint64_t peer, const char* ns, int result, void
|
|
|
|
|
/* ── send JOIN_INFO_REQ to start join handshake ── */ |
|
|
|
|
|
|
|
|
|
static void cs_start_channel_join(struct chat_sync* cs, uint64_t peer) { |
|
|
|
|
if (!cs || cs->info_req_timer) return; |
|
|
|
|
if (!cs || cs->info_req_timer || cs->join_timer) return; |
|
|
|
|
if (cs->join_transport_timer) { |
|
|
|
|
uasync_cancel_timeout(cs->inst->ua, cs->join_transport_timer); cs->join_transport_timer = NULL; |
|
|
|
|
} |
|
|
|
|
char ch_id_str[64]; |
|
|
|
|
snprintf(ch_id_str, sizeof(ch_id_str), "%llu", (unsigned long long)cs->pending_invite_ch_id); |
|
|
|
|
/* payload: [join_key:8][pass_len:1][password] */ |
|
|
|
|
@ -548,11 +606,10 @@ static void cs_on_conn_up(struct ETCP_CONN* conn, int event, void* arg) { (void)
|
|
|
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEMBER_SYNC, "%s: conn_up peer=%016llx pending_invite=%llu links_up=%d", CS_ID, (unsigned long long)peer, (unsigned long long)cs->pending_invite_ch_id, conn->links_up); |
|
|
|
|
|
|
|
|
|
if (cs->pending_invite_ch_id != 0) { |
|
|
|
|
if (cs->pending_invite_node_id != 0 && cs->pending_invite_node_id != peer) |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invite node_id MISMATCH: invite=0x%016llx ETCP_peer=0x%016llx — invite is STALE!", |
|
|
|
|
CS_ID, (unsigned long long)cs->pending_invite_node_id, (unsigned long long)peer); |
|
|
|
|
cs->pending_invite_node_id = peer; |
|
|
|
|
if (cs->pending_invite_ch_id != 0 && cs->pending_invite_node_id == peer) { |
|
|
|
|
if (cs->join_transport_timer) { |
|
|
|
|
uasync_cancel_timeout(cs->inst->ua, cs->join_transport_timer); cs->join_transport_timer = NULL; |
|
|
|
|
} |
|
|
|
|
if (cs->pending_invite_is_inviter) { |
|
|
|
|
/* we are the inviter — send CHANNEL_INVITE to the joiner */ |
|
|
|
|
char ch_id[64]; snprintf(ch_id, sizeof(ch_id), "%llu", |
|
|
|
|
@ -584,8 +641,11 @@ static void cs_on_conn_down(struct ETCP_CONN* conn, int event, void* arg) { (voi
|
|
|
|
|
|
|
|
|
|
cs_on_peer_status_changed(cs, peer, 0); |
|
|
|
|
|
|
|
|
|
cs_cancel_proto_timers(cs); |
|
|
|
|
if (cs->pending_invite_node_id == peer) { |
|
|
|
|
cs_cancel_proto_timers(cs); |
|
|
|
|
if (cs->join_transport_timer) uasync_cancel_timeout(cs->inst->ua, cs->join_transport_timer); |
|
|
|
|
cs->join_transport_timer = uasync_set_timeout(cs->inst->ua, CS_INFO_REQ_TIMEOUT_MS * 10, |
|
|
|
|
cs, cs_join_connect_timeout, "join_reconnect_timeout"); |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "%s: conn down while waiting invite resp peer=%016llx, timers cancelled, state kept for retry on reconnect", |
|
|
|
|
CS_ID, (unsigned long long)peer); |
|
|
|
|
/* invite state survives connection flaps: only timeout or explicit protocol end clears it */ |
|
|
|
|
@ -733,6 +793,7 @@ void chat_sync_destroy(struct UTUN_INSTANCE* inst) {
|
|
|
|
|
if (cs->refresh_wait) { standby_wait_cancel(cs->refresh_wait); cs->refresh_wait = NULL; } |
|
|
|
|
#endif |
|
|
|
|
cs_cancel_proto_timers(cs); |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < cs->channel_count; i++) |
|
|
|
|
if (cs->channels[i].peer_ids) u_free(cs->channels[i].peer_ids); |
|
|
|
|
@ -762,6 +823,11 @@ void chat_sync_connect_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
|
|
|
|
void chat_sync_on_channel_deleted(struct UTUN_INSTANCE* inst, const char* ch_id) { |
|
|
|
|
struct chat_sync* cs = cs_of(inst); |
|
|
|
|
if (!cs || !ch_id || !ch_id[0]) return; |
|
|
|
|
if (cs->pending_invite_ch_id == strtoull(ch_id, NULL, 10)) { |
|
|
|
|
cs_cancel_proto_timers(cs); |
|
|
|
|
cs->pending_invite_ch_id = 0; cs->pending_invite_node_id = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
for (int i = 0; i < cs->channel_count; i++) { |
|
|
|
|
if (strcmp(cs->channels[i].channel_id, ch_id) != 0) continue; |
|
|
|
|
if (cs->channels[i].peer_ids) u_free(cs->channels[i].peer_ids); |
|
|
|
|
@ -896,13 +962,13 @@ static void cm_invite_trampoline(void* arg) {
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, |
|
|
|
|
"channel invite: link to 0x%016llx already up — skipping connect, join handshake directly", |
|
|
|
|
(unsigned long long)node_id); |
|
|
|
|
cs_start_channel_join(cs, node_id); |
|
|
|
|
if (cs_join_hold_transport(cs, node_id, NULL) == 0) cs_start_channel_join(cs, node_id); |
|
|
|
|
} else { |
|
|
|
|
/* topo_group_invite_to_channel: прямое ncd-подключение к connection-узлу БЕЗ INVITE_INFO.
|
|
|
|
|
Реальный join-протокол (JOIN_INFO_REQ → … → JOIN_READY) запускается по cs_on_conn_up. */ |
|
|
|
|
/* Транспорт принадлежит join-протоколу до его результата, а не будущей BGP-группе.
|
|
|
|
|
JOIN_INFO_REQ → … → JOIN_READY запускается по cs_on_conn_up. */ |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "channel invite: connecting to 0x%016llx via %d addresses", |
|
|
|
|
(unsigned long long)node_id, inv->addr_count); |
|
|
|
|
int r = topo_group_invite_to_channel(inst, channel_id, ni, node_id); |
|
|
|
|
int r = cs_join_hold_transport(cs, node_id, ni); |
|
|
|
|
if (r < 0) |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "channel invite: FAILED — topo_group_invite_to_channel error=%d", r); |
|
|
|
|
} |
|
|
|
|
@ -989,6 +1055,7 @@ void chat_sync_join_channel(struct UTUN_INSTANCE* inst, const char* ch_id, uint6
|
|
|
|
|
|
|
|
|
|
cs->pending_invite_ch_id = channel_id; |
|
|
|
|
cs->pending_invite_node_id = target_node_id; |
|
|
|
|
if (cs_join_hold_transport(cs, target_node_id, NULL) < 0) return; |
|
|
|
|
cs_start_channel_join(cs, target_node_id); |
|
|
|
|
cs_on_peer_status_changed(cs, target_node_id, 1); |
|
|
|
|
} |
|
|
|
|
@ -1328,13 +1395,8 @@ static void cs_handle_join_request(struct chat_sync* cs, uint64_t peer,
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* валидный ключ → сразу добавляем узел в группу для BGP sync */ |
|
|
|
|
{ |
|
|
|
|
uint64_t gid = strtoull(ch_id, NULL, 10); |
|
|
|
|
struct TOPO_GROUP* g = topo_groups_find(cs->inst->topo_groups, gid); |
|
|
|
|
if (g && g->group_type == TOPO_GROUP_TYPE_CHAT) |
|
|
|
|
topo_group_new_conn(g, jconn); |
|
|
|
|
} |
|
|
|
|
/* Ключ разрешает протокол добавления. BGP начнётся после сохранения мембера,
|
|
|
|
|
локально либо через Merkle; существующий транспорт обслуживает JOIN_REQUEST. */ |
|
|
|
|
|
|
|
|
|
/* добавить в pending (ждут JOIN_READY по приходу рекорда через merkle) */ |
|
|
|
|
cs_pending_add(cs, m.node_id, ch_id, jconn); |
|
|
|
|
@ -1353,6 +1415,7 @@ static void cs_handle_join_request(struct chat_sync* cs, uint64_t peer,
|
|
|
|
|
cs->pending_invite_ch_id = 0; |
|
|
|
|
cs->pending_invite_node_id = 0; |
|
|
|
|
cs->pending_invite_is_inviter = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
chat_join_forward_request(cs->inst, channel_id, inviter, join_key, &m); |
|
|
|
|
@ -1394,6 +1457,7 @@ static void cs_handle_join_ready(struct chat_sync* cs, uint64_t peer,
|
|
|
|
|
chat_event_post(cs->inst, CHAT_EVT_CONNECT_RESULT, cevt, 20); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: JOIN_READY invite success ch=%s peer=%016llx", CS_ID, ch_id, (unsigned long long)peer); |
|
|
|
|
cs->pending_invite_node_id = 0; cs->pending_invite_ch_id = 0; cs->pending_join_key = 0; |
|
|
|
|
cs_join_release_transport(cs); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1602,7 +1666,7 @@ void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id,
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* Адреса грузятся из node_addresses в БД внутри node_conn_direct_open */ |
|
|
|
|
int r = topo_group_invite_to_channel(inst, channel_id, NULL, target_node_id); |
|
|
|
|
int r = cs_join_hold_transport(cs, target_node_id, NULL); |
|
|
|
|
if (r < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invite_to_channel topo_group_invite_to_channel failed rc=%d", CS_ID, r); |
|
|
|
|
} |
|
|
|
|
@ -1618,6 +1682,8 @@ void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id,
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Already connected — send invite now, wait for joiner to respond */ |
|
|
|
|
if (cs_join_hold_transport(cs, target_node_id, NULL) < 0) return; |
|
|
|
|
uasync_cancel_timeout(inst->ua, cs->join_transport_timer); cs->join_transport_timer = NULL; |
|
|
|
|
cs_send_channel_invite(cs, inst, ch_id, target_node_id, ch_name, owner, ch_x25519, ch_ed, ch_sig); |
|
|
|
|
cs->pending_invite_ch_id = channel_id; |
|
|
|
|
cs->pending_invite_node_id = target_node_id; |
|
|
|
|
@ -1661,6 +1727,8 @@ static void cs_invite_trampoline(void* arg) {
|
|
|
|
|
char ch_name[128]; uint8_t ch_x2[32], ch_ed2[32], ch_sig[64]; uint64_t ch_owner; |
|
|
|
|
if (topo_node_sqlite_channel_get(inst->topo_sqlite_db, |
|
|
|
|
ch_id, ch_name, (int)sizeof(ch_name), &ch_owner, ch_x2, ch_ed2, ch_sig) == 0) { |
|
|
|
|
if (cs_join_hold_transport(cs, target_node_id, NULL) < 0) { u_free(w); return; } |
|
|
|
|
uasync_cancel_timeout(inst->ua, cs->join_transport_timer); cs->join_transport_timer = NULL; |
|
|
|
|
cs_send_channel_invite(cs, inst, ch_id, target_node_id, ch_name, ch_owner, ch_x2, ch_ed2, ch_sig); |
|
|
|
|
cs->pending_invite_ch_id = channel_id; |
|
|
|
|
cs->pending_invite_node_id = target_node_id; |
|
|
|
|
@ -1717,7 +1785,7 @@ static void cs_invite_trampoline(void* arg) {
|
|
|
|
|
cs->pending_invite_node_id = target_node_id; |
|
|
|
|
cs->pending_invite_is_inviter = 1; |
|
|
|
|
|
|
|
|
|
int r = topo_group_invite_to_channel(inst, channel_id, ni, target_node_id); |
|
|
|
|
int r = cs_join_hold_transport(cs, target_node_id, ni); |
|
|
|
|
if (r < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invite_with_addrs topo_group_invite_to_channel failed rc=%d", CS_ID, r); |
|
|
|
|
cs->pending_invite_node_id = 0; cs->pending_invite_ch_id = 0; |
|
|
|
|
|