From 6b6657c6b8a0c048f83b572292f50fb2318ce9ea Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sun, 9 Aug 2026 15:37:42 +0300 Subject: [PATCH] fix NTP ping-pong + normalizer counters - ntp_node_time.c: respond to TIME_SYNC only when peer is unsynced (not always) Prevents infinite 10-byte ping-pong that ate bandwidth when both sides synced. - pkt_normalizer.c: add in_total_pkts/in_total_bytes increments in etcp_input_ready_cb (real send path), matching pn_packer_send (test path). - conn_mgr_core.c/conn_mgr_priv.h: full INVITE_INFO_RESP with channel info --- src/ntp_node_time.c | 7 +- src/routing_layer/conn_mgr_core.c | 244 ++++++++++++++++++++++++--- src/routing_layer/conn_mgr_priv.h | 28 +++ src/transport_layer/pkt_normalizer.c | 2 + 4 files changed, 257 insertions(+), 24 deletions(-) diff --git a/src/ntp_node_time.c b/src/ntp_node_time.c index eab6584b..ca878e8c 100644 --- a/src/ntp_node_time.c +++ b/src/ntp_node_time.c @@ -161,8 +161,11 @@ static void ntp_node_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { queue_dgram_free(entry); queue_entry_free(entry); - // Ответный TIME_SYNC — всегда - send_time_sync(inst, conn); + // Отвечаем только несинхронизированному пиру — помогаем ему получить время. + // Когда оба синхронизированы — не отвечаем, чтобы не создавать бесконечный пинг-понг. + if (!sender_synced && ntp_time_is_synced(inst)) { + send_time_sync(inst, conn); + } // Если мы только что скорректировались → раздаём коррекцию всем peer'ам if (was_unsynced && ntp_time_is_synced(inst)) { diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index f9f9bb4d..2fafcc43 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/src/routing_layer/conn_mgr_core.c @@ -23,6 +23,7 @@ #include "topo_group.h" #include "../transport_layer/etcp_connect.h" #include "route_ping.h" +#include "../chat/chat_core.h" /* ═══════ forward-декларации ═══════ */ @@ -741,55 +742,254 @@ void conn_mgr_direct_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry /* ═══════ invite обработчики ═══════ */ +/* Формирует полный INVITE_INFO_RESP: если сервер участник канала — возвращает + * is_member=1 + ВСЕ поля канала (name, owner, ключи, ch_sig, join/update_sig). + * Если не участник — is_member=0. Попутно создаёт инфраструктуру группы через + * chat_core_ensure_channel_ready, чтобы группа была готова к маршрутизации сразу. */ void cm_handle_invite_info_req(struct CONN_MGR* mgr, struct ETCP_CONN* conn, const uint8_t* data, size_t len) { if (len < sizeof(struct CM_INVITE_REQ)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_REQ too short (%zu)", len); return; } struct CM_INVITE_REQ* req = (struct CM_INVITE_REQ*)data; + DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "invite: INFO_REQ from=%016llx group=%016llx", + (unsigned long long)conn->peer_node_id, (unsigned long long)req->group_id); + + char ch_id[32]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)req->group_id); + + /* данные узла-ответчика для ed_pubkey и node_name */ struct TOPO_GROUP* grp = topo_groups_find(mgr->instance->topo_groups, req->group_id); struct TOPO_GROUP* sg = grp ? grp : mgr->group; struct TOPO_NODE* ln = sg->local_node ? topo_node_registry_find(sg->instance->topo_groups, sg->local_node->node_id) : NULL; const char* nm = ln ? ln->node_name : NULL; size_t nl = nm ? strlen(nm) : 0; if (nl > 255) nl = 255; - size_t rs = sizeof(struct CM_INVITE_RESP) + nl; - struct CM_INVITE_RESP* resp = u_calloc(1, rs); if (!resp) return; - resp->cmd=ETCP_RT_ID_CONN_MGR; resp->subcmd=CM_SUBCMD_INVITE_INFO_RESP; resp->group_id=req->group_id; resp->is_member=1; - memcpy(resp->ed25519_pubkey, sg->ed25519_public_key, SC_PUBKEY_SIZE); resp->node_name_len=(uint8_t)nl; - if (nl) memcpy(resp->node_name, nm, nl); - struct ll_entry* qe = queue_entry_new(0); - if (qe) { - size_t new_len = rs + 1; /* +1 for duplicate cmd: dgram[0]=dispatch, dgram[1]=handler-skip, dgram[2]=subcmd, dgram[3..]=payload */ - uint8_t* buf = u_malloc(new_len); - if (buf) { buf[0]=ETCP_RT_ID_CONN_MGR; buf[1]=ETCP_RT_ID_CONN_MGR; buf[2]=CM_SUBCMD_INVITE_INFO_RESP; - memcpy(buf+3, (uint8_t*)resp+2, rs-2); qe->dgram=buf; qe->len=(uint16_t)new_len; etcp_send(conn, qe); u_free(resp); } - else { queue_entry_free(qe); u_free(resp); } - } else u_free(resp); - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: membership check RECEIVED from 0x%016llx — responding is_member=yes name=\"%.*s\"", - (unsigned long long)conn->peer_node_id, (int)nl, nm ? nm : ""); + + /* ── ищем канал в SQLite ── */ + sqlite3* db = mgr->instance->topo_sqlite_db; + char ch_name[128] = ""; uint64_t owner = 0; + uint8_t ch_x25519[32] = {0}, ch_ed_pub[32] = {0}, ch_sig[64] = {0}; + int have_channel = db && topo_node_sqlite_channel_get(db, ch_id, ch_name, (int)sizeof(ch_name), + &owner, ch_x25519, ch_ed_pub, ch_sig) == 0; + + if (!have_channel) { + size_t rs = CM_INVITE_RESP_HDR_SIZE + nl; + struct CM_INVITE_RESP* resp = u_calloc(1, rs); if (!resp) return; + resp->cmd = ETCP_RT_ID_CONN_MGR; resp->subcmd = CM_SUBCMD_INVITE_INFO_RESP; + resp->group_id = req->group_id; resp->is_member = 0; + memcpy(resp->ed25519_pubkey, sg->ed25519_public_key, SC_PUBKEY_SIZE); + resp->node_name_len = (uint8_t)nl; if (nl) memcpy(resp->node_name, nm, nl); + struct ll_entry* qe = queue_entry_new(0); + if (qe) { size_t new_len = rs + 1; uint8_t* buf = u_malloc(new_len); + if (buf) { buf[0]=ETCP_RT_ID_CONN_MGR; buf[1]=ETCP_RT_ID_CONN_MGR; buf[2]=CM_SUBCMD_INVITE_INFO_RESP; + memcpy(buf + 3, (uint8_t*)resp + 2, rs - 2); qe->dgram = buf; qe->len = (uint16_t)new_len; etcp_send(conn, qe); } + else { queue_entry_free(qe); } + } + u_free(resp); + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: group=%016llx NOT in DB — responding is_member=NO to 0x%016llx", + (unsigned long long)req->group_id, (unsigned long long)conn->peer_node_id); + return; + } + + /* ── участник: генерируем join_sig / update_sig ── */ + uint64_t myid = mgr->instance->node_id; + uint8_t my_join_sig[64] = {0}; uint64_t my_join_ts = 0; + { + if (db && topo_node_sqlite_member_get_join(db, ch_id, myid, my_join_sig, &my_join_ts) != 0) { + uint8_t join_msg[256]; size_t mlen = 0; + memcpy(join_msg + mlen, ch_x25519, 32); mlen += 32; + memcpy(join_msg + mlen, ch_ed_pub, 32); mlen += 32; + memcpy(join_msg + mlen, &myid, 8); mlen += 8; + memcpy(join_msg + mlen, mgr->instance->my_keys.public_key, 32); mlen += 32; + my_join_ts = (uint64_t)ntp_time_get_seconds(mgr->instance); + memcpy(join_msg + mlen, &my_join_ts, 8); mlen += 8; + if (sc_ed25519_sign(mgr->instance->my_ed25519_privkey, join_msg, mlen, my_join_sig) != SC_OK) + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "invite: join_sig sign FAILED myid=%016llx ch=%s", (unsigned long long)myid, ch_id); + else + DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "invite: join_sig NEW myid=%016llx ts=%llu ch=%s", + (unsigned long long)myid, (unsigned long long)my_join_ts, ch_id); + } else { + DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "invite: join_sig REUSED myid=%016llx ts=%llu ch=%s", + (unsigned long long)myid, (unsigned long long)my_join_ts, ch_id); + } + } + + uint8_t my_update_sig[64] = {0}; uint64_t my_update_ts = 0; + { + char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", + mgr->instance->name[0] ? mgr->instance->name : ""); + uint8_t umsg[256]; size_t ulen = 0; + memcpy(umsg + ulen, my_join_sig, 64); ulen += 64; + my_update_ts = (uint64_t)ntp_time_get_seconds(mgr->instance); + memcpy(umsg + ulen, &my_update_ts, 8); ulen += 8; + size_t jl = strlen(juser); memcpy(umsg + ulen, juser, jl); umsg[ulen + jl] = '\0'; ulen += jl + 1; + if (sc_ed25519_sign(mgr->instance->my_ed25519_privkey, umsg, ulen, my_update_sig) != SC_OK) + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "invite: update_sig sign FAILED myid=%016llx", (unsigned long long)myid); + } + + /* ── создаём инфраструктуру группы СРАЗУ (topo_group, db_sync, msg-таблицу) ── */ + chat_core_ensure_channel_ready(ch_id); + + /* ── сериализуем ответ ── */ + size_t ch_nl = strlen(ch_name); if (ch_nl > 255) ch_nl = 255; + uint8_t inv_flags = (my_join_sig[0] || my_join_ts) ? PEERS_FLAG_HAS_JOIN : 0; + char userinfo[256]; size_t userinfo_len = (size_t)snprintf(userinfo, sizeof(userinfo), + "{\"name\":\"%s\"}", mgr->instance->name[0] ? mgr->instance->name : ""); + + size_t ch_info_len = 1 + ch_nl + 8 + 32 + 32 + 64 + 1 + + (inv_flags & PEERS_FLAG_HAS_JOIN ? 72 : 0) + + 64 + 8 + 1 + userinfo_len; + size_t rs = CM_INVITE_RESP_HDR_SIZE + nl + ch_info_len; + uint8_t* rb = u_malloc(rs); if (!rb) return; + memset(rb, 0, rs); + + { struct CM_INVITE_RESP* resp = (struct CM_INVITE_RESP*)rb; + resp->cmd = ETCP_RT_ID_CONN_MGR; resp->subcmd = CM_SUBCMD_INVITE_INFO_RESP; + resp->group_id = req->group_id; resp->is_member = 1; + memcpy(resp->ed25519_pubkey, sg->ed25519_public_key, SC_PUBKEY_SIZE); + resp->node_name_len = (uint8_t)nl; if (nl) memcpy(resp->node_name, nm, nl); } + + uint8_t* ch = rb + CM_INVITE_RESP_HDR_SIZE + nl; + *ch++ = (uint8_t)ch_nl; memcpy(ch, ch_name, ch_nl); ch += ch_nl; + memcpy(ch, &owner, 8); ch += 8; + memcpy(ch, ch_x25519, 32); ch += 32; + memcpy(ch, ch_ed_pub, 32); ch += 32; + memcpy(ch, ch_sig, 64); ch += 64; + *ch++ = inv_flags; + if (inv_flags & PEERS_FLAG_HAS_JOIN) { memcpy(ch, my_join_sig, 64); ch += 64; memcpy(ch, &my_join_ts, 8); ch += 8; } + memcpy(ch, my_update_sig, 64); ch += 64; + memcpy(ch, &my_update_ts, 8); ch += 8; + *ch++ = (uint8_t)userinfo_len; memcpy(ch, userinfo, userinfo_len); + + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, + "invite: membership CHECK OK from=0x%016llx group=%016llx ch_name=%.*s owner=%016llx " + "ch_x25519=%016llx... ch_ed=%016llx... inv_flags=%u join_ts=%llu upd_ts=%llu my_name=%s → is_member=YES full_ch_info (%zu+%zu=%zuB)", + (unsigned long long)conn->peer_node_id, (unsigned long long)req->group_id, + (int)ch_nl, ch_name, (unsigned long long)owner, + *(const uint64_t*)ch_x25519, *(const uint64_t*)ch_ed_pub, + (unsigned)inv_flags, (unsigned long long)my_join_ts, (unsigned long long)my_update_ts, + mgr->instance->name[0] ? mgr->instance->name : "?", + CM_INVITE_RESP_HDR_SIZE + nl, ch_info_len, rs); + + { struct ll_entry* qe = queue_entry_new(0); + if (qe) { size_t dgram_len = rs + 1; uint8_t* dgram = u_malloc(dgram_len); + if (dgram) { dgram[0]=ETCP_RT_ID_CONN_MGR; dgram[1]=ETCP_RT_ID_CONN_MGR; dgram[2]=CM_SUBCMD_INVITE_INFO_RESP; + memcpy(dgram + 3, rb + 2, rs - 2); qe->dgram = dgram; qe->len = (uint16_t)dgram_len; etcp_send(conn, qe); } + else { queue_entry_free(qe); } + } + } + u_free(rb); } void cm_handle_invite_info_resp(struct CONN_MGR* mgr, struct ETCP_CONN* conn, const uint8_t* data, size_t len) { - if (len < sizeof(struct CM_INVITE_RESP)) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP too short (%zu)", len); return; } + if (len < CM_INVITE_RESP_HDR_SIZE) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP too short (%zu < %zu)", len, CM_INVITE_RESP_HDR_SIZE); return; } struct CM_INVITE_RESP* resp = (struct CM_INVITE_RESP*)data; - if (len < sizeof(struct CM_INVITE_RESP) + (size_t)resp->node_name_len) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP truncated"); return; } + uint8_t nnl = resp->node_name_len; + if (len < CM_INVITE_RESP_HDR_SIZE + (size_t)nnl) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP truncated (need %zu, got %zu)", CM_INVITE_RESP_HDR_SIZE + (size_t)nnl, len); return; } + + /* ── ищем ожидающий invite ── */ struct cm_invite_pending* inv = NULL; { struct TOPO_GROUP* grp = topo_groups_find(mgr->instance->topo_groups, resp->group_id); - if (grp && grp->conn_mgr) { + if (grp && grp->conn_mgr) for (struct cm_invite_pending* p = grp->conn_mgr->invite_list; p; p = p->next) if (p->state == CM_INVITE_WAIT_INFO && (!conn->peer_node_id || p->node_id == conn->peer_node_id)) { inv = p; break; } - } } if (!inv) { for (struct cm_invite_pending* p = mgr->invite_list; p; p = p->next) if (p->state == CM_INVITE_WAIT_INFO) { inv = p; break; } } if (!inv) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP — no pending invite in WAIT_INFO state"); return; } + if (!resp->is_member) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: membership DENIED by 0x%016llx — not in channel", (unsigned long long)inv->node_id); cm_invite_fail(inv); return; } + + /* ── сохраняем Ed25519 pubkey ответчика для NODEINFO-верификации ── */ if (inv->temp_nq) { struct TOPO_NODE* ni2 = topo_node_registry_find(inv->mgr->instance->topo_groups, inv->temp_nq->node_id); if (ni2) memcpy(ni2->ed25519_public_key, resp->ed25519_pubkey, SC_PUBKEY_SIZE); - if (inv->mgr->instance->topo_sqlite_db) topo_node_sqlite_node_put(inv->mgr->instance->topo_sqlite_db, inv->mgr->instance->topo_groups, inv->temp_nq, (time_t)(get_time_tb()/10000)); + if (inv->mgr->instance->topo_sqlite_db) + topo_node_sqlite_node_put(inv->mgr->instance->topo_sqlite_db, inv->mgr->instance->topo_groups, inv->temp_nq, (time_t)(get_time_tb() / 10000)); + } + + /* ── парсим channel_info (новое) ── */ + const uint8_t* ch = data + CM_INVITE_RESP_HDR_SIZE + nnl; + size_t ch_remain = len - (CM_INVITE_RESP_HDR_SIZE + nnl); + if (ch_remain < CM_INVITE_RESP_CH_MIN_SIZE) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: INVITE_INFO_RESP channel info too short (%zu < %zu)", ch_remain, CM_INVITE_RESP_CH_MIN_SIZE); cm_invite_fail(inv); return; } + + uint8_t c_nl = *ch++; ch_remain--; + if (ch_remain < (size_t)c_nl) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: ch_name truncated"); cm_invite_fail(inv); return; } + char ch_name[128]; size_t cn = c_nl > 127 ? 127 : c_nl; memcpy(ch_name, ch, cn); ch_name[cn] = '\0'; ch += c_nl; ch_remain -= c_nl; + + uint64_t ch_owner; memcpy(&ch_owner, ch, 8); ch += 8; ch_remain -= 8; + const uint8_t* ch_x25519 = ch; ch += 32; ch_remain -= 32; + const uint8_t* ch_ed_pub = ch; ch += 32; ch_remain -= 32; + const uint8_t* ch_sig = ch; ch += 64; ch_remain -= 64; + + if (ch_remain < 1) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: ch_info truncated at inv_flags"); cm_invite_fail(inv); return; } + uint8_t inv_flags = *ch++; ch_remain--; + + const uint8_t* join_sig = NULL; uint64_t join_ts = 0; + if (inv_flags & PEERS_FLAG_HAS_JOIN) { + if (ch_remain < 72) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: ch_info truncated at join_sig"); cm_invite_fail(inv); return; } + join_sig = ch; ch += 64; memcpy(&join_ts, ch, 8); ch += 8; ch_remain -= 72; + } + + if (ch_remain < 73) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: ch_info truncated at update"); cm_invite_fail(inv); return; } + const uint8_t* upd_sig = ch; ch += 64; uint64_t upd_ts; memcpy(&upd_ts, ch, 8); ch += 8; ch_remain -= 72; + + uint8_t ui_len = *ch++; ch_remain--; + char ch_userinfo[256] = ""; + if (ui_len > 0 && ch_remain >= ui_len) { size_t ul = ui_len > 255 ? 255 : ui_len; memcpy(ch_userinfo, ch, ul); ch_userinfo[ul] = '\0'; } + + char ch_id[32]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)resp->group_id); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, + "invite: INFO_RESP from=0x%016llx group=%016llx node_name=%.*s " + "ch_name=%s ch_owner=%016llx ch_x25519=%016llx... ch_ed=%016llx... " + "inv_flags=%u join_ts=%llu upd_ts=%llu userinfo=%s", + (unsigned long long)conn->peer_node_id, (unsigned long long)resp->group_id, + (int)nnl, nnl ? (const char*)resp->node_name : "?", + ch_name, (unsigned long long)ch_owner, + *(const uint64_t*)ch_x25519, *(const uint64_t*)ch_ed_pub, + (unsigned)inv_flags, (unsigned long long)join_ts, (unsigned long long)upd_ts, + ch_userinfo[0] ? ch_userinfo : "?"); + + /* ── верифицируем ch_sig ── */ + { + uint8_t vmsg[1024]; size_t vlen = 0; + vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1; + vlen += (size_t)snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_name) + 1; + memcpy(vmsg + vlen, &ch_owner, 8); vlen += 8; + memcpy(vmsg + vlen, ch_x25519, 32); vlen += 32; + memcpy(vmsg + vlen, ch_ed_pub, 32); vlen += 32; + if (sc_ed25519_verify(ch_ed_pub, vmsg, vlen, ch_sig) != SC_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "invite: ch_sig VERIFY FAIL — rejecting group=%016llx from=0x%016llx", + (unsigned long long)resp->group_id, (unsigned long long)conn->peer_node_id); + cm_invite_fail(inv); return; + } + DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "invite: ch_sig VERIFY OK"); + } + + /* ── сохраняем канал в БД и создаём ПОЛНОЦЕННУЮ группу (topo_group + db_sync + msg-таблицу) ── */ + { + sqlite3* ldb = inv->mgr->instance->topo_sqlite_db; + if (ldb) topo_node_sqlite_channel_put(ldb, ch_id, ch_name, ch_owner, + ch_x25519, NULL, ch_ed_pub, NULL, ch_sig); } + chat_core_ensure_channel_ready(ch_id); + + /* ── верифицируем update_sig приглашающего ── */ + { + const uint8_t* inv_ed = conn->peer_ed25519_pubkey; + uint8_t ivmsg[256]; size_t ilen = 0; + if (join_sig && (inv_flags & PEERS_FLAG_HAS_JOIN)) { memcpy(ivmsg + ilen, join_sig, 64); ilen += 64; } + else { memset(ivmsg + ilen, 0, 64); ilen += 64; } + memcpy(ivmsg + ilen, &upd_ts, 8); ilen += 8; + { const char* nm2 = ch_userinfo[0] ? ch_userinfo : ""; + size_t nl2 = strlen(nm2); memcpy(ivmsg + ilen, nm2, nl2 + 1); ilen += nl2 + 1; } + if (sc_ed25519_verify(inv_ed, ivmsg, ilen, upd_sig) != SC_OK) { + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "invite: inviter_update_sig VERIFY FAIL node=0x%016llx", (unsigned long long)conn->peer_node_id); + } else { + DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "invite: inviter_update_sig VERIFY OK"); + } + } + if (inv->overall_timer) { uasync_cancel_timeout(inv->mgr->instance->ua, inv->overall_timer); inv->overall_timer = NULL; } - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: membership CONFIRMED by 0x%016llx name=\"%.*s\" — joined to channel, handle registered", - (unsigned long long)inv->node_id, resp->node_name_len, resp->node_name_len ? (const char*)resp->node_name : ""); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: membership CONFIRMED by 0x%016llx name=\"%.*s\" ch=%s — full channel ready", + (unsigned long long)inv->node_id, (int)nnl, nnl ? (const char*)resp->node_name : "?", ch_id); if (inv->cb) inv->cb(inv->handle, inv->node_id, inv->mgr->group->group_id, CONN_EVENT_JOIN, inv->cb_arg); if (inv->invite_conn) { node_conn_direct_open_node(inv->mgr->instance, inv->node_id, cm_ncd_callback, NULL, NULL, NULL, NULL); diff --git a/src/routing_layer/conn_mgr_priv.h b/src/routing_layer/conn_mgr_priv.h index b3330b74..82b1f7db 100644 --- a/src/routing_layer/conn_mgr_priv.h +++ b/src/routing_layer/conn_mgr_priv.h @@ -82,10 +82,38 @@ struct CM_DISCONNECT { uint8_t cmd, subcmd; uint64_t node_id; }; struct CM_INVITE_REQ { uint8_t cmd, subcmd; uint64_t group_id; }; #pragma pack(pop) +/* + * INVITE_INFO_RESP wire format (devel — без обратной совместимости): + * + * [0..1] cmd, subcmd + * [2..9] group_id + * [10] is_member (1=участник, 0=не в канале) + * [11..13] reserved + * [14..45] ed25519_pubkey (Ed25519 pubkey узла-ответчика, для NODEINFO-верификации) + * [46] node_name_len + * [47..N] node_name (node_name_len байт) + * + * Если is_member == 1 — далее полная информация о канале: + * [N+1] ch_name_len + * [N+2..] ch_name (ch_name_len байт) + * +8 owner (node_id создателя) + * +32 ch_x25519 (X25519 pubkey канала) + * +32 ch_ed_pub (Ed25519 pubkey канала) + * +64 ch_sig (Ed25519-подпись канала) + * +1 inv_flags (PEERS_FLAG_HAS_JOIN) + * [if PEERS_FLAG_HAS_JOIN: +64 join_sig + 8 join_ts] + * +64 update_sig + * +8 update_ts + * +1 userinfo_len + * +var userinfo (userinfo_len байт, JSON с именем узла) + */ #pragma pack(push, 1) struct CM_INVITE_RESP { uint8_t cmd, subcmd; uint64_t group_id; uint8_t is_member; uint8_t reserved[3]; uint8_t ed25519_pubkey[SC_PUBKEY_SIZE]; uint8_t node_name_len; uint8_t node_name[0]; }; #pragma pack(pop) +#define CM_INVITE_RESP_HDR_SIZE (sizeof(struct CM_INVITE_RESP)) /* 47 байт, без node_name */ +#define CM_INVITE_RESP_CH_MIN_SIZE (1 + 8 + 32 + 32 + 64 + 1 + 64 + 8 + 1) /* 211 байт, без учёта join и имён */ + #define CM_DIRECT_REQ_H_SIZE (sizeof(struct CM_DIRECT_REQ)) #define CM_DIRECT_RESP_HDR_SIZE (sizeof(struct CM_DIRECT_RESP)) #define CM_EXCHANGE_REQ_SIZE (sizeof(struct CM_EXCHANGE_REQ)) diff --git a/src/transport_layer/pkt_normalizer.c b/src/transport_layer/pkt_normalizer.c index e831a100..d7cf2a29 100644 --- a/src/transport_layer/pkt_normalizer.c +++ b/src/transport_layer/pkt_normalizer.c @@ -290,6 +290,8 @@ static void etcp_input_ready_cb(struct ll_queue* q, void* arg) { queue_resume_callback(pn->input); return; } + pn->in_total_pkts++; + pn->in_total_bytes += in_dgram->len; if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP)) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP, "->NORM", in_dgram->dgram, in_dgram->len);