media: validate SQLite records before local block announcements
This commit is contained in:
1 parent
1a212489fd
commit
81fc4291db
2 files changed
+45
-16
No files matched your search
@@ -1617,27 +1617,53 @@ void media_delivery_announce_local_blocks(struct UTUN_INSTANCE* inst) {
|
||||
media_delivery_refresh_roles(inst);
|
||||
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT media_id, block_id, chunk, chat_id FROM media_files WHERE node_id=?",
|
||||
if (sqlite3_prepare_v2(db, "SELECT id,media_id,block_id,chunk,chat_id FROM media_files WHERE node_id=?",
|
||||
-1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: announce_local_blocks prepare failed: %s", MD_ID, sqlite3_errmsg(db));
|
||||
return;
|
||||
}
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)inst->node_id);
|
||||
int rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)inst->node_id);
|
||||
|
||||
int announced = 0;
|
||||
while (sqlite3_step(st) == SQLITE_ROW) {
|
||||
const uint8_t* media_id = (const uint8_t*)sqlite3_column_blob(st, 0);
|
||||
const uint8_t* block_id = (const uint8_t*)sqlite3_column_blob(st, 1);
|
||||
uint32_t chunk = (uint32_t)sqlite3_column_int(st, 2);
|
||||
const char* chat_id = (const char*)sqlite3_column_text(st, 3);
|
||||
if (!media_id || !block_id || !chat_id || !chat_id[0]) continue;
|
||||
uint64_t gid = strtoull(chat_id, NULL, 10);
|
||||
if (gid == 0) continue;
|
||||
uint64_t rows = 0, eligible = 0, skipped = 0;
|
||||
if (rc == SQLITE_OK) while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
|
||||
rows++;
|
||||
sqlite3_int64 row = sqlite3_column_int64(st, 0), chunk = sqlite3_column_int64(st, 3);
|
||||
int media_type = sqlite3_column_type(st, 1), block_type = sqlite3_column_type(st, 2);
|
||||
int chunk_type = sqlite3_column_type(st, 3), chat_type = sqlite3_column_type(st, 4);
|
||||
int media_bytes = sqlite3_column_bytes(st, 1), block_bytes = sqlite3_column_bytes(st, 2);
|
||||
int chat_bytes = sqlite3_column_bytes(st, 4);
|
||||
const uint8_t* media = media_type == SQLITE_BLOB && media_bytes == 16 ? sqlite3_column_blob(st, 1) : NULL;
|
||||
const uint8_t* block = block_type == SQLITE_BLOB && block_bytes == 16 ? sqlite3_column_blob(st, 2) : NULL;
|
||||
const char* chat = chat_type == SQLITE_TEXT ? (const char*)sqlite3_column_text(st, 4) : NULL;
|
||||
uint64_t gid = 0;
|
||||
int valid_chat = chat && chat_bytes > 0 && chat_bytes <= 20 && chat[0] != '0';
|
||||
for (int i = 0; valid_chat && i < chat_bytes; i++) {
|
||||
unsigned digit = (unsigned char)chat[i] - '0';
|
||||
if (digit > 9 || gid > (UINT64_MAX - digit) / 10) valid_chat = 0;
|
||||
else gid = gid * 10 + digit;
|
||||
}
|
||||
const char* invalid = !media ? "media_id" : !block ? "block_id"
|
||||
: chunk_type != SQLITE_INTEGER || chunk < 0 || chunk > UINT32_MAX ? "chunk" : !valid_chat ? "chat_id" : NULL;
|
||||
if (invalid) {
|
||||
skipped++;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA,
|
||||
"%s: invalid local block row=%lld field=%s media_type=%d media_bytes=%d block_type=%d block_bytes=%d "
|
||||
"chunk_type=%d chunk=%lld chat_type=%d chat_bytes=%d", MD_ID, (long long)row, invalid,
|
||||
media_type, media_bytes, block_type, block_bytes, chunk_type, (long long)chunk, chat_type, chat_bytes);
|
||||
continue;
|
||||
}
|
||||
uint8_t media_id[16], block_id[16];
|
||||
memcpy(media_id, media, sizeof(media_id)); memcpy(block_id, block, sizeof(block_id));
|
||||
uint64_t supers[10]; int nsup = md_collect_supernodes(inst, gid, supers);
|
||||
for (int s = 0; s < nsup; s++)
|
||||
media_delivery_send_have_block(inst, gid, supers[s], media_id, block_id, chunk);
|
||||
announced++;
|
||||
media_delivery_send_have_block(inst, gid, supers[s], media_id, block_id, (uint32_t)chunk);
|
||||
eligible++;
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: announce_local_blocks done, blocks=%d", MD_ID, announced);
|
||||
if (md_sql_finish(db, st, rc, "enumerate local blocks")) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "%s: local block scan incomplete rows=%llu eligible=%llu skipped=%llu",
|
||||
MD_ID, (unsigned long long)rows, (unsigned long long)eligible, (unsigned long long)skipped);
|
||||
return;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "%s: local block scan complete rows=%llu eligible=%llu skipped=%llu",
|
||||
MD_ID, (unsigned long long)rows, (unsigned long long)eligible, (unsigned long long)skipped);
|
||||
}
|
||||
@@ -154,7 +154,10 @@ void media_delivery_send_have_block(struct UTUN_INSTANCE* inst, uint64_t group_i
|
||||
|
||||
/* Переанонсировать суперузлам ВСЕ локальные блоки (media_files WHERE node_id=self).
|
||||
* Вызывается при старте после синхронизации, чтобы суперузел восстановил свой
|
||||
* block_availability для уже скачанных ранее медиа. Идемпотентно на суперузле. */
|
||||
* block_availability для уже скачанных ранее медиа. Идемпотентно на суперузле.
|
||||
* Некорректные SQL-типы/длины UUID, chunk вне uint32 и неканонический ID канала пропускаются с WARN.
|
||||
* Ошибка SQL завершает обход с ERROR; уже отправленные корректные анонсы сохраняются.
|
||||
* Анонсы best-effort, void не подтверждает доставку; UUID копируются до вложенных SQL/отправок. */
|
||||
void media_delivery_announce_local_blocks(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// ── Отмена работы канала ──
|
||||
|
||||
Reference in new issue
Block a user