From 0767c0c07f0957764892a922e42efa66eeba9ce6 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 13 Apr 2019 19:34:27 -0700 Subject: [PATCH] Add DNS resolution to cli tools --- Cargo.lock | 3 ++ bench-tps/Cargo.toml | 1 + bench-tps/src/cli.rs | 6 ++-- book/src/getting-started.md | 2 +- book/src/testnet-participation.md | 21 +++++--------- fullnode/src/main.rs | 9 +++--- gossip/Cargo.toml | 1 + gossip/src/main.rs | 4 +-- multinode-demo/fullnode.sh | 14 +-------- netutil/src/lib.rs | 43 +++++++++++++++++++++++++++- replicator/src/main.rs | 5 ++-- wallet/Cargo.toml | 1 + wallet/src/main.rs | 47 ++++++++++++++++++++----------- 13 files changed, 100 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef2850d06e..bcfb3a041c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2200,6 +2200,7 @@ dependencies = [ "solana-drone 0.13.0", "solana-logger 0.13.0", "solana-metrics 0.13.0", + "solana-netutil 0.13.0", "solana-sdk 0.13.0", ] @@ -2388,6 +2389,7 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "solana 0.13.0", + "solana-netutil 0.13.0", "solana-sdk 0.13.0", ] @@ -2690,6 +2692,7 @@ dependencies = [ "solana-client 0.13.0", "solana-drone 0.13.0", "solana-logger 0.13.0", + "solana-netutil 0.13.0", "solana-sdk 0.13.0", "solana-vote-api 0.13.0", "solana-vote-signer 0.13.0", diff --git a/bench-tps/Cargo.toml b/bench-tps/Cargo.toml index dd1ac9695c..5b5f5ba350 100644 --- a/bench-tps/Cargo.toml +++ b/bench-tps/Cargo.toml @@ -16,6 +16,7 @@ solana-client = { path = "../client", version = "0.13.0" } solana-drone = { path = "../drone", version = "0.13.0" } solana-logger = { path = "../logger", version = "0.13.0" } solana-metrics = { path = "../metrics", version = "0.13.0" } +solana-netutil = { path = "../netutil", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" } [features] diff --git a/bench-tps/src/cli.rs b/bench-tps/src/cli.rs index a87e7624be..62a3794939 100644 --- a/bench-tps/src/cli.rs +++ b/bench-tps/src/cli.rs @@ -117,14 +117,14 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config { let mut args = Config::default(); if let Some(addr) = matches.value_of("network") { - args.network_addr = addr.parse().unwrap_or_else(|e| { - eprintln!("failed to parse network: {}", e); + args.network_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| { + eprintln!("failed to parse network address: {}", e); exit(1) }); } if let Some(addr) = matches.value_of("drone") { - args.drone_addr = addr.parse().unwrap_or_else(|e| { + args.drone_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| { eprintln!("failed to parse drone address: {}", e); exit(1) }); diff --git a/book/src/getting-started.md b/book/src/getting-started.md index a43dee49a0..a2e7befa95 100644 --- a/book/src/getting-started.md +++ b/book/src/getting-started.md @@ -161,7 +161,7 @@ This will dump all the threads stack traces into gdb.txt In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`. ```bash -$ ./multinode-demo/client.sh --network $(dig +short testnet.solana.com):8001 --duration 60 +$ ./multinode-demo/client.sh --network testnet.solana.com:8001 --duration 60 ``` You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet) diff --git a/book/src/testnet-participation.md b/book/src/testnet-participation.md index 84249d08b1..aa5d947696 100644 --- a/book/src/testnet-participation.md +++ b/book/src/testnet-participation.md @@ -26,14 +26,7 @@ Prebuilt binaries are available for Linux x86_64 (Ubuntu 18.04 recommended). MacOS or WSL users may build from source. ### Validator Setup -The shell commands in this section assume the following environment variables are -set: -```bash -$ export ip=$(dig +short beta.testnet.solana.com) -``` - #### Obtaining The Software - ##### Bootstrap with `solana-install` The `solana-install` tool can be used to easily install and upgrade the cluster @@ -82,8 +75,8 @@ just restarting itself before debugging further. Receive an airdrop of lamports from the testnet drone: ```bash -$ solana-wallet -n ${ip:?} airdrop 123 -$ solana-wallet -n ${ip:?} balance +$ solana-wallet -n beta.testnet.solana.com airdrop 123 +$ solana-wallet -n beta.testnet.solana.com balance ``` Fetch the current testnet transaction count over JSON RPC: @@ -95,7 +88,7 @@ Inspect the blockexplorer at http://beta.testnet.solana.com/ for activity. Run the following command to join the gossip network and view all the other nodes in the cluster: ```bash -$ RUST_LOG=info solana-gossip --network ${ip:?}:8001 +$ solana-gossip --network beta.testnet.solana.com:8001 ``` ### Starting The Validator @@ -103,24 +96,24 @@ The following command will start a new validator node. If this is a `solana-install`-installation: ```bash -$ fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} +$ fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001 ``` Alternatively, the `solana-install run` command can be used to run the validator node while periodically checking for and applying software updates: ```bash -$ solana-install run fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} +$ solana-install run fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001 ``` When not using `solana-install`: ```bash -$ USE_INSTALL=1 ./multinode-demo/fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} +$ USE_INSTALL=1 ./multinode-demo/fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001 ``` Then from another console, confirm the IP address if your node is now visible in the gossip network by running: ```bash -$ RUST_LOG=info solana-gossip --network ${ip:?}:8001 +$ solana-gossip --network beta.testnet.solana.com:8001 ``` Congratulations, you're now participating in the testnet cluster! diff --git a/fullnode/src/main.rs b/fullnode/src/main.rs index 9a49a411c0..1a7e7b1da3 100644 --- a/fullnode/src/main.rs +++ b/fullnode/src/main.rs @@ -187,9 +187,9 @@ fn main() { if matches.is_present("enable_rpc_exit") { fullnode_config.rpc_config.enable_fullnode_exit = true; } - fullnode_config.rpc_config.drone_addr = matches - .value_of("rpc_drone_address") - .map(|address| address.parse().expect("failed to parse drone address")); + fullnode_config.rpc_config.drone_addr = matches.value_of("rpc_drone_address").map(|address| { + solana_netutil::parse_host_port(address).expect("failed to parse drone address") + }); let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap()) .expect("invalid dynamic_port_range"); @@ -214,7 +214,8 @@ fn main() { fullnode_config.account_paths = None; } let cluster_entrypoint = matches.value_of("network").map(|network| { - let gossip_addr = network.parse().expect("failed to parse network address"); + let gossip_addr = + solana_netutil::parse_host_port(network).expect("failed to parse network address"); ContactInfo::new_gossip_entry_point(&gossip_addr) }); let (_signer_service, _signer_addr) = if let Some(signer_addr) = matches.value_of("signer") { diff --git a/gossip/Cargo.toml b/gossip/Cargo.toml index 767c9b3b47..b14ae67a63 100644 --- a/gossip/Cargo.toml +++ b/gossip/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://solana.com/" clap = "2.33.0" env_logger = "0.6.1" solana = { path = "../core", version = "0.13.0" } +solana-netutil = { path = "../netutil", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" } [features] diff --git a/gossip/src/main.rs b/gossip/src/main.rs index bddd6347cf..4871d6b87f 100644 --- a/gossip/src/main.rs +++ b/gossip/src/main.rs @@ -67,8 +67,8 @@ fn main() -> Result<(), Box> { .get_matches(); if let Some(addr) = matches.value_of("network") { - network_addr = addr.parse().unwrap_or_else(|e| { - eprintln!("failed to parse network: {}", e); + network_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| { + eprintln!("failed to parse network address: {}", e); exit(1) }); } diff --git a/multinode-demo/fullnode.sh b/multinode-demo/fullnode.sh index 5edf1f1465..c7a28f2ed4 100755 --- a/multinode-demo/fullnode.sh +++ b/multinode-demo/fullnode.sh @@ -78,19 +78,7 @@ find_leader() { leader_address=127.0.0.1:8001 # Default to local leader elif [[ -z $2 ]]; then leader=$1 - - declare leader_ip - if [[ $leader =~ ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then - leader_ip=$leader - else - leader_ip=$(dig +short "${leader%:*}" | head -n1) - - if [[ -z $leader_ip ]]; then - usage "Error: unable to resolve IP address for $leader" - fi - fi - - leader_address=$leader_ip:8001 + leader_address=$leader:8001 shift=1 else leader=$1 diff --git a/netutil/src/lib.rs b/netutil/src/lib.rs index 7f28d3b32c..c04149e083 100644 --- a/netutil/src/lib.rs +++ b/netutil/src/lib.rs @@ -7,7 +7,7 @@ use rand::{thread_rng, Rng}; use reqwest; use socket2::{Domain, SockAddr, Socket, Type}; use std::io; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, ToSocketAddrs, UdpSocket}; use std::os::unix::io::AsRawFd; /// A data type representing a public Udp socket @@ -70,6 +70,31 @@ pub fn parse_port_range(port_range: &str) -> Option { Some((start_port, end_port)) } +pub fn parse_host(host: &str) -> Result { + let ips: Vec<_> = (host, 0) + .to_socket_addrs() + .map_err(|err| err.to_string())? + .map(|socket_address| socket_address.ip()) + .collect(); + if ips.is_empty() { + Err(format!("Unable to resolve host: {}", host)) + } else { + Ok(ips[0]) + } +} + +pub fn parse_host_port(host_port: &str) -> Result { + let addrs: Vec<_> = host_port + .to_socket_addrs() + .map_err(|err| err.to_string())? + .collect(); + if addrs.is_empty() { + Err(format!("Unable to resolve host: {}", host_port)) + } else { + Ok(addrs[0]) + } +} + fn find_eth0ish_ip_addr( ifaces: &mut Vec, enable_ipv6: bool, @@ -334,6 +359,22 @@ mod tests { assert_eq!(parse_port_range("2-1"), None); } + #[test] + fn test_parse_host() { + parse_host("localhost:1234").unwrap_err(); + parse_host("localhost").unwrap(); + parse_host("127.0.0.0:1234").unwrap_err(); + parse_host("127.0.0.0").unwrap(); + } + + #[test] + fn test_parse_host_port() { + parse_host_port("localhost:1234").unwrap(); + parse_host_port("localhost").unwrap_err(); + parse_host_port("127.0.0.0:1234").unwrap(); + parse_host_port("127.0.0.0").unwrap_err(); + } + #[test] fn test_bind() { assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000); diff --git a/replicator/src/main.rs b/replicator/src/main.rs index 2f28b7bcab..3073ae1169 100644 --- a/replicator/src/main.rs +++ b/replicator/src/main.rs @@ -78,7 +78,9 @@ fn main() { let network_addr = matches .value_of("network") - .map(|network| network.parse().expect("failed to parse network address")) + .map(|network| { + solana_netutil::parse_host_port(network).expect("failed to parse network address") + }) .unwrap(); let leader_info = ContactInfo::new_gossip_entry_point(&network_addr); @@ -94,6 +96,5 @@ fn main() { .unwrap(); replicator.run(); - replicator.close(); } diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index d0a3872f69..53d32f55a6 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -20,6 +20,7 @@ solana-budget-api = { path = "../programs/budget_api", version = "0.13.0" } solana-client = { path = "../client", version = "0.13.0" } solana-drone = { path = "../drone", version = "0.13.0" } solana-logger = { path = "../logger", version = "0.13.0" } +solana-netutil = { path = "../netutil", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.13.0" } solana-vote-signer = { path = "../vote-signer", version = "0.13.0" } diff --git a/wallet/src/main.rs b/wallet/src/main.rs index de2fdd3b88..02c32c716c 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -7,28 +7,31 @@ use solana_wallet::wallet::{parse_command, process_command, WalletConfig, Wallet use std::error; pub fn parse_args(matches: &ArgMatches<'_>) -> Result> { - let host = matches - .value_of("host") - .unwrap() - .parse() - .or_else(|_| Err(WalletError::BadParameter("Invalid host".to_string())))?; + let host = solana_netutil::parse_host(matches.value_of("host").unwrap()).or_else(|err| { + Err(WalletError::BadParameter(format!( + "Invalid host: {:?}", + err + ))) + })?; let drone_host = if let Some(drone_host) = matches.value_of("drone_host") { - Some( - drone_host - .parse() - .or_else(|_| Err(WalletError::BadParameter("Invalid drone host".to_string())))?, - ) + Some(solana_netutil::parse_host(drone_host).or_else(|err| { + Err(WalletError::BadParameter(format!( + "Invalid drone host: {:?}", + err + ))) + })?) } else { None }; let rpc_host = if let Some(rpc_host) = matches.value_of("rpc_host") { - Some( - rpc_host - .parse() - .or_else(|_| Err(WalletError::BadParameter("Invalid rpc host".to_string())))?, - ) + Some(solana_netutil::parse_host(rpc_host).or_else(|err| { + Err(WalletError::BadParameter(format!( + "Invalid rpc host: {:?}", + err + ))) + })?) } else { None }; @@ -37,13 +40,23 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result