refactor get_contact_method
This commit is contained in:
@@ -1,5 +1,24 @@
|
||||
use super::*;
|
||||
|
||||
/// Mechanism required to contact another node
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) 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,
|
||||
@@ -147,8 +166,21 @@ pub trait RoutingDomainDetail {
|
||||
fn common(&self) -> &RoutingDomainDetailCommon;
|
||||
fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon;
|
||||
|
||||
// Per-domain accessors
|
||||
/// 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,
|
||||
node_a_id: &DHTKey,
|
||||
node_a: &NodeInfo,
|
||||
node_b_id: &DHTKey,
|
||||
node_b: &NodeInfo,
|
||||
dial_info_filter: DialInfoFilter,
|
||||
reliable: bool,
|
||||
) -> ContactMethod;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -168,6 +200,30 @@ impl Default for PublicInternetRoutingDomainDetail {
|
||||
}
|
||||
}
|
||||
|
||||
fn first_filtered_dial_info_detail(
|
||||
from_node: &NodeInfo,
|
||||
to_node: &NodeInfo,
|
||||
dial_info_filter: &DialInfoFilter,
|
||||
reliable: bool,
|
||||
) -> Option<DialInfoDetail> {
|
||||
let direct_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 = if reliable {
|
||||
Some(DialInfoDetail::reliable_sort)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let direct_filter = |did: &DialInfoDetail| did.matches_filter(&direct_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
|
||||
@@ -178,6 +234,128 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
fn can_contain_address(&self, address: Address) -> bool {
|
||||
address.is_global()
|
||||
}
|
||||
fn get_contact_method(
|
||||
&self,
|
||||
_rti: &RoutingTableInner,
|
||||
node_a_id: &DHTKey,
|
||||
node_a: &NodeInfo,
|
||||
node_b_id: &DHTKey,
|
||||
node_b: &NodeInfo,
|
||||
dial_info_filter: DialInfoFilter,
|
||||
reliable: bool,
|
||||
) -> ContactMethod {
|
||||
// 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, reliable)
|
||||
{
|
||||
// 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(inbound_relay) = node_b.relay_peer_info {
|
||||
// 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 inbound_relay.node_id.key == *node_a_id {
|
||||
return ContactMethod::Existing;
|
||||
}
|
||||
|
||||
// Can node A reach the inbound relay directly?
|
||||
if first_filtered_dial_info_detail(
|
||||
node_a,
|
||||
&inbound_relay.signed_node_info.node_info,
|
||||
&dial_info_filter,
|
||||
reliable,
|
||||
)
|
||||
.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,
|
||||
reliable,
|
||||
) {
|
||||
// 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(
|
||||
inbound_relay.node_id.key,
|
||||
*node_b_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////// 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,
|
||||
reliable,
|
||||
) {
|
||||
// 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,
|
||||
reliable,
|
||||
) {
|
||||
// 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(
|
||||
inbound_relay.node_id.key,
|
||||
*node_b_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise we have to inbound relay
|
||||
}
|
||||
|
||||
return ContactMethod::InboundRelay(inbound_relay.node_id.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the node B has no direct dial info, it needs to have an inbound relay
|
||||
else if let Some(inbound_relay) = node_b.relay_peer_info {
|
||||
// Can we reach the full relay?
|
||||
if first_filtered_dial_info_detail(
|
||||
node_a,
|
||||
&inbound_relay.signed_node_info.node_info,
|
||||
&dial_info_filter,
|
||||
reliable,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return ContactMethod::InboundRelay(inbound_relay.node_id.key);
|
||||
}
|
||||
}
|
||||
|
||||
// If node A can't reach the node by other means, it may need to use its own relay
|
||||
if let Some(outbound_relay) = node_a.relay_peer_info {
|
||||
return ContactMethod::OutboundRelay(outbound_relay.node_id.key);
|
||||
}
|
||||
|
||||
ContactMethod::Unreachable
|
||||
}
|
||||
}
|
||||
|
||||
/// Local Network routing domain internals
|
||||
@@ -225,4 +403,42 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_contact_method(
|
||||
&self,
|
||||
_rti: &RoutingTableInner,
|
||||
_node_a_id: &DHTKey,
|
||||
node_a: &NodeInfo,
|
||||
_node_b_id: &DHTKey,
|
||||
node_b: &NodeInfo,
|
||||
dial_info_filter: DialInfoFilter,
|
||||
reliable: bool,
|
||||
) -> 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(node_a.address_types)
|
||||
.with_protocol_type_set(node_a.outbound_protocols),
|
||||
);
|
||||
|
||||
// If the filter is dead then we won't be able to connect
|
||||
if dial_info_filter.is_dead() {
|
||||
return ContactMethod::Unreachable;
|
||||
}
|
||||
|
||||
// Get first filtered dialinfo
|
||||
let sort = if reliable {
|
||||
Some(DialInfoDetail::reliable_sort)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
|
||||
|
||||
let opt_target_did = node_b.first_filtered_dial_info_detail(sort, filter);
|
||||
if let Some(target_did) = opt_target_did {
|
||||
return ContactMethod::Direct(target_did.dial_info);
|
||||
}
|
||||
|
||||
ContactMethod::Unreachable
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user