mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-08-02 15:02:50 +02:00
JSON patching for RESTful object update and some other stuff.
This commit is contained in:
parent
5be66eda2b
commit
50675004ce
17 changed files with 273 additions and 122 deletions
|
@ -14,11 +14,25 @@
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
|
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
|
||||||
pub struct Address(pub u64);
|
pub struct Address(pub u64);
|
||||||
|
|
||||||
|
impl Default for Address {
|
||||||
|
#[inline(always)]
|
||||||
|
fn default() -> Address {
|
||||||
|
Address(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_bytes(&self) -> [u8; 5] {
|
||||||
|
[(self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for Address {
|
impl From<&[u8]> for Address {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(bytes: &[u8]) -> Self {
|
fn from(bytes: &[u8]) -> Self {
|
||||||
if bytes.len() >= 5 {
|
if bytes.len() >= 5 {
|
||||||
Address(((bytes[0] as u64) << 32) | ((bytes[0] as u64) << 24) | ((bytes[0] as u64) << 16) | ((bytes[0] as u64) << 8) | (bytes[0] as u64))
|
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
|
||||||
} else {
|
} else {
|
||||||
Address(0)
|
Address(0)
|
||||||
}
|
}
|
||||||
|
@ -46,14 +60,31 @@ impl From<&str> for Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Address {
|
impl serde::Serialize for Address {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
|
} else {
|
||||||
|
let b = self.to_bytes();
|
||||||
|
serializer.serialize_bytes(b.as_ref())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AddressVisitor;
|
struct AddressVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
||||||
type Value = Address;
|
type Value = Address;
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address in string format") }
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") }
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) }
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) }
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Address {
|
impl<'de> serde::Deserialize<'de> for Address {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(AddressVisitor) }
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
deserializer.deserialize_str(AddressVisitor)
|
||||||
|
} else {
|
||||||
|
deserializer.deserialize_bytes(AddressVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::capi as ztcore;
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub struct Fingerprint {
|
pub struct Fingerprint {
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
pub hash: [u8; 48]
|
pub hash: [u8; 48],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fingerprint {
|
impl Fingerprint {
|
||||||
|
@ -30,7 +30,7 @@ impl Fingerprint {
|
||||||
pub(crate) fn new_from_capi(fp: &ztcore::ZT_Fingerprint) -> Fingerprint {
|
pub(crate) fn new_from_capi(fp: &ztcore::ZT_Fingerprint) -> Fingerprint {
|
||||||
Fingerprint {
|
Fingerprint {
|
||||||
address: Address(fp.address),
|
address: Address(fp.address),
|
||||||
hash: fp.hash
|
hash: fp.hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ impl Fingerprint {
|
||||||
let fp = cfp.assume_init();
|
let fp = cfp.assume_init();
|
||||||
return Ok(Fingerprint {
|
return Ok(Fingerprint {
|
||||||
address: Address(fp.address),
|
address: Address(fp.address),
|
||||||
hash: fp.hash
|
hash: fp.hash,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,13 @@ impl ToString for Fingerprint {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Fingerprint {
|
impl serde::Serialize for Fingerprint {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct FingerprintVisitor;
|
struct FingerprintVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
|
impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
|
||||||
type Value = Fingerprint;
|
type Value = Fingerprint;
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Fingerprint in string format") }
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Fingerprint in string format") }
|
||||||
|
@ -98,6 +102,9 @@ impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
|
||||||
return Ok(id.ok().unwrap() as Self::Value);
|
return Ok(id.ok().unwrap() as Self::Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Fingerprint {
|
impl<'de> serde::Deserialize<'de> for Fingerprint {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(FingerprintVisitor) }
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||||
|
deserializer.deserialize_str(FingerprintVisitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,9 +200,13 @@ impl ToString for Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Identity {
|
impl serde::Serialize for Identity {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.intl_to_string(false).as_str()) }
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
|
serializer.serialize_str(self.intl_to_string(false).as_str())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct IdentityVisitor;
|
struct IdentityVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
||||||
type Value = Identity;
|
type Value = Identity;
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Identity in string format") }
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Identity in string format") }
|
||||||
|
@ -214,8 +218,11 @@ impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
||||||
return Ok(id.ok().unwrap() as Self::Value);
|
return Ok(id.ok().unwrap() as Self::Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for Identity {
|
impl<'de> serde::Deserialize<'de> for Identity {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(IdentityVisitor) }
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||||
|
deserializer.deserialize_str(IdentityVisitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -14,6 +14,20 @@
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||||
pub struct MAC(pub u64);
|
pub struct MAC(pub u64);
|
||||||
|
|
||||||
|
impl Default for MAC {
|
||||||
|
#[inline(always)]
|
||||||
|
fn default() -> MAC {
|
||||||
|
MAC(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MAC {
|
||||||
|
#[inline(always)]
|
||||||
|
fn to_bytes(&self) -> [u8; 6] {
|
||||||
|
[(self.0 >> 40) as u8, (self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToString for MAC {
|
impl ToString for MAC {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
let x = self.0;
|
let x = self.0;
|
||||||
|
@ -21,6 +35,17 @@ impl ToString for MAC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for MAC {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(bytes: &[u8]) -> Self {
|
||||||
|
if bytes.len() >= 6 {
|
||||||
|
MAC(((bytes[0] as u64) << 40) | ((bytes[1] as u64) << 32) | ((bytes[2] as u64) << 24) | ((bytes[3] as u64) << 16) | ((bytes[4] as u64) << 8) | (bytes[5] as u64))
|
||||||
|
} else {
|
||||||
|
MAC(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for MAC {
|
impl From<&str> for MAC {
|
||||||
fn from(s: &str) -> MAC {
|
fn from(s: &str) -> MAC {
|
||||||
MAC(u64::from_str_radix(s.replace(":", "").as_str(), 16).unwrap_or(0))
|
MAC(u64::from_str_radix(s.replace(":", "").as_str(), 16).unwrap_or(0))
|
||||||
|
@ -28,14 +53,31 @@ impl From<&str> for MAC {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for MAC {
|
impl serde::Serialize for MAC {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
|
} else {
|
||||||
|
let b = self.to_bytes();
|
||||||
|
serializer.serialize_bytes(b.as_ref())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AddressVisitor;
|
struct AddressVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
||||||
type Value = MAC;
|
type Value = MAC;
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Ethernet MAC address in string format (with or without : separators)") }
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Ethernet MAC address in string format (with or without : separators)") }
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(MAC::from(s)) }
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(MAC::from(s)) }
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(MAC::from(v)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for MAC {
|
impl<'de> serde::Deserialize<'de> for MAC {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(AddressVisitor) }
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
deserializer.deserialize_str(AddressVisitor)
|
||||||
|
} else {
|
||||||
|
deserializer.deserialize_bytes(AddressVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||||
pub struct NetworkId(pub u64);
|
pub struct NetworkId(pub u64);
|
||||||
|
|
||||||
impl NetworkId {
|
impl Default for NetworkId {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_from_string(s: &str) -> NetworkId {
|
fn default() -> NetworkId {
|
||||||
return NetworkId(u64::from_str_radix(s, 16).unwrap_or(0));
|
NetworkId(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,19 +37,35 @@ impl From<u64> for NetworkId {
|
||||||
impl From<&str> for NetworkId {
|
impl From<&str> for NetworkId {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
NetworkId::new_from_string(s)
|
NetworkId(u64::from_str_radix(s, 16).unwrap_or(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::Serialize for NetworkId {
|
impl serde::Serialize for NetworkId {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.serialize_str(self.to_string().as_str())
|
||||||
|
} else {
|
||||||
|
serializer.serialize_u64(self.0)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct NetworkIdVisitor;
|
struct NetworkIdVisitor;
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
|
impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
|
||||||
type Value = NetworkId;
|
type Value = NetworkId;
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address in string format") }
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier network ID") }
|
||||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId::new_from_string(s)) }
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId::from(s)) }
|
||||||
|
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId(v)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for NetworkId {
|
impl<'de> serde::Deserialize<'de> for NetworkId {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(NetworkIdVisitor) }
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
deserializer.deserialize_str(NetworkIdVisitor)
|
||||||
|
} else {
|
||||||
|
deserializer.deserialize_u64(NetworkIdVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,15 @@ build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zerotier-core = { path = "../rust-zerotier-core" }
|
zerotier-core = { path = "../rust-zerotier-core" }
|
||||||
num_cpus = "1"
|
num_cpus = "*"
|
||||||
tokio = { version = "1", features = ["rt", "net", "time", "signal", "macros"] }
|
tokio = { version = "1", features = ["rt", "net", "time", "signal", "macros"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
futures = "0"
|
futures = "0"
|
||||||
clap = { version = "2", features = ["suggestions", "wrap_help"] }
|
clap = { version = "2", features = ["suggestions", "wrap_help"] }
|
||||||
chrono = "0"
|
chrono = "0"
|
||||||
hex = "0"
|
hex = "*"
|
||||||
lazy_static = "1"
|
lazy_static = "*"
|
||||||
num-traits = "0"
|
num-traits = "0"
|
||||||
num-derive = "0"
|
num-derive = "0"
|
||||||
hyper = { version = "0", features = ["http1", "runtime", "server", "client", "tcp", "stream"] }
|
hyper = { version = "0", features = ["http1", "runtime", "server", "client", "tcp", "stream"] }
|
||||||
|
|
|
@ -12,21 +12,28 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use crate::service::Service;
|
use crate::service::Service;
|
||||||
use hyper::{Request, Body, StatusCode, Response, Method};
|
use hyper::{Request, Body, StatusCode, Method};
|
||||||
|
|
||||||
pub(crate) fn status(service: Service, req: Request<Body>) -> (StatusCode, Body) {
|
pub(crate) fn status(service: Service, req: Request<Body>) -> (StatusCode, Body) {
|
||||||
if req.method() == Method::GET {
|
if req.method() == Method::GET {
|
||||||
let status = service.status();
|
service.status().map_or_else(|| {
|
||||||
if status.is_none() {
|
|
||||||
(StatusCode::SERVICE_UNAVAILABLE, Body::from("node shutdown in progress"))
|
(StatusCode::SERVICE_UNAVAILABLE, Body::from("node shutdown in progress"))
|
||||||
} else {
|
}, |status| {
|
||||||
(StatusCode::OK, Body::from(serde_json::to_string(status.as_ref().unwrap()).unwrap()))
|
(StatusCode::OK, Body::from(serde_json::to_string(&status).unwrap()))
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
(StatusCode::METHOD_NOT_ALLOWED, Body::from("/status allows method(s): GET"))
|
(StatusCode::METHOD_NOT_ALLOWED, Body::from("/status allows method(s): GET"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn config(service: Service, req: Request<Body>) -> (StatusCode, Body) {
|
||||||
|
let config = service.local_config();
|
||||||
|
if req.method() == Method::POST || req.method() == Method::PUT {
|
||||||
|
// TODO: diff config
|
||||||
|
}
|
||||||
|
(StatusCode::OK, Body::from(serde_json::to_string(config.as_ref()).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn peer(service: Service, req: Request<Body>) -> (StatusCode, Body) {
|
pub(crate) fn peer(service: Service, req: Request<Body>) -> (StatusCode, Body) {
|
||||||
(StatusCode::NOT_IMPLEMENTED, Body::from(""))
|
(StatusCode::NOT_IMPLEMENTED, Body::from(""))
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
|
||||||
if nwid.len() != 16 {
|
if nwid.len() != 16 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let nwid = NetworkId::new_from_string(nwid.as_str());
|
let nwid = NetworkId::from(nwid.as_str());
|
||||||
|
|
||||||
let fingerprint: String = Input::with_theme(theme)
|
let fingerprint: String = Input::with_theme(theme)
|
||||||
.with_prompt(format!(" [{}] Fingerprint of primary controller (optional)", networks.len() + 1))
|
.with_prompt(format!(" [{}] Fingerprint of primary controller (optional)", networks.len() + 1))
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
use zerotier_core::{Identity, IdentityType};
|
||||||
|
|
||||||
use crate::store::Store;
|
use crate::store::Store;
|
||||||
use zerotier_core::{IdentityType, Identity};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
fn new_(cli_args: &ArgMatches) -> i32 {
|
fn new_(cli_args: &ArgMatches) -> i32 {
|
||||||
let id_type = cli_args.value_of("type").map_or(IdentityType::Curve25519, |idt| {
|
let id_type = cli_args.value_of("type").map_or(IdentityType::Curve25519, |idt| {
|
||||||
|
|
|
@ -15,8 +15,6 @@ use clap::ArgMatches;
|
||||||
|
|
||||||
use zerotier_core::*;
|
use zerotier_core::*;
|
||||||
|
|
||||||
use crate::store::Store;
|
|
||||||
|
|
||||||
fn new_(cli_args: &ArgMatches) -> i32 {
|
fn new_(cli_args: &ArgMatches) -> i32 {
|
||||||
let timestamp = cli_args.value_of("timestamp").map_or(crate::utils::ms_since_epoch(), |ts| {
|
let timestamp = cli_args.value_of("timestamp").map_or(crate::utils::ms_since_epoch(), |ts| {
|
||||||
if ts.is_empty() {
|
if ts.is_empty() {
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hyper::{Uri, Method, StatusCode};
|
use hyper::{Uri, Method, StatusCode};
|
||||||
|
|
|
@ -82,14 +82,12 @@ fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPR
|
||||||
|
|
||||||
#[cfg(target_os = "linux")] {
|
#[cfg(target_os = "linux")] {
|
||||||
if !_device_name.is_empty() {
|
if !_device_name.is_empty() {
|
||||||
unsafe {
|
|
||||||
let _ = std::ffi::CString::new(_device_name).map(|dn| {
|
let _ = std::ffi::CString::new(_device_name).map(|dn| {
|
||||||
let dnb = dn.as_bytes_with_nul();
|
let dnb = dn.as_bytes_with_nul();
|
||||||
let _ = osdep::setsockopt(s.as_(), osdep::SOL_SOCKET.as_(), osdep::SO_BINDTODEVICE.as_(), dnb.as_ptr().cast(), (dnb.len() - 1).as_());
|
let _ = osdep::setsockopt(s.as_(), osdep::SOL_SOCKET.as_(), osdep::SO_BINDTODEVICE.as_(), dnb.as_ptr().cast(), (dnb.len() - 1).as_());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if setsockopt_results != 0 {
|
if setsockopt_results != 0 {
|
||||||
osdep::close(s);
|
osdep::close(s);
|
||||||
|
|
|
@ -99,14 +99,16 @@ impl HttpListener {
|
||||||
let req_path = req.uri().path();
|
let req_path = req.uri().path();
|
||||||
let (status, body) = if req_path == "/status" {
|
let (status, body) = if req_path == "/status" {
|
||||||
api::status(service, req)
|
api::status(service, req)
|
||||||
|
} else if req_path.starts_with("/config") {
|
||||||
|
api::config(service, req)
|
||||||
} else if req_path.starts_with("/peer") {
|
} else if req_path.starts_with("/peer") {
|
||||||
api::peer(service, req)
|
api::peer(service, req)
|
||||||
} else if req_path.starts_with("/network") {
|
} else if req_path.starts_with("/network") {
|
||||||
api::network(service, req)
|
api::network(service, req)
|
||||||
} else {
|
} else {
|
||||||
(StatusCode::NOT_FOUND, "not found")
|
(StatusCode::NOT_FOUND, Body::from("not found"))
|
||||||
};
|
};
|
||||||
Ok(Response::builder().header("Content-Type", "application/json").status(status).body(body).unwrap())
|
Ok::<Response<Body>, Infallible>(Response::builder().header("Content-Type", "application/json").status(status).body(body).unwrap())
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,14 @@ pub struct LocalConfigPhysicalPathConfig {
|
||||||
pub blacklist: bool
|
pub blacklist: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LocalConfigPhysicalPathConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
LocalConfigPhysicalPathConfig {
|
||||||
|
blacklist: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigVirtualConfig {
|
pub struct LocalConfigVirtualConfig {
|
||||||
|
@ -74,6 +82,14 @@ pub struct LocalConfigVirtualConfig {
|
||||||
pub try_: Vec<InetAddress>
|
pub try_: Vec<InetAddress>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LocalConfigVirtualConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
LocalConfigVirtualConfig {
|
||||||
|
try_: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigNetworkSettings {
|
pub struct LocalConfigNetworkSettings {
|
||||||
|
@ -89,6 +105,18 @@ pub struct LocalConfigNetworkSettings {
|
||||||
pub allow_default_route_override: bool,
|
pub allow_default_route_override: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LocalConfigNetworkSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
LocalConfigNetworkSettings {
|
||||||
|
allow_managed_ips: true,
|
||||||
|
allow_global_ips: false,
|
||||||
|
allow_managed_routes: true,
|
||||||
|
allow_global_routes: false,
|
||||||
|
allow_default_route_override: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigLogSettings {
|
pub struct LocalConfigLogSettings {
|
||||||
|
@ -105,6 +133,22 @@ pub struct LocalConfigLogSettings {
|
||||||
pub stderr: bool,
|
pub stderr: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LocalConfigLogSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
// TODO: change before release to saner defaults
|
||||||
|
LocalConfigLogSettings {
|
||||||
|
path: None,
|
||||||
|
max_size: 131072,
|
||||||
|
vl1: true,
|
||||||
|
vl2: true,
|
||||||
|
vl2_trace_rules: true,
|
||||||
|
vl2_trace_multicast: true,
|
||||||
|
debug: true,
|
||||||
|
stderr: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct LocalConfigSettings {
|
pub struct LocalConfigSettings {
|
||||||
|
@ -124,56 +168,22 @@ pub struct LocalConfigSettings {
|
||||||
pub explicit_addresses: Vec<InetAddress>,
|
pub explicit_addresses: Vec<InetAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
impl Default for LocalConfigSettings {
|
||||||
#[serde(default)]
|
fn default() -> Self {
|
||||||
pub struct LocalConfig {
|
let mut bl: Vec<String> = Vec::new();
|
||||||
pub physical: BTreeMap<InetAddress, LocalConfigPhysicalPathConfig>,
|
bl.reserve(LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.len());
|
||||||
#[serde(rename = "virtual")]
|
for n in LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.iter() {
|
||||||
pub virtual_: BTreeMap<Address, LocalConfigVirtualConfig>,
|
bl.push(String::from(*n));
|
||||||
pub network: BTreeMap<NetworkId, LocalConfigNetworkSettings>,
|
|
||||||
pub settings: LocalConfigSettings,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LocalConfigPhysicalPathConfig {
|
LocalConfigSettings {
|
||||||
fn default() -> Self {
|
primary_port: zerotier_core::DEFAULT_PORT,
|
||||||
LocalConfigPhysicalPathConfig {
|
secondary_port: Some(zerotier_core::DEFAULT_SECONDARY_PORT),
|
||||||
blacklist: false
|
auto_port_search: true,
|
||||||
}
|
port_mapping: true,
|
||||||
}
|
log: LocalConfigLogSettings::default(),
|
||||||
}
|
interface_prefix_blacklist: bl,
|
||||||
|
explicit_addresses: Vec::new()
|
||||||
impl Default for LocalConfigVirtualConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
LocalConfigVirtualConfig {
|
|
||||||
try_: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LocalConfigNetworkSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
LocalConfigNetworkSettings {
|
|
||||||
allow_managed_ips: true,
|
|
||||||
allow_global_ips: false,
|
|
||||||
allow_managed_routes: true,
|
|
||||||
allow_global_routes: false,
|
|
||||||
allow_default_route_override: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LocalConfigLogSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
// TODO: change before release to saner defaults
|
|
||||||
LocalConfigLogSettings {
|
|
||||||
path: None,
|
|
||||||
max_size: 131072,
|
|
||||||
vl1: true,
|
|
||||||
vl2: true,
|
|
||||||
vl2_trace_rules: true,
|
|
||||||
vl2_trace_multicast: true,
|
|
||||||
debug: true,
|
|
||||||
stderr: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,24 +208,14 @@ impl LocalConfigSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LocalConfigSettings {
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
fn default() -> Self {
|
#[serde(default)]
|
||||||
let mut bl: Vec<String> = Vec::new();
|
pub struct LocalConfig {
|
||||||
bl.reserve(LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.len());
|
pub physical: BTreeMap<InetAddress, LocalConfigPhysicalPathConfig>,
|
||||||
for n in LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.iter() {
|
#[serde(rename = "virtual")]
|
||||||
bl.push(String::from(*n));
|
pub virtual_: BTreeMap<Address, LocalConfigVirtualConfig>,
|
||||||
}
|
pub network: BTreeMap<NetworkId, LocalConfigNetworkSettings>,
|
||||||
|
pub settings: LocalConfigSettings,
|
||||||
LocalConfigSettings {
|
|
||||||
primary_port: zerotier_core::DEFAULT_PORT,
|
|
||||||
secondary_port: Some(zerotier_core::DEFAULT_SECONDARY_PORT),
|
|
||||||
auto_port_search: true,
|
|
||||||
port_mapping: true,
|
|
||||||
log: LocalConfigLogSettings::default(),
|
|
||||||
interface_prefix_blacklist: bl,
|
|
||||||
explicit_addresses: Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LocalConfig {
|
impl Default for LocalConfig {
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
*/
|
*/
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::net::{SocketAddr, Ipv4Addr, IpAddr, Ipv6Addr};
|
use std::net::{SocketAddr, Ipv4Addr, IpAddr, Ipv6Addr};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
|
use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -41,7 +39,7 @@ const CONFIG_CHECK_INTERVAL: i64 = 5000;
|
||||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub struct ServiceStatus {
|
pub struct ServiceStatus {
|
||||||
#[serde(rename = "objectType")]
|
#[serde(rename = "objectType")]
|
||||||
pub object_type: &'static str,
|
pub object_type: String,
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
pub clock: i64,
|
pub clock: i64,
|
||||||
#[serde(rename = "startTime")]
|
#[serde(rename = "startTime")]
|
||||||
|
@ -220,7 +218,7 @@ impl Service {
|
||||||
let ver = zerotier_core::version();
|
let ver = zerotier_core::version();
|
||||||
self.node().map(|node| {
|
self.node().map(|node| {
|
||||||
ServiceStatus {
|
ServiceStatus {
|
||||||
object_type: "status",
|
object_type: "status".to_owned(),
|
||||||
address: node.address(),
|
address: node.address(),
|
||||||
clock: ms_since_epoch(),
|
clock: ms_since_epoch(),
|
||||||
start_time: self.intl.startup_time,
|
start_time: self.intl.startup_time,
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*/
|
*/
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use std::error::Error;
|
use std::borrow::Borrow;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
@ -20,6 +20,9 @@ use std::path::Path;
|
||||||
|
|
||||||
use zerotier_core::{Identity, Locator};
|
use zerotier_core::{Identity, Locator};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
use crate::osdep;
|
use crate::osdep;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -121,3 +124,44 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> u64 {
|
||||||
nonce[0]
|
nonce[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively patch a JSON object.
|
||||||
|
/// This is slightly different from a usual JSON merge. For objects in the target their fields
|
||||||
|
/// are updated by recursively calling json_patch if the same field is present in the source.
|
||||||
|
/// If the source tries to set an object to something other than another object, this is ignored.
|
||||||
|
/// Other fields are replaced. This is used for RESTful config object updates. The depth limit
|
||||||
|
/// field is to prevent stack overflows via the API.
|
||||||
|
pub(crate) fn json_patch(target: &mut serde_json::value::Value, source: &serde_json::value::Value, depth_limit: usize) {
|
||||||
|
if target.is_object() {
|
||||||
|
if source.is_object() {
|
||||||
|
let source = source.as_object().unwrap();
|
||||||
|
for kv in target.as_object_mut().unwrap() {
|
||||||
|
let _ = source.get(kv.0).map(|new_value| {
|
||||||
|
if depth_limit > 0 {
|
||||||
|
json_patch(kv.1, new_value, depth_limit - 1)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*target = source.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Patch a serializable object with the fields present in a JSON object.
|
||||||
|
/// If there are no changes, None is returned. The depth limit is passed through to json_patch and
|
||||||
|
/// should be set to a sanity check value to prevent overflows.
|
||||||
|
pub(crate) fn json_patch_object<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
|
||||||
|
serde_json::from_str::<serde_json::value::Value>(patch).map_or_else(|e| Err(e), |patch| {
|
||||||
|
serde_json::value::to_value(obj.borrow()).map_or_else(|e| Err(e), |mut obj_value| {
|
||||||
|
json_patch(&mut obj_value, &patch, depth_limit);
|
||||||
|
serde_json::value::from_value::<O>(obj_value).map_or_else(|e| Err(e), |obj_merged| {
|
||||||
|
if obj.eq(&obj_merged) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(obj_merged))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,12 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::ptr::null_mut;
|
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
use zerotier_core::{MAC, MulticastGroup};
|
use zerotier_core::{MAC, MulticastGroup};
|
||||||
use num_traits::cast::AsPrimitive;
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use num_traits::AsPrimitive;
|
||||||
|
|
||||||
use crate::osdep as osdep;
|
use crate::osdep as osdep;
|
||||||
|
|
||||||
|
@ -25,7 +27,7 @@ pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGro
|
||||||
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
|
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
|
||||||
let dev = dev.as_bytes();
|
let dev = dev.as_bytes();
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut maddrs: *mut osdep::ifmaddrs = null_mut();
|
let mut maddrs: *mut osdep::ifmaddrs = std::ptr::null_mut();
|
||||||
if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 {
|
if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 {
|
||||||
let mut i = maddrs;
|
let mut i = maddrs;
|
||||||
while !i.is_null() {
|
while !i.is_null() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue