speed up public address detection
This commit is contained in:
		
				
					committed by
					
						 Christien Rioux
						Christien Rioux
					
				
			
			
				
	
			
			
			
						parent
						
							4eca53fd9b
						
					
				
				
					commit
					945215aba1
				
			| @@ -90,7 +90,7 @@ pub static DEFAULT_LOG_IGNORE_LIST: [&str; 23] = [ | ||||
| use cfg_if::*; | ||||
| use enumset::*; | ||||
| use eyre::{bail, eyre, Report as EyreReport, Result as EyreResult, WrapErr}; | ||||
| use futures_util::stream::FuturesUnordered; | ||||
| use futures_util::stream::{FuturesOrdered, FuturesUnordered}; | ||||
| use parking_lot::*; | ||||
| use schemars::{schema_for, JsonSchema}; | ||||
| use serde::*; | ||||
|   | ||||
| @@ -27,12 +27,20 @@ struct DiscoveryContextInner { | ||||
|     detected_public_dial_info: Option<DetectedPublicDialInfo>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct DiscoveryContext { | ||||
|     routing_table: RoutingTable, | ||||
|     net: Network, | ||||
|     inner: Arc<Mutex<DiscoveryContextInner>>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| struct DetectedDialInfo { | ||||
|     dial_info: DialInfo, | ||||
|     dial_info_class: DialInfoClass, | ||||
|     network_class: NetworkClass, | ||||
| } | ||||
|  | ||||
| impl DiscoveryContext { | ||||
|     pub fn new(routing_table: RoutingTable, net: Network) -> Self { | ||||
|         Self { | ||||
| @@ -412,53 +420,74 @@ impl DiscoveryContext { | ||||
|     // If we know we are not behind NAT, check our firewall status | ||||
|     #[instrument(level = "trace", skip(self), err)] | ||||
|     pub async fn protocol_process_no_nat(&self) -> EyreResult<()> { | ||||
|         let (node_1, external_1_dial_info) = { | ||||
|             let inner = self.inner.lock(); | ||||
|             ( | ||||
|                 inner.node_1.as_ref().unwrap().clone(), | ||||
|                 inner.external_1_dial_info.as_ref().unwrap().clone(), | ||||
|             ) | ||||
|         }; | ||||
|         // Do these detections in parallel, but with ordering preference | ||||
|         let mut ord = FuturesOrdered::new(); | ||||
|  | ||||
|         // Attempt a port mapping via all available and enabled mechanisms | ||||
|         // Try this before the direct mapping in the event that we are restarting | ||||
|         // and may not have recorded a mapping created the last time | ||||
|         if let Some(external_mapped_dial_info) = self.try_port_mapping().await { | ||||
|             // Got a port mapping, let's use it | ||||
|             self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped); | ||||
|             self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|         } | ||||
|         // Do a validate_dial_info on the external address from a redirected node | ||||
|         else if self | ||||
|             .validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true) | ||||
|             .await | ||||
|         { | ||||
|             // Add public dial info with Direct dialinfo class | ||||
|             self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Direct); | ||||
|             self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|         } else { | ||||
|             // Add public dial info with Blocked dialinfo class | ||||
|             self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Blocked); | ||||
|             self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|         // UPNP Automatic Mapping | ||||
|         /////////// | ||||
|         let this = self.clone(); | ||||
|         let do_mapped_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move { | ||||
|             // Attempt a port mapping via all available and enabled mechanisms | ||||
|             // Try this before the direct mapping in the event that we are restarting | ||||
|             // and may not have recorded a mapping created the last time | ||||
|             if let Some(external_mapped_dial_info) = this.try_port_mapping().await { | ||||
|                 // Got a port mapping, let's use it | ||||
|                 return Some(DetectedDialInfo { | ||||
|                     dial_info: external_mapped_dial_info.clone(), | ||||
|                     dial_info_class: DialInfoClass::Mapped, | ||||
|                     network_class: NetworkClass::InboundCapable, | ||||
|                 }); | ||||
|             } | ||||
|             None | ||||
|         }); | ||||
|         ord.push_back(do_mapped_fut); | ||||
|  | ||||
|         let this = self.clone(); | ||||
|         let do_direct_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move { | ||||
|             let (node_1, external_1_dial_info) = { | ||||
|                 let inner = this.inner.lock(); | ||||
|                 ( | ||||
|                     inner.node_1.as_ref().unwrap().clone(), | ||||
|                     inner.external_1_dial_info.as_ref().unwrap().clone(), | ||||
|                 ) | ||||
|             }; | ||||
|             // Do a validate_dial_info on the external address from a redirected node | ||||
|             if this | ||||
|                 .validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true) | ||||
|                 .await | ||||
|             { | ||||
|                 // Add public dial info with Direct dialinfo class | ||||
|                 Some(DetectedDialInfo { | ||||
|                     dial_info: external_1_dial_info.clone(), | ||||
|                     dial_info_class: DialInfoClass::Direct, | ||||
|                     network_class: NetworkClass::InboundCapable, | ||||
|                 }) | ||||
|             } else { | ||||
|                 // Add public dial info with Blocked dialinfo class | ||||
|                 Some(DetectedDialInfo { | ||||
|                     dial_info: external_1_dial_info.clone(), | ||||
|                     dial_info_class: DialInfoClass::Blocked, | ||||
|                     network_class: NetworkClass::InboundCapable, | ||||
|                 }) | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         ord.push_back(do_direct_fut); | ||||
|  | ||||
|         while let Some(res) = ord.next().await { | ||||
|             if let Some(ddi) = res { | ||||
|                 self.set_detected_public_dial_info(ddi.dial_info, ddi.dial_info_class); | ||||
|                 self.set_detected_network_class(ddi.network_class); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // If we know we are behind NAT check what kind | ||||
|     #[instrument(level = "trace", skip(self), ret, err)] | ||||
|     pub async fn protocol_process_nat(&self) -> EyreResult<bool> { | ||||
|         // Attempt a port mapping via all available and enabled mechanisms | ||||
|         // Try this before the direct mapping in the event that we are restarting | ||||
|         // and may not have recorded a mapping created the last time | ||||
|         if let Some(external_mapped_dial_info) = self.try_port_mapping().await { | ||||
|             // Got a port mapping, let's use it | ||||
|             self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped); | ||||
|             self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|  | ||||
|             // No more retries | ||||
|             return Ok(true); | ||||
|         } | ||||
|  | ||||
|         // Get the external dial info for our use here | ||||
|         let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = { | ||||
|             let inner = self.inner.lock(); | ||||
| @@ -471,51 +500,111 @@ impl DiscoveryContext { | ||||
|             ) | ||||
|         }; | ||||
|  | ||||
|         // Do a validate_dial_info on the external address, but with the same port as the local port of local interface, from a redirected node | ||||
|         // This test is to see if a node had manual port forwarding done with the same port number as the local listener | ||||
|         if let Some(local_port) = self.net.get_local_port(protocol_type) { | ||||
|             if external_1_dial_info.port() != local_port { | ||||
|                 let mut external_1_dial_info_with_local_port = external_1_dial_info.clone(); | ||||
|                 external_1_dial_info_with_local_port.set_port(local_port); | ||||
|         // Do these detections in parallel, but with ordering preference | ||||
|         let mut ord = FuturesOrdered::new(); | ||||
|  | ||||
|                 if self | ||||
|                     .validate_dial_info( | ||||
|                         node_1.clone(), | ||||
|                         external_1_dial_info_with_local_port.clone(), | ||||
|                         true, | ||||
|                     ) | ||||
|                     .await | ||||
|                 { | ||||
|                     // Add public dial info with Direct dialinfo class | ||||
|                     self.set_detected_public_dial_info( | ||||
|                         external_1_dial_info_with_local_port, | ||||
|                         DialInfoClass::Direct, | ||||
|                     ); | ||||
|                     self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|                     return Ok(true); | ||||
|                 } | ||||
|         // UPNP Automatic Mapping | ||||
|         /////////// | ||||
|         let this = self.clone(); | ||||
|         let do_mapped_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move { | ||||
|             // Attempt a port mapping via all available and enabled mechanisms | ||||
|             // Try this before the direct mapping in the event that we are restarting | ||||
|             // and may not have recorded a mapping created the last time | ||||
|             if let Some(external_mapped_dial_info) = this.try_port_mapping().await { | ||||
|                 // Got a port mapping, let's use it | ||||
|                 return Some(DetectedDialInfo { | ||||
|                     dial_info: external_mapped_dial_info.clone(), | ||||
|                     dial_info_class: DialInfoClass::Mapped, | ||||
|                     network_class: NetworkClass::InboundCapable, | ||||
|                 }); | ||||
|             } | ||||
|             None | ||||
|         }); | ||||
|         ord.push_back(do_mapped_fut); | ||||
|  | ||||
|         // Manual Mapping Detection | ||||
|         /////////// | ||||
|         let this = self.clone(); | ||||
|         if let Some(local_port) = this.net.get_local_port(protocol_type) { | ||||
|             if external_1_dial_info.port() != local_port { | ||||
|                 let c_external_1_dial_info = external_1_dial_info.clone(); | ||||
|                 let c_node_1 = node_1.clone(); | ||||
|                 let do_manual_map_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = | ||||
|                     Box::pin(async move { | ||||
|                         // Do a validate_dial_info on the external address, but with the same port as the local port of local interface, from a redirected node | ||||
|                         // This test is to see if a node had manual port forwarding done with the same port number as the local listener | ||||
|                         let mut external_1_dial_info_with_local_port = | ||||
|                             c_external_1_dial_info.clone(); | ||||
|                         external_1_dial_info_with_local_port.set_port(local_port); | ||||
|  | ||||
|                         if this | ||||
|                             .validate_dial_info( | ||||
|                                 c_node_1.clone(), | ||||
|                                 external_1_dial_info_with_local_port.clone(), | ||||
|                                 true, | ||||
|                             ) | ||||
|                             .await | ||||
|                         { | ||||
|                             // Add public dial info with Direct dialinfo class | ||||
|                             return Some(DetectedDialInfo { | ||||
|                                 dial_info: external_1_dial_info_with_local_port, | ||||
|                                 dial_info_class: DialInfoClass::Direct, | ||||
|                                 network_class: NetworkClass::InboundCapable, | ||||
|                             }); | ||||
|                         } | ||||
|  | ||||
|                         None | ||||
|                     }); | ||||
|                 ord.push_back(do_manual_map_fut); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Port mapping was not possible, and things aren't accessible directly. | ||||
|         // Let's see what kind of NAT we have | ||||
|         // Full Cone NAT Detection | ||||
|         /////////// | ||||
|         let this = self.clone(); | ||||
|         let c_node_1 = node_1.clone(); | ||||
|         let c_external_1_dial_info = external_1_dial_info.clone(); | ||||
|         let do_full_cone_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move { | ||||
|             // Let's see what kind of NAT we have | ||||
|             // Does a redirected dial info validation from a different address and a random port find us? | ||||
|             if this | ||||
|                 .validate_dial_info(c_node_1.clone(), c_external_1_dial_info.clone(), true) | ||||
|                 .await | ||||
|             { | ||||
|                 // Yes, another machine can use the dial info directly, so Full Cone | ||||
|                 // Add public dial info with full cone NAT network class | ||||
|  | ||||
|         // Does a redirected dial info validation from a different address and a random port find us? | ||||
|         if self | ||||
|             .validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true) | ||||
|             .await | ||||
|         { | ||||
|             // Yes, another machine can use the dial info directly, so Full Cone | ||||
|             // Add public dial info with full cone NAT network class | ||||
|             self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::FullConeNAT); | ||||
|             self.set_detected_network_class(NetworkClass::InboundCapable); | ||||
|                 return Some(DetectedDialInfo { | ||||
|                     dial_info: c_external_1_dial_info, | ||||
|                     dial_info_class: DialInfoClass::FullConeNAT, | ||||
|                     network_class: NetworkClass::InboundCapable, | ||||
|                 }); | ||||
|             } | ||||
|             None | ||||
|         }); | ||||
|         ord.push_back(do_full_cone_fut); | ||||
|  | ||||
|             // No more retries | ||||
|             return Ok(true); | ||||
|         // Run detections in parallel and take the first one, ordered by preference, that returns a result | ||||
|         while let Some(res) = ord.next().await { | ||||
|             if let Some(ddi) = res { | ||||
|                 self.set_detected_public_dial_info(ddi.dial_info, ddi.dial_info_class); | ||||
|                 self.set_detected_network_class(ddi.network_class); | ||||
|                 return Ok(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // No, we are restricted, determine what kind of restriction | ||||
|  | ||||
|         // We are restricted, determine what kind of restriction | ||||
|         // Get the external dial info for our use here | ||||
|         let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = { | ||||
|             let inner = self.inner.lock(); | ||||
|             ( | ||||
|                 inner.node_1.as_ref().unwrap().clone(), | ||||
|                 inner.external_1_dial_info.as_ref().unwrap().clone(), | ||||
|                 inner.external_1_address.unwrap(), | ||||
|                 inner.protocol_type.unwrap(), | ||||
|                 inner.address_type.unwrap(), | ||||
|             ) | ||||
|         }; | ||||
|         // Get our external address from some fast node, that is not node 1, call it node 2 | ||||
|         let (external_2_address, node_2) = match self | ||||
|             .discover_external_address(protocol_type, address_type, Some(node_1.node_ids())) | ||||
| @@ -589,7 +678,7 @@ impl Network { | ||||
|         // Loop for restricted NAT retries | ||||
|         loop { | ||||
|             log_net!(debug | ||||
|                 "=== update_protocol_dialino {:?} {:?} tries_left={} ===", | ||||
|                 "=== update_protocol_dialinfo {:?} {:?} tries_left={} ===", | ||||
|                 address_type, | ||||
|                 protocol_type, | ||||
|                 retry_count | ||||
|   | ||||
		Reference in New Issue
	
	Block a user