Merge branch 'fix-geolocation-after-refactor' into 'main'

Fix geolocation feature after recent refactor

See merge request veilid/veilid!355
This commit is contained in:
Christien Rioux 2025-02-26 14:24:39 +00:00
commit 8de19e0d26
3 changed files with 84 additions and 119 deletions

View File

@ -235,6 +235,11 @@ impl RouteSpecStore {
.relay_node(RoutingDomain::PublicInternet) .relay_node(RoutingDomain::PublicInternet)
.map(|nr| nr.locked(rti)); .map(|nr| nr.locked(rti));
#[cfg(feature = "geolocation")]
let country_code_denylist = self
.config()
.with(|config| config.network.privacy.country_code_denylist.clone());
// Get list of all nodes, and sort them for selection // Get list of all nodes, and sort them for selection
let cur_ts = Timestamp::now(); let cur_ts = Timestamp::now();
let filter = Box::new( let filter = Box::new(
@ -277,78 +282,66 @@ impl RouteSpecStore {
// Exclude nodes from blacklisted countries // Exclude nodes from blacklisted countries
#[cfg(feature = "geolocation")] #[cfg(feature = "geolocation")]
{ if !country_code_denylist.is_empty() {
let country_code_denylist = self let geolocation_info =
.unlocked_inner sni.get_geolocation_info(RoutingDomain::PublicInternet);
.routing_table
.config
.get()
.network
.privacy
.country_code_denylist
.clone();
if !country_code_denylist.is_empty() { // Since denylist is used, consider nodes with unknown countries to be automatically
let geolocation_info = // excluded as well
sni.get_geolocation_info(RoutingDomain::PublicInternet); if geolocation_info.country_code().is_none() {
veilid_log!(self
debug "allocate_route_inner: skipping node {} from unknown country",
e.best_node_id()
);
return false;
}
// Same thing applies to relays used by the node
if geolocation_info
.relay_country_codes()
.iter()
.any(Option::is_none)
{
veilid_log!(self
debug "allocate_route_inner: skipping node {} using relay from unknown country",
e.best_node_id()
);
return false;
}
// Since denylist is used, consider nodes with unknown countries to be automatically // Ensure that node is not excluded
// excluded as well // Safe to unwrap here, checked above
if geolocation_info.country_code().is_none() { if country_code_denylist.contains(&geolocation_info.country_code().unwrap())
veilid_log!(self {
debug "allocate_route_inner: skipping node {} from unknown country", veilid_log!(self
e.best_node_id() debug "allocate_route_inner: skipping node {} from excluded country {}",
); e.best_node_id(),
return false; geolocation_info.country_code().unwrap()
} );
// Same thing applies to relays used by the node return false;
if geolocation_info }
.relay_country_codes()
.iter()
.any(Option::is_none)
{
veilid_log!(self
debug "allocate_route_inner: skipping node {} using relay from unknown country",
e.best_node_id()
);
return false;
}
// Ensure that node is not excluded // Ensure that node relays are not excluded
// Safe to unwrap here, checked above // Safe to unwrap here, checked above
if country_code_denylist.contains(&geolocation_info.country_code().unwrap()) if geolocation_info
{ .relay_country_codes()
veilid_log!(self .iter()
debug "allocate_route_inner: skipping node {} from excluded country {}", .cloned()
e.best_node_id(), .map(Option::unwrap)
geolocation_info.country_code().unwrap() .any(|cc| country_code_denylist.contains(&cc))
); {
return false; veilid_log!(self
} debug "allocate_route_inner: skipping node {} using relay from excluded country {:?}",
e.best_node_id(),
// Ensure that node relays are not excluded geolocation_info
// Safe to unwrap here, checked above .relay_country_codes()
if geolocation_info .iter()
.relay_country_codes() .cloned()
.iter() .map(Option::unwrap)
.cloned() .filter(|cc| country_code_denylist.contains(&cc))
.map(Option::unwrap) .next()
.any(|cc| country_code_denylist.contains(&cc)) .unwrap()
{ );
veilid_log!(self return false;
debug "allocate_route_inner: skipping node {} using relay from excluded country {:?}",
e.best_node_id(),
geolocation_info
.relay_country_codes()
.iter()
.cloned()
.map(Option::unwrap)
.filter(|cc| country_code_denylist.contains(&cc))
.next()
.unwrap()
);
return false;
}
} }
} }

View File

@ -239,7 +239,7 @@ pub fn fix_veilidconfiginner() -> VeilidConfigInner {
}, },
#[cfg(feature = "geolocation")] #[cfg(feature = "geolocation")]
privacy: VeilidConfigPrivacy { privacy: VeilidConfigPrivacy {
country_code_denylist: vec![CountryCode([b'N', b'Z'])], country_code_denylist: vec![CountryCode::from_str("NZ").unwrap()],
}, },
#[cfg(feature = "virtual-network")] #[cfg(feature = "virtual-network")]
virtual_network: VeilidConfigVirtualNetwork { virtual_network: VeilidConfigVirtualNetwork {

View File

@ -1,17 +1,12 @@
use super::*; use super::*;
use std::hash::{Hash, Hasher};
/// Two-letter country code. Case-insensitive when comparing. /// Two-letter country code. Stored in upper case internally.
#[derive(Copy, Default, Clone, Ord, Eq, Serialize, Deserialize, JsonSchema)] #[derive(
Copy, Default, Clone, Hash, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema,
)]
#[serde(try_from = "String")] #[serde(try_from = "String")]
#[serde(into = "String")] #[serde(into = "String")]
pub struct CountryCode(pub [u8; 2]); pub struct CountryCode([u8; 2]);
impl From<[u8; 2]> for CountryCode {
fn from(b: [u8; 2]) -> Self {
Self(b)
}
}
impl From<CountryCode> for String { impl From<CountryCode> for String {
fn from(u: CountryCode) -> Self { fn from(u: CountryCode) -> Self {
@ -22,7 +17,18 @@ impl From<CountryCode> for String {
impl TryFrom<&[u8]> for CountryCode { impl TryFrom<&[u8]> for CountryCode {
type Error = VeilidAPIError; type Error = VeilidAPIError;
fn try_from(b: &[u8]) -> Result<Self, Self::Error> { fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(b.try_into().map_err(VeilidAPIError::generic)?)) let cc: [u8; 2] = b.try_into().map_err(VeilidAPIError::generic)?;
if !cc[0].is_ascii_alphabetic() || !cc[1].is_ascii_alphabetic() {
return Err(VeilidAPIError::generic(
"country code must only contain alphabetic chars",
));
}
Ok(Self([
cc[0].to_ascii_uppercase(),
cc[1].to_ascii_uppercase(),
]))
} }
} }
@ -35,55 +41,21 @@ impl TryFrom<String> for CountryCode {
impl fmt::Display for CountryCode { impl fmt::Display for CountryCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", String::from_utf8_lossy(&self.0)) // self.0 is guaranteed to be a valid ASCII string, checked in Self::try_from(&[u8])
write!(f, "{}{}", self.0[0] as char, self.0[1] as char)
} }
} }
impl fmt::Debug for CountryCode { impl fmt::Debug for CountryCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", String::from_utf8_lossy(&self.0)) <Self as fmt::Display>::fmt(self, f)
} }
} }
impl FromStr for CountryCode { impl FromStr for CountryCode {
type Err = VeilidAPIError; type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self( Ok(Self::try_from(s.as_bytes())?)
s.as_bytes().try_into().map_err(VeilidAPIError::generic)?,
))
}
}
impl Hash for CountryCode {
fn hash<H: Hasher>(&self, state: &mut H) {
let this = [
self.0[0].to_ascii_uppercase(),
self.0[1].to_ascii_uppercase(),
];
state.write(&this[..]);
}
}
impl PartialEq for CountryCode {
fn eq(&self, other: &Self) -> bool {
self.0[0].to_ascii_uppercase() == other.0[0].to_ascii_uppercase()
&& self.0[1].to_ascii_uppercase() == other.0[1].to_ascii_uppercase()
}
}
impl PartialOrd for CountryCode {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
let this = [
self.0[0].to_ascii_uppercase(),
self.0[1].to_ascii_uppercase(),
];
let other = [
other.0[0].to_ascii_uppercase(),
other.0[1].to_ascii_uppercase(),
];
this.partial_cmp(&other)
} }
} }