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;
|
||||
|
||||
pub mod address;
|
||||
use address::AddressType;
|
||||
|
||||
pub mod command;
|
||||
pub mod config;
|
||||
pub mod db;
|
||||
|
|
@ -518,6 +520,22 @@ impl Address {
|
|||
&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.
|
||||
#[cfg(feature = "tor")]
|
||||
pub fn is_onion(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -182,9 +182,42 @@ impl Store for Database {
|
|||
stmt.bind((1, node))?;
|
||||
|
||||
for row in stmt.into_iter() {
|
||||
let row = row?;
|
||||
let _type = row.try_read::<AddressType, _>("type")?;
|
||||
let addr = row.try_read::<Address, _>("value")?;
|
||||
let mut row = row?;
|
||||
let address_type = match row.try_read::<AddressType, _>("type") {
|
||||
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 last_attempt = row
|
||||
.read::<Option<i64>, _>("last_attempt")
|
||||
|
|
@ -1066,4 +1099,86 @@ mod test {
|
|||
|
||||
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