diff --git a/core/Endpoint.cpp b/core/Endpoint.cpp index f902247de..d501d9a64 100644 --- a/core/Endpoint.cpp +++ b/core/Endpoint.cpp @@ -115,6 +115,7 @@ bool Endpoint::fromString(const char *s) noexcept } } else if (strchr(s, '/') != nullptr) { // IP/port is parsed as an IP_UDP endpoint for backward compatibility. + this->type = ZT_ENDPOINT_TYPE_IP_UDP; return asInetAddress(this->value.ss).fromString(s); } return false; diff --git a/rust-zerotier-core/src/endpoint.rs b/rust-zerotier-core/src/endpoint.rs index 68f9789d2..262aa81ed 100644 --- a/rust-zerotier-core/src/endpoint.rs +++ b/rust-zerotier-core/src/endpoint.rs @@ -120,20 +120,12 @@ impl PartialEq for Endpoint { impl Eq for Endpoint {} impl serde::Serialize for Endpoint { - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(self.to_string().as_str()) - } + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) } } - struct EndpointVisitor; - impl<'de> serde::de::Visitor<'de> for EndpointVisitor { type Value = Endpoint; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("Endpoint value in string form") - } - + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Endpoint value in string form") } fn visit_str(self, s: &str) -> Result where E: serde::de::Error { let id = Endpoint::new_from_string(s); if id.is_err() { @@ -142,9 +134,6 @@ impl<'de> serde::de::Visitor<'de> for EndpointVisitor { return Ok(id.ok().unwrap() as Self::Value); } } - impl<'de> serde::Deserialize<'de> for Endpoint { - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { - deserializer.deserialize_str(EndpointVisitor) - } + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { deserializer.deserialize_str(EndpointVisitor) } } diff --git a/rust-zerotier-core/src/locator.rs b/rust-zerotier-core/src/locator.rs index 00b338f7a..b31132305 100644 --- a/rust-zerotier-core/src/locator.rs +++ b/rust-zerotier-core/src/locator.rs @@ -13,10 +13,10 @@ use std::ffi::CString; use std::os::raw::{c_char, c_int, c_uint}; +use std::ptr::null; use crate::*; use crate::capi as ztcore; -use std::ptr::null; pub struct Locator { pub(crate) capi: *const ztcore::ZT_Locator, @@ -84,6 +84,20 @@ impl Locator { } eps } + + pub fn verify(&self, id: &Identity) -> bool { + unsafe { ztcore::ZT_Locator_verify(self.capi, id.capi) != 0 } + } + + pub fn signer(&self) -> Fingerprint { + unsafe { + let fp = ztcore::ZT_Locator_fingerprint(self.capi); + if fp.is_null() { + panic!("ZT_Locator_fingerprint() returned null, should not be allowed"); + } + Fingerprint::new_from_capi(&*fp) + } + } } impl Drop for Locator { @@ -125,20 +139,12 @@ impl PartialEq for Locator { impl Eq for Locator {} impl serde::Serialize for Locator { - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(self.to_string().as_str()) - } + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) } } - struct LocatorVisitor; - impl<'de> serde::de::Visitor<'de> for LocatorVisitor { type Value = Locator; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("Locator value in string form") - } - + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Locator value in string form") } fn visit_str(self, s: &str) -> Result where E: serde::de::Error { let id = Locator::new_from_string(s); if id.is_err() { @@ -147,9 +153,6 @@ impl<'de> serde::de::Visitor<'de> for LocatorVisitor { return Ok(id.ok().unwrap() as Self::Value); } } - impl<'de> serde::Deserialize<'de> for Locator { - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { - deserializer.deserialize_str(LocatorVisitor) - } + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { deserializer.deserialize_str(LocatorVisitor) } } diff --git a/service/src/cli.rs b/service/src/cli.rs index 4e17af8b0..96afe7f6b 100644 --- a/service/src/cli.rs +++ b/service/src/cli.rs @@ -67,7 +67,7 @@ Common Operations: Advanced Operations: service Start this node - (usually not run directly) + (usually not invoked directly) controller [option] ยท list List networks on controller @@ -110,16 +110,8 @@ Advanced Operations: An
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 must be -full identities and may be specified either verbatim or as a path to a file. - -An 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) +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() { diff --git a/service/src/commands/cert.rs b/service/src/commands/cert.rs index 989fde1dc..7d687a5ef 100644 --- a/service/src/commands/cert.rs +++ b/service/src/commands/cert.rs @@ -19,8 +19,6 @@ use dialoguer::Input; use zerotier_core::*; use crate::store::Store; -use crate::utils::read_identity; -use futures::SinkExt; fn list(store: &Store, auth_token: &Option) -> i32 { 0 @@ -98,7 +96,7 @@ fn newcsr<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { } fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { - 0 + 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(); + let locator = crate::utils::read_locator(cli_args.value_of("locator").unwrap()); + if locator.is_err() { + println!("ERROR: locator invalid: {}", locator.err().unwrap()); + return 1; + } + if locator.unwrap().verify(&identity) { + println!("OK"); + 0 + } else { + println!("FAILED"); + 1 + } } fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>) -> i32 { + let locator = crate::utils::read_locator(cli_args.value_of("locator").unwrap()); + if locator.is_err() { + println!("ERROR: locator invalid: {}", locator.err().unwrap()); + return 1; + } + let locator = locator.unwrap(); + println!("{} timestamp {}", locator.signer().to_string(), (locator.timestamp() as f64) / 1000.0); + let endpoints = locator.endpoints(); + for ep in endpoints.iter() { + println!(" {}", (*ep).to_string()) + } 0 } diff --git a/service/src/utils.rs b/service/src/utils.rs index 08272937d..32cf0c10b 100644 --- a/service/src/utils.rs +++ b/service/src/utils.rs @@ -18,7 +18,7 @@ use std::mem::MaybeUninit; use std::os::raw::c_uint; use std::path::Path; -use zerotier_core::Identity; +use zerotier_core::{Identity, Locator}; use crate::osdep; @@ -57,38 +57,53 @@ pub(crate) fn read_limit>(path: P, limit: usize) -> std::io::Resu } /// Read an identity as either a literal or from a file. -/// This is used in parsing command lines, allowing either a literal or a path -/// to be specified and automagically disambiguating. pub(crate) fn read_identity(input: &str, validate: bool) -> Result { - let id = Identity::new_from_string(input); - if id.is_err() { - let input = Path::new(input); - if !input.exists() || !input.is_file() { - return Err(format!("invalid identity: {}", id.err().unwrap().to_str())); - } + let parse_func = |s: &str| { + Identity::new_from_string(s).map_or_else(|e| { + Err(format!("invalid identity: {}", e.to_str())) + }, |id| { + if !validate || id.validate() { + Ok(id) + } else { + Err(String::from("invalid identity: local validation failed")) + } + }) + }; + if Path::new(input).exists() { read_limit(input, 16384).map_or_else(|e| { Err(e.to_string()) }, |v| { String::from_utf8(v).map_or_else(|e| { Err(e.to_string()) }, |s| { - Identity::new_from_string(s.as_str()).map_or_else(|_| { - Err(format!("Invalid identity in file {}", input.to_str().unwrap_or(""))) - }, |id| { - if validate && !id.validate() { - Err(String::from("invalid identity: local validation failed")) - } else { - Ok(id) - } - }) + parse_func(s.as_str()) }) }) } else { - let id = id.ok().unwrap(); - if validate && !id.validate() { - Err(String::from("invalid identity: local validation failed")) - } else { - Ok(id) - } + parse_func(input) + } +} + +/// Read a locator as either a literal or from a file. +pub(crate) fn read_locator(input: &str) -> Result { + let parse_func = |s: &str| { + Locator::new_from_string(s).map_or_else(|e| { + Err(format!("invalid locator: {}", e.to_str())) + }, |loc| { + Ok(loc) + }) + }; + if Path::new(input).exists() { + read_limit(input, 16384).map_or_else(|e| { + Err(e.to_string()) + }, |v| { + String::from_utf8(v).map_or_else(|e| { + Err(e.to_string()) + }, |s| { + parse_func(s.as_str()) + }) + }) + } else { + parse_func(input) } }