491 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			491 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
use super::*;
 | 
						|
 | 
						|
/// Mechanism required to contact another node
 | 
						|
#[derive(Clone, Debug)]
 | 
						|
pub enum ContactMethod {
 | 
						|
    /// Node is not reachable by any means
 | 
						|
    Unreachable,
 | 
						|
    /// Connection should have already existed
 | 
						|
    Existing,
 | 
						|
    /// Contact the node directly
 | 
						|
    Direct(DialInfo),
 | 
						|
    /// Request via signal the node connect back directly (relay, target)
 | 
						|
    SignalReverse(DHTKey, DHTKey),
 | 
						|
    /// Request via signal the node negotiate a hole punch (relay, target_node)
 | 
						|
    SignalHolePunch(DHTKey, DHTKey),
 | 
						|
    /// Must use an inbound relay to reach the node
 | 
						|
    InboundRelay(DHTKey),
 | 
						|
    /// Must use outbound relay to reach the node
 | 
						|
    OutboundRelay(DHTKey),
 | 
						|
}
 | 
						|
 | 
						|
#[derive(Debug)]
 | 
						|
pub struct RoutingDomainDetailCommon {
 | 
						|
    routing_domain: RoutingDomain,
 | 
						|
    network_class: Option<NetworkClass>,
 | 
						|
    outbound_protocols: ProtocolTypeSet,
 | 
						|
    inbound_protocols: ProtocolTypeSet,
 | 
						|
    address_types: AddressTypeSet,
 | 
						|
    relay_node: Option<NodeRef>,
 | 
						|
    dial_info_details: Vec<DialInfoDetail>,
 | 
						|
    // caches
 | 
						|
    cached_peer_info: Mutex<Option<PeerInfo>>,
 | 
						|
}
 | 
						|
 | 
						|
impl RoutingDomainDetailCommon {
 | 
						|
    pub fn new(routing_domain: RoutingDomain) -> Self {
 | 
						|
        Self {
 | 
						|
            routing_domain,
 | 
						|
            network_class: Default::default(),
 | 
						|
            outbound_protocols: Default::default(),
 | 
						|
            inbound_protocols: Default::default(),
 | 
						|
            address_types: Default::default(),
 | 
						|
            relay_node: Default::default(),
 | 
						|
            dial_info_details: Default::default(),
 | 
						|
            cached_peer_info: Mutex::new(Default::default()),
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Set from network manager
 | 
						|
    pub(super) fn setup_network(
 | 
						|
        &mut self,
 | 
						|
        outbound_protocols: ProtocolTypeSet,
 | 
						|
        inbound_protocols: ProtocolTypeSet,
 | 
						|
        address_types: AddressTypeSet,
 | 
						|
    ) {
 | 
						|
        self.outbound_protocols = outbound_protocols;
 | 
						|
        self.inbound_protocols = inbound_protocols;
 | 
						|
        self.address_types = address_types;
 | 
						|
        self.clear_cache();
 | 
						|
    }
 | 
						|
 | 
						|
    pub(super) fn set_network_class(&mut self, network_class: Option<NetworkClass>) {
 | 
						|
        self.network_class = network_class;
 | 
						|
        self.clear_cache();
 | 
						|
    }
 | 
						|
    pub fn network_class(&self) -> Option<NetworkClass> {
 | 
						|
        self.network_class
 | 
						|
    }
 | 
						|
    pub fn outbound_protocols(&self) -> ProtocolTypeSet {
 | 
						|
        self.outbound_protocols
 | 
						|
    }
 | 
						|
    pub fn inbound_protocols(&self) -> ProtocolTypeSet {
 | 
						|
        self.inbound_protocols
 | 
						|
    }
 | 
						|
    pub fn address_types(&self) -> AddressTypeSet {
 | 
						|
        self.address_types
 | 
						|
    }
 | 
						|
    pub fn relay_node(&self) -> Option<NodeRef> {
 | 
						|
        self.relay_node.clone()
 | 
						|
    }
 | 
						|
    pub(super) fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
 | 
						|
        self.relay_node = opt_relay_node.map(|nr| {
 | 
						|
            nr.filtered_clone(NodeRefFilter::new().with_routing_domain(self.routing_domain))
 | 
						|
        });
 | 
						|
        self.clear_cache();
 | 
						|
    }
 | 
						|
    pub fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
 | 
						|
        &self.dial_info_details
 | 
						|
    }
 | 
						|
    pub(super) fn clear_dial_info_details(&mut self) {
 | 
						|
        self.dial_info_details.clear();
 | 
						|
        self.clear_cache();
 | 
						|
    }
 | 
						|
    pub(super) fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
 | 
						|
        self.dial_info_details.push(did);
 | 
						|
        self.dial_info_details.sort();
 | 
						|
        self.clear_cache();
 | 
						|
    }
 | 
						|
 | 
						|
    pub fn has_valid_own_node_info(&self) -> bool {
 | 
						|
        self.network_class.unwrap_or(NetworkClass::Invalid) != NetworkClass::Invalid
 | 
						|
    }
 | 
						|
 | 
						|
    fn make_peer_info(&self, rti: &RoutingTableInner) -> PeerInfo {
 | 
						|
        let node_info = NodeInfo {
 | 
						|
            network_class: self.network_class.unwrap_or(NetworkClass::Invalid),
 | 
						|
            outbound_protocols: self.outbound_protocols,
 | 
						|
            address_types: self.address_types,
 | 
						|
            min_version: MIN_CRYPTO_VERSION,
 | 
						|
            max_version: MAX_CRYPTO_VERSION,
 | 
						|
            dial_info_detail_list: self.dial_info_details.clone(),
 | 
						|
        };
 | 
						|
 | 
						|
        let relay_info = self
 | 
						|
            .relay_node
 | 
						|
            .as_ref()
 | 
						|
            .and_then(|rn| {
 | 
						|
                let opt_relay_pi = rn.locked(rti).make_peer_info(self.routing_domain);
 | 
						|
                if let Some(relay_pi) = opt_relay_pi {
 | 
						|
                    match relay_pi.signed_node_info {
 | 
						|
                        SignedNodeInfo::Direct(d) => Some((relay_pi.node_id, d)), 
 | 
						|
                        SignedNodeInfo::Relayed(_) => {
 | 
						|
                            warn!("relay node should not have a relay itself! if this happens, a relay updated its signed node info and became a relay, which should cause the relay to be dropped");
 | 
						|
                            None
 | 
						|
                        },
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    None
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
        let signed_node_info = match relay_info {
 | 
						|
            Some((relay_id, relay_sdni)) => SignedNodeInfo::Relayed(
 | 
						|
                SignedRelayedNodeInfo::with_secret(
 | 
						|
                    NodeId::new(rti.unlocked_inner.node_id),
 | 
						|
                    node_info,
 | 
						|
                    relay_id,
 | 
						|
                    relay_sdni,                    
 | 
						|
                    &rti.unlocked_inner.node_id_secret,
 | 
						|
                )
 | 
						|
                .unwrap(),
 | 
						|
            ),
 | 
						|
            None => SignedNodeInfo::Direct(
 | 
						|
                SignedDirectNodeInfo::with_secret(
 | 
						|
                    NodeId::new(rti.unlocked_inner.node_id),
 | 
						|
                    node_info,
 | 
						|
                    &rti.unlocked_inner.node_id_secret,
 | 
						|
                )
 | 
						|
                .unwrap()
 | 
						|
            ),
 | 
						|
        };
 | 
						|
 | 
						|
        PeerInfo::new(NodeId::new(rti.unlocked_inner.node_id), signed_node_info)
 | 
						|
    }
 | 
						|
 | 
						|
    pub fn with_peer_info<F, R>(&self, rti: &RoutingTableInner, f: F) -> R
 | 
						|
    where
 | 
						|
        F: FnOnce(&PeerInfo) -> R,
 | 
						|
    {
 | 
						|
        let mut cpi = self.cached_peer_info.lock();
 | 
						|
        if cpi.is_none() {
 | 
						|
            // Regenerate peer info
 | 
						|
            let pi = self.make_peer_info(rti);
 | 
						|
 | 
						|
            // Cache the peer info
 | 
						|
            *cpi = Some(pi);
 | 
						|
        }
 | 
						|
        f(cpi.as_ref().unwrap())
 | 
						|
    }
 | 
						|
 | 
						|
    pub fn inbound_dial_info_filter(&self) -> DialInfoFilter {
 | 
						|
        DialInfoFilter::all()
 | 
						|
            .with_protocol_type_set(self.inbound_protocols)
 | 
						|
            .with_address_type_set(self.address_types)
 | 
						|
    }
 | 
						|
    pub fn outbound_dial_info_filter(&self) -> DialInfoFilter {
 | 
						|
        DialInfoFilter::all()
 | 
						|
            .with_protocol_type_set(self.outbound_protocols)
 | 
						|
            .with_address_type_set(self.address_types)
 | 
						|
    }
 | 
						|
 | 
						|
    pub(super) fn clear_cache(&self) {
 | 
						|
        *self.cached_peer_info.lock() = None;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// General trait for all routing domains
 | 
						|
pub trait RoutingDomainDetail {
 | 
						|
    // Common accessors
 | 
						|
    fn common(&self) -> &RoutingDomainDetailCommon;
 | 
						|
    fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon;
 | 
						|
 | 
						|
    /// Can this routing domain contain a particular address
 | 
						|
    fn can_contain_address(&self, address: Address) -> bool;
 | 
						|
 | 
						|
    /// Get the contact method required for node A to reach node B in this routing domain
 | 
						|
    /// Routing table must be locked for reading to use this function
 | 
						|
    fn get_contact_method(
 | 
						|
        &self,
 | 
						|
        rti: &RoutingTableInner,
 | 
						|
        peer_a: &PeerInfo,
 | 
						|
        peer_b: &PeerInfo,
 | 
						|
        dial_info_filter: DialInfoFilter,
 | 
						|
        sequencing: Sequencing,
 | 
						|
    ) -> ContactMethod;
 | 
						|
}
 | 
						|
 | 
						|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/// Public Internet routing domain internals
 | 
						|
#[derive(Debug)]
 | 
						|
pub struct PublicInternetRoutingDomainDetail {
 | 
						|
    /// Common implementation for all routing domains
 | 
						|
    common: RoutingDomainDetailCommon,
 | 
						|
}
 | 
						|
 | 
						|
impl Default for PublicInternetRoutingDomainDetail {
 | 
						|
    fn default() -> Self {
 | 
						|
        Self {
 | 
						|
            common: RoutingDomainDetailCommon::new(RoutingDomain::PublicInternet),
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn first_filtered_dial_info_detail(
 | 
						|
    from_node: &NodeInfo,
 | 
						|
    to_node: &NodeInfo,
 | 
						|
    dial_info_filter: &DialInfoFilter,
 | 
						|
    sequencing: Sequencing,
 | 
						|
) -> Option<DialInfoDetail> {
 | 
						|
    let dial_info_filter = dial_info_filter.clone().filtered(
 | 
						|
        &DialInfoFilter::all()
 | 
						|
            .with_address_type_set(from_node.address_types)
 | 
						|
            .with_protocol_type_set(from_node.outbound_protocols),
 | 
						|
    );
 | 
						|
 | 
						|
    // Get first filtered dialinfo
 | 
						|
    let (sort, dial_info_filter) = match sequencing {
 | 
						|
        Sequencing::NoPreference => (None, dial_info_filter),
 | 
						|
        Sequencing::PreferOrdered => (
 | 
						|
            Some(DialInfoDetail::ordered_sequencing_sort),
 | 
						|
            dial_info_filter,
 | 
						|
        ),
 | 
						|
        Sequencing::EnsureOrdered => (
 | 
						|
            Some(DialInfoDetail::ordered_sequencing_sort),
 | 
						|
            dial_info_filter.filtered(
 | 
						|
                &DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
 | 
						|
            ),
 | 
						|
        ),
 | 
						|
    };
 | 
						|
    // If the filter is dead then we won't be able to connect
 | 
						|
    if dial_info_filter.is_dead() {
 | 
						|
        return None;
 | 
						|
    }
 | 
						|
 | 
						|
    let direct_filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
 | 
						|
 | 
						|
    // Get the best match dial info for node B if we have it
 | 
						|
    to_node.first_filtered_dial_info_detail(sort, direct_filter)
 | 
						|
}
 | 
						|
 | 
						|
impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
 | 
						|
    fn common(&self) -> &RoutingDomainDetailCommon {
 | 
						|
        &self.common
 | 
						|
    }
 | 
						|
    fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon {
 | 
						|
        &mut self.common
 | 
						|
    }
 | 
						|
    fn can_contain_address(&self, address: Address) -> bool {
 | 
						|
        address.is_global()
 | 
						|
    }
 | 
						|
    fn get_contact_method(
 | 
						|
        &self,
 | 
						|
        _rti: &RoutingTableInner,
 | 
						|
        peer_a: &PeerInfo,
 | 
						|
        peer_b: &PeerInfo,
 | 
						|
        dial_info_filter: DialInfoFilter,
 | 
						|
        sequencing: Sequencing,
 | 
						|
    ) -> ContactMethod {
 | 
						|
        // Get the nodeinfos for convenience
 | 
						|
        let node_a = peer_a.signed_node_info.node_info();
 | 
						|
        let node_b = peer_b.signed_node_info.node_info();
 | 
						|
        
 | 
						|
        // Get the best match dial info for node B if we have it
 | 
						|
        if let Some(target_did) =
 | 
						|
            first_filtered_dial_info_detail(node_a, node_b, &dial_info_filter, sequencing)
 | 
						|
        {
 | 
						|
            // Do we need to signal before going inbound?
 | 
						|
            if !target_did.class.requires_signal() {
 | 
						|
                // Go direct without signaling
 | 
						|
                return ContactMethod::Direct(target_did.dial_info);
 | 
						|
            }
 | 
						|
 | 
						|
            // Get the target's inbound relay, it must have one or it is not reachable
 | 
						|
            if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
 | 
						|
                let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
 | 
						|
                // Note that relay_peer_info could be node_a, in which case a connection already exists
 | 
						|
                // and we shouldn't have even gotten here
 | 
						|
                if node_b_relay_id.key == peer_a.node_id.key {
 | 
						|
                    return ContactMethod::Existing;
 | 
						|
                }
 | 
						|
 | 
						|
                // Can node A reach the inbound relay directly?
 | 
						|
                if first_filtered_dial_info_detail(
 | 
						|
                    node_a,
 | 
						|
                    node_b_relay,
 | 
						|
                    &dial_info_filter,
 | 
						|
                    sequencing,
 | 
						|
                )
 | 
						|
                .is_some()
 | 
						|
                {
 | 
						|
                    // Can node A receive anything inbound ever?
 | 
						|
                    if matches!(node_a.network_class, NetworkClass::InboundCapable) {
 | 
						|
                        ///////// Reverse connection
 | 
						|
 | 
						|
                        // Get the best match dial info for an reverse inbound connection from node B to node A
 | 
						|
                        if let Some(reverse_did) = first_filtered_dial_info_detail(
 | 
						|
                            node_b,
 | 
						|
                            node_a,
 | 
						|
                            &dial_info_filter,
 | 
						|
                            sequencing,
 | 
						|
                        ) {
 | 
						|
                            // Ensure we aren't on the same public IP address (no hairpin nat)
 | 
						|
                            if reverse_did.dial_info.to_ip_addr()
 | 
						|
                                != target_did.dial_info.to_ip_addr()
 | 
						|
                            {
 | 
						|
                                // Can we receive a direct reverse connection?
 | 
						|
                                if !reverse_did.class.requires_signal() {
 | 
						|
                                    return ContactMethod::SignalReverse(
 | 
						|
                                        node_b_relay_id.key,
 | 
						|
                                        peer_b.node_id.key,
 | 
						|
                                    );
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        ///////// UDP hole-punch
 | 
						|
 | 
						|
                        // Does node B have a direct udp dialinfo node A can reach?
 | 
						|
                        let udp_dial_info_filter = dial_info_filter
 | 
						|
                            .clone()
 | 
						|
                            .filtered(&DialInfoFilter::all().with_protocol_type(ProtocolType::UDP));
 | 
						|
                        if let Some(target_udp_did) = first_filtered_dial_info_detail(
 | 
						|
                            node_a,
 | 
						|
                            node_b,
 | 
						|
                            &udp_dial_info_filter,
 | 
						|
                            sequencing,
 | 
						|
                        ) {
 | 
						|
                            // Does node A have a direct udp dialinfo that node B can reach?
 | 
						|
                            if let Some(reverse_udp_did) = first_filtered_dial_info_detail(
 | 
						|
                                node_b,
 | 
						|
                                node_a,
 | 
						|
                                &udp_dial_info_filter,
 | 
						|
                                sequencing,
 | 
						|
                            ) {
 | 
						|
                                // Ensure we aren't on the same public IP address (no hairpin nat)
 | 
						|
                                if reverse_udp_did.dial_info.to_ip_addr()
 | 
						|
                                    != target_udp_did.dial_info.to_ip_addr()
 | 
						|
                                {
 | 
						|
                                    // The target and ourselves have a udp dialinfo that they can reach
 | 
						|
                                    return ContactMethod::SignalHolePunch(
 | 
						|
                                        node_b_relay_id.key,
 | 
						|
                                        peer_a.node_id.key,
 | 
						|
                                    );
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        // Otherwise we have to inbound relay
 | 
						|
                    }
 | 
						|
 | 
						|
                    return ContactMethod::InboundRelay(node_b_relay_id.key);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If the node B has no direct dial info, it needs to have an inbound relay
 | 
						|
        else if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
 | 
						|
            let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
 | 
						|
 | 
						|
            // Can we reach the full relay?
 | 
						|
            if first_filtered_dial_info_detail(
 | 
						|
                node_a,
 | 
						|
                &node_b_relay,
 | 
						|
                &dial_info_filter,
 | 
						|
                sequencing,
 | 
						|
            )
 | 
						|
            .is_some()
 | 
						|
            {
 | 
						|
                return ContactMethod::InboundRelay(node_b_relay_id.key);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // If node A can't reach the node by other means, it may need to use its own relay
 | 
						|
        if let Some(node_a_relay_id) = peer_a.signed_node_info.relay_id() {
 | 
						|
            return ContactMethod::OutboundRelay(node_a_relay_id.key);
 | 
						|
        }
 | 
						|
 | 
						|
        ContactMethod::Unreachable
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Local Network routing domain internals
 | 
						|
#[derive(Debug)]
 | 
						|
pub struct LocalNetworkRoutingDomainDetail {
 | 
						|
    /// The local networks this domain will communicate with
 | 
						|
    local_networks: Vec<(IpAddr, IpAddr)>,
 | 
						|
    /// Common implementation for all routing domains
 | 
						|
    common: RoutingDomainDetailCommon,
 | 
						|
}
 | 
						|
 | 
						|
impl Default for LocalNetworkRoutingDomainDetail {
 | 
						|
    fn default() -> Self {
 | 
						|
        Self {
 | 
						|
            local_networks: Default::default(),
 | 
						|
            common: RoutingDomainDetailCommon::new(RoutingDomain::LocalNetwork),
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl LocalNetworkRoutingDomainDetail {
 | 
						|
    pub fn set_local_networks(&mut self, mut local_networks: Vec<(IpAddr, IpAddr)>) -> bool {
 | 
						|
        local_networks.sort();
 | 
						|
        if local_networks == self.local_networks {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        self.local_networks = local_networks;
 | 
						|
        true
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
 | 
						|
    fn common(&self) -> &RoutingDomainDetailCommon {
 | 
						|
        &self.common
 | 
						|
    }
 | 
						|
    fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon {
 | 
						|
        &mut self.common
 | 
						|
    }
 | 
						|
    fn can_contain_address(&self, address: Address) -> bool {
 | 
						|
        let ip = address.to_ip_addr();
 | 
						|
        for localnet in &self.local_networks {
 | 
						|
            if ipaddr_in_network(ip, localnet.0, localnet.1) {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        false
 | 
						|
    }
 | 
						|
 | 
						|
    fn get_contact_method(
 | 
						|
        &self,
 | 
						|
        _rti: &RoutingTableInner,
 | 
						|
        peer_a: &PeerInfo,
 | 
						|
        peer_b: &PeerInfo,
 | 
						|
        dial_info_filter: DialInfoFilter,
 | 
						|
        sequencing: Sequencing,
 | 
						|
    ) -> ContactMethod {
 | 
						|
        // Scope the filter down to protocols node A can do outbound
 | 
						|
        let dial_info_filter = dial_info_filter.filtered(
 | 
						|
            &DialInfoFilter::all()
 | 
						|
                .with_address_type_set(peer_a.signed_node_info.node_info().address_types)
 | 
						|
                .with_protocol_type_set(peer_a.signed_node_info.node_info().outbound_protocols),
 | 
						|
        );
 | 
						|
        
 | 
						|
        // Get first filtered dialinfo
 | 
						|
        let (sort, dial_info_filter) = match sequencing {
 | 
						|
            Sequencing::NoPreference => (None, dial_info_filter),
 | 
						|
            Sequencing::PreferOrdered => (
 | 
						|
                Some(DialInfoDetail::ordered_sequencing_sort),
 | 
						|
                dial_info_filter,
 | 
						|
            ),
 | 
						|
            Sequencing::EnsureOrdered => (
 | 
						|
                Some(DialInfoDetail::ordered_sequencing_sort),
 | 
						|
                dial_info_filter.filtered(
 | 
						|
                    &DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
 | 
						|
                ),
 | 
						|
            ),
 | 
						|
        };
 | 
						|
        // If the filter is dead then we won't be able to connect
 | 
						|
        if dial_info_filter.is_dead() {
 | 
						|
            return ContactMethod::Unreachable;
 | 
						|
        }
 | 
						|
 | 
						|
        let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
 | 
						|
 | 
						|
        let opt_target_did = peer_b.signed_node_info.node_info().first_filtered_dial_info_detail(sort, filter);
 | 
						|
        if let Some(target_did) = opt_target_did {
 | 
						|
            return ContactMethod::Direct(target_did.dial_info);
 | 
						|
        }
 | 
						|
 | 
						|
        ContactMethod::Unreachable
 | 
						|
    }
 | 
						|
}
 |