media_delivery: add tests + fix queue_new NULL ua and index_offset bugs
- test_media_delivery_sql: 15 tests (SQL tables, CRUD, packed structs) - test_media_delivery_download: 5 tests (chunk write/append, sig verify, assembly, cancel) - Fix: queue_new(NULL) returns NULL — pass inst->ua everywhere - Fix: queue index offset must be offsetof(struct, key_field) not 0 - Move struct media_download to header for test visibility
This commit is contained in:
1 parent
fb3e34080d
commit
a863da7737
6 files changed
+842
-116
No files matched your search
@@ -62,7 +62,10 @@ static int md_ba_insert(sqlite3* db, const uint8_t* block_uuid, uint64_t group_i
|
||||
"INSERT OR REPLACE INTO block_availability(block_uuid,group_id,node_id,chunk,timestamp)"
|
||||
" VALUES(?,?,?,?,?)";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ba_insert prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return -1;
|
||||
}
|
||||
sqlite3_bind_blob(st, 1, block_uuid, 16, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(st, 2, (sqlite3_int64)group_id);
|
||||
sqlite3_bind_int64(st, 3, (sqlite3_int64)node_id);
|
||||
@@ -70,7 +73,11 @@ static int md_ba_insert(sqlite3* db, const uint8_t* block_uuid, uint64_t group_i
|
||||
sqlite3_bind_int64(st, 5, (sqlite3_int64)ts);
|
||||
int rc = sqlite3_step(st);
|
||||
sqlite3_finalize(st);
|
||||
return (rc == SQLITE_DONE) ? 0 : -1;
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ba_insert step failed rc=%d: %s", MD_ID, rc, sqlite3_errmsg(db));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int md_ba_find_nodes(sqlite3* db, const uint8_t* block_uuid,
|
||||
@@ -78,7 +85,10 @@ static int md_ba_find_nodes(sqlite3* db, const uint8_t* block_uuid,
|
||||
const char* sql =
|
||||
"SELECT node_id, timestamp FROM block_availability WHERE block_uuid=? ORDER BY timestamp DESC LIMIT ?";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return 0;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ba_find_nodes prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
sqlite3_bind_blob(st, 1, block_uuid, 16, SQLITE_STATIC);
|
||||
sqlite3_bind_int(st, 2, max);
|
||||
int n = 0;
|
||||
@@ -96,7 +106,10 @@ static int md_ba_get_since(sqlite3* db, uint64_t since_id,
|
||||
"SELECT id,block_uuid,group_id,node_id,chunk,timestamp"
|
||||
" FROM block_availability WHERE id > ? ORDER BY id ASC LIMIT 4";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return 0;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ba_get_since prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)since_id);
|
||||
int off = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) {
|
||||
@@ -116,7 +129,10 @@ static int md_ba_get_since(sqlite3* db, uint64_t since_id,
|
||||
static uint64_t md_ss_get(sqlite3* db, uint64_t peer_node_id) {
|
||||
const char* sql = "SELECT last_recv_id FROM super_sync WHERE peer_node_id=?";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return 0;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ss_get prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)peer_node_id);
|
||||
uint64_t v = 0;
|
||||
if (sqlite3_step(st) == SQLITE_ROW) v = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
@@ -127,7 +143,10 @@ static uint64_t md_ss_get(sqlite3* db, uint64_t peer_node_id) {
|
||||
static void md_ss_set(sqlite3* db, uint64_t peer_node_id, uint64_t last_recv_id) {
|
||||
const char* sql = "INSERT OR REPLACE INTO super_sync(peer_node_id,last_recv_id) VALUES(?,?)";
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: ss_set prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return;
|
||||
}
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)peer_node_id);
|
||||
sqlite3_bind_int64(st, 2, (sqlite3_int64)last_recv_id);
|
||||
sqlite3_step(st);
|
||||
@@ -146,11 +165,11 @@ static struct media_super_peer* md_super_peer_add(struct media_delivery_ctx* md,
|
||||
struct media_super_peer* sp = md_super_peer_find(md, node_id);
|
||||
if (sp) return sp;
|
||||
if (!md->super_peers) {
|
||||
md->super_peers = queue_new(NULL, 64, 0, 8, "md_super");
|
||||
if (!md->super_peers) return NULL;
|
||||
md->super_peers = queue_new(md->inst->ua, 64, offsetof(struct media_super_peer, peer_node_id), 8, "md_super");
|
||||
if (!md->super_peers) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_new(super_peers) failed", MD_ID); return NULL; }
|
||||
}
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(struct media_super_peer));
|
||||
if (!qe) return NULL;
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new(super_peer) failed", MD_ID); return NULL; }
|
||||
sp = (struct media_super_peer*)qe->data;
|
||||
memset(sp, 0, sizeof(*sp));
|
||||
sp->peer_node_id = node_id;
|
||||
@@ -274,13 +293,13 @@ static void md_handle_serve_reg(struct media_delivery_ctx* md, uint64_t from_nod
|
||||
struct media_pkt_serve_reg* pkt = (struct media_pkt_serve_reg*)data;
|
||||
|
||||
if (!md->served_nodes) {
|
||||
md->served_nodes = queue_new(NULL, 64, 0, 8, "md_served");
|
||||
if (!md->served_nodes) return;
|
||||
md->served_nodes = queue_new(md->inst->ua, 64, offsetof(struct media_served_node, node_id), 8, "md_served");
|
||||
if (!md->served_nodes) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_new(served_nodes) failed", MD_ID); return; }
|
||||
}
|
||||
struct ll_entry* e = queue_find_data_by_index(md->served_nodes, &from_node);
|
||||
if (!e) {
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(struct media_served_node));
|
||||
if (!qe) return;
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new(served_node) failed", MD_ID); return; }
|
||||
struct media_served_node* sn = (struct media_served_node*)qe->data;
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
sn->node_id = from_node; sn->group_id = pkt->group_id;
|
||||
@@ -298,7 +317,8 @@ static void md_handle_serve_reg(struct media_delivery_ctx* md, uint64_t from_nod
|
||||
|
||||
static void md_handle_query(struct media_delivery_ctx* md, uint64_t from_node,
|
||||
const uint8_t* data, size_t len) {
|
||||
if (len < MEDIA_QUERY_HDR_SIZE || !md->db) return;
|
||||
if (len < MEDIA_QUERY_HDR_SIZE) return;
|
||||
if (!md->db) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: QUERY but no DB", MD_ID); return; }
|
||||
struct media_pkt_query* q = (struct media_pkt_query*)data;
|
||||
uint16_t nb = q->num_blocks;
|
||||
if (nb > 64) nb = 64;
|
||||
@@ -332,7 +352,8 @@ static void md_handle_query(struct media_delivery_ctx* md, uint64_t from_node,
|
||||
|
||||
static void md_handle_have_block(struct media_delivery_ctx* md, uint64_t from_node,
|
||||
const uint8_t* data, size_t len) {
|
||||
if (len < MEDIA_HAVE_BLOCK_SIZE || !md->db) return;
|
||||
if (len < MEDIA_HAVE_BLOCK_SIZE) return;
|
||||
if (!md->db) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: HAVE_BLOCK but no DB", MD_ID); return; }
|
||||
struct media_pkt_have_block* hb = (struct media_pkt_have_block*)data;
|
||||
|
||||
int rc = md_ba_insert(md->db, hb->block_id, hb->group_id, from_node, hb->chunk, hb->timestamp);
|
||||
@@ -364,7 +385,8 @@ static void md_handle_have_block(struct media_delivery_ctx* md, uint64_t from_no
|
||||
|
||||
static void md_handle_super_repl(struct media_delivery_ctx* md, uint64_t from_node,
|
||||
const uint8_t* data, size_t len) {
|
||||
if (len < MEDIA_SUPER_REPL_HDR_SIZE || !md->db) return;
|
||||
if (len < MEDIA_SUPER_REPL_HDR_SIZE) return;
|
||||
if (!md->db) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: SUPER_REPL but no DB", MD_ID); return; }
|
||||
struct media_pkt_super_repl* rp = (struct media_pkt_super_repl*)data;
|
||||
uint16_t num = rp->num_entries;
|
||||
int64_t max_id = 0;
|
||||
@@ -399,7 +421,7 @@ static void md_handle_super_ack(struct media_delivery_ctx* md, uint64_t from_nod
|
||||
struct media_pkt_super_ack* sa = (struct media_pkt_super_ack*)data;
|
||||
|
||||
struct media_super_peer* peer = md_super_peer_find(md, from_node);
|
||||
if (!peer) return;
|
||||
if (!peer) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: SUPER_ACK from unknown peer 0x%016llx", MD_ID, (unsigned long long)from_node); return; }
|
||||
|
||||
peer->peer_last_recv_id = sa->ack_seq;
|
||||
peer->inflight_count--;
|
||||
@@ -413,11 +435,12 @@ static void md_handle_super_ack(struct media_delivery_ctx* md, uint64_t from_nod
|
||||
|
||||
static void md_handle_super_hello(struct media_delivery_ctx* md, uint64_t from_node,
|
||||
const uint8_t* data, size_t len) {
|
||||
if (len < sizeof(struct media_pkt_super_hello) || !md->db) return;
|
||||
if (len < sizeof(struct media_pkt_super_hello)) return;
|
||||
if (!md->db) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: SUPER_HELLO but no DB", MD_ID); return; }
|
||||
struct media_pkt_super_hello* sh = (struct media_pkt_super_hello*)data;
|
||||
|
||||
struct media_super_peer* peer = md_super_peer_add(md, from_node);
|
||||
if (!peer) return;
|
||||
if (!peer) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: super_peer_add failed for 0x%016llx", MD_ID, (unsigned long long)from_node); return; }
|
||||
|
||||
peer->peer_last_recv_id = sh->last_recv_id;
|
||||
peer->connected = 1;
|
||||
@@ -443,7 +466,7 @@ static void md_handle_super_hello(struct media_delivery_ctx* md, uint64_t from_n
|
||||
static void md_super_conn_cb(int result, uint64_t node_id, void* arg) {
|
||||
struct media_delivery_ctx* md = (struct media_delivery_ctx*)arg;
|
||||
struct media_super_peer* peer = md_super_peer_find(md, node_id);
|
||||
if (!peer) return;
|
||||
if (!peer) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: conn_cb for unknown peer 0x%016llx", MD_ID, (unsigned long long)node_id); return; }
|
||||
|
||||
if (result != CONN_MGR_OK) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: connect to supernode 0x%016llx failed rc=%d",
|
||||
@@ -466,13 +489,16 @@ static void md_super_conn_cb(int result, uint64_t node_id, void* arg) {
|
||||
if (md_send(md->inst, node_id, (const uint8_t*)&hello, sizeof(hello)) == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: SUPER_HELLO sent to 0x%016llx last_recv=%lld",
|
||||
MD_ID, (unsigned long long)node_id, (long long)my_last);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: SUPER_HELLO send failed to 0x%016llx",
|
||||
MD_ID, (unsigned long long)node_id);
|
||||
}
|
||||
/* wait for HELLO response — md_handle_super_hello will start replication */
|
||||
}
|
||||
|
||||
static void md_super_connect(struct media_delivery_ctx* md, uint64_t peer_node_id) {
|
||||
struct media_super_peer* peer = md_super_peer_add(md, peer_node_id);
|
||||
if (!peer) return;
|
||||
if (!peer) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: super_peer_add failed for 0x%016llx", MD_ID, (unsigned long long)peer_node_id); return; }
|
||||
|
||||
struct TOPO_GROUP* grp = NULL;
|
||||
/* find group containing this peer */
|
||||
@@ -495,7 +521,10 @@ static void md_super_connect(struct media_delivery_ctx* md, uint64_t peer_node_i
|
||||
/* ── supernode start/stop ── */
|
||||
|
||||
static int md_super_start(struct media_delivery_ctx* md) {
|
||||
if (!md->inst || !md->inst->topo_sqlite_db || !md->inst->topo_groups) return -1;
|
||||
if (!md->inst || !md->inst->topo_sqlite_db || !md->inst->topo_groups) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: super_start missing inst/db/groups", MD_ID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: supernode START node_id=0x%016llx",
|
||||
MD_ID, (unsigned long long)md->self_node_id);
|
||||
@@ -524,7 +553,7 @@ static int md_super_start(struct media_delivery_ctx* md) {
|
||||
}
|
||||
|
||||
static void md_super_stop(struct media_delivery_ctx* md) {
|
||||
if (!md->db) return;
|
||||
if (!md->db) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: super_stop but no DB", MD_ID); return; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: supernode STOP, cleaning foreign blocks", MD_ID);
|
||||
|
||||
@@ -631,7 +660,7 @@ static void md_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct UTUN_INSTANCE* inst = conn->instance;
|
||||
if (!inst) return;
|
||||
struct media_delivery_ctx* md = &inst->md;
|
||||
if (!md->initialized) return;
|
||||
if (!md->initialized) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: recv before init", MD_ID); return; }
|
||||
|
||||
const uint8_t* data = entry->dgram;
|
||||
size_t len = entry->len;
|
||||
@@ -646,6 +675,8 @@ static void md_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
md_handle_serve_reg(md, from_node, data, len);
|
||||
break;
|
||||
case MEDIA_SUBCMD_SERVE_LEAVE:
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: SERVE_LEAVE from 0x%016llx",
|
||||
MD_ID, (unsigned long long)from_node);
|
||||
if (md->served_nodes) {
|
||||
struct ll_entry* e = queue_find_data_by_index(md->served_nodes, &from_node);
|
||||
if (e) { queue_remove_data(md->served_nodes, e); queue_entry_free(e); }
|
||||
@@ -653,18 +684,23 @@ static void md_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
break;
|
||||
case MEDIA_SUBCMD_QUERY:
|
||||
if (md->is_supernode) md_handle_query(md, from_node, data, len);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: QUERY ignored — not supernode", MD_ID);
|
||||
break;
|
||||
case MEDIA_SUBCMD_HAVE_BLOCK:
|
||||
if (md->is_supernode) md_handle_have_block(md, from_node, data, len);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: HAVE_BLOCK ignored — not supernode", MD_ID);
|
||||
break;
|
||||
case MEDIA_SUBCMD_SUPER_REPL:
|
||||
if (md->is_supernode) md_handle_super_repl(md, from_node, data, len);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: SUPER_REPL ignored — not supernode", MD_ID);
|
||||
break;
|
||||
case MEDIA_SUBCMD_SUPER_ACK:
|
||||
if (md->is_supernode) md_handle_super_ack(md, from_node, data, len);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: SUPER_ACK ignored — not supernode", MD_ID);
|
||||
break;
|
||||
case MEDIA_SUBCMD_SUPER_HELLO:
|
||||
if (md->is_supernode) md_handle_super_hello(md, from_node, data, len);
|
||||
else DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: SUPER_HELLO ignored — not supernode", MD_ID);
|
||||
break;
|
||||
case MEDIA_SUBCMD_QUERY_RESP:
|
||||
media_download_handle_query_resp(inst, entry->dgram, entry->len);
|
||||
@@ -727,7 +763,7 @@ static int media_delivery_create_tables(struct UTUN_INSTANCE* inst) {
|
||||
/* ── public API ── */
|
||||
|
||||
int media_delivery_init(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst) return -1;
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: init with NULL inst", MD_ID); return -1; }
|
||||
struct media_delivery_ctx* md = &inst->md;
|
||||
|
||||
memset(md, 0, sizeof(*md));
|
||||
|
||||
@@ -19,39 +19,6 @@
|
||||
|
||||
#define MDL_ID "media_download"
|
||||
|
||||
/* ── internal download state ── */
|
||||
|
||||
struct media_download {
|
||||
struct ll_entry ll; // индекс по media_id (16 байт)
|
||||
uint8_t media_id[16];
|
||||
uint64_t group_id;
|
||||
char dest_path[1024];
|
||||
char media_base[512];
|
||||
int num_blocks;
|
||||
uint8_t* block_ids; // num_blocks * 16
|
||||
uint8_t* block_sigs; // num_blocks * 64
|
||||
uint8_t content_hash[32];
|
||||
int64_t file_size;
|
||||
int64_t block_size;
|
||||
int blocks_received;
|
||||
int blocks_validated;
|
||||
int num_peers;
|
||||
struct media_download_peer peers[10];
|
||||
uint8_t active;
|
||||
uint8_t assembled;
|
||||
int err;
|
||||
|
||||
/* supernode list */
|
||||
uint64_t super_nodes[10];
|
||||
int super_count;
|
||||
int super_current;
|
||||
|
||||
void* query_timer;
|
||||
void* timeout_timer;
|
||||
void (*done_cb)(void* arg, int err);
|
||||
void* done_arg;
|
||||
};
|
||||
|
||||
/* ── helpers ── */
|
||||
|
||||
static struct media_download* md_dl_find(struct UTUN_INSTANCE* inst, const uint8_t* media_id) {
|
||||
@@ -73,12 +40,12 @@ static void md_dl_free(struct media_download* dl) {
|
||||
|
||||
static int md_dl_send(struct UTUN_INSTANCE* inst, uint64_t dst, const uint8_t* data, size_t len) {
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) return -1;
|
||||
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new failed for send to 0x%016llx", MDL_ID, (unsigned long long)dst); return -1; }
|
||||
e->dgram = u_malloc(len);
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: u_malloc(%zu) failed for send to 0x%016llx", MDL_ID, len, (unsigned long long)dst); queue_entry_free(e); return -1; }
|
||||
memcpy(e->dgram, data, len); e->len = (uint16_t)len;
|
||||
int rc = etcp_route_send(inst, 0, dst, e, 1);
|
||||
if (rc != 0) { u_free(e->dgram); queue_entry_free(e); }
|
||||
if (rc != 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: etcp_route_send failed rc=%d to 0x%016llx", MDL_ID, rc, (unsigned long long)dst); u_free(e->dgram); queue_entry_free(e); }
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -103,7 +70,7 @@ static int md_dl_send_block_req(struct UTUN_INSTANCE* inst, uint64_t dst,
|
||||
|
||||
static void md_dl_send_have_block(struct UTUN_INSTANCE* inst, struct media_download* dl, int bi) {
|
||||
uint64_t super = dl->super_count > 0 ? dl->super_nodes[dl->super_current] : 0;
|
||||
if (!super) return;
|
||||
if (!super) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: HAVE_BLOCK but no supernode available", MDL_ID); return; }
|
||||
|
||||
struct media_pkt_have_block hb;
|
||||
memset(&hb, 0, sizeof(hb));
|
||||
@@ -114,13 +81,15 @@ static void md_dl_send_have_block(struct UTUN_INSTANCE* inst, struct media_downl
|
||||
hb.chunk = (uint32_t)bi;
|
||||
hb.timestamp = (int64_t)time(NULL);
|
||||
|
||||
/* sign: block_id + chunk + timestamp + my_node_id */
|
||||
uint8_t smsg[64]; size_t soff = 0;
|
||||
memcpy(smsg + soff, dl->block_ids + bi * 16, 16); soff += 16;
|
||||
memcpy(smsg + soff, &hb.chunk, 4); soff += 4;
|
||||
memcpy(smsg + soff, &hb.timestamp, 8); soff += 8;
|
||||
uint64_t self = inst->node_id; memcpy(smsg + soff, &self, 8); soff += 8;
|
||||
sc_ed25519_sign(inst->my_ed25519_privkey, smsg, soff, hb.node_sign);
|
||||
if (sc_ed25519_sign(inst->my_ed25519_privkey, smsg, soff, hb.node_sign) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: Ed25519 sign failed for HAVE_BLOCK", MDL_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: HAVE_BLOCK to super 0x%016llx block=%d",
|
||||
MDL_ID, (unsigned long long)super, bi);
|
||||
@@ -191,7 +160,7 @@ static void md_dl_send_query(struct UTUN_INSTANCE* inst, struct media_download*
|
||||
|
||||
size_t pkt_len = MEDIA_QUERY_HDR_SIZE + (size_t)dl->num_blocks * 16;
|
||||
uint8_t* pkt = u_malloc(pkt_len);
|
||||
if (!pkt) return;
|
||||
if (!pkt) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: u_malloc(%zu) failed for QUERY pkt", MDL_ID, pkt_len); return; }
|
||||
struct media_pkt_query* q = (struct media_pkt_query*)pkt;
|
||||
memset(q, 0, sizeof(*q));
|
||||
q->subcmd = MEDIA_SUBCMD_QUERY;
|
||||
@@ -221,9 +190,8 @@ void media_download_handle_query_resp(struct UTUN_INSTANCE* inst,
|
||||
const uint8_t* bid = entries[0].block_id;
|
||||
struct media_download* dl = md_dl_find(inst, bid);
|
||||
if (!dl) {
|
||||
/* search all blocks */
|
||||
for (int i = 0; i < ne; i++) { dl = md_dl_find(inst, entries[i].block_id); if (dl) break; }
|
||||
if (!dl) return;
|
||||
if (!dl) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: QUERY_RESP — no matching download", MDL_ID); return; }
|
||||
}
|
||||
|
||||
dl->num_peers = 0;
|
||||
@@ -260,7 +228,7 @@ void media_download_handle_query_resp(struct UTUN_INSTANCE* inst,
|
||||
if (g->conn_mgr) { grp = g; break; }
|
||||
gle = gle->next;
|
||||
}
|
||||
if (!grp || !grp->conn_mgr) return;
|
||||
if (!grp || !grp->conn_mgr) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: QUERY_RESP — no conn_mgr to connect peers", MDL_ID); return; }
|
||||
|
||||
for (int pi = 0; pi < dl->num_peers; pi++) {
|
||||
conn_mgr_connect_node(grp->conn_mgr, dl->peers[pi].node_id, 0, md_dl_conn_cb, dl);
|
||||
@@ -276,26 +244,27 @@ void media_download_handle_chunk(struct UTUN_INSTANCE* inst,
|
||||
const uint8_t* d = data;
|
||||
struct media_pkt_block_chunk* ch = (struct media_pkt_block_chunk*)data;
|
||||
struct media_download* dl = md_dl_find(inst, ch->media_id);
|
||||
if (!dl || !dl->active) return;
|
||||
if (!dl) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: CHUNK for unknown media %02x%02x...", MDL_ID, ch->media_id[0], ch->media_id[1]); return; }
|
||||
if (!dl->active) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: CHUNK for inactive download", MDL_ID); return; }
|
||||
|
||||
/* find block index */
|
||||
int bi = -1;
|
||||
for (int i = 0; i < dl->num_blocks; i++) {
|
||||
if (memcmp(dl->block_ids + i * 16, ch->block_id, 16) == 0) { bi = i; break; }
|
||||
}
|
||||
if (bi < 0) return;
|
||||
if (bi < 0) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: CHUNK for unknown block %02x%02x...", MDL_ID, ch->block_id[0], ch->block_id[1]); return; }
|
||||
|
||||
/* write chunk to temp file */
|
||||
char tmp[2048];
|
||||
snprintf(tmp, sizeof(tmp), "%s.chunk_%d", dl->dest_path, bi);
|
||||
FILE* f = fopen(tmp, "ab");
|
||||
if (f) {
|
||||
size_t wlen = ch->data_len;
|
||||
if (len >= MEDIA_BLOCK_CHUNK_HDR_SIZE + wlen) {
|
||||
fwrite(d + MEDIA_BLOCK_CHUNK_HDR_SIZE, 1, wlen, f);
|
||||
}
|
||||
fclose(f);
|
||||
if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for chunk write", MDL_ID, tmp); return; }
|
||||
size_t wlen = ch->data_len;
|
||||
if (len >= MEDIA_BLOCK_CHUNK_HDR_SIZE + wlen) {
|
||||
fwrite(d + MEDIA_BLOCK_CHUNK_HDR_SIZE, 1, wlen, f);
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: CHUNK truncated: len=%zu need=%zu+%zu",
|
||||
MDL_ID, len, (size_t)MEDIA_BLOCK_CHUNK_HDR_SIZE, (size_t)wlen);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* ── handle incoming BLOCK_DONE ── */
|
||||
@@ -307,13 +276,14 @@ void media_download_handle_done(struct UTUN_INSTANCE* inst,
|
||||
const uint8_t* d = data;
|
||||
struct media_pkt_block_done* bd = (struct media_pkt_block_done*)data;
|
||||
struct media_download* dl = md_dl_find(inst, bd->media_id);
|
||||
if (!dl || !dl->active) return;
|
||||
if (!dl) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: BLOCK_DONE for unknown media", MDL_ID); return; }
|
||||
if (!dl->active) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: BLOCK_DONE for inactive download", MDL_ID); return; }
|
||||
|
||||
int bi = -1;
|
||||
for (int i = 0; i < dl->num_blocks; i++) {
|
||||
if (memcmp(dl->block_ids + i * 16, bd->block_id, 16) == 0) { bi = i; break; }
|
||||
}
|
||||
if (bi < 0) return;
|
||||
if (bi < 0) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: BLOCK_DONE for unknown block", MDL_ID); return; }
|
||||
|
||||
/* verify signature */
|
||||
EVP_PKEY* pkey = NULL;
|
||||
@@ -324,31 +294,27 @@ void media_download_handle_done(struct UTUN_INSTANCE* inst,
|
||||
char tmp[2048];
|
||||
snprintf(tmp, sizeof(tmp), "%s.chunk_%d", dl->dest_path, bi);
|
||||
FILE* f = fopen(tmp, "rb");
|
||||
if (f) {
|
||||
fseeko(f, 0, SEEK_END);
|
||||
off_t fsz = ftello(f);
|
||||
fseeko(f, 0, SEEK_SET);
|
||||
uint8_t* buf = u_malloc((size_t)fsz);
|
||||
if (buf) {
|
||||
size_t rd = fread(buf, 1, (size_t)fsz, f);
|
||||
uint64_t node_id = inst->node_id;
|
||||
uint8_t smsg[128]; size_t soff = 0;
|
||||
memcpy(smsg + soff, buf, rd); soff += rd;
|
||||
memcpy(smsg + soff, &node_id, 8); soff += 8;
|
||||
EVP_DigestVerifyInit(vctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
/* fallback: compare raw block_sig */
|
||||
sig_ok = (rd == (size_t)bd->total_size && rd > 0) ? 1 : 0;
|
||||
if (sig_ok && rd == (size_t)bd->total_size) {
|
||||
/* basic check: block_sig should match pre-computed */
|
||||
if (memcmp(bd->block_sig, dl->block_sigs + bi * 64, 64) == 0) sig_ok = 1;
|
||||
else sig_ok = 0;
|
||||
}
|
||||
u_free(buf);
|
||||
}
|
||||
fclose(f);
|
||||
if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for sig check", MDL_ID, tmp); EVP_MD_CTX_free(vctx); return; }
|
||||
fseeko(f, 0, SEEK_END);
|
||||
off_t fsz = ftello(f);
|
||||
fseeko(f, 0, SEEK_SET);
|
||||
uint8_t* buf = u_malloc((size_t)fsz);
|
||||
if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: u_malloc(%lld) failed for sig check", MDL_ID, (long long)fsz); fclose(f); EVP_MD_CTX_free(vctx); return; }
|
||||
size_t rd = fread(buf, 1, (size_t)fsz, f);
|
||||
uint64_t node_id = inst->node_id;
|
||||
uint8_t smsg[128]; size_t soff = 0;
|
||||
memcpy(smsg + soff, buf, rd); soff += rd;
|
||||
memcpy(smsg + soff, &node_id, 8); soff += 8;
|
||||
EVP_DigestVerifyInit(vctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
sig_ok = (rd == (size_t)bd->total_size && rd > 0) ? 1 : 0;
|
||||
if (sig_ok && rd == (size_t)bd->total_size) {
|
||||
if (memcmp(bd->block_sig, dl->block_sigs + bi * 64, 64) == 0) sig_ok = 1;
|
||||
else sig_ok = 0;
|
||||
}
|
||||
EVP_MD_CTX_free(vctx);
|
||||
u_free(buf);
|
||||
fclose(f);
|
||||
}
|
||||
EVP_MD_CTX_free(vctx);
|
||||
|
||||
if (sig_ok) {
|
||||
dl->blocks_received++;
|
||||
@@ -363,7 +329,8 @@ void media_download_handle_done(struct UTUN_INSTANCE* inst,
|
||||
if (dl->blocks_validated == dl->num_blocks) {
|
||||
/* assemble file */
|
||||
FILE* out = fopen(dl->dest_path, "wb");
|
||||
if (out) {
|
||||
if (!out) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for assembly", MDL_ID, dl->dest_path); }
|
||||
else {
|
||||
for (int n = 0; n < dl->num_blocks; n++) {
|
||||
char tmp2[2048];
|
||||
snprintf(tmp2, sizeof(tmp2), "%s.chunk_%d", dl->dest_path, n);
|
||||
@@ -408,12 +375,16 @@ int media_download_start(struct UTUN_INSTANCE* inst, uint64_t group_id,
|
||||
const struct media_index_result* result,
|
||||
const char* dest_path, const char* media_base,
|
||||
void (*done_cb)(void* arg, int err), void* done_arg) {
|
||||
if (!inst || !result || !dest_path || !media_base || !done_cb) return -1;
|
||||
if (!inst || !result || !dest_path || !media_base || !done_cb) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: start with NULL args inst=%p result=%p dest=%p base=%p cb=%p",
|
||||
MDL_ID, (void*)inst, (void*)result, (void*)dest_path, (void*)media_base, (void*)done_cb);
|
||||
return -1;
|
||||
}
|
||||
struct media_delivery_ctx* md = &inst->md;
|
||||
if (!md->initialized) return -1;
|
||||
if (!md->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: start before media_delivery init", MDL_ID); return -1; }
|
||||
|
||||
struct media_download* dl = u_calloc(1, sizeof(*dl));
|
||||
if (!dl) return -1;
|
||||
if (!dl) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: u_calloc for download failed", MDL_ID); return -1; }
|
||||
|
||||
memcpy(dl->media_id, result->media_id, 16);
|
||||
dl->group_id = group_id;
|
||||
@@ -429,18 +400,18 @@ int media_download_start(struct UTUN_INSTANCE* inst, uint64_t group_id,
|
||||
|
||||
dl->block_ids = u_malloc((size_t)dl->num_blocks * 16);
|
||||
dl->block_sigs = u_malloc((size_t)dl->num_blocks * 64);
|
||||
if (!dl->block_ids || !dl->block_sigs) { u_free(dl->block_ids); u_free(dl); return -1; }
|
||||
if (!dl->block_ids || !dl->block_sigs) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: u_malloc for block_ids/sigs failed", MDL_ID); u_free(dl->block_ids); u_free(dl); return -1; }
|
||||
memcpy(dl->block_ids, result->block_ids, (size_t)dl->num_blocks * 16);
|
||||
memcpy(dl->block_sigs, result->block_sigs, (size_t)dl->num_blocks * 64);
|
||||
|
||||
/* register in downloads queue */
|
||||
if (!md->downloads) {
|
||||
md->downloads = queue_new(NULL, 256, 0, 16, "md_dl");
|
||||
if (!md->downloads) { md_dl_free(dl); u_free(dl); return -1; }
|
||||
md->downloads = queue_new(inst->ua, 256, offsetof(struct media_download, media_id), 16, "md_dl");
|
||||
if (!md->downloads) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_new(downloads) failed", MDL_ID); md_dl_free(dl); u_free(dl); return -1; }
|
||||
}
|
||||
memcpy(dl->ll.data, dl->media_id, 16);
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(struct media_download));
|
||||
if (!qe) { md_dl_free(dl); u_free(dl); return -1; }
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: queue_entry_new(download) failed", MDL_ID); md_dl_free(dl); u_free(dl); return -1; }
|
||||
memcpy(qe->data, dl, sizeof(*dl));
|
||||
u_free(dl);
|
||||
queue_data_put_with_index(md->downloads, qe);
|
||||
@@ -459,7 +430,7 @@ int media_download_cancel(struct UTUN_INSTANCE* inst,
|
||||
const uint8_t* media_id, const uint8_t* block_id, uint32_t chunk) {
|
||||
(void)chunk;
|
||||
struct media_download* dl = md_dl_find(inst, media_id);
|
||||
if (!dl) return -1;
|
||||
if (!dl) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: cancel — download not found", MDL_ID); return -1; }
|
||||
|
||||
/* send CANCEL to all connected peers */
|
||||
struct media_pkt_cancel can;
|
||||
|
||||
@@ -9,6 +9,10 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct media_index_result;
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct media_index_result;
|
||||
@@ -18,16 +22,45 @@ struct media_index_result;
|
||||
|
||||
struct media_download_peer {
|
||||
uint64_t node_id;
|
||||
uint8_t connected; // 1 = conn_mgr подключён
|
||||
int num_blocks; // сколько блоков назначено этому узлу
|
||||
uint8_t connected;
|
||||
int num_blocks;
|
||||
struct {
|
||||
uint8_t block_id[16];
|
||||
uint8_t started; // 1 = BLOCK_REQ отправлен
|
||||
uint8_t received; // 1 = BLOCK_DONE получен
|
||||
uint8_t validated; // 1 = подпись проверена
|
||||
uint8_t started;
|
||||
uint8_t received;
|
||||
uint8_t validated;
|
||||
} blocks[MD_MAX_BLOCKS_PER_PEER];
|
||||
};
|
||||
|
||||
/* внутреннее состояние загрузки (доступно для тестов) */
|
||||
struct media_download {
|
||||
struct ll_entry ll;
|
||||
uint8_t media_id[16];
|
||||
uint64_t group_id;
|
||||
char dest_path[1024];
|
||||
char media_base[512];
|
||||
int num_blocks;
|
||||
uint8_t* block_ids;
|
||||
uint8_t* block_sigs;
|
||||
uint8_t content_hash[32];
|
||||
int64_t file_size;
|
||||
int64_t block_size;
|
||||
int blocks_received;
|
||||
int blocks_validated;
|
||||
int num_peers;
|
||||
struct media_download_peer peers[10];
|
||||
uint8_t active;
|
||||
uint8_t assembled;
|
||||
int err;
|
||||
uint64_t super_nodes[10];
|
||||
int super_count;
|
||||
int super_current;
|
||||
void* query_timer;
|
||||
void* timeout_timer;
|
||||
void (*done_cb)(void* arg, int err);
|
||||
void* done_arg;
|
||||
};
|
||||
|
||||
/* состояние одного стрима на стороне блок-холдера */
|
||||
struct media_block_stream {
|
||||
uint8_t media_id[16];
|
||||
|
||||
Reference in new issue
Block a user