JSON patching for RESTful object update and some other stuff.

This commit is contained in:
Adam Ierymenko 2021-03-25 15:36:12 -04:00
parent 5be66eda2b
commit 50675004ce
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
17 changed files with 273 additions and 122 deletions

View file

@ -14,11 +14,25 @@
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
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 {
#[inline(always)]
fn from(bytes: &[u8]) -> Self {
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 {
Address(0)
}
@ -46,14 +60,31 @@ impl From<&str> 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;
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
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_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 {
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)
}
}
}

View file

@ -22,15 +22,15 @@ use crate::capi as ztcore;
#[derive(PartialEq, Eq, Clone)]
pub struct Fingerprint {
pub address: Address,
pub hash: [u8; 48]
pub hash: [u8; 48],
}
impl Fingerprint {
#[inline(always)]
pub(crate) fn new_from_capi(fp: &ztcore::ZT_Fingerprint) -> Fingerprint {
Fingerprint{
Fingerprint {
address: Address(fp.address),
hash: fp.hash
hash: fp.hash,
}
}
@ -44,9 +44,9 @@ impl Fingerprint {
unsafe {
if ztcore::ZT_Fingerprint_fromString(cfp.as_mut_ptr(), cs.as_ptr()) != 0 {
let fp = cfp.assume_init();
return Ok(Fingerprint{
return Ok(Fingerprint {
address: Address(fp.address),
hash: fp.hash
hash: fp.hash,
});
}
}
@ -84,9 +84,13 @@ impl ToString 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;
impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
type Value = Fingerprint;
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);
}
}
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)
}
}

View file

@ -200,9 +200,13 @@ impl ToString 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;
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
type Value = Identity;
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);
}
}
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)]
@ -255,7 +262,7 @@ mod tests {
let from_str_fail = Identity::new_from_string("asdf:foo:invalid");
assert!(from_str_fail.is_err());
let mut to_sign: [u8; 4] = [ 1,2,3,4 ];
let mut to_sign: [u8; 4] = [1, 2, 3, 4];
let signed = test1.sign(&to_sign);
assert!(signed.is_ok());

View file

@ -14,6 +14,20 @@
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
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 {
fn to_string(&self) -> String {
let x = self.0;
@ -21,21 +35,49 @@ 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 {
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))
}
}
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;
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
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 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 {
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)
}
}
}

View file

@ -14,10 +14,10 @@
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct NetworkId(pub u64);
impl NetworkId {
impl Default for NetworkId {
#[inline(always)]
pub fn new_from_string(s: &str) -> NetworkId {
return NetworkId(u64::from_str_radix(s, 16).unwrap_or(0));
fn default() -> NetworkId {
NetworkId(0)
}
}
@ -37,19 +37,35 @@ impl From<u64> for NetworkId {
impl From<&str> for NetworkId {
#[inline(always)]
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 {
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;
impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
type Value = NetworkId;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address in string format") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId::new_from_string(s)) }
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::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 {
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)
}
}
}

View file

@ -9,15 +9,15 @@ build = "build.rs"
[dependencies]
zerotier-core = { path = "../rust-zerotier-core" }
num_cpus = "1"
num_cpus = "*"
tokio = { version = "1", features = ["rt", "net", "time", "signal", "macros"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures = "0"
clap = { version = "2", features = ["suggestions", "wrap_help"] }
chrono = "0"
hex = "0"
lazy_static = "1"
hex = "*"
lazy_static = "*"
num-traits = "0"
num-derive = "0"
hyper = { version = "0", features = ["http1", "runtime", "server", "client", "tcp", "stream"] }

View file

@ -12,21 +12,28 @@
/****/
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) {
if req.method() == Method::GET {
let status = service.status();
if status.is_none() {
service.status().map_or_else(|| {
(StatusCode::SERVICE_UNAVAILABLE, Body::from("node shutdown in progress"))
} else {
(StatusCode::OK, Body::from(serde_json::to_string(status.as_ref().unwrap()).unwrap()))
}
}, |status| {
(StatusCode::OK, Body::from(serde_json::to_string(&status).unwrap()))
})
} else {
(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) {
(StatusCode::NOT_IMPLEMENTED, Body::from(""))
}

View file

@ -146,7 +146,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
if nwid.len() != 16 {
break;
}
let nwid = NetworkId::new_from_string(nwid.as_str());
let nwid = NetworkId::from(nwid.as_str());
let fingerprint: String = Input::with_theme(theme)
.with_prompt(format!(" [{}] Fingerprint of primary controller (optional)", networks.len() + 1))

View file

@ -12,9 +12,10 @@
/****/
use clap::ArgMatches;
use zerotier_core::{Identity, IdentityType};
use crate::store::Store;
use zerotier_core::{IdentityType, Identity};
use std::sync::Arc;
fn new_(cli_args: &ArgMatches) -> i32 {
let id_type = cli_args.value_of("type").map_or(IdentityType::Curve25519, |idt| {

View file

@ -15,8 +15,6 @@ use clap::ArgMatches;
use zerotier_core::*;
use crate::store::Store;
fn new_(cli_args: &ArgMatches) -> i32 {
let timestamp = cli_args.value_of("timestamp").map_or(crate::utils::ms_since_epoch(), |ts| {
if ts.is_empty() {

View file

@ -12,8 +12,6 @@
/****/
use std::error::Error;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use hyper::{Uri, Method, StatusCode};

View file

@ -82,12 +82,10 @@ fn bind_udp_socket(_device_name: &str, address: &InetAddress) -> Result<FastUDPR
#[cfg(target_os = "linux")] {
if !_device_name.is_empty() {
unsafe {
let _ = std::ffi::CString::new(_device_name).map(|dn| {
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 _ = std::ffi::CString::new(_device_name).map(|dn| {
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_());
});
}
}

View file

@ -99,14 +99,16 @@ impl HttpListener {
let req_path = req.uri().path();
let (status, body) = if req_path == "/status" {
api::status(service, req)
} else if req_path.starts_with("/config") {
api::config(service, req)
} else if req_path.starts_with("/peer") {
api::peer(service, req)
} else if req_path.starts_with("/network") {
api::network(service, req)
} 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())
}
}))
}

View file

@ -67,6 +67,14 @@ pub struct LocalConfigPhysicalPathConfig {
pub blacklist: bool
}
impl Default for LocalConfigPhysicalPathConfig {
fn default() -> Self {
LocalConfigPhysicalPathConfig {
blacklist: false
}
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigVirtualConfig {
@ -74,6 +82,14 @@ pub struct LocalConfigVirtualConfig {
pub try_: Vec<InetAddress>
}
impl Default for LocalConfigVirtualConfig {
fn default() -> Self {
LocalConfigVirtualConfig {
try_: Vec::new()
}
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfigNetworkSettings {
@ -89,6 +105,18 @@ pub struct LocalConfigNetworkSettings {
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)]
#[serde(default)]
pub struct LocalConfigLogSettings {
@ -105,6 +133,22 @@ pub struct LocalConfigLogSettings {
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)]
#[serde(default)]
pub struct LocalConfigSettings {
@ -124,56 +168,22 @@ pub struct LocalConfigSettings {
pub explicit_addresses: Vec<InetAddress>,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfig {
pub physical: BTreeMap<InetAddress, LocalConfigPhysicalPathConfig>,
#[serde(rename = "virtual")]
pub virtual_: BTreeMap<Address, LocalConfigVirtualConfig>,
pub network: BTreeMap<NetworkId, LocalConfigNetworkSettings>,
pub settings: LocalConfigSettings,
}
impl Default for LocalConfigPhysicalPathConfig {
impl Default for LocalConfigSettings {
fn default() -> Self {
LocalConfigPhysicalPathConfig {
blacklist: false
let mut bl: Vec<String> = Vec::new();
bl.reserve(LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.len());
for n in LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.iter() {
bl.push(String::from(*n));
}
}
}
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,
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()
}
}
}
@ -198,24 +208,14 @@ impl LocalConfigSettings {
}
}
impl Default for LocalConfigSettings {
fn default() -> Self {
let mut bl: Vec<String> = Vec::new();
bl.reserve(LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.len());
for n in LocalConfigSettings::DEFAULT_PREFIX_BLACKLIST.iter() {
bl.push(String::from(*n));
}
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()
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(default)]
pub struct LocalConfig {
pub physical: BTreeMap<InetAddress, LocalConfigPhysicalPathConfig>,
#[serde(rename = "virtual")]
pub virtual_: BTreeMap<Address, LocalConfigVirtualConfig>,
pub network: BTreeMap<NetworkId, LocalConfigNetworkSettings>,
pub settings: LocalConfigSettings,
}
impl Default for LocalConfig {

View file

@ -11,10 +11,8 @@
*/
/****/
use std::cell::Cell;
use std::collections::BTreeMap;
use std::net::{SocketAddr, Ipv4Addr, IpAddr, Ipv6Addr};
use std::str::FromStr;
use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
use std::time::Duration;
@ -41,7 +39,7 @@ const CONFIG_CHECK_INTERVAL: i64 = 5000;
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ServiceStatus {
#[serde(rename = "objectType")]
pub object_type: &'static str,
pub object_type: String,
pub address: Address,
pub clock: i64,
#[serde(rename = "startTime")]
@ -220,7 +218,7 @@ impl Service {
let ver = zerotier_core::version();
self.node().map(|node| {
ServiceStatus {
object_type: "status",
object_type: "status".to_owned(),
address: node.address(),
clock: ms_since_epoch(),
start_time: self.intl.startup_time,

View file

@ -11,7 +11,7 @@
*/
/****/
use std::error::Error;
use std::borrow::Borrow;
use std::fs::File;
use std::io::Read;
use std::mem::MaybeUninit;
@ -20,6 +20,9 @@ use std::path::Path;
use zerotier_core::{Identity, Locator};
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::osdep;
#[inline(always)]
@ -121,3 +124,44 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> u64 {
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))
}
})
})
})
}

View file

@ -12,10 +12,12 @@
/****/
use std::collections::BTreeSet;
use std::ptr::null_mut;
#[allow(unused_imports)]
use zerotier_core::{MAC, MulticastGroup};
use num_traits::cast::AsPrimitive;
#[allow(unused_imports)]
use num_traits::AsPrimitive;
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 dev = dev.as_bytes();
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 {
let mut i = maddrs;
while !i.is_null() {