Keep member snapshots atomic and verify escaped socket names
This commit is contained in:
1 parent
89a4630477
commit
af776deed6
2 files changed
+39
-72
No files matched your search
@@ -177,7 +177,7 @@ static int check_addresses(const char *channel) {
|
||||
/* Собственные сокеты передают профиль независимо от адреса интерфейса; тестовые порты скрыты. */
|
||||
static int check_local_sockets(void) {
|
||||
struct ETCP_SOCKET* saved = instance->etcp_sockets;
|
||||
struct ETCP_SOCKET tcp = {.sock_id = 7, .is_tcp = 1, .name = "as_wlan0_v4_tcp",
|
||||
struct ETCP_SOCKET tcp = {.sock_id = 7, .is_tcp = 1, .name = "wlan\"😀\\tcp\n",
|
||||
.address_profile = {ADDRESS_MAP_UNKNOWN, ADDRESS_IN_UNKNOWN, ADDRESS_SOURCE_ETCP}};
|
||||
struct ETCP_SOCKET udp = {.sock_id = 8, .name = "as_wlan0_v6_udp",
|
||||
.address_profile = {ADDRESS_MAP_EIM, ADDRESS_IN_OPEN, ADDRESS_SOURCE_ETCP}};
|
||||
@@ -195,6 +195,7 @@ static int check_local_sockets(void) {
|
||||
instance->etcp_sockets = saved;
|
||||
char value[128];
|
||||
REQUIRE(json && !extract(json, "$[0].nmapping", value, sizeof(value)) && !strcmp(value, "0"));
|
||||
REQUIRE(!extract(json, "$[0].nm", value, sizeof(value)) && !strcmp(value, tcp.name));
|
||||
REQUIRE(!extract(json, "$[0].nsource", value, sizeof(value)) && !strcmp(value, "1"));
|
||||
REQUIRE(!extract(json, "$[0].ad", value, sizeof(value)) && !strcmp(value, "192.168.40.250"));
|
||||
REQUIRE(!extract(json, "$[0].external", value, sizeof(value)) && !strcmp(value, "194.63.142.140"));
|
||||
@@ -301,10 +302,15 @@ static int check_snapshot_errors(const char* channel) {
|
||||
REQUIRE(sqlite3_set_authorizer(db, deny_snapshot, table) == SQLITE_OK);
|
||||
char* messages = utun_bridge_get_messages_json(channel, 10, NULL, 0);
|
||||
REQUIRE(sqlite3_set_authorizer(db, NULL, NULL) == SQLITE_OK && !messages);
|
||||
peers_table_name(channel, table, sizeof(table));
|
||||
REQUIRE(sqlite3_set_authorizer(db, deny_snapshot, table) == SQLITE_OK);
|
||||
char* members = utun_bridge_get_channel_members_json(channel);
|
||||
REQUIRE(sqlite3_set_authorizer(db, NULL, NULL) == SQLITE_OK && !members);
|
||||
messages = utun_bridge_get_messages_json(channel, 10, NULL, 0);
|
||||
channels = utun_bridge_get_channels_json();
|
||||
REQUIRE(messages && !strcmp(messages, "[]") && channels && strcmp(channels, "[]"));
|
||||
u_free(messages); u_free(channels);
|
||||
members = utun_bridge_get_channel_members_json(channel);
|
||||
REQUIRE(messages && !strcmp(messages, "[]") && channels && strcmp(channels, "[]") && members && strcmp(members, "[]"));
|
||||
u_free(messages); u_free(channels); u_free(members);
|
||||
printf("[PASS] Android snapshots: SQL denial returns failure, empty history stays valid, retry restores full snapshot\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -748,44 +748,22 @@ int utun_bridge_chat_setting_get_int(const char* name, int def) {
|
||||
char* utun_bridge_get_channel_members_json(const char* channel_id) {
|
||||
struct UTUN_INSTANCE* inst = instance_lite_get_instance();
|
||||
sqlite3* db = chat_core_get_db(inst);
|
||||
if (!chat_core_is_initialized(inst) || !db || !channel_id || !channel_id[0]) return u_strdup("[]");
|
||||
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
|
||||
sqlite3_stmt* cnt_st = NULL;
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM \"%s\"", peers_tbl);
|
||||
/* if table doesn't exist, return empty */
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &cnt_st, NULL) != SQLITE_OK) return u_strdup("[]");
|
||||
int member_count = 0;
|
||||
if (sqlite3_step(cnt_st) == SQLITE_ROW) member_count = sqlite3_column_int(cnt_st, 0);
|
||||
sqlite3_finalize(cnt_st);
|
||||
if (member_count == 0) return u_strdup("[]");
|
||||
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT p.node_id, COALESCE(n.name,''),"
|
||||
" COALESCE(p.local_nick,''), COALESCE(p.adm_tags,''),"
|
||||
" COALESCE(p.node_type,0), COALESCE(p.node_RTT,0), COALESCE(p.storage,0),"
|
||||
" COALESCE(p.userinfo,''),p.source,length(p.adm_tags_sig),COALESCE(p.deleted,0),p.radio_muted"
|
||||
" FROM \"%s\" p LEFT JOIN nodes n ON p.node_id=n.node_id"
|
||||
" ORDER BY p.node_id ASC", peers_tbl);
|
||||
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
bridge_log(BLEV_ERROR, "get_channel_members: main prepare failed ch=%s err=%s",
|
||||
channel_id, sqlite3_errmsg(db));
|
||||
return u_strdup("[]");
|
||||
if (!chat_core_is_initialized(inst) || !db || !channel_id || !channel_id[0]) {
|
||||
bridge_log(BLEV_WARN, "Members snapshot: core/database/channel unavailable"); return NULL;
|
||||
}
|
||||
|
||||
size_t cap = 4096, pos = 1;
|
||||
char* json = u_malloc(cap);
|
||||
if (!json) { sqlite3_finalize(st); return u_strdup("[]"); }
|
||||
json[0] = '[';
|
||||
|
||||
char peers_tbl[80], sql[512]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT p.node_id,COALESCE(n.name,''),COALESCE(p.local_nick,''),COALESCE(p.adm_tags,''),"
|
||||
"COALESCE(p.node_type,0),COALESCE(p.node_RTT,0),COALESCE(p.storage,0),COALESCE(p.userinfo,''),"
|
||||
"p.source,length(p.adm_tags_sig),COALESCE(p.deleted,0),p.radio_muted"
|
||||
" FROM \"%s\" p LEFT JOIN nodes n ON p.node_id=n.node_id ORDER BY p.node_id ASC", peers_tbl);
|
||||
struct strbuf out = strbuf_new();
|
||||
sqlite3_stmt* st = NULL;
|
||||
int rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL);
|
||||
if (rc != SQLITE_OK || strbuf_addf(&out, "[") < 0) goto failed;
|
||||
uint64_t my_id = inst->node_id;
|
||||
int first = 1;
|
||||
|
||||
while (sqlite3_step(st) == SQLITE_ROW) {
|
||||
unsigned count = 0;
|
||||
while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
|
||||
uint64_t node_id = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
const char* name = (const char*)sqlite3_column_text(st, 1);
|
||||
const char* local_nick = (const char*)sqlite3_column_text(st, 2);
|
||||
@@ -813,39 +791,21 @@ char* utun_bridge_get_channel_members_json(const char* channel_id) {
|
||||
json_flat_get(adm_tags, "storage", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) storage_role = 1;
|
||||
}
|
||||
|
||||
char* esc_name = json_escape_alloc(local_nick && local_nick[0] ? local_nick :
|
||||
(name && name[0] ? name : ""));
|
||||
char* esc_userinfo = json_escape_alloc((const char*)sqlite3_column_text(st, 7));
|
||||
if (!esc_name || !esc_userinfo) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "Android members: JSON allocation failed channel=%s node=%016llx",
|
||||
channel_id, (unsigned long long)node_id);
|
||||
u_free(esc_name); u_free(esc_userinfo); continue;
|
||||
}
|
||||
const char* sep = first ? "" : ",";
|
||||
first = 0;
|
||||
|
||||
size_t needed = snprintf(NULL, 0,
|
||||
"%s{\"n\":\"%s\",\"ni\":%llu,\"on\":0,\"ad\":%d,\"sp\":%d,\"md\":%d,\"st\":%d,"
|
||||
"\"vd\":%d,\"dl\":%d,\"tp\":%d,\"rt\":%d,\"is\":%d,\"ui\":\"%s\",\"sr\":%d,\"rm\":%d}",
|
||||
sep, esc_name, (unsigned long long)node_id,
|
||||
is_admin, is_super, is_moder, storage_flag,
|
||||
is_verified, is_deleted, node_type, node_rtt, (node_id == my_id) ? 1 : 0, esc_userinfo, storage_role, sqlite3_column_int(st, 11));
|
||||
|
||||
while (pos + needed + 2 > cap) { char* tmp = u_realloc(json, cap * 2); if (!tmp) break; json = tmp; cap *= 2; }
|
||||
if (pos + needed + 2 <= cap)
|
||||
pos += (size_t)snprintf(json + pos, cap - pos,
|
||||
"%s{\"n\":\"%s\",\"ni\":%llu,\"on\":0,\"ad\":%d,\"sp\":%d,\"md\":%d,\"st\":%d,"
|
||||
"\"vd\":%d,\"dl\":%d,\"tp\":%d,\"rt\":%d,\"is\":%d,\"ui\":\"%s\",\"sr\":%d,\"rm\":%d}",
|
||||
sep, esc_name, (unsigned long long)node_id,
|
||||
is_admin, is_super, is_moder, storage_flag,
|
||||
is_verified, is_deleted, node_type, node_rtt, (node_id == my_id) ? 1 : 0, esc_userinfo, storage_role, sqlite3_column_int(st, 11));
|
||||
u_free(esc_userinfo);
|
||||
u_free(esc_name);
|
||||
if (strbuf_addf(&out, "%s{\"n\":", count++ ? "," : "") < 0 ||
|
||||
strbuf_add_json_string(&out, local_nick && local_nick[0] ? local_nick : name) < 0 ||
|
||||
strbuf_addf(&out, ",\"ni\":%llu,\"on\":0,\"ad\":%d,\"sp\":%d,\"md\":%d,\"st\":%d,"
|
||||
"\"vd\":%d,\"dl\":%d,\"tp\":%d,\"rt\":%d,\"is\":%d,\"ui\":",
|
||||
(unsigned long long)node_id, is_admin, is_super, is_moder, storage_flag,
|
||||
is_verified, is_deleted, node_type, node_rtt, node_id == my_id) < 0 ||
|
||||
strbuf_add_json_string(&out, (const char*)sqlite3_column_text(st, 7)) < 0 ||
|
||||
strbuf_addf(&out, ",\"sr\":%d,\"rm\":%d}", storage_role, sqlite3_column_int(st, 11)) < 0) goto failed;
|
||||
}
|
||||
if (rc != SQLITE_DONE || strbuf_addf(&out, "]") < 0) goto failed;
|
||||
sqlite3_finalize(st);
|
||||
|
||||
json[pos++] = ']'; json[pos] = '\0';
|
||||
return json;
|
||||
return strbuf_detach(&out);
|
||||
failed:
|
||||
bridge_log(BLEV_ERROR, "Members complete snapshot failed channel=%s: %s", channel_id, sqlite3_errmsg(db));
|
||||
sqlite3_finalize(st); strbuf_free(&out); return NULL;
|
||||
}
|
||||
|
||||
/* Не продолжаем сборку JSON после усечения строки: pos должен оставаться внутри буфера. */
|
||||
@@ -883,6 +843,7 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i
|
||||
int64_t last_seen = sqlite3_column_int64(st, 1);
|
||||
int64_t created = sqlite3_column_int64(st, 2);
|
||||
char* esc_name = json_escape_alloc(name && name[0] ? name : "");
|
||||
if (!esc_name) { sqlite3_finalize(st); return NULL; }
|
||||
int rc = member_detail_append(buf, sizeof(buf), &pos,
|
||||
",\"n\":\"%s\",\"on\":0,\"ls\":%lld,\"cr\":%lld",
|
||||
esc_name, (long long)last_seen, (long long)created);
|
||||
@@ -1208,11 +1169,11 @@ char* utun_bridge_get_local_sockets_json(void) {
|
||||
bridge_address_parts(&socket->nat_addr, external, sizeof(external), &external_family, &external_port)) goto failed;
|
||||
int up = 0, down = 0;
|
||||
if (inst->connections) for (struct ll_entry* entry = inst->connections->head; entry; entry = entry->next) {
|
||||
struct conn_queue_entry* value = entry->data;
|
||||
struct conn_queue_entry* value = (struct conn_queue_entry*)entry->data;
|
||||
if (value) bridge_count_socket_links(value->conn, socket, &up, &down);
|
||||
}
|
||||
if (inst->tcp_connections) for (struct ll_entry* entry = inst->tcp_connections->head; entry; entry = entry->next) {
|
||||
struct tcp_conn_entry* value = entry->data;
|
||||
struct tcp_conn_entry* value = (struct tcp_conn_entry*)entry->data;
|
||||
if (value) bridge_count_socket_links(value->etcp_conn, socket, &up, &down);
|
||||
}
|
||||
const char* protocol = socket->is_tcp ? socket->reality_enabled ? "TCP+REALITY" : "TCP" : "UDP";
|
||||
|
||||
Reference in new issue
Block a user