diff --git a/.gitignore b/.gitignore index a839feeb9..be49d8627 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,5 @@ node_modules /*.opendb /rust-zerotier-core/src/capi.rs /rust-zerotier-core/target -/rust-zerotier-service/target -/rust-zerotier-service/src/osdep.rs +/service/target +/service/src/osdep.rs diff --git a/rust-zerotier-core/src/endpoint.rs b/rust-zerotier-core/src/endpoint.rs index db7ffaed1..68f9789d2 100644 --- a/rust-zerotier-core/src/endpoint.rs +++ b/rust-zerotier-core/src/endpoint.rs @@ -36,7 +36,7 @@ pub enum EndpointType { pub struct Endpoint { pub type_: EndpointType, - capi: ztcore::ZT_Endpoint + pub(crate) capi: ztcore::ZT_Endpoint } impl Endpoint { diff --git a/rust-zerotier-core/src/locator.rs b/rust-zerotier-core/src/locator.rs index eb50b24f7..00b338f7a 100644 --- a/rust-zerotier-core/src/locator.rs +++ b/rust-zerotier-core/src/locator.rs @@ -16,6 +16,7 @@ use std::os::raw::{c_char, c_int, c_uint}; use crate::*; use crate::capi as ztcore; +use std::ptr::null; pub struct Locator { pub(crate) capi: *const ztcore::ZT_Locator, @@ -23,6 +24,22 @@ pub struct Locator { } impl Locator { + /// Create and sign a new locator. + /// The signer must include its secret key. + pub fn new(signer: &Identity, timestamp: i64, endpoints: &Vec) -> Result { + let mut capi_endpoints: Vec = Vec::new(); + capi_endpoints.reserve(endpoints.len()); + for ep in endpoints.iter() { + capi_endpoints.push(ep.capi); + } + let loc = unsafe { ztcore::ZT_Locator_create(timestamp, capi_endpoints.as_ptr(), null(), capi_endpoints.len() as c_uint, signer.capi) }; + if loc.is_null() { + Err(ResultCode::ErrorBadParameter) + } else { + Ok(Locator::new_from_capi(loc, true)) + } + } + #[inline(always)] pub(crate) fn new_from_capi(l: *const ztcore::ZT_Locator, requires_delete: bool) -> Locator { Locator{ @@ -89,7 +106,7 @@ impl Clone for Locator { impl ToString for Locator { fn to_string(&self) -> String { - let mut buf: [u8; 4096] = [0; 4096]; + let mut buf = [0_u8; 16384]; unsafe { if ztcore::ZT_Locator_toString(self.capi, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int).is_null() { return String::from("(invalid)"); diff --git a/service/src/cli.rs b/service/src/cli.rs index 2c10911a5..4e17af8b0 100644 --- a/service/src/cli.rs +++ b/service/src/cli.rs @@ -20,89 +20,91 @@ fn make_help() -> String { (c)2013-2021 ZeroTier, Inc. Licensed under the ZeroTier BSL (see LICENSE.txt) -Usage: zerotier [-global options] [command args] +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 auth token on command line + -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 + help Show this help + version Print version -· status Show node status and configuration +· 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 +· 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 +· 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 space routes be set? -· defaultroute Can default route be overridden? +· 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 [-options] Join a virtual network - -c Controller identity or fingerprint -· leave Leave a virtual network +· join [-...] Join a virtual network + -c Controller identity / fingerprint +· leave Leave a virtual network Advanced Operations: - service Start this node (runs until stopped) + service Start this node + (usually not run 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 +· 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 only 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 + 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 - verify Verify locator signature - show Show contents of a locator + 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 +· 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. @@ -144,6 +146,8 @@ fn is_valid_port(v: String) -> Result<(), String> { /// 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")) @@ -219,6 +223,7 @@ pub(crate) fn parse_cli_args() -> ArgMatches<'static> { .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") diff --git a/service/src/commands/cert.rs b/service/src/commands/cert.rs index 1f938d533..989fde1dc 100644 --- a/service/src/commands/cert.rs +++ b/service/src/commands/cert.rs @@ -16,8 +16,6 @@ use std::str::FromStr; use clap::ArgMatches; use dialoguer::Input; -use lazy_static::lazy_static; - use zerotier_core::*; use crate::store::Store; @@ -260,7 +258,7 @@ fn import<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option) -> i32 { +fn factoryreset(store: &Store, auth_token: &Option) -> i32 { 0 } @@ -282,7 +280,7 @@ pub(crate) fn run<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Opt ("verify", Some(sub_cli_args)) => verify(store, sub_cli_args, auth_token), ("dump", Some(sub_cli_args)) => dump(store, sub_cli_args, auth_token), ("import", Some(sub_cli_args)) => import(store, sub_cli_args, auth_token), - ("factoryreset", None) => restore(store, auth_token), + ("factoryreset", None) => factoryreset(store, auth_token), ("export", Some(sub_cli_args)) => export(store, sub_cli_args, auth_token), ("delete", Some(sub_cli_args)) => delete(store, sub_cli_args, auth_token), _ => { diff --git a/service/src/commands/locator.rs b/service/src/commands/locator.rs index e69de29bb..e0ef5f4c5 100644 --- a/service/src/commands/locator.rs +++ b/service/src/commands/locator.rs @@ -0,0 +1,90 @@ +/* + * 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 clap::ArgMatches; + +use zerotier_core::*; + +use crate::store::Store; + +fn new_<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { + let timestamp = cli_args.value_of("timestamp").map_or(crate::utils::ms_since_epoch(), |ts| { + if ts.is_empty() { + 0_i64 + } else { + i64::from_str_radix(ts, 10).unwrap_or(0_i64) * 1000_i64 // internally uses ms since epoch + } + }); + if timestamp <= 0 { + println!("ERROR: invalid or empty timestamp specified."); + return 1; + } + + let identity = crate::utils::read_identity(cli_args.value_of("identity").unwrap(), true); + if identity.is_err() { + println!("ERROR: identity invalid: {}", identity.err().unwrap()); + return 1; + } + let identity = identity.unwrap(); + if !identity.has_private() { + println!("ERROR: identity must include secret key to create and sign a locator."); + return 1; + } + + let endpoints_cli = cli_args.values_of("endpoint"); + if endpoints_cli.is_none() { + println!("ERROR: at least one endpoint required."); + return 1; + } + let mut endpoints: Vec = Vec::new(); + let mut endpoint_bad = false; + endpoints_cli.unwrap().for_each(|ep_str| { + Endpoint::new_from_string(ep_str).map_or_else(|e| { + println!("ERROR: endpoint {} invalid: {}", ep_str, e.to_str()); + endpoint_bad = true; + }, |ep| { + endpoints.push(ep); + }); + }); + if endpoint_bad { + return 1; + } + + Locator::new(&identity, timestamp, &endpoints).map_or_else(|e| { + println!("ERROR: failure creating locator: {}", e.to_str()); + 1 + }, |loc| { + println!("{}", loc.to_string()); + 0 + }) +} + +fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { + 0 +} + +fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { + 0 +} + +pub(crate) fn run<'a>(store: &Store, cli_args: &ArgMatches<'a>, _: &Option) -> i32 { + match cli_args.subcommand() { + ("new", Some(sub_cli_args)) => new_(store, sub_cli_args), + ("verify", Some(sub_cli_args)) => verify(store, sub_cli_args), + ("show", Some(sub_cli_args)) => show(store, sub_cli_args), + _ => { + crate::cli::print_help(); + 1 + } + } +} diff --git a/service/src/main.rs b/service/src/main.rs index 88a857db3..0c58c9e2a 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -102,7 +102,9 @@ fn main() { } ("controller", Some(sub_cli_args)) => {} ("identity", Some(sub_cli_args)) => {} - ("locator", Some(sub_cli_args)) => {} + ("locator", Some(sub_cli_args)) => { + process_exit_value = crate::commands::locator::run(&store, sub_cli_args, &auth_token); + } ("cert", Some(sub_cli_args)) => { process_exit_value = crate::commands::cert::run(&store, sub_cli_args, &auth_token); } diff --git a/service/src/service.rs b/service/src/service.rs index 5ee1a914e..97ad7a019 100644 --- a/service/src/service.rs +++ b/service/src/service.rs @@ -220,7 +220,7 @@ async fn run_async(store: &Arc, auth_token: String, log: &Arc, local let service = service; // make immutable after setting node let mut local_config = service.local_config(); - store.write_port(local_config.settings.primary_port); + let _ = store.write_port(local_config.settings.primary_port); let mut now: i64 = ms_since_epoch(); let mut loop_delay = zerotier_core::NODE_BACKGROUND_TASKS_MAX_INTERVAL;