radicle/node/address: Skip unknown AddressType value
Since `tor` and `i2p` are features, dependents may compile the `radicle` crate without these features and will not be able to parse `AddressType`. Allow parsing of the `AddressType` to fail, and skip if that is the case. Further, the same holds for `Address`, and skip if that fails to parse as well. Finally, `Address` may parse an address string successfully to `HostName`, but it may have an `AddressType` that does not match the `HostName` variant. Guard against this, again skipping if they are not equal. For example, `HostName::Dns` is a `String` underneath, so there is higher chance that something like `deadbeef.onion` parses to this variant.
This commit is contained in:
parent
ff8660d872
commit
704b6cdabb
|
|
@ -1,6 +1,8 @@
|
||||||
mod features;
|
mod features;
|
||||||
|
|
||||||
pub mod address;
|
pub mod address;
|
||||||
|
use address::AddressType;
|
||||||
|
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
|
@ -518,6 +520,22 @@ impl Address {
|
||||||
&self.inner.host
|
&self.inner.host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the [`AddressType`] of the [`Address`].
|
||||||
|
///
|
||||||
|
/// Returns `None` if the [`AddressType`] is not known.
|
||||||
|
pub fn address_type(&self) -> Option<AddressType> {
|
||||||
|
match self.host() {
|
||||||
|
HostName::Ip(IpAddr::V4(_)) => Some(AddressType::Ipv4),
|
||||||
|
HostName::Ip(IpAddr::V6(_)) => Some(AddressType::Ipv6),
|
||||||
|
HostName::Dns(_) => Some(AddressType::Dns),
|
||||||
|
#[cfg(feature = "tor")]
|
||||||
|
HostName::Tor(_) => Some(AddressType::Onion),
|
||||||
|
#[cfg(feature = "i2p")]
|
||||||
|
HostName::I2p(_) => Some(AddressType::I2p),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the [`HostName`] is a Tor onion address.
|
/// Returns `true` if the [`HostName`] is a Tor onion address.
|
||||||
#[cfg(feature = "tor")]
|
#[cfg(feature = "tor")]
|
||||||
pub fn is_onion(&self) -> bool {
|
pub fn is_onion(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -182,9 +182,42 @@ impl Store for Database {
|
||||||
stmt.bind((1, node))?;
|
stmt.bind((1, node))?;
|
||||||
|
|
||||||
for row in stmt.into_iter() {
|
for row in stmt.into_iter() {
|
||||||
let row = row?;
|
let mut row = row?;
|
||||||
let _type = row.try_read::<AddressType, _>("type")?;
|
let address_type = match row.try_read::<AddressType, _>("type") {
|
||||||
let addr = row.try_read::<Address, _>("value")?;
|
Ok(address_type) => address_type,
|
||||||
|
Err(err) => {
|
||||||
|
let value = match row.take("type") {
|
||||||
|
sqlite::Value::String(value) => value,
|
||||||
|
value => format!("{value:?}"),
|
||||||
|
};
|
||||||
|
log::debug!("Failed to parse address type '{value}' for {node}: {err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let addr = match row.try_read::<Address, _>("value") {
|
||||||
|
Ok(addr) => addr,
|
||||||
|
Err(err) => {
|
||||||
|
let value = match row.take("type") {
|
||||||
|
sqlite::Value::String(value) => value,
|
||||||
|
value => format!("{value:?}"),
|
||||||
|
};
|
||||||
|
log::debug!("Failed to parse address '{value}' for {node}: {err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match addr.address_type() {
|
||||||
|
None => {
|
||||||
|
log::debug!("Address {addr} has unknown address type for {node}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(actual_type) if actual_type != address_type => {
|
||||||
|
log::debug!(
|
||||||
|
"The address '{addr}' was expected to be of type '{address_type:?}' but was found to be of type '{actual_type:?}'"
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let source = row.try_read::<Source, _>("source")?;
|
let source = row.try_read::<Source, _>("source")?;
|
||||||
let last_attempt = row
|
let last_attempt = row
|
||||||
.read::<Option<i64>, _>("last_attempt")
|
.read::<Option<i64>, _>("last_attempt")
|
||||||
|
|
@ -1066,4 +1099,86 @@ mod test {
|
||||||
|
|
||||||
node::properties::test_reverse_lookup(&db, input)
|
node::properties::test_reverse_lookup(&db, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn skip_invalid_address_type() {
|
||||||
|
let alice = arbitrary::r#gen::<NodeId>(1);
|
||||||
|
let mut cache = Database::memory().unwrap();
|
||||||
|
let timestamp = Timestamp::from(LocalTime::now());
|
||||||
|
let ua = UserAgent::default();
|
||||||
|
let features = node::Features::SEED;
|
||||||
|
let good_addr: Address = "[2001:db8::1]:64312".parse().unwrap();
|
||||||
|
let good_ka = KnownAddress {
|
||||||
|
addr: good_addr.clone(),
|
||||||
|
source: Source::Peer,
|
||||||
|
last_success: None,
|
||||||
|
last_attempt: None,
|
||||||
|
banned: false,
|
||||||
|
};
|
||||||
|
cache
|
||||||
|
.insert(
|
||||||
|
&alice,
|
||||||
|
1,
|
||||||
|
features,
|
||||||
|
&Alias::new("alice"),
|
||||||
|
0,
|
||||||
|
&ua,
|
||||||
|
timestamp,
|
||||||
|
[good_ka.clone()],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
cache
|
||||||
|
.db
|
||||||
|
.execute(format!(
|
||||||
|
"INSERT INTO addresses (node, type, value, source, timestamp)
|
||||||
|
VALUES ('{alice}', 'invalid-address-type', 'example.com:64312', 'peer', 0)"
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let node = cache.get(&alice).unwrap().unwrap();
|
||||||
|
assert_eq!(node.addrs.len(), 1);
|
||||||
|
assert_eq!(node.alias, Alias::new("alice"));
|
||||||
|
assert_eq!(node.agent, ua);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn skip_mismatched_address_type() {
|
||||||
|
let alice = arbitrary::r#gen::<NodeId>(1);
|
||||||
|
let mut cache = Database::memory().unwrap();
|
||||||
|
let timestamp = Timestamp::from(LocalTime::now());
|
||||||
|
let ua = UserAgent::default();
|
||||||
|
let features = node::Features::SEED;
|
||||||
|
let good_addr: Address = "[2001:db8::1]:64312".parse().unwrap();
|
||||||
|
let good_ka = KnownAddress {
|
||||||
|
addr: good_addr.clone(),
|
||||||
|
source: Source::Peer,
|
||||||
|
last_success: None,
|
||||||
|
last_attempt: None,
|
||||||
|
banned: false,
|
||||||
|
};
|
||||||
|
cache
|
||||||
|
.insert(
|
||||||
|
&alice,
|
||||||
|
1,
|
||||||
|
features,
|
||||||
|
&Alias::new("alice"),
|
||||||
|
0,
|
||||||
|
&ua,
|
||||||
|
timestamp,
|
||||||
|
[good_ka.clone()],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
cache
|
||||||
|
.db
|
||||||
|
.execute(format!(
|
||||||
|
"INSERT INTO addresses (node, type, value, source, timestamp)
|
||||||
|
VALUES ('{alice}', 'ipv4', 'example.com:64312', 'peer', 0)"
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let node = cache.get(&alice).unwrap().unwrap();
|
||||||
|
assert_eq!(node.addrs.len(), 1);
|
||||||
|
assert_eq!(node.alias, Alias::new("alice"));
|
||||||
|
assert_eq!(node.agent, ua);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue