stcp: ed25519 pubkey exchange in handshake, fix instance destroy segfault, fix chat_tcp_test
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
This commit is contained in:
1 parent
a3783b5f55
commit
38c9f229bc
1272 files changed
+968388
-1
No files matched your search
@@ -0,0 +1,795 @@
|
||||
#include "topo_node_sqlite.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PEERS_JOIN_SIG_SIZE 64
|
||||
#define PEERS_JOIN_TS_SIZE 8
|
||||
|
||||
static void sanitize_ch_id(const char* ch_id, char* out, size_t out_sz) {
|
||||
size_t i = 0;
|
||||
while (*ch_id && i < out_sz - 1) {
|
||||
char c = *ch_id++;
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9') || c == '_')
|
||||
out[i++] = c;
|
||||
else
|
||||
out[i++] = '_';
|
||||
}
|
||||
out[i] = '\0';
|
||||
}
|
||||
|
||||
static void peers_table_name(const char* ch_id, char* buf, size_t sz) {
|
||||
char san[64]; sanitize_ch_id(ch_id, san, sizeof(san));
|
||||
snprintf(buf, sz, "peers_%s", san);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_init(sqlite3* db) {
|
||||
if (!db) return -1;
|
||||
|
||||
const char* sql =
|
||||
"CREATE TABLE IF NOT EXISTS nodes ("
|
||||
" node_id INTEGER PRIMARY KEY,"
|
||||
" name TEXT,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_pubkey BLOB,"
|
||||
" last_seen_at INTEGER,"
|
||||
" online INTEGER DEFAULT 0,"
|
||||
" update_ts INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS node_addresses ("
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
" node_id INTEGER NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,"
|
||||
" family INTEGER NOT NULL CHECK(family IN (4, 6)),"
|
||||
" protocol INTEGER NOT NULL DEFAULT 1,"
|
||||
" address BLOB NOT NULL,"
|
||||
" port INTEGER NOT NULL CHECK(port > 0 AND port <= 65535),"
|
||||
" rtt INTEGER,"
|
||||
" addr_type INTEGER DEFAULT 0,"
|
||||
" socket_id INTEGER NOT NULL DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
"CREATE INDEX IF NOT EXISTS idx_na_node ON node_addresses(node_id);"
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_na_unique ON node_addresses(node_id, family, socket_id, addr_type);"
|
||||
"CREATE INDEX IF NOT EXISTS idx_na_addr_lookup ON node_addresses(family, address, port, socket_id);"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS channels ("
|
||||
" channel_id TEXT PRIMARY KEY," // ID канала
|
||||
" name TEXT NOT NULL," // Название канала
|
||||
" owner_node_id INTEGER," // node id создателя (надо выпилить)
|
||||
" x25519_pubkey BLOB NOT NULL," // ключ для шифрования канала
|
||||
" x25519_privkey BLOB," // если админ - то ключ канала
|
||||
" ed25519_pubkey BLOB NOT NULL," // ключ для проверки подписи
|
||||
" ed25519_privkey BLOB," // ключ для создания подписи
|
||||
" signature BLOB NOT NULL," // подпись при создании канал приватным ключом канала channel_id(NUL-terminated) || name(NUL-terminated) || owner_node_id(8 байт, LE uint64) || x25519_pubkey(32 байта) || ed25519_pubkey(32 байта)
|
||||
" last_read_msg_id INTEGER," // для скроллинга чата
|
||||
" last_pos_msg_id INTEGER," // для скроллинга чата
|
||||
" last_msg_at INTEGER DEFAULT 0," // время последнего сообщения (unix timestamp)
|
||||
" created_at INTEGER DEFAULT (unixepoch())" // когда создан
|
||||
");";
|
||||
|
||||
char* err = NULL;
|
||||
int rc = sqlite3_exec(db, sql, NULL, NULL, &err);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_node_sqlite_init: %s", err);
|
||||
sqlite3_free(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'peers\\_%' ESCAPE '\\'",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
char isql[256];
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const char* tbl = (const char*)sqlite3_column_text(s, 0);
|
||||
snprintf(isql, sizeof(isql),
|
||||
"CREATE INDEX IF NOT EXISTS \"idx_%s_type_rtt\" ON \"%s\" (node_type, node_RTT)", tbl, tbl);
|
||||
sqlite3_exec(db, isql, NULL, NULL, NULL);
|
||||
snprintf(isql, sizeof(isql),
|
||||
"ALTER TABLE \"%s\" ADD COLUMN connected INTEGER NOT NULL DEFAULT 0", tbl);
|
||||
sqlite3_exec(db, isql, NULL, NULL, NULL);
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_node_sqlite tables initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq, time_t now_sec) {
|
||||
if (!db || !nq) return -1;
|
||||
struct TOPO_NODE* ni = topo_node_registry_find(groups, nq->node_id);
|
||||
if (!ni) return -1;
|
||||
|
||||
sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, last_seen_at, online)"
|
||||
" VALUES(?,?,?,?,?,1)"
|
||||
" ON CONFLICT(node_id) DO UPDATE SET"
|
||||
" name=CASE WHEN excluded.name != '' THEN excluded.name ELSE nodes.name END, x25519_pubkey=excluded.x25519_pubkey,"
|
||||
" ed25519_pubkey=excluded.ed25519_pubkey, last_seen_at=excluded.last_seen_at,\n"
|
||||
" created_at=COALESCE(nodes.created_at, excluded.created_at)",
|
||||
-1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_bind_text(stmt, 2, ni->node_name ? ni->node_name : "", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, ni->public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 4, ni->ed25519_public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 5, (sqlite3_int64)now_sec);
|
||||
sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_put: nid=%016llx COMMITTED (nodes only, addrs via member_sync)",
|
||||
(unsigned long long)ni->node_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
const char* name, uint64_t owner_node_id,
|
||||
const uint8_t* x25519_pub, const uint8_t* x25519_priv,
|
||||
const uint8_t* ed25519_pub, const uint8_t* ed25519_priv,
|
||||
const uint8_t* signature) {
|
||||
if (!db || !channel_id || !name || !x25519_pub || !ed25519_pub || !signature) return -1;
|
||||
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
|
||||
const char* ddl_tmpl =
|
||||
"CREATE TABLE IF NOT EXISTS \"%s\" ("
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT," /* суррогатный ключ */
|
||||
" node_id INTEGER NOT NULL UNIQUE," /* уникальный ID узла (64-bit) */
|
||||
" x25519_pubkey BLOB NOT NULL," /* X25519 публичный ключ (32 байта) */
|
||||
" ed25519_pubkey BLOB NOT NULL," /* Ed25519 публичный ключ (32 байта) — верификация join_sig/update_sig */
|
||||
" join_sig BLOB NOT NULL," /* Ed25519 подпись присоединения (PEERS_JOIN_SIG_SIZE=64).
|
||||
signed: ch_x25519(32)||ch_ed25519(32)||node_id(8 LE)||x25519(32)||join_ts(8 LE) */
|
||||
" join_ts INTEGER NOT NULL," /* NTP timestamp подписи join_sig — защита от replay */
|
||||
" update_sig BLOB," /* Ed25519 подпись обновления userinfo (PEERS_UPDATE_SIG_SIZE=64).
|
||||
signed: join_sig(64)||update_ts(8 LE)||userinfo(NUL-terminated) */
|
||||
" update_ts INTEGER DEFAULT 0," /* NTP timestamp подписи update_sig */
|
||||
" userinfo TEXT NOT NULL DEFAULT ''," /* JSON с информацией о пользователе: {"name":"<display_name>"} */
|
||||
" creator_sig BLOB," /* Ed25519 подпись создателя (PEERS_JOIN_SIG_SIZE=64), пока заглушка */
|
||||
" adm_tags TEXT," /* теги администратора, key=value через пробел/запятую.
|
||||
сейчас: "supernode=yes" → node_type=4 */
|
||||
" adm_tags_sig BLOB," /* Ed25519 подпись над adm_tags (64 байта) — верификация пока не реализована */
|
||||
" node_type INTEGER NOT NULL DEFAULT 0," /* вычисляемый тип узла (topo_node_sqlite_nodeinfo_updated):
|
||||
0=неизвестно 1=прямой(addr_type NETIF/DIRECT/NAT_STRICT)
|
||||
2=EIM NAT 3=(не исп.) 4=суперузел(DIRECT+"supernode=yes") */
|
||||
" node_RTT INTEGER," /* RTT до узла в мс, часть индекса (node_type, node_RTT) */
|
||||
" connected INTEGER NOT NULL DEFAULT 0," /* 1 = было активное подключение к узлу */
|
||||
" storage INTEGER NOT NULL DEFAULT 0," /* 1 = приоритетный хранитель аттачей */
|
||||
" local_nick TEXT" /* локальный ник (GUI only, не синхронизируется) */
|
||||
")";
|
||||
|
||||
int ddl_sz = snprintf(NULL, 0, ddl_tmpl, peers_tbl);
|
||||
char* ddl = u_malloc(ddl_sz + 1);
|
||||
if (ddl) {
|
||||
snprintf(ddl, ddl_sz + 1, ddl_tmpl, peers_tbl);
|
||||
char* err = NULL;
|
||||
sqlite3_exec(db, ddl, NULL, NULL, &err);
|
||||
if (err) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_node_sqlite_channel_put peers: %s", err); sqlite3_free(err); }
|
||||
u_free(ddl);
|
||||
}
|
||||
|
||||
int idx_sz = snprintf(NULL, 0,
|
||||
"CREATE INDEX IF NOT EXISTS \"idx_%s_type_rtt\" ON \"%s\" (node_type, node_RTT)", peers_tbl, peers_tbl);
|
||||
char* idx_sql = u_malloc(idx_sz + 1);
|
||||
if (idx_sql) {
|
||||
snprintf(idx_sql, idx_sz + 1,
|
||||
"CREATE INDEX IF NOT EXISTS \"idx_%s_type_rtt\" ON \"%s\" (node_type, node_RTT)", peers_tbl, peers_tbl);
|
||||
sqlite3_exec(db, idx_sql, NULL, NULL, NULL);
|
||||
u_free(idx_sql);
|
||||
}
|
||||
|
||||
int sidx_sz = snprintf(NULL, 0,
|
||||
"CREATE INDEX IF NOT EXISTS \"idx_%s_storage\" ON \"%s\" (storage)", peers_tbl, peers_tbl);
|
||||
char* sidx_sql = u_malloc(sidx_sz + 1);
|
||||
if (sidx_sql) {
|
||||
snprintf(sidx_sql, sidx_sz + 1,
|
||||
"CREATE INDEX IF NOT EXISTS \"idx_%s_storage\" ON \"%s\" (storage)", peers_tbl, peers_tbl);
|
||||
sqlite3_exec(db, sidx_sql, NULL, NULL, NULL);
|
||||
u_free(sidx_sql);
|
||||
}
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT OR REPLACE INTO channels(channel_id, name, owner_node_id,"
|
||||
" x25519_pubkey, x25519_privkey, ed25519_pubkey, ed25519_privkey, signature)"
|
||||
" VALUES(?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)owner_node_id);
|
||||
sqlite3_bind_blob(stmt, 4, x25519_pub, 32, SQLITE_STATIC);
|
||||
if (x25519_priv) sqlite3_bind_blob(stmt, 5, x25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 5);
|
||||
sqlite3_bind_blob(stmt, 6, ed25519_pub, 32, SQLITE_STATIC);
|
||||
if (ed25519_priv) sqlite3_bind_blob(stmt, 7, ed25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 7);
|
||||
sqlite3_bind_blob(stmt, 8, signature, 64, SQLITE_STATIC);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return rc == SQLITE_DONE ? 0 : -1;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
const uint8_t* join_sig, uint64_t join_ts,
|
||||
const uint8_t* update_sig, uint64_t update_ts,
|
||||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* userinfo,
|
||||
const uint8_t* creator_sig,
|
||||
const char* adm_tags, const uint8_t* adm_tags_sig, int storage) {
|
||||
if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT OR REPLACE INTO \"%s\"(node_id, x25519_pubkey, ed25519_pubkey,"
|
||||
" join_sig, join_ts, update_sig, update_ts, userinfo, creator_sig)"
|
||||
" VALUES(?,?,?,?,?,?,?,?,?)", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
sqlite3_bind_blob(stmt, 2, x25519_pubkey, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, ed25519_pubkey, 32, SQLITE_STATIC);
|
||||
if (join_sig) sqlite3_bind_blob(stmt, 4, join_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC);
|
||||
else { static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; sqlite3_bind_blob(stmt, 4, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); }
|
||||
sqlite3_bind_int64(stmt, 5, (sqlite3_int64)join_ts);
|
||||
if (update_sig) sqlite3_bind_blob(stmt, 6, update_sig, PEERS_UPDATE_SIG_SIZE, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 6);
|
||||
sqlite3_bind_int64(stmt, 7, (sqlite3_int64)update_ts);
|
||||
sqlite3_bind_text(stmt, 8, userinfo ? userinfo : "", -1, SQLITE_STATIC);
|
||||
if (creator_sig) sqlite3_bind_blob(stmt, 9, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 9);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
if (rc != SQLITE_DONE) return -1;
|
||||
|
||||
if (adm_tags || adm_tags_sig || storage > 0) {
|
||||
snprintf(sql, sizeof(sql),
|
||||
"UPDATE \"%s\" SET adm_tags=?, adm_tags_sig=?, storage=? WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* u = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &u, NULL) == SQLITE_OK) {
|
||||
if (adm_tags && adm_tags[0]) sqlite3_bind_text(u, 1, adm_tags, -1, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(u, 1);
|
||||
if (adm_tags_sig) sqlite3_bind_blob(u, 2, adm_tags_sig, 64, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(u, 2);
|
||||
sqlite3_bind_int(u, 3, storage);
|
||||
sqlite3_bind_int64(u, 4, (sqlite3_int64)node_id);
|
||||
sqlite3_step(u); sqlite3_finalize(u);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_update_verified(sqlite3* db, uint64_t node_id,
|
||||
const char* name, const uint8_t* x25519, const uint8_t* ed25519, uint64_t update_ts, time_t created_at) {
|
||||
if (!db || !x25519 || !ed25519) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, created_at, update_ts, online)"
|
||||
" VALUES(?,?,?,?,?,?,1)"
|
||||
" ON CONFLICT(node_id) DO UPDATE SET"
|
||||
" name=CASE WHEN excluded.name IS NOT NULL AND excluded.name != ''"
|
||||
" THEN excluded.name ELSE nodes.name END,"
|
||||
" x25519_pubkey=excluded.x25519_pubkey,"
|
||||
" ed25519_pubkey=excluded.ed25519_pubkey,"
|
||||
" created_at=COALESCE(nodes.created_at, excluded.created_at),"
|
||||
" update_ts=CASE WHEN nodes.update_ts IS NULL OR excluded.update_ts > nodes.update_ts"
|
||||
" THEN excluded.update_ts ELSE nodes.update_ts END",
|
||||
-1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
sqlite3_bind_text(stmt, 2, name && name[0] ? name : "", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, x25519, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 4, ed25519, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 5, (sqlite3_int64)created_at);
|
||||
sqlite3_bind_int64(stmt, 6, (sqlite3_int64)update_ts);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
uint8_t* join_sig_out, uint64_t* join_ts_out) {
|
||||
if (!db || !channel_id || !join_sig_out || !join_ts_out) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql), "SELECT join_sig, join_ts FROM \"%s\" WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
int rc = -1;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const uint8_t* sig = (const uint8_t*)sqlite3_column_blob(stmt, 0);
|
||||
if (sig) { memcpy(join_sig_out, sig, PEERS_JOIN_SIG_SIZE); *join_ts_out = (uint64_t)sqlite3_column_int64(stmt, 1); rc = 0; }
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id) {
|
||||
if (!db || !channel_id) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql), "DELETE FROM \"%s\" WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
char* name_out, int name_sz, uint64_t* owner_node_id,
|
||||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature) {
|
||||
if (!db || !channel_id) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name, owner_node_id, x25519_pubkey, ed25519_pubkey, signature"
|
||||
" FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
|
||||
if (name_out && name_sz > 0) {
|
||||
const unsigned char* txt = sqlite3_column_text(stmt, 0);
|
||||
if (txt) snprintf(name_out, name_sz, "%s", txt); else name_out[0] = '\0';
|
||||
}
|
||||
if (owner_node_id) *owner_node_id = (uint64_t)sqlite3_column_int64(stmt, 1);
|
||||
if (x25519_pub) memcpy(x25519_pub, sqlite3_column_blob(stmt, 2), 32);
|
||||
if (ed25519_pub) memcpy(ed25519_pub, sqlite3_column_blob(stmt, 3), 32);
|
||||
if (signature) memcpy(signature, sqlite3_column_blob(stmt, 4), 64);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint8_t* buf, size_t buf_sz, size_t* out_len) {
|
||||
if (!db || !channel_id || !buf || !out_len) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT node_id, x25519_pubkey, ed25519_pubkey,"
|
||||
" join_sig, join_ts, update_sig, update_ts, userinfo, adm_tags, adm_tags_sig"
|
||||
" FROM \"%s\" ORDER BY node_id ASC", peers_tbl);
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
|
||||
size_t off = 0;
|
||||
if (off + 2 > buf_sz) { sqlite3_finalize(stmt); return -2; }
|
||||
uint16_t* cnt_ptr = (uint16_t*)(buf + off); off += 2;
|
||||
*cnt_ptr = 0;
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
uint64_t node_id = (uint64_t)sqlite3_column_int64(stmt, 0);
|
||||
const uint8_t* x25519 = (const uint8_t*)sqlite3_column_blob(stmt, 1);
|
||||
const uint8_t* ed_pub = (const uint8_t*)sqlite3_column_blob(stmt, 2);
|
||||
const uint8_t* join_sig = (const uint8_t*)sqlite3_column_blob(stmt, 3);
|
||||
uint64_t join_ts = (uint64_t)sqlite3_column_int64(stmt, 4);
|
||||
const uint8_t* update_sig = (const uint8_t*)sqlite3_column_blob(stmt, 5);
|
||||
uint64_t update_ts = (uint64_t)sqlite3_column_int64(stmt, 6);
|
||||
const char* puserinfo = (const char*)sqlite3_column_text(stmt, 7);
|
||||
const char* adm_tags_s = (const char*)sqlite3_column_text(stmt, 8);
|
||||
const uint8_t* adm_tags_sig_b = (const uint8_t*)sqlite3_column_blob(stmt, 9);
|
||||
if (!x25519 || !ed_pub) continue;
|
||||
uint8_t nl = puserinfo ? (uint8_t)strnlen(puserinfo, 255) : 0;
|
||||
uint8_t atl = adm_tags_s ? (uint8_t)strnlen(adm_tags_s, 255) : 0;
|
||||
|
||||
sqlite3_stmt* ast = NULL;
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT family, socket_id, protocol, address, port FROM node_addresses"
|
||||
" WHERE node_id=? LIMIT 16");
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &ast, NULL) != SQLITE_OK) continue;
|
||||
sqlite3_bind_int64(ast, 1, (sqlite3_int64)node_id);
|
||||
|
||||
uint8_t addr_cnt = 0; size_t addr_start = 0;
|
||||
while (sqlite3_step(ast) == SQLITE_ROW && addr_cnt < 255) {
|
||||
int family = sqlite3_column_int(ast, 0);
|
||||
int sock_id = sqlite3_column_int(ast, 1);
|
||||
int proto = sqlite3_column_int(ast, 2);
|
||||
int ip_len = (family == 4) ? 4 : 16;
|
||||
const uint8_t* ip = (const uint8_t*)sqlite3_column_blob(ast, 3);
|
||||
uint16_t port = (uint16_t)sqlite3_column_int(ast, 4);
|
||||
if (!ip) continue;
|
||||
if (off + 1 + 1 + 1 + ip_len + 2 > buf_sz) { sqlite3_finalize(ast); sqlite3_finalize(stmt); return -2; }
|
||||
if (addr_cnt == 0) addr_start = off;
|
||||
buf[off++] = (uint8_t)family;
|
||||
buf[off++] = (uint8_t)sock_id;
|
||||
buf[off++] = (uint8_t)proto;
|
||||
memcpy(buf + off, ip, (size_t)ip_len); off += (size_t)ip_len;
|
||||
buf[off++] = (uint8_t)((port >> 8) & 0xFF);
|
||||
buf[off++] = (uint8_t)(port & 0xFF);
|
||||
addr_cnt++;
|
||||
}
|
||||
sqlite3_finalize(ast);
|
||||
|
||||
uint8_t flags = (join_sig && join_ts) ? PEERS_FLAG_HAS_JOIN : 0;
|
||||
size_t peer_needed = 8 + 32 + 32 + 1 + (flags & PEERS_FLAG_HAS_JOIN ? 64+8 : 0) + 64 + 8 + 1 + (size_t)nl + 1 + 1 + (size_t)atl + 64;
|
||||
size_t addr_sz = off - addr_start;
|
||||
if (addr_cnt > 0) {
|
||||
if (off + peer_needed > buf_sz) { sqlite3_finalize(stmt); return -2; }
|
||||
memmove(buf + addr_start + peer_needed, buf + addr_start, addr_sz);
|
||||
off = addr_start;
|
||||
} else {
|
||||
if (off + peer_needed + 1 > buf_sz) { sqlite3_finalize(stmt); return -2; }
|
||||
addr_sz = 0;
|
||||
}
|
||||
memcpy(buf + off, &node_id, 8); off += 8;
|
||||
memcpy(buf + off, x25519, 32); off += 32;
|
||||
memcpy(buf + off, ed_pub, 32); off += 32;
|
||||
buf[off++] = flags;
|
||||
if (flags & PEERS_FLAG_HAS_JOIN) {
|
||||
memcpy(buf + off, join_sig, 64); off += 64;
|
||||
memcpy(buf + off, &join_ts, 8); off += 8;
|
||||
}
|
||||
if (update_sig && update_ts) {
|
||||
memcpy(buf + off, update_sig, 64); off += 64;
|
||||
memcpy(buf + off, &update_ts, 8); off += 8;
|
||||
} else {
|
||||
memset(buf + off, 0, 64); off += 64;
|
||||
uint64_t z = 0; memcpy(buf + off, &z, 8); off += 8;
|
||||
}
|
||||
buf[off++] = nl;
|
||||
if (nl) { memcpy(buf + off, puserinfo, nl); off += nl; }
|
||||
buf[off++] = addr_cnt;
|
||||
buf[off++] = atl;
|
||||
if (atl) { memcpy(buf + off, adm_tags_s, atl); off += atl; }
|
||||
if (adm_tags_sig_b && sqlite3_column_bytes(stmt, 9) >= 64) { memcpy(buf + off, adm_tags_sig_b, 64); off += 64; }
|
||||
else { memset(buf + off, 0, 64); off += 64; }
|
||||
off += addr_sz;
|
||||
(*cnt_ptr)++;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
*out_len = off;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online) {
|
||||
if (!db) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "UPDATE nodes SET online=? WHERE node_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int(stmt, 1, online ? 1 : 0);
|
||||
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)node_id);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_get_online(sqlite3* db, uint64_t node_id) {
|
||||
if (!db) return 0;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT online FROM nodes WHERE node_id=?", -1, &stmt, NULL) != SQLITE_OK) return 0;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
int online = 0;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) online = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return online;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_ed25519_pubkey(sqlite3* db, uint64_t node_id, uint8_t pubkey_out[32])
|
||||
{
|
||||
if (!db || !pubkey_out) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT ed25519_pubkey FROM nodes WHERE node_id=?",
|
||||
-1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
int rc = -1;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const void* b = sqlite3_column_blob(stmt, 0);
|
||||
int bytes = sqlite3_column_bytes(stmt, 0);
|
||||
if (b && bytes >= 32) { uint64_t chk; memcpy(&chk, b, 8); if (chk != 0) { memcpy(pubkey_out, b, 32); rc = 0; } }
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_nodeinfo_updated(sqlite3* db, uint64_t node_id) {
|
||||
if (!db) return -1;
|
||||
|
||||
int best_at = -1;
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT addr_type FROM node_addresses WHERE node_id=?", -1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id);
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
int at = sqlite3_column_int(s, 0);
|
||||
if (at == ADDR_TYPE_DIRECT) { best_at = ADDR_TYPE_DIRECT; break; }
|
||||
if (best_at < 0 || at < best_at) best_at = at;
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
|
||||
static const int at2type[] = { [ADDR_TYPE_NETIF]=1, [ADDR_TYPE_DIRECT]=1, [ADDR_TYPE_NAT_EIM]=2, [ADDR_TYPE_NAT_STRICT]=1 };
|
||||
int base_type = (best_at < 0) ? 0 : at2type[best_at];
|
||||
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'peers\\_%' ESCAPE '\\'",
|
||||
-1, &s, NULL) != SQLITE_OK) return -1;
|
||||
|
||||
char sql[256];
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const char* tbl = (const char*)sqlite3_column_text(s, 0);
|
||||
|
||||
int node_type = base_type; int is_storage = 0;
|
||||
if (best_at == ADDR_TYPE_DIRECT && base_type == 1) {
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT adm_tags FROM \"%s\" WHERE node_id=?", tbl);
|
||||
sqlite3_stmt* a = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &a, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(a, 1, (sqlite3_int64)node_id);
|
||||
if (sqlite3_step(a) == SQLITE_ROW) {
|
||||
const char* tags = (const char*)sqlite3_column_text(a, 0);
|
||||
if (tags) {
|
||||
if (strstr(tags, "supernode=yes")) node_type = 4;
|
||||
if (strstr(tags, "storage=yes")) is_storage = 1;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(a);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(sql, sizeof(sql),
|
||||
"UPDATE \"%s\" SET node_type=?, storage=? WHERE node_id=?", tbl);
|
||||
sqlite3_stmt* u = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &u, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int(u, 1, node_type);
|
||||
sqlite3_bind_int(u, 2, is_storage);
|
||||
sqlite3_bind_int64(u, 3, (sqlite3_int64)node_id);
|
||||
sqlite3_step(u); sqlite3_finalize(u);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "nodeinfo_updated: node=%016llx best_at=%d base_type=%d",
|
||||
(unsigned long long)node_id, best_at, base_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void topo_node_sqlite_update_rtt(sqlite3* db, uint64_t node_id, uint16_t rtt) {
|
||||
if (!db) return;
|
||||
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"UPDATE node_addresses SET rtt=? WHERE node_id=?",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int(s, 1, (int)rtt);
|
||||
sqlite3_bind_int64(s, 2, (sqlite3_int64)node_id);
|
||||
sqlite3_step(s);
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'peers\\_%' ESCAPE '\\'",
|
||||
-1, &s, NULL) != SQLITE_OK) return;
|
||||
|
||||
char sql[256];
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const char* tbl = (const char*)sqlite3_column_text(s, 0);
|
||||
snprintf(sql, sizeof(sql),
|
||||
"UPDATE \"%s\" SET node_RTT=? WHERE node_id=?", tbl);
|
||||
sqlite3_stmt* u = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &u, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int(u, 1, (int)rtt);
|
||||
sqlite3_bind_int64(u, 2, (sqlite3_int64)node_id);
|
||||
sqlite3_step(u); sqlite3_finalize(u);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "sqlite_update_rtt: node=%016llx rtt=%u",
|
||||
(unsigned long long)node_id, (unsigned)rtt);
|
||||
}
|
||||
|
||||
struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* groups, uint64_t node_id) {
|
||||
if (!db || !groups) return NULL;
|
||||
|
||||
uint8_t pubkey[32] = {0}, ed_pubkey[32] = {0};
|
||||
char* name = NULL;
|
||||
int found = 0;
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT name,x25519_pubkey,ed25519_pubkey FROM nodes WHERE node_id=?",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id);
|
||||
if (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const char* n = (const char*)sqlite3_column_text(s, 0);
|
||||
if (n) name = u_strdup(n);
|
||||
const void* pk = sqlite3_column_blob(s, 1);
|
||||
if (pk && sqlite3_column_bytes(s, 1) >= 32) memcpy(pubkey, pk, 32);
|
||||
const void* ep = sqlite3_column_blob(s, 2);
|
||||
if (ep && sqlite3_column_bytes(s, 2) >= 32) memcpy(ed_pubkey, ep, 32);
|
||||
found = 1;
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
if (!found) { u_free(name); return NULL; }
|
||||
|
||||
struct TOPO_ADDR4* v4_head = NULL; struct TOPO_ADDR4* v4_tail = NULL;
|
||||
uint8_t sock_id_counter = 0;
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT address,port,addr_type,socket_id FROM node_addresses"
|
||||
" WHERE node_id=? AND family=4 AND protocol=1 ORDER BY addr_type",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id);
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const void* addr = sqlite3_column_blob(s, 0);
|
||||
if (!addr || sqlite3_column_bytes(s, 0) < 4) continue;
|
||||
int port = sqlite3_column_int(s, 1);
|
||||
int at = sqlite3_column_int(s, 2);
|
||||
int sock_id_raw = sqlite3_column_int(s, 3);
|
||||
uint8_t sock_id = (uint8_t)(sock_id_raw ? sock_id_raw : ++sock_id_counter);
|
||||
uint8_t type = (at == ADDR_TYPE_NETIF) ? TOPO_ADDR_INTERFACE : TOPO_ADDR_NAT;
|
||||
struct TOPO_ADDR4* a4 = memory_pool_alloc(groups->v4_addr_pool);
|
||||
if (!a4) continue;
|
||||
memset(a4, 0, sizeof(*a4));
|
||||
memcpy(a4->addr, addr, 4); a4->port = (uint16_t)port;
|
||||
a4->type = type; a4->socket_id = sock_id; a4->protocol = TOPO_PROTO_UDP;
|
||||
if (!v4_head) v4_head = v4_tail = a4;
|
||||
else { v4_tail->next = a4; v4_tail = a4; }
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
/* IPv6 addresses */
|
||||
struct TOPO_ADDR6* v6_head = NULL; struct TOPO_ADDR6* v6_tail = NULL;
|
||||
{
|
||||
sqlite3_stmt* s = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT address,port,addr_type,socket_id FROM node_addresses"
|
||||
" WHERE node_id=? AND family=6 AND protocol=1 ORDER BY addr_type",
|
||||
-1, &s, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(s, 1, (sqlite3_int64)node_id);
|
||||
while (sqlite3_step(s) == SQLITE_ROW) {
|
||||
const void* addr = sqlite3_column_blob(s, 0);
|
||||
if (!addr || sqlite3_column_bytes(s, 0) < 16) continue;
|
||||
int port = sqlite3_column_int(s, 1);
|
||||
int at = sqlite3_column_int(s, 2);
|
||||
int sock_id_raw = sqlite3_column_int(s, 3);
|
||||
uint8_t sock_id = (uint8_t)(sock_id_raw ? sock_id_raw : ++sock_id_counter);
|
||||
uint8_t type = (at == ADDR_TYPE_NETIF) ? TOPO_ADDR_INTERFACE : TOPO_ADDR_NAT;
|
||||
struct TOPO_ADDR6* a6 = memory_pool_alloc(groups->v6_addr_pool);
|
||||
if (!a6) continue;
|
||||
memset(a6, 0, sizeof(*a6));
|
||||
memcpy(a6->addr, addr, 16); a6->port = (uint16_t)port;
|
||||
a6->type = type; a6->socket_id = sock_id; a6->protocol = TOPO_PROTO_UDP;
|
||||
if (!v6_head) v6_head = v6_tail = a6;
|
||||
else { v6_tail->next = a6; v6_tail = a6; }
|
||||
}
|
||||
sqlite3_finalize(s);
|
||||
}
|
||||
}
|
||||
if (!v4_head && !v6_head) { u_free(name); DEBUG_WARN(DEBUG_CATEGORY_BGP, "node_load: node 0x%016llx has no addresses (may be stub/seed — will retry when addresses arrive)", (unsigned long long)node_id); return NULL; }
|
||||
|
||||
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
|
||||
if (!ni) { u_free(name); return NULL; }
|
||||
ni->node_id = node_id; ni->ver = 1; ni->group_ref_count = 0;
|
||||
memcpy(ni->public_key, pubkey, 32); memcpy(ni->ed25519_public_key, ed_pubkey, 32);
|
||||
ni->node_name = name;
|
||||
ni->v4_addrs = v4_head;
|
||||
ni->v6_addrs = v6_head;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_load: node 0x%016llx loaded from DB name=%s v4=%d v6=%d",
|
||||
(unsigned long long)node_id, name ? name : "",
|
||||
topo_list_count((struct _topo_head*)v4_head),
|
||||
topo_list_count((struct _topo_head*)v6_head));
|
||||
return ni;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_set_connected(sqlite3* db, const char* channel_id, uint64_t node_id, int connected) {
|
||||
if (!db || !channel_id) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[128]; snprintf(sql, sizeof(sql), "UPDATE \"%s\" SET connected=? WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int(st, 1, connected ? 1 : 0);
|
||||
sqlite3_bind_int64(st, 2, (sqlite3_int64)node_id);
|
||||
int rc = sqlite3_step(st); sqlite3_finalize(st);
|
||||
return (rc == SQLITE_DONE) ? 0 : -1;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_connected_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[128]; snprintf(sql, sizeof(sql), "SELECT node_id FROM \"%s\" WHERE connected=1", peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_public_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (!db || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
(void)channel_id;
|
||||
const char* sql =
|
||||
"SELECT DISTINCT na.node_id FROM node_addresses na"
|
||||
" WHERE na.node_id!=?1 AND na.family IN (4,6) AND na.addr_type IN (1,2)"
|
||||
" AND EXISTS (SELECT 1 FROM nodes n WHERE n.node_id=na.node_id)";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)self_node_id);
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count) {
|
||||
if (!db || !channel_id || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256]; snprintf(sql, sizeof(sql),
|
||||
"SELECT p.node_id FROM \"%s\" p WHERE p.node_type=4"
|
||||
" AND EXISTS (SELECT 1 FROM node_addresses na WHERE na.node_id=p.node_id AND na.family IN (4,6))",
|
||||
peers_tbl);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_get_local_peers(sqlite3* db, const char* channel_id,
|
||||
uint64_t** out_ids, int* out_count, uint64_t self_node_id) {
|
||||
if (!db || !out_ids || !out_count) return -1;
|
||||
*out_ids = NULL; *out_count = 0;
|
||||
(void)channel_id;
|
||||
const char* sql =
|
||||
"SELECT DISTINCT na.node_id FROM node_addresses na"
|
||||
" WHERE na.node_id!=?1 AND na.family IN (4,6) AND na.addr_type IN (0,3)"
|
||||
" AND EXISTS (SELECT 1 FROM nodes n WHERE n.node_id=na.node_id)";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)self_node_id);
|
||||
int cnt = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) cnt++;
|
||||
if (cnt == 0) { sqlite3_finalize(st); return 0; }
|
||||
uint64_t* ids = u_malloc((size_t)cnt * sizeof(uint64_t));
|
||||
if (!ids) { sqlite3_finalize(st); return -1; }
|
||||
sqlite3_reset(st); int i = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) ids[i++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
*out_ids = ids; *out_count = cnt;
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user