Browse Source

add chat_sync_invite_to_channel_with_addrs — connect with addresses, send CHANNEL_INVITE, 3s timeout, post result to GUI

topo_upd
evgeny 2 months ago
parent
commit
1895f23034
  1. 264
      src/chat/chat_sync.c
  2. 11
      src/chat/chat_sync.h
  3. 5
      tools/chatgui/src/inviteby.cpp

264
src/chat/chat_sync.c

@ -47,10 +47,12 @@ struct chat_sync {
void* info_req_timer;
void* join_timer;
void* sync_timer;
void* invite_send_timer; /* timer waiting for joiner to respond to CHANNEL_INVITE */
uint8_t initialized;
uint8_t sync_scheduled;
uint64_t pending_invite_ch_id;
uint64_t pending_invite_node_id;
uint8_t pending_invite_is_inviter; /* 1 = inviter sends CHANNEL_INVITE on conn_up, 0 = joiner sends CHANNEL_INFO_REQ */
char pending_password[128]; /* пароль для CHANNEL_INFO_REQ */
/* Pending incoming invites (для ask/deny) */
uint64_t pending_inv_in_ch_id[4];
@ -61,6 +63,7 @@ struct chat_sync {
#define CS_ID "chat_sync"
#define CS_INFO_REQ_TIMEOUT_MS 5000
#define CS_JOIN_TIMEOUT_MS 8000
#define CS_INVITE_SEND_TIMEOUT_MS 3000 /* inviter waits for joiner to respond */
#define CS_SYNC_INTERVAL_MS 1000
static const char* cs_msg_name(uint8_t type) {
@ -86,6 +89,7 @@ static const char* cs_msg_name(uint8_t type) {
static void cs_cancel_proto_timers(struct chat_sync* cs) {
if (cs->info_req_timer) { uasync_cancel_timeout(cs->inst->ua, cs->info_req_timer); cs->info_req_timer = NULL; }
if (cs->join_timer) { uasync_cancel_timeout(cs->inst->ua, cs->join_timer); cs->join_timer = NULL; }
if (cs->invite_send_timer) { uasync_cancel_timeout(cs->inst->ua, cs->invite_send_timer); cs->invite_send_timer = NULL; }
}
/* ── Forward decl ── */
@ -145,6 +149,9 @@ static void cs_handle_peer_remove(struct chat_sync* cs, uint64_t peer,
const char* ch_id, const uint8_t* pl, size_t len);
static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer,
const char* ch_id, const uint8_t* pl, size_t len);
static int cs_send_channel_invite(struct chat_sync* cs, struct UTUN_INSTANCE* inst,
const char* ch_id, uint64_t target_node_id, const char* ch_name,
const uint8_t* ch_x25519, const uint8_t* ch_ed, const uint8_t* ch_sig);
/* ── Recv dispatcher ── */
@ -226,6 +233,23 @@ static void cs_join_timeout_cb(void* arg) {
chat_event_post(CHAT_EVT_CONNECT_RESULT, err, 20);
cs->pending_invite_node_id = 0;
cs->pending_invite_ch_id = 0;
cs->pending_invite_is_inviter = 0;
}
static void cs_invite_send_timeout_cb(void* arg) {
struct chat_sync* cs = (struct chat_sync*)arg;
if (!cs || !cs->initialized) return;
cs->invite_send_timer = NULL;
uint64_t peer = cs->pending_invite_node_id;
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE timeout — joiner did not respond peer=%016llx ch=%llu",
CS_ID, (unsigned long long)peer, (unsigned long long)cs->pending_invite_ch_id);
uint8_t err[20]; memcpy(err, &peer, 8);
int r = CONN_EVENT_TIMEOUT; memcpy(err + 8, &r, 4);
memcpy(err + 12, &cs->pending_invite_ch_id, 8);
chat_event_post(CHAT_EVT_CONNECT_RESULT, err, 20);
cs->pending_invite_node_id = 0;
cs->pending_invite_ch_id = 0;
cs->pending_invite_is_inviter = 0;
}
/* ── helper: find active ETCP_CONN for node ── */
@ -447,7 +471,21 @@ static void cs_on_conn_up(struct ETCP_CONN* conn, int event, void* arg) { (void)
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: invite node_id MISMATCH: invite=0x%016llx ETCP_peer=0x%016llx — invite is STALE!",
CS_ID, (unsigned long long)g_cs->pending_invite_node_id, (unsigned long long)peer);
g_cs->pending_invite_node_id = peer;
if (g_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",
(unsigned long long)g_cs->pending_invite_ch_id);
char ch_name[128]; uint8_t ch_x2[32], ch_ed2[32], ch_sig[64];
if (topo_node_sqlite_channel_get(g_cs->inst->topo_sqlite_db,
ch_id, ch_name, (int)sizeof(ch_name), NULL, ch_x2, ch_ed2, ch_sig) == 0) {
cs_send_channel_invite(g_cs, g_cs->inst, ch_id, peer, ch_name, ch_x2, ch_ed2, ch_sig);
if (g_cs->invite_send_timer) { uasync_cancel_timeout(g_cs->inst->ua, g_cs->invite_send_timer); }
g_cs->invite_send_timer = uasync_set_timeout(g_cs->inst->ua,
CS_INVITE_SEND_TIMEOUT_MS * 10, g_cs, cs_invite_send_timeout_cb, "cs_invite_send");
}
} else {
cs_start_channel_join(g_cs, peer);
}
cs_on_peer_status_changed(peer, 1);
return;
}
@ -590,6 +628,7 @@ void chat_sync_destroy(struct UTUN_INSTANCE* inst) {
if (!cs || !inst) return;
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: destroy", CS_ID);
if (cs->sync_timer) { uasync_cancel_timeout(inst->ua, cs->sync_timer); cs->sync_timer = NULL; }
if (cs->invite_send_timer) { uasync_cancel_timeout(inst->ua, cs->invite_send_timer); cs->invite_send_timer = NULL; }
if (cs->sync_scheduled) { cs->sync_scheduled = 0; cs_flush_sync(cs); }
member_sync_destroy(inst);
cs->initialized = 0; g_cs = NULL;
@ -855,6 +894,9 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
const char* ch_id, const uint8_t* pl, size_t len) {
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: INFO_REQ ch=%s from=%016llx len=%zu", CS_ID, ch_id, (unsigned long long)peer, len);
/* joiner responded to our CHANNEL_INVITE — cancel invite send timer */
if (cs->invite_send_timer) { uasync_cancel_timeout(cs->inst->ua, cs->invite_send_timer); cs->invite_send_timer = NULL; }
/* Parse password from request (v2 format: type + pass_len + optional_password) */
char req_password[128] = "";
if (len >= 1) {
@ -1241,6 +1283,19 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer,
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN starting member_sync with joiner node=0x%016llx ch=%s done_cb=%s",
CS_ID, (unsigned long long)node_id, ch_id, sa ? "yes" : "no");
/* if we're the inviter — joiner joined successfully, notify GUI */
if (cs->pending_invite_ch_id != 0 && cs->pending_invite_is_inviter) {
uint64_t ch_id_num = strtoull(ch_id, NULL, 10);
uint8_t evt[20]; memcpy(evt, &node_id, 8); int r = 0; memcpy(evt + 8, &r, 4);
memcpy(evt + 12, &ch_id_num, 8);
chat_event_post(CHAT_EVT_CONNECT_RESULT, evt, 20);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN posted CHAT_EVT_CONNECT_RESULT to GUI node=0x%016llx ch=%s",
CS_ID, (unsigned long long)node_id, ch_id);
cs->pending_invite_node_id = 0; cs->pending_invite_ch_id = 0;
cs->pending_invite_is_inviter = 0;
if (cs->invite_send_timer) { uasync_cancel_timeout(cs->inst->ua, cs->invite_send_timer); cs->invite_send_timer = NULL; }
}
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN accepted node=0x%016llx ch=%s addrs=%d",
CS_ID, (unsigned long long)node_id, ch_id, addr_cnt);
}
@ -1587,6 +1642,50 @@ static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer,
}
}
/* ─── helper: build and send CS_MSG_CHANNEL_INVITE to peer ─── */
static int cs_send_channel_invite(struct chat_sync* cs, struct UTUN_INSTANCE* inst,
const char* ch_id, uint64_t target_node_id,
const char* ch_name, const uint8_t* ch_x25519,
const uint8_t* ch_ed, const uint8_t* ch_sig) {
uint64_t owner = inst->node_id;
uint8_t join_sig[64] = {0}; uint64_t join_ts = 0;
{ sqlite3* vdb = inst->topo_sqlite_db;
topo_node_sqlite_member_get_join(vdb, ch_id, inst->node_id, join_sig, &join_ts); }
uint8_t upd_sig[64] = {0}; uint64_t upd_ts = (uint64_t)ntp_time_get_seconds(inst);
{
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : "");
uint8_t umsg[256]; size_t ulen = 0;
memcpy(umsg + ulen, join_sig, 64); ulen += 64;
memcpy(umsg + ulen, &upd_ts, 8); ulen += 8;
size_t jl = strlen(juser); memcpy(umsg + ulen, juser, jl); umsg[ulen + jl] = '\0'; ulen += jl + 1;
cs_ed25519_sign(inst->my_ed25519_privkey, umsg, ulen, upd_sig);
}
uint8_t buf[1024]; size_t boff = 0;
buf[boff++] = CS_MSG_CHANNEL_INVITE;
uint8_t cnl = (uint8_t)strlen(ch_name);
buf[boff++] = cnl; memcpy(buf + boff, ch_name, cnl); boff += cnl;
memcpy(buf + boff, &owner, 8); boff += 8;
memcpy(buf + boff, ch_x25519, 32); boff += 32;
memcpy(buf + boff, ch_ed, 32); boff += 32;
memcpy(buf + boff, ch_sig, 64); boff += 64;
uint8_t inv_flags = (join_sig[0] || join_ts) ? PEERS_FLAG_HAS_JOIN : 0;
buf[boff++] = inv_flags;
if (inv_flags & PEERS_FLAG_HAS_JOIN) {
memcpy(buf + boff, join_sig, 64); boff += 64;
memcpy(buf + boff, &join_ts, 8); boff += 8;
}
memcpy(buf + boff, upd_sig, 64); boff += 64;
memcpy(buf + boff, &upd_ts, 8); boff += 8;
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : "");
uint8_t il = (uint8_t)strlen(juser);
buf[boff++] = il; memcpy(buf + boff, juser, il); boff += il;
int rc = cs_send(cs, ch_id, target_node_id, buf, boff);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: sent CHANNEL_INVITE to 0x%016llx ch=%s rc=%d",
CS_ID, (unsigned long long)target_node_id, ch_id, rc);
return rc;
}
/* ─── chat_sync_invite_to_channel: пригласить узел в наш канал ─── */
void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id) {
@ -1644,46 +1743,145 @@ void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id,
Use a separate flag. */
g_cs->pending_invite_ch_id = channel_id;
g_cs->pending_invite_node_id = target_node_id;
g_cs->pending_invite_is_inviter = 1;
return;
}
/* Already connected — send invite now */
uint8_t join_sig[64] = {0}; uint64_t join_ts = 0;
{ sqlite3* vdb = inst->topo_sqlite_db;
topo_node_sqlite_member_get_join(vdb, ch_id, inst->node_id, join_sig, &join_ts); }
uint8_t upd_sig[64] = {0}; uint64_t upd_ts = (uint64_t)ntp_time_get_seconds(inst);
{
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : "");
uint8_t umsg[256]; size_t ulen = 0;
memcpy(umsg + ulen, join_sig, 64); ulen += 64;
memcpy(umsg + ulen, &upd_ts, 8); ulen += 8;
size_t jl = strlen(juser); memcpy(umsg + ulen, juser, jl); umsg[ulen + jl] = '\0'; ulen += jl + 1;
cs_ed25519_sign(inst->my_ed25519_privkey, umsg, ulen, upd_sig);
/* Already connected — send invite now, wait for joiner to respond */
cs_send_channel_invite(g_cs, inst, ch_id, target_node_id, ch_name, ch_x25519, ch_ed, ch_sig);
g_cs->pending_invite_ch_id = channel_id;
g_cs->pending_invite_node_id = target_node_id;
g_cs->pending_invite_is_inviter = 1;
if (g_cs->invite_send_timer) { uasync_cancel_timeout(inst->ua, g_cs->invite_send_timer); }
g_cs->invite_send_timer = uasync_set_timeout(inst->ua,
CS_INVITE_SEND_TIMEOUT_MS * 10, g_cs, cs_invite_send_timeout_cb, "cs_invite_send");
}
/* ─── chat_sync_invite_to_channel_with_addrs: как invite_to_channel, но с адресами ─── */
struct cs_invite_wrap {
uint64_t channel_id;
uint64_t target_node_id;
uint8_t pubkey[32];
int addr_count;
int addrs_len;
uint8_t addrs_data[]; /* flexible array */
};
static void cs_invite_trampoline(void* arg) {
struct cs_invite_wrap* w = (struct cs_invite_wrap*)arg;
if (!g_cs || !g_cs->inst) { u_free(w); return; }
struct UTUN_INSTANCE* inst = g_cs->inst;
uint64_t channel_id = w->channel_id;
uint64_t target_node_id = w->target_node_id;
if (channel_id == 0 || target_node_id == 0 || target_node_id == inst->node_id) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs invalid params ch=%llu target=0x%016llx",
CS_ID, (unsigned long long)channel_id, (unsigned long long)target_node_id);
u_free(w); return;
}
uint8_t buf[1024]; size_t boff = 0;
buf[boff++] = CS_MSG_CHANNEL_INVITE;
uint8_t cnl = (uint8_t)strlen(ch_name);
buf[boff++] = cnl; memcpy(buf + boff, ch_name, cnl); boff += cnl;
memcpy(buf + boff, &owner, 8); boff += 8;
memcpy(buf + boff, ch_x25519, 32); boff += 32;
memcpy(buf + boff, ch_ed, 32); boff += 32;
memcpy(buf + boff, ch_sig, 64); boff += 64;
uint8_t inv_flags = (join_sig[0] || join_ts) ? PEERS_FLAG_HAS_JOIN : 0;
buf[boff++] = inv_flags;
if (inv_flags & PEERS_FLAG_HAS_JOIN) {
memcpy(buf + boff, join_sig, 64); boff += 64;
memcpy(buf + boff, &join_ts, 8); boff += 8;
char ch_id[64]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)channel_id);
/* Check if already connected */
struct ETCP_CONN* conn = cs_find_conn_for_node(inst, target_node_id);
if (conn && conn->links_up && conn->initialized) {
char ch_name[128]; uint8_t ch_x2[32], ch_ed2[32], ch_sig[64];
if (topo_node_sqlite_channel_get(inst->topo_sqlite_db,
ch_id, ch_name, (int)sizeof(ch_name), NULL, ch_x2, ch_ed2, ch_sig) == 0) {
cs_send_channel_invite(g_cs, inst, ch_id, target_node_id, ch_name, ch_x2, ch_ed2, ch_sig);
g_cs->pending_invite_ch_id = channel_id;
g_cs->pending_invite_node_id = target_node_id;
g_cs->pending_invite_is_inviter = 1;
if (g_cs->invite_send_timer) { uasync_cancel_timeout(inst->ua, g_cs->invite_send_timer); }
g_cs->invite_send_timer = uasync_set_timeout(inst->ua,
CS_INVITE_SEND_TIMEOUT_MS * 10, g_cs, cs_invite_send_timeout_cb, "cs_invite_send");
}
u_free(w); return;
}
memcpy(buf + boff, upd_sig, 64); boff += 64;
memcpy(buf + boff, &upd_ts, 8); boff += 8;
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : "");
uint8_t il = (uint8_t)strlen(juser);
buf[boff++] = il; memcpy(buf + boff, juser, il); boff += il;
cs_send(g_cs, ch_id, target_node_id, buf, boff);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: sent CHANNEL_INVITE to 0x%016llx ch=%s",
CS_ID, (unsigned long long)target_node_id, ch_id);
/* Not connected — build TOPO_NODE with addresses and connect */
if (!inst->topo_groups) {
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs — no topo_groups, cannot connect", CS_ID);
u_free(w); return;
}
struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni));
if (!ni) { u_free(w); return; }
ni->node_id = target_node_id; memcpy(ni->public_key, w->pubkey, 32);
const uint8_t* ap = w->addrs_data;
const uint8_t* ap_end = ap + w->addrs_len;
for (int i = 0; i < w->addr_count && ap + 1 <= ap_end; i++) {
uint8_t family = *ap++; uint8_t sock_id = *ap++; uint8_t proto = *ap++;
if (family == 4 && ap + 6 <= ap_end) {
struct TOPO_ADDR4* a4 = u_calloc(1, sizeof(*a4));
if (!a4) break;
memcpy(a4->addr, ap, 4); ap += 4;
a4->port = ((uint16_t)ap[0] << 8) | ap[1]; ap += 2;
a4->type = TOPO_ADDR_NAT; a4->protocol = proto;
a4->next = ni->v4_addrs; ni->v4_addrs = a4;
} else if (family == 6 && ap + 18 <= ap_end) {
struct TOPO_ADDR6* a6 = u_calloc(1, sizeof(*a6));
if (!a6) break;
memcpy(a6->addr, ap, 16); ap += 16;
a6->port = ((uint16_t)ap[0] << 8) | ap[1]; ap += 2;
a6->type = TOPO_ADDR_NAT; a6->protocol = proto;
a6->next = ni->v6_addrs; ni->v6_addrs = a6;
} else { break; }
}
if (!ni->v4_addrs && !ni->v6_addrs) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs no valid addrs for node=0x%016llx",
CS_ID, (unsigned long long)target_node_id);
u_free(ni); u_free(w); return;
}
g_cs->pending_invite_ch_id = channel_id;
g_cs->pending_invite_node_id = target_node_id;
g_cs->pending_invite_is_inviter = 1;
int r = conn_mgr_open_invite(inst, channel_id, ni, target_node_id, cs_invite_conn_cb, NULL, NULL);
if (r < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs conn_mgr_open_invite failed rc=%d", CS_ID, r);
g_cs->pending_invite_node_id = 0; g_cs->pending_invite_ch_id = 0;
g_cs->pending_invite_is_inviter = 0;
}
while (ni->v4_addrs) { struct TOPO_ADDR4* n = ni->v4_addrs->next; u_free(ni->v4_addrs); ni->v4_addrs = n; }
while (ni->v6_addrs) { struct TOPO_ADDR6* n = ni->v6_addrs->next; u_free(ni->v6_addrs); ni->v6_addrs = n; }
u_free(ni);
u_free(w);
}
void chat_sync_invite_to_channel_with_addrs(
struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id,
const uint8_t* pubkey_bin, const uint8_t* addrs_data, int addr_count,
int addrs_data_len)
{
if (!g_cs || !inst || !ch_id || !ch_id[0] || !pubkey_bin || !addrs_data || addr_count <= 0 || addrs_data_len <= 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs invalid args", CS_ID);
return;
}
uint64_t channel_id = strtoull(ch_id, NULL, 10);
if (channel_id == 0 || target_node_id == 0 || target_node_id == inst->node_id) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs invalid params ch=%s target=0x%016llx",
CS_ID, ch_id, (unsigned long long)target_node_id);
return;
}
struct cs_invite_wrap* w = u_calloc(1, sizeof(*w) + (size_t)addrs_data_len);
if (!w) return;
w->channel_id = channel_id;
w->target_node_id = target_node_id;
memcpy(w->pubkey, pubkey_bin, 32);
w->addr_count = addr_count;
w->addrs_len = addrs_data_len;
memcpy(w->addrs_data, addrs_data, (size_t)addrs_data_len);
uasync_post(inst->ua, cs_invite_trampoline, w);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite_with_addrs posted to uasync ch=%s target=0x%016llx addrs=%d",
CS_ID, ch_id, (unsigned long long)target_node_id, addr_count);
}
/* ─── accept/deny incoming invite ─── */

11
src/chat/chat_sync.h

@ -81,6 +81,17 @@ void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id,
int addrs_data_len,
const char* password);
/* Пригласить target_node_id в канал ch_id с адресами из invite-ссылки (inviter → joiner).
Принимает pubkey и адреса целевого узла. Если соединения ещё нет — строит TOPO_NODE
с адресами, запускает conn_mgr для подключения, и по conn_up отправляет CS_MSG_CHANNEL_INVITE.
Если соединение уже есть — отправляет CHANNEL_INVITE сразу.
Копирует pubkey_bin и addrs_data (вызывающий может освободить после возврата).
Вызов из любого потока — внутри постит в uasync. */
void chat_sync_invite_to_channel_with_addrs(
struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id,
const uint8_t* pubkey_bin, const uint8_t* addrs_data, int addr_count,
int addrs_data_len);
/* Пригласить target_node_id в канал ch_id (inviter → joiner).
Отправляет CS_MSG_CHANNEL_INVITE с полной информацией о канале и своих адресах */
void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id);

5
tools/chatgui/src/inviteby.cpp

@ -179,7 +179,10 @@ void InviteByLinkDialog::onSendInvite() {
addrsBuf.append(static_cast<char>(a.port & 0xFF));
}
chat_sync_invite_to_channel(chat_core_get_inst(), m_channelId.toUtf8().constData(), targetNodeId);
chat_sync_invite_to_channel_with_addrs(
chat_core_get_inst(), m_channelId.toUtf8().constData(), targetNodeId,
(const uint8_t*)d.pubkey.constData(),
(const uint8_t*)addrsBuf.constData(), d.addrs.size(), addrsBuf.size());
setInviteStatus(QString::fromUtf8("Приглашение отправлено. OK"));
QTimer::singleShot(2000, this, [this]() {

Loading…
Cancel
Save