p2pnet: validate external address of inbound connection

This commit is contained in:
aggstam
2022-10-06 20:44:45 +03:00
parent 26535fa529
commit 34dff4e2d2
5 changed files with 116 additions and 10 deletions

View File

@@ -279,6 +279,7 @@ impl Channel {
message_subsystem.add_dispatch::<message::PongMessage>().await;
message_subsystem.add_dispatch::<message::GetAddrsMessage>().await;
message_subsystem.add_dispatch::<message::AddrsMessage>().await;
message_subsystem.add_dispatch::<message::ExtAddrsMessage>().await;
}
/// Convenience function that returns the Message Subsystem.

View File

@@ -48,7 +48,20 @@ impl Hosts {
}
}
// TODO: add single host store, which also checks that resolved ips are the same as the connection
/// Add a new hosts external adders to the host list, after filtering and verifying
/// the address url resolves to the provided connection address.
pub async fn store_ext(&self, connection_addr: Url, input_addrs: Vec<Url>) {
let addrs = if !self.localnet {
let filtered = filter_localnet(input_addrs);
let filtered = filter_invalid(&self.ipv4_range, &self.ipv6_range, filtered);
filter_non_resolving(connection_addr, filtered)
} else {
input_addrs
};
for addr in addrs {
self.addrs.lock().await.insert(addr);
}
}
/// Return the list of hosts.
pub async fn load_all(&self) -> Vec<Url> {
@@ -135,6 +148,64 @@ fn filter_invalid(
filtered
}
/// Auxiliary function to filter unresolvable hosts, based on provided connection addr (excluding onion).
fn filter_non_resolving(connection_addr: Url, input_addrs: Vec<Url>) -> Vec<Url> {
let connection_domain = connection_addr.domain().unwrap();
// Validate connection onion domain
if connection_domain.ends_with(".onion") && !is_valid_onion(connection_domain) {
return vec![]
}
// Retrieve connection IPs
let mut ipv4_range = vec![];
let mut ipv6_range = vec![];
for i in connection_addr.socket_addrs(|| None).unwrap() {
match i.ip() {
IpAddr::V4(a) => {
ipv4_range.push(a);
}
IpAddr::V6(a) => {
ipv6_range.push(a);
}
}
}
// Filter input addresses
let mut filtered = vec![];
for addr in input_addrs {
// Keep valid onion domains
let addr_domain = addr.domain().unwrap();
if addr_domain.ends_with(".onion") && addr_domain == connection_domain {
filtered.push(addr.clone());
continue
}
// Checking IP validity
let mut valid = true;
let socket_addrs = addr.socket_addrs(|| None).unwrap();
for i in socket_addrs {
match i.ip() {
IpAddr::V4(a) => {
if !ipv4_range.contains(&a) {
valid = false;
break
}
}
IpAddr::V6(a) => {
if !ipv6_range.contains(&a) {
valid = false;
break
}
}
}
}
if valid {
filtered.push(addr.clone());
}
}
filtered
}
/// Auxiliary function to validate an onion.
fn is_valid_onion(onion: &str) -> bool {
let onion = match onion.strip_suffix(".onion") {

View File

@@ -37,6 +37,12 @@ pub struct AddrsMessage {
pub addrs: Vec<Url>,
}
/// Sends external address information to inbound connection.
#[derive(SerialEncodable, SerialDecodable)]
pub struct ExtAddrsMessage {
pub ext_addrs: Vec<Url>,
}
/// Requests version information of outbound connection.
#[derive(SerialEncodable, SerialDecodable)]
pub struct VersionMessage {
@@ -74,6 +80,12 @@ impl Message for AddrsMessage {
}
}
impl Message for ExtAddrsMessage {
fn name() -> &'static str {
"extaddr"
}
}
impl Message for VersionMessage {
fn name() -> &'static str {
"version"

View File

@@ -20,6 +20,7 @@ const SEND_ADDR_SLEEP_SECONDS: u64 = 900;
pub struct ProtocolAddress {
channel: ChannelPtr,
addrs_sub: MessageSubscription<message::AddrsMessage>,
ext_addrs_sub: MessageSubscription<message::ExtAddrsMessage>,
get_addrs_sub: MessageSubscription<message::GetAddrsMessage>,
hosts: HostsPtr,
jobsman: ProtocolJobsManagerPtr,
@@ -27,8 +28,8 @@ pub struct ProtocolAddress {
}
impl ProtocolAddress {
/// Create a new address protocol. Makes an address and get-address
/// subscription and adds them to the address protocol instance.
/// Create a new address protocol. Makes an address, an external address
/// and a get-address subscription and adds them to the address protocol instance.
pub async fn init(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
let settings = p2p.settings();
let hosts = p2p.hosts();
@@ -40,6 +41,13 @@ impl ProtocolAddress {
.await
.expect("Missing addrs dispatcher!");
// Creates a subscription to external address message.
let ext_addrs_sub = channel
.clone()
.subscribe_msg::<message::ExtAddrsMessage>()
.await
.expect("Missing ext_addrs dispatcher!");
// Creates a subscription to get-address message.
let get_addrs_sub = channel
.clone()
@@ -50,6 +58,7 @@ impl ProtocolAddress {
Arc::new(Self {
channel: channel.clone(),
addrs_sub,
ext_addrs_sub,
get_addrs_sub,
hosts,
jobsman: ProtocolJobsManager::new("ProtocolAddress", channel),
@@ -69,6 +78,18 @@ impl ProtocolAddress {
}
}
/// Handles receiving the external address message. Loops to continually recieve
/// external address messages on the address subsciption. Adds the recieved
/// external addresses to the list of hosts.
async fn handle_receive_ext_addrs(self: Arc<Self>) -> Result<()> {
debug!(target: "net", "ProtocolAddress::handle_receive_ext_addrs() [START]");
loop {
let ext_addrs_msg = self.ext_addrs_sub.receive().await?;
debug!(target: "net", "ProtocolAddress::handle_receive_ext_addrs() received {} addrs", ext_addrs_msg.ext_addrs.len());
self.hosts.store_ext(self.channel.address(), ext_addrs_msg.ext_addrs.clone()).await;
}
}
/// Handles receiving the get-address message. Continually recieves
/// get-address messages on the get-address subsciption. Then replies
/// with an address message.
@@ -92,9 +113,9 @@ impl ProtocolAddress {
async fn send_my_addrs(self: Arc<Self>) -> Result<()> {
debug!(target: "net", "ProtocolAddress::send_addrs() [START]");
loop {
let addrs = self.settings.external_addr.clone();
let addr_msg = message::AddrsMessage { addrs };
self.channel.clone().send(addr_msg).await?;
let ext_addrs = self.settings.external_addr.clone();
let ext_addr_msg = message::ExtAddrsMessage { ext_addrs };
self.channel.clone().send(ext_addr_msg).await?;
async_util::sleep(SEND_ADDR_SLEEP_SECONDS).await;
}
}
@@ -118,6 +139,7 @@ impl ProtocolBase for ProtocolAddress {
debug!(target: "net", "ProtocolAddress::start() [START]");
self.jobsman.clone().start(executor.clone());
self.jobsman.clone().spawn(self.clone().handle_receive_addrs(), executor.clone()).await;
self.jobsman.clone().spawn(self.clone().handle_receive_ext_addrs(), executor.clone()).await;
self.jobsman.clone().spawn(self.clone().handle_receive_get_addrs(), executor).await;
// Send get_address message.

View File

@@ -46,10 +46,10 @@ impl ProtocolSeed {
return Ok(())
}
let addrs = self.settings.external_addr.clone();
debug!(target: "net", "ProtocolSeed::send_own_address() addrs={:?}", addrs);
let addrs = message::AddrsMessage { addrs };
self.channel.clone().send(addrs).await
let ext_addrs = self.settings.external_addr.clone();
debug!(target: "net", "ProtocolSeed::send_self_address() ext_addrs={:?}", ext_addrs);
let ext_addr_msg = message::ExtAddrsMessage { ext_addrs };
self.channel.clone().send(ext_addr_msg).await
}
}