use std::net::{SocketAddr, ToSocketAddrs};
use std::num::ParseIntError;
use std::str::FromStr;
use clap::{App, AppSettings, Arg, ArgGroup};
use hex::FromHex;
use itertools::Itertools;
use tox::toxcore::crypto_core::*;
use tox::toxcore::dht::packed_node::PackedNode;
use tox::toxcore::dht::packet::BOOSTRAP_SERVER_MAX_MOTD_LENGTH;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ThreadsConfig {
Auto,
N(u16)
}
impl FromStr for ThreadsConfig {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "auto" {
Ok(ThreadsConfig::Auto)
} else {
u16::from_str(s).map(ThreadsConfig::N)
}
}
}
#[cfg(unix)]
arg_enum! {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LogType {
Stderr,
Stdout,
Syslog,
None,
}
}
#[cfg(not(unix))]
arg_enum! {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LogType {
Stderr,
Stdout,
None,
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CliConfig {
pub udp_addr: Option<SocketAddr>,
pub tcp_addrs: Vec<SocketAddr>,
pub sk: Option<SecretKey>,
pub keys_file: Option<String>,
pub bootstrap_nodes: Vec<PackedNode>,
pub threads_config: ThreadsConfig,
pub log_type: LogType,
pub motd: String,
pub lan_discovery_enabled: bool,
}
pub fn cli_parse() -> CliConfig {
let matches = App::new(crate_name!())
.version(crate_version!())
.author(crate_authors!("\n"))
.about(crate_description!())
.setting(AppSettings::ColoredHelp)
.arg(Arg::with_name("udp-address")
.short("u")
.long("udp-address")
.help("UDP address to run DHT node")
.takes_value(true)
.required_unless("tcp-address"))
.arg(Arg::with_name("tcp-address")
.short("t")
.long("tcp-address")
.help("TCP address to run TCP relay")
.multiple(true)
.takes_value(true)
.use_delimiter(true)
.required_unless("udp-address"))
.group(ArgGroup::with_name("credentials")
.args(&["secret-key", "keys-file"])
.required(true))
.arg(Arg::with_name("secret-key")
.short("s")
.long("secret-key")
.help("DHT secret key")
.takes_value(true))
.arg(Arg::with_name("keys-file")
.short("k")
.long("keys-file")
.help("Path to the file where DHT keys are stored")
.takes_value(true))
.arg(Arg::with_name("bootstrap-node")
.short("b")
.long("bootstrap-node")
.help("Node to perform initial bootstrap")
.multiple(true)
.takes_value(true)
.number_of_values(2)
.value_names(&["public key", "address"]))
.arg(Arg::with_name("threads")
.short("j")
.long("threads")
.help("Number of threads to use. The value 'auto' means that the \
number of threads will be determined automatically by the \
number of CPU cores")
.takes_value(true)
.default_value("1"))
.arg(Arg::with_name("log-type")
.short("l")
.long("log-type")
.help("Where to write logs")
.takes_value(true)
.default_value("Stderr")
.possible_values(&LogType::variants()))
.arg(Arg::with_name("motd")
.short("m")
.long("motd")
.help("Message of the day")
.takes_value(true)
.validator(|m|
if m.len() > BOOSTRAP_SERVER_MAX_MOTD_LENGTH {
Err(format!("Message of the day must not be longer than {} bytes", BOOSTRAP_SERVER_MAX_MOTD_LENGTH))
} else {
Ok(())
}
)
.default_value("This is tox-rs"))
.arg(Arg::with_name("no-lan")
.long("no-lan")
.help("Disable LAN discovery"))
.get_matches();
let udp_addr = if matches.is_present("udp-address") {
Some(value_t!(matches.value_of("udp-address"), SocketAddr).unwrap_or_else(|e| e.exit()))
} else {
None
};
let tcp_addrs = if matches.is_present("tcp-address") {
values_t!(matches.values_of("tcp-address"), SocketAddr).unwrap_or_else(|e| e.exit())
} else {
Vec::new()
};
let sk = matches.value_of("secret-key").map(|s| {
let sk_bytes: [u8; 32] = FromHex::from_hex(s).expect("Invalid DHT secret key");
SecretKey::from_slice(&sk_bytes).expect("Invalid DHT secret key")
});
let keys_file = matches.value_of("keys-file").map(|s| s.to_owned());
let bootstrap_nodes = matches
.values_of("bootstrap-node")
.into_iter()
.flat_map(|values| values)
.tuples()
.map(|(pk, saddr)| {
let bootstrap_pk_bytes: [u8; 32] = FromHex::from_hex(pk).expect("Invalid node key");
let bootstrap_pk = PublicKey::from_slice(&bootstrap_pk_bytes).expect("Invalid node key");
let saddr = saddr
.to_socket_addrs()
.expect("Invalid node address")
.next()
.expect("Invalid node address");
PackedNode::new(saddr, &bootstrap_pk)
})
.collect();
let threads_config = value_t!(matches.value_of("threads"), ThreadsConfig).unwrap_or_else(|e| e.exit());
let log_type = value_t!(matches.value_of("log-type"), LogType).unwrap_or_else(|e| e.exit());
let motd = value_t!(matches.value_of("motd"), String).unwrap_or_else(|e| e.exit());
let lan_discovery_enabled = !matches.is_present("no-lan");
CliConfig {
udp_addr,
tcp_addrs,
sk,
keys_file,
bootstrap_nodes,
threads_config,
log_type,
motd,
lan_discovery_enabled,
}
}