Make the world flat again; remove utils/ subdirectory (#5752)

automerge
This commit is contained in:
Michael Vines
2019-08-30 11:57:39 -07:00
committed by Grimes
parent 3cc5d8df7f
commit 643d0b0868
19 changed files with 12 additions and 12 deletions

2
netutil/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target/
/farf/

30
netutil/Cargo.toml Normal file
View File

@@ -0,0 +1,30 @@
[package]
name = "solana-netutil"
version = "0.19.0-pre0"
description = "Solana Network Utilities"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
bincode = "1.1.4"
clap = "2.33.0"
log = "0.4.8"
nix = "0.15.0"
rand = "0.6.1"
socket2 = "0.3.11"
solana-logger = { path = "../logger", version = "0.19.0-pre0" }
tokio = "0.1"
[lib]
name = "solana_netutil"
[[bin]]
name = "solana-ip-address"
path = "src/bin/ip_address.rs"
[[bin]]
name = "solana-ip-address-server"
path = "src/bin/ip_address_server.rs"

View File

@@ -0,0 +1,26 @@
use clap::{crate_version, App, Arg};
fn main() {
solana_logger::setup();
let matches = App::new("solana-ip-address")
.version(crate_version!())
.arg(
Arg::with_name("host_port")
.index(1)
.required(true)
.help("Host:port to connect to"),
)
.get_matches();
let host_port = matches.value_of("host_port").unwrap();
let addr = solana_netutil::parse_host_port(host_port)
.unwrap_or_else(|_| panic!("failed to parse {}", host_port));
match solana_netutil::get_public_ip_addr(&addr) {
Ok(ip) => println!("{}", ip),
Err(err) => {
eprintln!("{}: {}", addr, err);
std::process::exit(1)
}
}
}

View File

@@ -0,0 +1,23 @@
use clap::{crate_version, App, Arg};
fn main() {
solana_logger::setup();
let matches = App::new("solana-ip-address-server")
.version(crate_version!())
.arg(
Arg::with_name("port")
.index(1)
.required(true)
.help("TCP port to bind to"),
)
.get_matches();
let port = matches.value_of("port").unwrap();
let port = port
.parse()
.unwrap_or_else(|_| panic!("Unable to parse {}", port));
let _runtime = solana_netutil::ip_echo_server(port);
loop {
std::thread::park();
}
}

View File

@@ -0,0 +1,44 @@
use log::*;
use std::net::SocketAddr;
use tokio;
use tokio::net::TcpListener;
use tokio::prelude::{Future, Stream};
use tokio::runtime::Runtime;
pub type IpEchoServer = Runtime;
/// Starts a simple TCP server on the given port that echos the IP address of any peer that
/// connects. Used by |get_public_ip_addr|
pub fn ip_echo_server(port: u16) -> IpEchoServer {
let bind_addr = SocketAddr::from(([0, 0, 0, 0], port));
let tcp = TcpListener::bind(&bind_addr)
.unwrap_or_else(|err| panic!("Unable to bind to {}: {}", bind_addr, err));
info!("bound to {:?}", bind_addr);
let server = tcp
.incoming()
.map_err(|err| warn!("accept failed: {:?}", err))
.for_each(move |socket| {
let ip = socket
.peer_addr()
.and_then(|peer_addr| {
bincode::serialize(&peer_addr.ip()).map_err(|err| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to serialize: {:?}", err),
)
})
})
.unwrap_or_else(|_| vec![]);
let write_future = tokio::io::write_all(socket, ip)
.map_err(|err| warn!("write error: {:?}", err))
.map(|_| ());
tokio::spawn(write_future)
});
let mut rt = Runtime::new().expect("Failed to create Runtime");
rt.spawn(server);
rt
}

292
netutil/src/lib.rs Normal file
View File

@@ -0,0 +1,292 @@
//! The `netutil` module assists with networking
use log::*;
use rand::{thread_rng, Rng};
use socket2::{Domain, SockAddr, Socket, Type};
use std::io;
use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket};
use std::time::Duration;
mod ip_echo_server;
pub use ip_echo_server::*;
/// A data type representing a public Udp socket
pub struct UdpSocketPair {
pub addr: SocketAddr, // Public address of the socket
pub receiver: UdpSocket, // Locally bound socket that can receive from the public address
pub sender: UdpSocket, // Locally bound socket to send via public address
}
pub type PortRange = (u16, u16);
/// Determine the public IP address of this machine by asking an ip_echo_server at the given
/// address
pub fn get_public_ip_addr(ip_echo_server_addr: &SocketAddr) -> Result<IpAddr, String> {
let mut data = Vec::new();
let timeout = Duration::new(5, 0);
TcpStream::connect_timeout(ip_echo_server_addr, timeout)
.and_then(|mut stream| {
stream
.set_read_timeout(Some(Duration::new(10, 0)))
.expect("set_read_timeout");
stream.read_to_end(&mut data)
})
.and_then(|_| {
bincode::deserialize(&data).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("Failed to deserialize: {:?}", err),
)
})
})
.map_err(|err| err.to_string())
}
pub fn parse_port_or_addr(optstr: Option<&str>, default_addr: SocketAddr) -> SocketAddr {
if let Some(addrstr) = optstr {
if let Ok(port) = addrstr.parse() {
let mut addr = default_addr;
addr.set_port(port);
addr
} else if let Ok(addr) = addrstr.parse() {
addr
} else {
default_addr
}
} else {
default_addr
}
}
pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
let ports: Vec<&str> = port_range.split('-').collect();
if ports.len() != 2 {
return None;
}
let start_port = ports[0].parse();
let end_port = ports[1].parse();
if start_port.is_err() || end_port.is_err() {
return None;
}
let start_port = start_port.unwrap();
let end_port = end_port.unwrap();
if end_port < start_port {
return None;
}
Some((start_port, end_port))
}
pub fn parse_host(host: &str) -> Result<IpAddr, String> {
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<SocketAddr, String> {
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])
}
}
pub fn is_host_port(string: String) -> Result<(), String> {
parse_host_port(&string)?;
Ok(())
}
#[cfg(windows)]
fn udp_socket(_reuseaddr: bool) -> io::Result<Socket> {
let sock = Socket::new(Domain::ipv4(), Type::dgram(), None)?;
Ok(sock)
}
#[cfg(not(windows))]
fn udp_socket(reuseaddr: bool) -> io::Result<Socket> {
use nix::sys::socket::setsockopt;
use nix::sys::socket::sockopt::{ReuseAddr, ReusePort};
use std::os::unix::io::AsRawFd;
let sock = Socket::new(Domain::ipv4(), Type::dgram(), None)?;
let sock_fd = sock.as_raw_fd();
if reuseaddr {
// best effort, i.e. ignore errors here, we'll get the failure in caller
setsockopt(sock_fd, ReusePort, &true).ok();
setsockopt(sock_fd, ReuseAddr, &true).ok();
}
Ok(sock)
}
pub fn bind_in_range(range: PortRange) -> io::Result<(u16, UdpSocket)> {
let sock = udp_socket(false)?;
let (start, end) = range;
let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end);
loop {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), rand_port);
match sock.bind(&SockAddr::from(addr)) {
Ok(_) => {
let sock = sock.into_udp_socket();
break Result::Ok((sock.local_addr().unwrap().port(), sock));
}
Err(err) => {
if tries_left == 0 {
return Err(err);
}
}
}
rand_port += 1;
if rand_port == end {
rand_port = start;
}
tries_left -= 1;
}
}
// binds many sockets to the same port in a range
pub fn multi_bind_in_range(range: PortRange, mut num: usize) -> io::Result<(u16, Vec<UdpSocket>)> {
if cfg!(windows) && num != 1 {
// TODO: Can we do better for windows?
warn!(
"multi_bind_in_range() only supports 1 socket in windows ({} requested)",
num
);
num = 1;
}
let mut sockets = Vec::with_capacity(num);
let port = {
let (port, _) = bind_in_range(range)?;
port
}; // drop the probe, port should be available... briefly.
for _ in 0..num {
sockets.push(bind_to(port, true)?);
}
Ok((port, sockets))
}
pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result<UdpSocket> {
let sock = udp_socket(reuseaddr)?;
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port);
match sock.bind(&SockAddr::from(addr)) {
Ok(_) => Result::Ok(sock.into_udp_socket()),
Err(err) => Err(err),
}
}
pub fn find_available_port_in_range(range: PortRange) -> io::Result<u16> {
let (start, end) = range;
let mut tries_left = end - start;
let mut rand_port = thread_rng().gen_range(start, end);
loop {
match TcpListener::bind(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
rand_port,
)) {
Ok(_) => {
break Ok(rand_port);
}
Err(err) => {
if tries_left == 0 {
return Err(err);
}
}
}
rand_port += 1;
if rand_port == end {
rand_port = start;
}
tries_left -= 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_port_or_addr() {
let p1 = parse_port_or_addr(Some("9000"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p1.port(), 9000);
let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p2.port(), 7000);
let p2 = parse_port_or_addr(Some("hi there"), SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p2.port(), 1);
let p3 = parse_port_or_addr(None, SocketAddr::from(([1, 2, 3, 4], 1)));
assert_eq!(p3.port(), 1);
}
#[test]
fn test_parse_port_range() {
assert_eq!(parse_port_range("garbage"), None);
assert_eq!(parse_port_range("1-"), None);
assert_eq!(parse_port_range("1-2"), Some((1, 2)));
assert_eq!(parse_port_range("1-2-3"), None);
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);
let x = bind_to(2002, true).unwrap();
let y = bind_to(2002, true).unwrap();
assert_eq!(
x.local_addr().unwrap().port(),
y.local_addr().unwrap().port()
);
let (port, v) = multi_bind_in_range((2010, 2110), 10).unwrap();
for sock in &v {
assert_eq!(port, sock.local_addr().unwrap().port());
}
}
#[test]
#[should_panic]
fn test_bind_in_range_nil() {
let _ = bind_in_range((2000, 2000));
}
#[test]
fn test_find_available_port_in_range() {
assert_eq!(find_available_port_in_range((3000, 3001)).unwrap(), 3000);
let port = find_available_port_in_range((3000, 3050)).unwrap();
assert!(3000 <= port && port < 3050);
}
}