From 203947388b9f7fe6a6dda5e5e55c76c3fe66646a Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 16 Feb 2016 02:05:36 +0100 Subject: [PATCH] Get public address/UPNP refactoring --- Cargo.lock | 1 + parity/main.rs | 40 ++++++++----- util/Cargo.toml | 1 + util/src/lib.rs | 1 + util/src/network/discovery.rs | 14 ++--- util/src/network/host.rs | 105 +++++++++++++++------------------- util/src/network/mod.rs | 1 + util/src/network/service.rs | 2 +- 8 files changed, 84 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c451a6477..2e38f5aeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ dependencies = [ "itertools 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "json-tests 0.1.0", "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/parity/main.rs b/parity/main.rs index 903b471c5..5fec05eb4 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -64,8 +64,8 @@ Options: -d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity] --no-bootstrap Don't bother trying to connect to any nodes initially. - --listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304]. - --public-address URL Specify the IP/port on which peers may connect [default: 0.0.0.0:30304]. + --listen-address URL Specify the IP/port on which to listen for peers. + --public-address URL Specify the IP/port on which peers may connect. --address URL Equivalent to --listen-address URL --public-address URL. --upnp Use UPnP to try to figure out the correct network settings. --node-key KEY Specify node secret key as hex string. @@ -79,7 +79,12 @@ Options: -l --logging LOGGING Specify the logging level. -v --version Show information about version. -h --help Show this screen. -", flag_cache_pref_size: usize, flag_cache_max_size: usize, flag_address: Option, flag_node_key: Option); +", flag_cache_pref_size: usize, + flag_cache_max_size: usize, + flag_address: Option, + flag_listen_address: Option, + flag_public_address: Option, + flag_node_key: Option); fn setup_log(init: &str) { let mut builder = LogBuilder::new(); @@ -155,21 +160,26 @@ impl Configuration { } } - fn net_addresses(&self) -> (SocketAddr, SocketAddr) { - let listen_address; - let public_address; + fn net_addresses(&self) -> (Option, Option) { + let mut listen_address = None; + let mut public_address = None; - match self.args.flag_address { - None => { - listen_address = SocketAddr::from_str(self.args.flag_listen_address.as_ref()).expect("Invalid listen address given with --listen-address"); - public_address = SocketAddr::from_str(self.args.flag_public_address.as_ref()).expect("Invalid public address given with --public-address"); + if let Some(ref a) = self.args.flag_address { + public_address = Some(SocketAddr::from_str(a.as_ref()).expect("Invalid listen/public address given with --address")); + listen_address = public_address; + } + if let Some(ref a) = self.args.flag_listen_address { + if listen_address.is_some() { + panic!("Conflicting flags: --address and --listen-address"); } - Some(ref a) => { - public_address = SocketAddr::from_str(a.as_ref()).expect("Invalid listen/public address given with --address"); - listen_address = public_address; + listen_address = Some(SocketAddr::from_str(a.as_ref()).expect("Invalid listen address given with --listen-address")); + } + if let Some(ref a) = self.args.flag_public_address { + if public_address.is_some() { + panic!("Conflicting flags: --address and --public-address"); } - }; - + public_address = Some(SocketAddr::from_str(a.as_ref()).expect("Invalid listen address given with --public-address")); + } (listen_address, public_address) } } diff --git a/util/Cargo.toml b/util/Cargo.toml index b1e9bbc1e..5bdf8c5a6 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -30,3 +30,4 @@ clippy = "0.0.41" json-tests = { path = "json-tests" } target_info = "0.1.0" igd = "0.4.2" +libc = "0.2.7" diff --git a/util/src/lib.rs b/util/src/lib.rs index 6dde49a01..7592cd17a 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -110,6 +110,7 @@ extern crate serde; #[macro_use] extern crate log as rlog; extern crate igd; +extern crate libc; pub mod standard; #[macro_use] diff --git a/util/src/network/discovery.rs b/util/src/network/discovery.rs index 8ed49b274..c15dbbbc4 100644 --- a/util/src/network/discovery.rs +++ b/util/src/network/discovery.rs @@ -78,7 +78,7 @@ struct Datagramm { pub struct Discovery { id: NodeId, secret: Secret, - address: NodeEndpoint, + public_endpoint: NodeEndpoint, udp_socket: UdpSocket, token: StreamToken, discovery_round: u16, @@ -94,12 +94,12 @@ pub struct TableUpdates { } impl Discovery { - pub fn new(key: &KeyPair, address: NodeEndpoint, token: StreamToken) -> Discovery { - let socket = UdpSocket::bound(&address.udp_address()).expect("Error binding UDP socket"); + pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken) -> Discovery { + let socket = UdpSocket::bound(&listen).expect("Error binding UDP socket"); Discovery { id: key.public().clone(), secret: key.secret().clone(), - address: address, + public_endpoint: public, token: token, discovery_round: 0, discovery_id: NodeId::new(), @@ -199,7 +199,7 @@ impl Discovery { fn ping(&mut self, node: &NodeEndpoint) { let mut rlp = RlpStream::new_list(3); rlp.append(&PROTOCOL_VERSION); - self.address.to_rlp_list(&mut rlp); + self.public_endpoint.to_rlp_list(&mut rlp); node.to_rlp_list(&mut rlp); trace!(target: "discovery", "Sent Ping to {:?}", &node); self.send_packet(PACKET_PING, &node.udp_address(), &rlp.drain()); @@ -507,8 +507,8 @@ mod tests { let key2 = KeyPair::create().unwrap(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; - let mut discovery1 = Discovery::new(&key1, ep1.clone(), 0); - let mut discovery2 = Discovery::new(&key2, ep2.clone(), 0); + let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0); + let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0); let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7770").unwrap(); let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7771").unwrap(); diff --git a/util/src/network/host.rs b/util/src/network/host.rs index 806ef2b92..3d55430bd 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::net::{SocketAddr, SocketAddrV4}; +use std::net::{SocketAddr}; use std::collections::{HashMap}; use std::hash::{Hasher}; use std::str::{FromStr}; @@ -39,8 +39,8 @@ use network::{NetworkProtocolHandler, PROTOCOL_VERSION}; use network::node_table::*; use network::stats::NetworkStats; use network::error::DisconnectReason; -use igd::{PortMappingProtocol, search_gateway}; use network::discovery::{Discovery, TableUpdates, NodeEntry}; +use network::ip_utils::{map_external_address, select_public_address}; type Slab = ::slab::Slab; @@ -55,10 +55,12 @@ const MAINTENANCE_TIMEOUT: u64 = 1000; pub struct NetworkConfiguration { /// Directory path to store network configuration. None means nothing will be saved pub config_path: Option, - /// IP address to listen for incoming connections - pub listen_address: SocketAddr, - /// IP address to advertise - pub public_address: SocketAddr, + /// IP address to listen for incoming connections. Listen to all connections by default + pub listen_address: Option, + /// IP address to advertise. Detected automatically if none. + pub public_address: Option, + /// Port for UDP connections, same as TCP by default + pub udp_port: Option, /// Enable NAT configuration pub nat_enabled: bool, /// Enable discovery @@ -78,8 +80,9 @@ impl NetworkConfiguration { pub fn new() -> NetworkConfiguration { NetworkConfiguration { config_path: None, - listen_address: SocketAddr::from_str("0.0.0.0:30304").unwrap(), - public_address: SocketAddr::from_str("0.0.0.0:30304").unwrap(), + listen_address: None, + public_address: None, + udp_port: None, nat_enabled: true, discovery_enabled: true, pin: false, @@ -92,48 +95,9 @@ impl NetworkConfiguration { /// Create new default configuration with sepcified listen port. pub fn new_with_port(port: u16) -> NetworkConfiguration { let mut config = NetworkConfiguration::new(); - config.listen_address = SocketAddr::from_str(&format!("0.0.0.0:{}", port)).unwrap(); - config.public_address = SocketAddr::from_str(&format!("0.0.0.0:{}", port)).unwrap(); + config.listen_address = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", port)).unwrap()); config } - - /// Conduct NAT if needed. - pub fn prepared(self) -> Self { - let mut listen = self.listen_address; - let mut public = self.public_address; - - if self.nat_enabled { - info!("Enabling NAT..."); - match search_gateway() { - Err(ref err) => warn!("Port mapping error: {}", err), - Ok(gateway) => { - let int_addr = SocketAddrV4::from_str("127.0.0.1:30304").unwrap(); - match gateway.get_any_address(PortMappingProtocol::TCP, int_addr, 0, "Parity Node/TCP") { - Err(ref err) => { - warn!("Port mapping error: {}", err); - }, - Ok(ext_addr) => { - info!("Local gateway: {}, External ip address: {}", gateway, ext_addr); - public = SocketAddr::V4(ext_addr); - listen = SocketAddr::V4(int_addr); - }, - } - }, - } - } - - NetworkConfiguration { - config_path: self.config_path, - listen_address: listen, - public_address: public, - nat_enabled: false, - discovery_enabled: self.discovery_enabled, - pin: self.pin, - boot_nodes: self.boot_nodes, - use_secret: self.use_secret, - ideal_peers: self.ideal_peers, - } - } } // Tokens @@ -333,16 +297,39 @@ pub struct Host where Message: Send + Sync + Clone { timers: RwLock>, timer_counter: RwLock, stats: Arc, + public_endpoint: NodeEndpoint, } impl Host where Message: Send + Sync + Clone { /// Create a new instance pub fn new(config: NetworkConfiguration) -> Host { - let config = config.prepared(); + let listen_address = match config.listen_address { + None => SocketAddr::from_str("0.0.0.0:30304").unwrap(), + Some(addr) => addr, + }; + + let udp_port = config.udp_port.unwrap_or(listen_address.port()); + let public_endpoint = match config.public_address { + None => { + let public_address = select_public_address(listen_address.port()); + let local_endpoint = NodeEndpoint { address: public_address, udp_port: udp_port }; + if config.nat_enabled { + match map_external_address(&local_endpoint) { + Some(endpoint) => { + info!("NAT Mappped to external address {}", endpoint.address); + endpoint + }, + None => local_endpoint + } + } else { + local_endpoint + } + } + Some(addr) => NodeEndpoint { address: addr, udp_port: udp_port } + }; - let addr = config.listen_address; // Setup the server socket - let tcp_listener = TcpListener::bind(&addr).unwrap(); + let tcp_listener = TcpListener::bind(&listen_address).unwrap(); let keys = if let Some(ref secret) = config.use_secret { KeyPair::from_secret(secret.clone()).unwrap() } else { @@ -356,8 +343,7 @@ impl Host where Message: Send + Sync + Clone { }, |s| KeyPair::from_secret(s).expect("Error creating node secret key")) }; - let endpoint = NodeEndpoint { address: config.public_address.clone(), udp_port: addr.port() }; - let discovery = Discovery::new(&keys, endpoint, DISCOVERY); + let discovery = Discovery::new(&keys, listen_address.clone(), public_endpoint.clone(), DISCOVERY); let path = config.config_path.clone(); let mut host = Host:: { info: RwLock::new(HostInfo { @@ -378,8 +364,9 @@ impl Host where Message: Send + Sync + Clone { timers: RwLock::new(HashMap::new()), timer_counter: RwLock::new(USER_TIMER), stats: Arc::new(NetworkStats::default()), + public_endpoint: public_endpoint, }; - let port = host.info.read().unwrap().config.listen_address.port(); + let port = listen_address.port(); host.info.write().unwrap().deref_mut().listen_port = port; let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone(); @@ -409,8 +396,8 @@ impl Host where Message: Send + Sync + Clone { self.info.read().unwrap().client_version.clone() } - pub fn client_id(&self) -> NodeId { - self.info.read().unwrap().id().clone() + pub fn client_url(&self) -> String { + format!("{}", Node::new(self.info.read().unwrap().id().clone(), self.public_endpoint.clone())) } fn maintain_network(&self, io: &IoContext>) { @@ -456,13 +443,15 @@ impl Host where Message: Send + Sync + Clone { } let handshake_count = self.handshake_count(); - if handshake_count >= MAX_HANDSHAKES { + // allow 16 slots for incoming connections + let handshake_limit = MAX_HANDSHAKES - 16; + if handshake_count >= handshake_limit { return; } let nodes = { self.nodes.read().unwrap().nodes() }; for id in nodes.iter().filter(|ref id| !self.have_session(id) && !self.connecting_to(id)) - .take(min(MAX_HANDSHAKES_PER_ROUND, MAX_HANDSHAKES - handshake_count)) { + .take(min(MAX_HANDSHAKES_PER_ROUND, handshake_limit - handshake_count)) { self.connect_peer(&id, io); } debug!(target: "net", "Connecting peers: {} sessions, {} pending", self.session_count(), self.handshake_count()); diff --git a/util/src/network/mod.rs b/util/src/network/mod.rs index c5066ff99..ff52212af 100644 --- a/util/src/network/mod.rs +++ b/util/src/network/mod.rs @@ -73,6 +73,7 @@ mod service; mod error; mod node_table; mod stats; +mod ip_utils; #[cfg(test)] mod tests; diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 60f0ec415..1cd48abe1 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -42,7 +42,7 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat let host = Arc::new(Host::new(config)); let stats = host.stats().clone(); let host_info = host.client_version(); - info!("Host ID={:?}", host.client_id()); + info!("Node URL: {}", host.client_url()); try!(io_service.register_handler(host)); Ok(NetworkService { io_service: io_service,