node/db: delete unparseable ipv6 rows

- Drop `type='ipv6'` rows whose bracket-stripped inner part contains
  no `:`, since every valid IPv6 textual form has at least one
- Cover the offender (`[]:8776`), bracketed garbage (`[abc]:8776`),
  the unspecified (`::`), loopback, and full forms in tests
- Keep `dns` and `ipv4` rows untouched

Migration 8 retyped any `[..]:N` dns row to ipv6 by bracket pattern
alone, without checking the inner part is parseable
This commit is contained in:
Daniel Norman 2026-04-30 11:00:01 +02:00 committed by Lorenz Leutgeb
parent a45a1078ab
commit 998f527bd4
3 changed files with 134 additions and 0 deletions

View File

@ -57,6 +57,9 @@ COB type names and payload IDs remain unchanged for backwards compatibility.
peers. Previously, if it found a row it could not parse, then it would fail peers. Previously, if it found a row it could not parse, then it would fail
the whole process. Now, it will continue, providing valid entries to connect the whole process. Now, it will continue, providing valid entries to connect
to. to.
- Delete invalid node addresses, of the IPv6 type, from the SQLite address book.
Entries deleted include ones that have an address of the form `[]:port`, or
that have an invalid address within the `[]`.
## 1.8.0 ## 1.8.0

View File

@ -39,6 +39,7 @@ const MIGRATIONS: &[&str] = &[
include_str!("db/migrations/6.sql"), include_str!("db/migrations/6.sql"),
include_str!("db/migrations/7.sql"), include_str!("db/migrations/7.sql"),
include_str!("db/migrations/8.sql"), include_str!("db/migrations/8.sql"),
include_str!("db/migrations/9.sql"),
]; ];
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -446,4 +447,126 @@ mod test {
} }
} }
} }
mod migration_9 {
use super::*;
const NODE1: &str = "node1";
fn db_before_migration() -> Database {
let db = Database::memory_up_to_migration(8).unwrap();
db.execute(
"INSERT INTO nodes (id, features, alias, timestamp)
VALUES ('node1', 0, 'alias', 0)",
)
.unwrap();
db
}
fn run_migration(db: &Database) {
db.execute(MIGRATIONS[8]).unwrap();
}
fn address_count(db: &Database, address_type: &str, value: &str) -> i64 {
db.prepare(format!(
"SELECT COUNT(*) FROM addresses
WHERE node = '{NODE1}' AND type = '{address_type}' AND value = '{value}'"
))
.unwrap()
.into_iter()
.next()
.unwrap()
.unwrap()
.read::<i64, _>(0)
}
fn insert_address(db: &Database, address_type: &str, value: &str) {
db.execute(format!(
"INSERT INTO addresses (node, type, value, source, timestamp)
VALUES ('{NODE1}', '{address_type}', '{value}', 'peer', 0)"
))
.unwrap();
}
#[test]
fn empty_brackets_ipv6_row_is_deleted() {
let db = db_before_migration();
let address: &str = "[]:8776";
insert_address(&db, "ipv6", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv6", address), 0);
}
#[test]
fn unspecified_address_is_kept() {
// `[::]:8776` parses fine as the IPv6 unspecified address.
let db = db_before_migration();
let address = "[::]:8776";
insert_address(&db, "ipv6", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv6", address), 1);
}
#[test]
fn loopback_address_is_kept() {
let db = db_before_migration();
let address = "[::1]:8776";
insert_address(&db, "ipv6", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv6", address), 1);
}
#[test]
fn full_ipv6_address_is_kept() {
let db = db_before_migration();
let address = "[2001:db8::1]:8776";
insert_address(&db, "ipv6", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv6", address), 1);
}
#[test]
fn bracketed_non_ipv6_garbage_is_deleted() {
// No `:` between the brackets means it can't be IPv6.
let db = db_before_migration();
let address = "[abc]:8776";
insert_address(&db, "ipv6", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv6", address), 0);
}
#[test]
fn ipv4_row_is_unaffected() {
let db = db_before_migration();
let address = "192.168.1.1:8776";
insert_address(&db, "ipv4", address);
run_migration(&db);
assert_eq!(address_count(&db, "ipv4", address), 1);
}
#[test]
fn dns_row_is_unaffected_even_when_inner_part_has_no_colon() {
// Migration 9 only targets `type = 'ipv6'`. A bracketless DNS row
// like `example.com:8776` shouldn't be touched.
let db = db_before_migration();
let address = "example.com:8776";
insert_address(&db, "dns", address);
run_migration(&db);
assert_eq!(address_count(&db, "dns", address), 1);
}
}
} }

View File

@ -0,0 +1,8 @@
-- every valid IPv6 textual representation (including `::`) contains
-- at least one `:`. So if the bracket-stripped inner part has no `:`, the value
-- can't be a real IPv6 address.
delete from addresses
where type = 'ipv6'
and instr(value, '[') = 1
and instr(value, ']:') > 1
and instr(substr(value, 2, instr(value, ']:') - 2), ':') = 0;