CLI parser stuff...

This commit is contained in:
Adam Ierymenko 2021-01-28 19:08:28 -05:00
parent fd6cf33e97
commit c29a64602a
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
8 changed files with 331 additions and 23 deletions

View file

@ -33,10 +33,15 @@ extern "C" {
/* ---------------------------------------------------------------------------------------------------------------- */
/**
* Default primary UDP port for devices running a ZeroTier endpoint
* Default primary UDP port
*/
#define ZT_DEFAULT_PORT 9993
/**
* Default secondary UDP port (if enabled)
*/
#define ZT_DEFAULT_SECONDARY_PORT 293
/**
* IP protocol number for naked IP encapsulation (this is not currently used)
*/

View file

@ -53,14 +53,17 @@ pub use portableatomici64::PortableAtomicI64;
pub use virtualnetworkconfig::*;
pub use multicastgroup::MulticastGroup;
/// Recommended minimum thread stack size for ZeroTier threads.
/// Recommended minimum thread stack size for background threads.
pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 262144;
/// Default TCP and UDP port for ZeroTier.
/// Default TCP and UDP port.
pub const DEFAULT_PORT: u16 = ztcore::ZT_DEFAULT_PORT as u16;
/// Default secondary UDP port if enabled.
pub const DEFAULT_SECONDARY_PORT: u16 = ztcore::ZT_DEFAULT_SECONDARY_PORT as u16;
/// Size of a ZeroTier core "Buffer" in bytes.
pub const BUF_SIZE: u32 = ztcore::ZT_BUF_SIZE;
pub const BUF_SIZE: usize = ztcore::ZT_BUF_SIZE as usize;
/// Minimum physical MTU.
pub const MIN_MTU: u32 = ztcore::ZT_MIN_MTU;

View file

@ -131,6 +131,7 @@ dependencies = [
"atty",
"bitflags",
"strsim",
"term_size",
"textwrap",
"unicode-width",
"vec_map",
@ -1037,12 +1038,23 @@ dependencies = [
"winapi",
]
[[package]]
name = "term_size"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"term_size",
"unicode-width",
]

View file

@ -9,18 +9,18 @@ build = "build.rs"
[dependencies]
zerotier-core = { path = "../rust-zerotier-core" }
num_cpus = "1.13"
num_cpus = "1"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
warp = "0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures = "0.3"
clap = { version = "2", features = ["default"] }
chrono = "0.4"
hex = "0.4"
futures = "0"
clap = { version = "2", features = ["suggestions", "wrap_help"] }
chrono = "0"
hex = "0"
lazy_static = "1"
num-traits = "0.2"
num-derive = "0.3"
num-traits = "0"
num-derive = "0"
[target."cfg(windows)".dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }

View file

@ -0,0 +1,259 @@
use std::str::FromStr;
use clap::{App, Arg, ArgMatches, ErrorKind};
fn make_help() -> String {
let ver = zerotier_core::version();
format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{}
(c)2013-2021 ZeroTier, Inc.
Licensed under the ZeroTier BSL (see LICENSE.txt)
Usage: zerotier [-global options] <command> [command args]
Global Options:
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <path> Load secret auth token from a file
-T <token> Set secret auth token on command line
Common Operations:
help Show this help
version Print version
· status Show node status and configuration
· set [setting] [value] List all settings (with no args)
· port <port> Primary P2P port
· secondaryport <port/0> Secondary P2P port (0 to disable)
· blacklist cidr <IP/bits> <boolean> Toggle physical path blacklisting
· blacklist if <prefix> <boolean> Toggle interface prefix blacklisting
· portmap <boolean> Toggle use of uPnP or NAT-PMP
· peer <command> [option]
· show <address> Show detailed peer information
· list List peers
· listroots List root peers
· try <address> <endpoint> [...] Try peer at explicit endpoint
· network <command> [option]
· show <network ID> Show detailed network information
· list List networks
· set <network ID> [option] [value] Get or set network options
· manageips <boolean> Is IP management allowed?
· manageroutes <boolean> Is route management allowed?
· managedns <boolean> Allow network to push DNS config
· globalips <boolean> Allow assignment of global IPs?
· globalroutes <boolean> Can global IP space routes be set?
· defaultroute <boolean> Can default route be overridden?
· join [-options] <network> Join a virtual network
-t <token> Token to submit to controller
-c <identity | fingerprint> Controller identity or fingerprint
· leave <network> Leave a virtual network
Advanced Operations:
service Start this node (runs until stopped)
controller <command> [option]
· list List networks on controller
· new Create a new network
· set <network> [setting] [value] Show or modify network settings
· show <network> [<address>] Show network or member status
· auth <address> Authorize a peer
· deauth <address> Deauthorize a peer
identity <command> [args]
new [c25519 | p384] Create identity (default: c25519)
getpublic <identity> Extract only public part of identity
fingerprint <identity> Get an identity's fingerprint
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
locator <command> [args]
new <identity> <endpoint> [...] Create new signed locator
verify <identity> <locator> Verify locator signature
show <locator> Show contents of a locator
cert <command> [args]
· list List certificates at local node
· show <serial> Show certificate details
newsid <secret out> Create a new subject unique ID
newcsr <subject> <secret> <csr out> Create a subject CSR
sign <csr> <identity> <cert out> Sign a CSR to create a certificate
verify <cert> Verify certificate (not chain)
dump <cert> Verify and print certificate
· import <cert> [trust] [...] Import certificate into this node
trust flag: rootca Certificate is a root CA
trust flag: ztrootset ZeroTier root node set
· restore Re-import default certificates
· export <serial> [path] Export a certificate from this node
· delete <serial|ALL> Delete certificate from this node
· Command requires a running node and access to a local API token.
An <address> may be specified as a 10-digit short ZeroTier address, a
fingerprint containing both an address and a SHA384 hash, or an identity.
The latter two options are equivalent in terms of specificity and may be
used if stronger security guarantees are desired than those provided by
the basic ZeroTier addressing system. Fields of type <identity> must be
full identities and may be specified either verbatim or as a path to a file.
An <endpoint> is a place where a peer may be reached. Currently these are
just 'IP/port' format addresses but other types may be added in the future.
The 'service' command starts a node. It will run until the node receives
an exit signal and is normally not used directly."###, ver.0, ver.1, ver.2)
}
pub(crate) fn print_help() {
println!("{}", make_help());
}
fn is_bool(v: String) -> Result<(), String> {
if !v.is_empty() {
match v.chars().next().unwrap() {
'y' | 'Y' | '1' | 't' | 'T' | 'n' | 'N' | '0' | 'f' | 'F' => { return Ok(()); }
_ => {}
}
}
Err(format!("invalid boolean value: '{}'", v))
}
fn is_valid_port(v: String) -> Result<(), String> {
let i = u16::from_str(v.as_str()).unwrap_or(0);
if i >= 1 {
return Ok(());
}
Err(format!("invalid TCP/IP port number: {}", v))
}
/// Parses CLI arguments, prints error and exits on failure.
pub(crate) fn parse_cli_args() -> ArgMatches<'static> {
let help = make_help();
let args = App::new("zerotier")
.arg(Arg::with_name("json").short("j"))
.arg(Arg::with_name("path").short("p").takes_value(true))
.arg(Arg::with_name("token_path").short("t").takes_value(true))
.arg(Arg::with_name("token").short("T").takes_value(true))
.subcommand(App::new("help"))
.subcommand(App::new("version"))
.subcommand(App::new("status"))
.subcommand(App::new("set")
.subcommand(App::new("port")
.arg(Arg::with_name("port#").index(1).validator(is_valid_port)))
.subcommand(App::new("secondaryport")
.arg(Arg::with_name("port#").index(1).validator(is_valid_port)))
.subcommand(App::new("blacklist")
.subcommand(App::new("cidr")
.arg(Arg::with_name("ip/bits").index(1))
.arg(Arg::with_name("boolean").index(2).validator(is_bool)))
.subcommand(App::new("if")
.arg(Arg::with_name("prefix").index(1))
.arg(Arg::with_name("boolean").index(2).validator(is_bool))))
.subcommand(App::new("portmap")
.arg(Arg::with_name("boolean").index(1).validator(is_bool))))
.subcommand(App::new("peer")
.subcommand(App::new("show")
.arg(Arg::with_name("address").index(1).required(true)))
.subcommand(App::new("list"))
.subcommand(App::new("listroots"))
.subcommand(App::new("try")))
.subcommand(App::new("network")
.subcommand(App::new("show"))
.subcommand(App::new("list"))
.subcommand(App::new("set")))
.subcommand(App::new("join")
.arg(Arg::with_name("token").short("t").takes_value(true))
.arg(Arg::with_name("controller").short("c").takes_value(true))
.arg(Arg::with_name("id").index(1).required(true)))
.subcommand(App::new("leave")
.arg(Arg::with_name("id").index(1).required(true)))
.subcommand(App::new("service"))
.subcommand(App::new("controller")
.subcommand(App::new("list"))
.subcommand(App::new("new"))
.subcommand(App::new("set")
.arg(Arg::with_name("id").index(1).required(true))
.arg(Arg::with_name("setting").index(2))
.arg(Arg::with_name("value").index(3)))
.subcommand(App::new("show")
.arg(Arg::with_name("id").index(1).required(true))
.arg(Arg::with_name("member").index(2)))
.subcommand(App::new("auth")
.arg(Arg::with_name("member").index(1).required(true)))
.subcommand(App::new("deauth")
.arg(Arg::with_name("member").index(1).required(true))))
.subcommand(App::new("identity")
.subcommand(App::new("new")
.arg(Arg::with_name("type").possible_value("p384").possible_value("c25519").index(1)))
.subcommand(App::new("getpublic")
.arg(Arg::with_name("identity").index(1).required(true)))
.subcommand(App::new("fingerprint")
.arg(Arg::with_name("identity").index(1).required(true)))
.subcommand(App::new("validate")
.arg(Arg::with_name("identity").index(1).required(true)))
.subcommand(App::new("sign")
.arg(Arg::with_name("identity").index(1).required(true))
.arg(Arg::with_name("file").index(2).required(true)))
.subcommand(App::new("verify")
.arg(Arg::with_name("identity").index(1).required(true))
.arg(Arg::with_name("file").index(2).required(true))
.arg(Arg::with_name("signature").index(3).required(true))))
.subcommand(App::new("locator")
.subcommand(App::new("new")
.arg(Arg::with_name("identity").index(1).required(true))
.arg(Arg::with_name("endpoint").index(2).multiple(true).required(true)))
.subcommand(App::new("verify")
.arg(Arg::with_name("identity").index(1).required(true))
.arg(Arg::with_name("locator").index(2).required(true)))
.subcommand(App::new("show")
.arg(Arg::with_name("locator").index(1).required(true))))
.subcommand(App::new("cert")
.subcommand(App::new("list"))
.subcommand(App::new("show")
.arg(Arg::with_name("serial").index(1).required(true)))
.subcommand(App::new("newsid")
.arg(Arg::with_name("path").index(1).required(true)))
.subcommand(App::new("newcsr")
.arg(Arg::with_name("subject").index(1).required(true))
.arg(Arg::with_name("secret").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(true)))
.subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("identity").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(true)))
.subcommand(App::new("verify")
.arg(Arg::with_name("cert").index(1).required(true)))
.subcommand(App::new("dump")
.arg(Arg::with_name("cert").index(1).required(true)))
.subcommand(App::new("import")
.arg(Arg::with_name("cert").index(1).required(true))
.arg(Arg::with_name("trust").possible_value("rootca").possible_value("ztrootset").index(2).multiple(true)))
.subcommand(App::new("restore"))
.subcommand(App::new("export")
.arg(Arg::with_name("serial").index(1).required(true))
.arg(Arg::with_name("path").index(2)))
.subcommand(App::new("delete")
.arg(Arg::with_name("serial").index(1).required(true))))
.help(help.as_str())
.get_matches_from_safe(std::env::args());
if args.is_err() {
let e = args.err().unwrap();
match e.kind {
ErrorKind::HelpDisplayed => {}
_ => { print_help(); }
}
std::process::exit(1);
}
let args = args.unwrap();
if args.subcommand_name().is_none() {
print_help();
std::process::exit(1);
}
args
}

View file

@ -61,20 +61,20 @@ pub const UNASSIGNED_PRIVILEGED_PORTS: [u16; 299] = [
1023,
];
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigPhysicalPathConfig {
pub blacklist: bool
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigVirtualConfig {
#[serde(rename = "try")]
pub try_: Vec<InetAddress>
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigNetworkSettings {
#[serde(rename = "allowManagedIPs")]
@ -89,7 +89,7 @@ pub struct LocalConfigNetworkSettings {
pub allow_default_route_override: bool,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigSettings {
#[serde(rename = "primaryPort")]
@ -118,7 +118,7 @@ pub struct LocalConfigSettings {
pub explicit_addresses: Vec<InetAddress>,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfig {
pub physical: BTreeMap<InetAddress, LocalConfigPhysicalPathConfig>,
@ -163,7 +163,6 @@ impl LocalConfigSettings {
#[cfg(target_os = "linux")]
const DEFAULT_PREFIX_BLACKLIST: [&'static str; 5] = ["lo", "tun", "tap", "ipsec", "zt"];
// This is not applicable on Windows as it doesn't use the same device name semantics.
#[cfg(windows)]
const DEFAULT_PREFIX_BLACKLIST: [&'static str; 0] = [];
@ -187,7 +186,7 @@ impl Default for LocalConfigSettings {
LocalConfigSettings {
primary_port: zerotier_core::DEFAULT_PORT,
secondary_port: Some(293), // this is one of UNASSIGNED_PRIVILEGED_PORTS that we will default to
secondary_port: Some(zerotier_core::DEFAULT_SECONDARY_PORT),
auto_port_search: true,
port_mapping: true,
log_size_max: 1048576,
@ -212,3 +211,29 @@ impl Default for LocalConfig {
}
}
}
zerotier_core::implement_to_from_json!(LocalConfig);
impl LocalConfig {
pub fn load(path: &String) -> std::io::Result<LocalConfig> {
let md = std::path::Path::new(path).metadata();
if md.is_err() {
return Err(md.err().unwrap());
}
if md.unwrap().len() > 1048576 {
}
let json = std::fs::read_to_string(path);
if json.is_err() {
return Err(json.err().unwrap());
}
let json = Self::new_from_json(json.unwrap().as_str());
if json.is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, json.err().unwrap().to_string()));
}
Ok(json.unwrap())
}
pub fn save(&self, path: &String) -> std::io::Result<()> {
std::fs::write(path, self.to_json())
}
}

View file

@ -11,6 +11,7 @@
*/
/****/
mod cli;
mod fastudpsocket;
mod localconfig;
mod physicallink;
@ -70,6 +71,7 @@ impl NodeEventHandler<Network> for ServiceEventHandler {
0
}
#[inline(always)]
fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool {
true
}
@ -80,6 +82,8 @@ impl NodeEventHandler<Network> for ServiceEventHandler {
}
fn main() {
let cli_args = Some(cli::parse_cli_args());
let inaddr_v6_any = IpAddr::from_str("::0").unwrap();
let mut process_exit_value: i32 = 0;
@ -137,7 +141,7 @@ fn main() {
.or(status)
.or(network)
.or(peer)
)).try_bind_with_graceful_shutdown((inaddr_v6_any, warp_server_port), async { shutdown_rx.await; });
)).try_bind_with_graceful_shutdown((inaddr_v6_any, warp_server_port), async { let _ = shutdown_rx.await; });
if warp_server.is_err() {
// TODO: log unable to bind to primary port
run.store(false, Ordering::Relaxed);

View file

@ -101,7 +101,7 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
let mut nd: osdep::in6_ndireq = zeroed();
copy_nonoverlapping(dev.as_ptr(), nd.ifname.as_mut_ptr().cast::<u8>(), if dev.len() > (nd.ifname.len() - 1) { nd.ifname.len() - 1 } else { dev.len() });
if osdep::ioctl(s, osdep::c_SIOCGIFINFO_IN6, (&nd as *mut osdep::in6_ndireq).cast()) == 0 {
if osdep::ioctl(s, osdep::c_SIOCGIFINFO_IN6, (&mut nd as *mut osdep::in6_ndireq).cast::<c_void>()) == 0 {
let oldflags = nd.ndi.flags;
if perform_nud {
nd.ndi.flags |= osdep::ND6_IFF_PERFORMNUD as osdep::u_int32_t;
@ -109,7 +109,7 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
nd.ndi.flags &= !(osdep::ND6_IFF_PERFORMNUD as osdep::u_int32_t);
}
if nd.ndi.flags != oldflags {
if osdep::ioctl(s, osdep::c_SIOCSIFINFO_FLAGS, (&nd as *mut osdep::in6_ndireq).cast()) != 0 {
if osdep::ioctl(s, osdep::c_SIOCSIFINFO_FLAGS, (&mut nd as *mut osdep::in6_ndireq).cast::<c_void>()) != 0 {
ok = false;
}
}
@ -119,7 +119,7 @@ fn device_ipv6_set_params(device: &String, perform_nud: bool, accept_ra: bool) -
let mut ifr: osdep::in6_ifreq = zeroed();
copy_nonoverlapping(dev.as_ptr(), ifr.ifr_name.as_mut_ptr().cast::<u8>(), if dev.len() > (ifr.ifr_name.len() - 1) { ifr.ifr_name.len() - 1 } else { dev.len() });
if osdep::ioctl(s, if accept_ra { osdep::c_SIOCAUTOCONF_START } else { osdep::c_SIOCAUTOCONF_STOP }, (&ifr as *mut osdep::in6_ifreq).cast()) != 0 {
if osdep::ioctl(s, if accept_ra { osdep::c_SIOCAUTOCONF_START } else { osdep::c_SIOCAUTOCONF_STOP }, (&mut ifr as *mut osdep::in6_ifreq).cast::<c_void>()) != 0 {
ok = false;
}