diff --git a/service/src/cli.rs b/service/src/cli.rs deleted file mode 100644 index 1916c198b..000000000 --- a/service/src/cli.rs +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c)2013-2021 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -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 [-...] [command args] - -Global Options: - - -j Output raw JSON where applicable - -p Use alternate base path - -t Load secret auth token from a file - -T Set secret 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 Primary P2P port -· secondaryport Secondary P2P port (0 to disable) -· blacklist cidr Toggle physical path blacklisting -· blacklist if [Un]blacklist interface prefix -· portmap Toggle use of uPnP and NAT-PMP - -· peer [option] -· show
Show detailed peer information -· list List peers -· listroots List root peers -· try
[...] Try peer at explicit endpoint - -· network [option] -· show Show detailed network information -· list List networks -· set [option] [value] Get or set network options -· manageips Is IP management allowed? -· manageroutes Is route management allowed? -· managedns Allow network to push DNS config -· globalips Allow assignment of global IPs? -· globalroutes Can global IP routes be set? -· defaultroute Can default route be overridden? - -· join [-...] Join a virtual network - -c Controller identity / fingerprint -· leave Leave a virtual network - -Advanced Operations: - - service Start this node - (usually not invoked directly) - - controller [option] -· list List networks on controller -· new Create a new network -· set [setting] [value] Show or modify network settings -· show [
] Show network or member status -· auth
Authorize a peer -· deauth
Deauthorize a peer - - identity [args] - new [c25519 | p384] Create identity (default: c25519) - getpublic Extract public part of identity - fingerprint Get an identity's fingerprint - validate Locally validate an identity - sign Sign a file with an identity's key - verify Verify a signature - - locator [args] - new [-...] [...] Create new signed locator - -t Timestamp (default: system time) - verify Verify locator signature - show Show contents of a locator - - cert [args] -· list List certificates at local node -· show Show certificate details - newsid [sid secret out] Create a new subject unique ID - newcsr Create a subject CSR (interactive) - sign [cert out] Sign a CSR to create a certificate - verify Verify certificate (not chain) - dump Verify and print certificate -· import [trust,trust,...] Import certificate into this node - trust flag: rootca Certificate is a root CA - trust flag: ztrootset ZeroTier root node set -· factoryreset Re-import compiled-in default certs -· export [path] Export a certificate from this node -· delete Delete certificate from this node - -· Command requires a running node and access to a local API token. - -An
may be specified as a 10-digit short ZeroTier address, a -fingerprint containing both an address and a SHA384 hash, or an identity. -Identities and locators can be specified as either paths to files on the -filesystem or verbatim objects in string format. This is auto-detected."###, 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> { - // NOTE: the names of these arguments must match the names used to look - // them up in the various command demuxers under commands/. - 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") - .arg(Arg::with_name("nwid").index(1).required(true))) - .subcommand(App::new("list")) - .subcommand(App::new("set") - .arg(Arg::with_name("nwid").index(1).required(true)) - .arg(Arg::with_name("setting").index(2).required(false)) - .arg(Arg::with_name("value").index(3).required(false)))) - .subcommand(App::new("join") - .arg(Arg::with_name("controller").short("c").takes_value(true)) - .arg(Arg::with_name("nwid").index(1).required(true))) - .subcommand(App::new("leave") - .arg(Arg::with_name("nwid").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").default_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("path").index(2).required(true))) - .subcommand(App::new("verify") - .arg(Arg::with_name("identity").index(1).required(true)) - .arg(Arg::with_name("path").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("timestamp").short("t").required(false)) - .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(false))) - .subcommand(App::new("newcsr") - .arg(Arg::with_name("path").index(1).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(false))) - .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").index(2).required(false))) - .subcommand(App::new("factoryreset")) - .subcommand(App::new("export") - .arg(Arg::with_name("serial").index(1).required(true)) - .arg(Arg::with_name("path").index(2).required(false))) - .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 -} diff --git a/service/src/commands/cert.rs b/service/src/commands/cert.rs index acbdd4e4d..8415babdf 100644 --- a/service/src/commands/cert.rs +++ b/service/src/commands/cert.rs @@ -288,7 +288,7 @@ pub(crate) fn run<'a>(store: Arc, cli_args: &ArgMatches<'a>) -> i32 { ("export", Some(sub_cli_args)) => export(&store, sub_cli_args), ("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args), _ => { - crate::cli::print_help(); + crate::print_help(); 1 } } diff --git a/service/src/commands/identity.rs b/service/src/commands/identity.rs index 7dd8b7a21..a857f5a5e 100644 --- a/service/src/commands/identity.rs +++ b/service/src/commands/identity.rs @@ -128,7 +128,7 @@ pub(crate) fn run<'a>(cli_args: &ArgMatches<'a>) -> i32 { ("sign", Some(sub_cli_args)) => sign(sub_cli_args), ("verify", Some(sub_cli_args)) => verify(sub_cli_args), _ => { - crate::cli::print_help(); + crate::print_help(); 1 } } diff --git a/service/src/commands/locator.rs b/service/src/commands/locator.rs index 29df5e7af..ea4deadde 100644 --- a/service/src/commands/locator.rs +++ b/service/src/commands/locator.rs @@ -111,7 +111,7 @@ pub(crate) fn run<'a>(cli_args: &ArgMatches<'a>) -> i32 { ("verify", Some(sub_cli_args)) => verify(sub_cli_args), ("show", Some(sub_cli_args)) => show(sub_cli_args), _ => { - crate::cli::print_help(); + crate::print_help(); 1 } } diff --git a/service/src/main.rs b/service/src/main.rs index a60b5098e..f2004ca68 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -12,7 +12,6 @@ /****/ mod api; -mod cli; mod commands; mod fastudpsocket; mod localconfig; @@ -31,12 +30,262 @@ mod osdep; // bindgen generated use std::boxed::Box; use std::rc::Rc; use std::sync::Arc; +use std::str::FromStr; + +use clap::{App, Arg, ArgMatches, ErrorKind}; use crate::store::Store; -use clap::ArgMatches; + +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 [-...] [command args] + +Global Options: + + -j Output raw JSON where applicable + -p Use alternate base path + -t Load secret auth token from a file + -T Set secret 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 Primary P2P port +· secondaryport Secondary P2P port (0 to disable) +· blacklist cidr Toggle physical path blacklisting +· blacklist if [Un]blacklist interface prefix +· portmap Toggle use of uPnP and NAT-PMP + +· peer [option] +· show
Show detailed peer information +· list List peers +· listroots List root peers +· try
[...] Try peer at explicit endpoint + +· network [option] +· show Show detailed network information +· list List networks +· set [option] [value] Get or set network options +· manageips Is IP management allowed? +· manageroutes Is route management allowed? +· managedns Allow network to push DNS config +· globalips Allow assignment of global IPs? +· globalroutes Can global IP routes be set? +· defaultroute Can default route be overridden? + +· join [-...] Join a virtual network + -c Controller identity / fingerprint +· leave Leave a virtual network + +Advanced Operations: + + service Start this node + (usually not invoked directly) + + controller [option] +· list List networks on controller +· new Create a new network +· set [setting] [value] Show or modify network settings +· show [
] Show network or member status +· auth
Authorize a peer +· deauth
Deauthorize a peer + + identity [args] + new [c25519 | p384] Create identity (default: c25519) + getpublic Extract public part of identity + fingerprint Get an identity's fingerprint + validate Locally validate an identity + sign Sign a file with an identity's key + verify Verify a signature + + locator [args] + new [-...] [...] Create new signed locator + -t Timestamp (default: system time) + verify Verify locator signature + show Show contents of a locator + + cert [args] +· list List certificates at local node +· show Show certificate details + newsid [sid secret out] Create a new subject unique ID + newcsr Create a subject CSR (interactive) + sign [cert out] Sign a CSR to create a certificate + verify Verify certificate (not chain) + dump Verify and print certificate +· import [trust,trust,...] Import certificate into this node + trust flag: rootca Certificate is a root CA + trust flag: ztrootset ZeroTier root node set +· factoryreset Re-import compiled-in default certs +· export [path] Export a certificate from this node +· delete Delete certificate from this node + +· Command requires a running node and access to a local API token. + +An
may be specified as a 10-digit short ZeroTier address, a +fingerprint containing both an address and a SHA384 hash, or an identity. +Identities and locators can be specified as either paths to files on the +filesystem or verbatim objects in string format. This is auto-detected."###, 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)) +} fn main() { - let cli_args = cli::parse_cli_args(); + let cli_args = { + 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") + .arg(Arg::with_name("nwid").index(1).required(true))) + .subcommand(App::new("list")) + .subcommand(App::new("set") + .arg(Arg::with_name("nwid").index(1).required(true)) + .arg(Arg::with_name("setting").index(2).required(false)) + .arg(Arg::with_name("value").index(3).required(false)))) + .subcommand(App::new("join") + .arg(Arg::with_name("controller").short("c").takes_value(true)) + .arg(Arg::with_name("nwid").index(1).required(true))) + .subcommand(App::new("leave") + .arg(Arg::with_name("nwid").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").default_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("path").index(2).required(true))) + .subcommand(App::new("verify") + .arg(Arg::with_name("identity").index(1).required(true)) + .arg(Arg::with_name("path").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("timestamp").short("t").required(false)) + .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(false))) + .subcommand(App::new("newcsr") + .arg(Arg::with_name("path").index(1).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(false))) + .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").index(2).required(false))) + .subcommand(App::new("factoryreset")) + .subcommand(App::new("export") + .arg(Arg::with_name("serial").index(1).required(true)) + .arg(Arg::with_name("path").index(2).required(false))) + .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 + }; + let store = || { //let json_output = cli_args.is_present("json"); // TODO let zerotier_path = cli_args.value_of("path").map_or_else(|| unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), -1) }, |ztp| ztp.to_string()); @@ -47,9 +296,10 @@ fn main() { } Arc::new(store.unwrap()) }; + std::process::exit(match cli_args.subcommand() { ("help", None) => { - cli::print_help(); + print_help(); 0 } ("version", None) => { @@ -63,13 +313,17 @@ fn main() { ("network", Some(sub_cli_args)) => { 0 } ("join", Some(sub_cli_args)) => { 0 } ("leave", Some(sub_cli_args)) => { 0 } - ("service", None) => service::run(store()), + ("service", None) => { + let store = store(); + drop(cli_args); // try to let go of unnecessary heap before running service + service::run(store) + }, ("controller", Some(sub_cli_args)) => { 0 } ("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args), ("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args), ("cert", Some(sub_cli_args)) => crate::commands::cert::run(store(), sub_cli_args), _ => { - cli::print_help(); + print_help(); 1 } });