diff --git a/zerotier-network-hypervisor/Cargo.toml b/zerotier-network-hypervisor/Cargo.toml index c16b7454b..d0db44825 100644 --- a/zerotier-network-hypervisor/Cargo.toml +++ b/zerotier-network-hypervisor/Cargo.toml @@ -15,10 +15,10 @@ panic = 'abort' zerotier-core-crypto = { path = "../zerotier-core-crypto" } base64 = "^0" lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-decode"] } -dashmap = "^4" +dashmap = "^5" parking_lot = "^0" lazy_static = "^1" -serde = { version = "^1", features = ["derive"], default-features = false } +serde = { version = "^1", features = [], default-features = false } [target."cfg(not(windows))".dependencies] libc = "^0" diff --git a/zerotier-network-hypervisor/src/util/buffer.rs b/zerotier-network-hypervisor/src/util/buffer.rs index af9947b25..e14545444 100644 --- a/zerotier-network-hypervisor/src/util/buffer.rs +++ b/zerotier-network-hypervisor/src/util/buffer.rs @@ -42,12 +42,44 @@ fn overflow_err() -> std::io::Error { impl Buffer { pub const CAPACITY: usize = L; + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] + #[inline(always)] + fn read_obj_internal(&self, i: usize) -> T { + unsafe { *self.1.as_ptr().add(i).cast() } + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] + #[inline(always)] + fn read_obj_internal(&self, i: usize) -> T { + unsafe { std::mem::transmute_copy(&*self.1.as_ptr().add(i).cast::()) } + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] + #[inline(always)] + fn write_obj_internal(&mut self, i: usize, o: T) { + unsafe { *self.1.as_mut_ptr().add(i).cast::() = o }; + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] + #[inline(always)] + fn write_obj_internal(&mut self, i: usize, o: T) { + unsafe { + std::ptr::copy_nonoverlapping((&o as *const T).cast::(), self.1.as_mut_ptr().add(i), size_of::()); + } + } + /// Create an empty zeroed buffer. #[inline(always)] pub fn new() -> Self { Self(0, [0_u8; L]) } + /// Create an empty zeroed buffer on the heap without intermediate stack allocation. + /// This can be used to allocate buffers too large for the stack. + pub fn new_boxed() -> Box { + unsafe { Box::from_raw(std::alloc::alloc_zeroed(std::alloc::Layout::new::()).cast()) } + } + /// Create an empty buffer without internally zeroing its memory. /// /// This is technically unsafe because unwritten memory in the buffer will have undefined contents. @@ -241,84 +273,39 @@ impl Buffer { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] #[inline(always)] pub fn append_u16(&mut self, i: u16) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 2; if end <= L { self.0 = end; - unsafe { *self.1.as_mut_ptr().add(ptr).cast::() = i.to_be() }; + self.write_obj_internal(ptr, i.to_be()); Ok(()) } else { Err(overflow_err()) } } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn append_u16(&mut self, i: u16) -> std::io::Result<()> { - let ptr = self.0; - let end = ptr + 2; - if end <= L { - self.0 = end; - self.1[ptr..end].copy_from_slice(&i.to_be_bytes()); - Ok(()) - } else { - Err(overflow_err()) - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] #[inline(always)] pub fn append_u32(&mut self, i: u32) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 4; if end <= L { self.0 = end; - unsafe { *self.1.as_mut_ptr().add(ptr).cast::() = i.to_be() }; + self.write_obj_internal(ptr, i.to_be()); Ok(()) } else { Err(overflow_err()) } } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn append_u32(&mut self, i: u32) -> std::io::Result<()> { - let ptr = self.0; - let end = ptr + 4; - if end <= L { - self.0 = end; - self.1[ptr..end].copy_from_slice(&i.to_be_bytes()); - Ok(()) - } else { - Err(overflow_err()) - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] #[inline(always)] pub fn append_u64(&mut self, i: u64) -> std::io::Result<()> { let ptr = self.0; let end = ptr + 8; if end <= L { self.0 = end; - unsafe { *self.1.as_mut_ptr().add(ptr).cast::() = i.to_be() }; - Ok(()) - } else { - Err(overflow_err()) - } - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn append_u64(&mut self, i: u64) -> std::io::Result<()> { - let ptr = self.0; - let end = ptr + 8; - if end <= L { - self.0 = end; - self.1[ptr..end].copy_from_slice(&i.to_be_bytes()); + self.write_obj_internal(ptr, i.to_be()); Ok(()) } else { Err(overflow_err()) @@ -421,7 +408,6 @@ impl Buffer { } } - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] #[inline(always)] pub fn read_u16(&self, cursor: &mut usize) -> std::io::Result { let ptr = *cursor; @@ -429,27 +415,12 @@ impl Buffer { debug_assert!(end <= L); if end <= self.0 { *cursor = end; - Ok(u16::from_be(unsafe { *self.1.as_ptr().add(ptr).cast::() })) + Ok(u16::from_be(self.read_obj_internal(ptr))) } else { Err(overflow_err()) } } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn read_u16(&self, cursor: &mut usize) -> std::io::Result { - let ptr = *cursor; - let end = ptr + 2; - debug_assert!(end <= L); - if end <= self.0 { - *cursor = end; - Ok(u16::from_be_bytes(*self.1[ptr..end])) - } else { - Err(overflow_err()) - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] #[inline(always)] pub fn read_u32(&self, cursor: &mut usize) -> std::io::Result { let ptr = *cursor; @@ -457,27 +428,12 @@ impl Buffer { debug_assert!(end <= L); if end <= self.0 { *cursor = end; - Ok(u32::from_be(unsafe { *self.1.as_ptr().add(ptr).cast::() })) + Ok(u32::from_be(self.read_obj_internal(ptr))) } else { Err(overflow_err()) } } - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn read_u32(&self, cursor: &mut usize) -> std::io::Result { - let ptr = *cursor; - let end = ptr + 4; - debug_assert!(end <= L); - if end <= self.0 { - *cursor = end; - Ok(u32::from_be_bytes(*self.1[ptr..end])) - } else { - Err(overflow_err()) - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64"))] #[inline(always)] pub fn read_u64(&self, cursor: &mut usize) -> std::io::Result { let ptr = *cursor; @@ -485,21 +441,7 @@ impl Buffer { debug_assert!(end <= L); if end <= self.0 { *cursor = end; - Ok(u64::from_be(unsafe { *self.1.as_ptr().add(ptr).cast::() })) - } else { - Err(overflow_err()) - } - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))] - #[inline(always)] - pub fn read_u64(&self, cursor: &mut usize) -> std::io::Result { - let ptr = *cursor; - let end = ptr + 8; - debug_assert!(end <= L); - if end <= self.0 { - *cursor = end; - Ok(u64::from_be_bytes(*self.1[ptr..end])) + Ok(u64::from_be(self.read_obj_internal(ptr))) } else { Err(overflow_err()) } diff --git a/zerotier-network-hypervisor/src/util/marshalable.rs b/zerotier-network-hypervisor/src/util/marshalable.rs new file mode 100644 index 000000000..6080d2998 --- /dev/null +++ b/zerotier-network-hypervisor/src/util/marshalable.rs @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * (c)2021 ZeroTier, Inc. + * https://www.zerotier.com/ + */ + +use crate::util::buffer::Buffer; + +/// A super-lightweight zero-allocation serialization interface. +pub trait Marshalable: Sized { + const MAX_MARSHAL_SIZE: usize; + + /// Write this object into a buffer. + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()>; + + /// Read this object from a buffer. + /// + /// The supplied cursor is advanced by the number of bytes read. If an Err is returned + /// the value of the cursor is undefined but likely points to about where the error + /// occurred. It may also point beyond the buffer, which would indicate an overrun error. + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result; + + /// Write this marshalable entity into a buffer of the given size. + /// + /// This will return an Err if the buffer is too small or some other error occurs. It's just + /// a shortcut to creating a buffer and marshaling into it. + #[inline(always)] + fn to_buffer(&self) -> std::io::Result> { + assert!(BL >= Self::MAX_MARSHAL_SIZE); + let mut tmp = Buffer::new(); + self.marshal(&mut tmp)?; + Ok(tmp) + } + + /// Unmarshal this object from a buffer. + /// + /// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor. + #[inline(always)] + fn from_buffer(buf: &Buffer) -> std::io::Result { + let mut tmp = 0; + Self::unmarshal(buf, &mut tmp) + } +} diff --git a/zerotier-network-hypervisor/src/util/mod.rs b/zerotier-network-hypervisor/src/util/mod.rs index b7ab5cfcf..642dcb970 100644 --- a/zerotier-network-hypervisor/src/util/mod.rs +++ b/zerotier-network-hypervisor/src/util/mod.rs @@ -8,6 +8,7 @@ pub mod buffer; pub(crate) mod gate; +pub mod marshalable; pub(crate) mod pool; pub use zerotier_core_crypto::hex; diff --git a/zerotier-network-hypervisor/src/vl1/address.rs b/zerotier-network-hypervisor/src/vl1/address.rs index 7e2c1b327..2e586264c 100644 --- a/zerotier-network-hypervisor/src/vl1/address.rs +++ b/zerotier-network-hypervisor/src/vl1/address.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::error::InvalidFormatError; use crate::util::buffer::Buffer; use crate::util::hex::HEX_CHARS; +use crate::util::marshalable::Marshalable; use crate::vl1::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE}; /// A unique address on the global ZeroTier VL1 network. @@ -54,15 +55,19 @@ impl Address { pub fn to_u64(&self) -> u64 { self.0.get() } +} + +impl Marshalable for Address { + const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE; #[inline(always)] - pub(crate) fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { buf.append_bytes(&self.0.get().to_be_bytes()[8 - ADDRESS_SIZE..]) } #[inline(always)] - pub(crate) fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result> { - buf.read_bytes_fixed::<{ ADDRESS_SIZE }>(cursor).map(|b| Self::from_bytes_fixed(b)) + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a)) } } diff --git a/zerotier-network-hypervisor/src/vl1/dictionary.rs b/zerotier-network-hypervisor/src/vl1/dictionary.rs index 414b83c07..ea5d140d4 100644 --- a/zerotier-network-hypervisor/src/vl1/dictionary.rs +++ b/zerotier-network-hypervisor/src/vl1/dictionary.rs @@ -18,7 +18,7 @@ use crate::util::hex::HEX_CHARS; /// entirely human readable. Keys are serialized in natural sort order so the result can be consistently /// checksummed or hashed. #[derive(Clone, PartialEq, Eq)] -pub struct Dictionary(BTreeMap>); +pub struct Dictionary(pub(crate) BTreeMap>); fn write_escaped(b: &[u8], w: &mut W) -> std::io::Result<()> { let mut i = 0_usize; diff --git a/zerotier-network-hypervisor/src/vl1/endpoint.rs b/zerotier-network-hypervisor/src/vl1/endpoint.rs index 86b18241f..c2d37f305 100644 --- a/zerotier-network-hypervisor/src/vl1/endpoint.rs +++ b/zerotier-network-hypervisor/src/vl1/endpoint.rs @@ -16,6 +16,7 @@ use zerotier_core_crypto::hash::SHA512_HASH_SIZE; use crate::error::InvalidFormatError; use crate::util::buffer::Buffer; +use crate::util::marshalable::Marshalable; use crate::vl1::inetaddress::InetAddress; use crate::vl1::{Address, MAC}; @@ -31,6 +32,8 @@ pub const TYPE_HTTP: u8 = 8; pub const TYPE_WEBRTC: u8 = 9; pub const TYPE_ZEROTIER_ENCAP: u8 = 10; +pub(crate) const MAX_MARSHAL_SIZE: usize = 1024; + /// A communication endpoint on the network where a ZeroTier node can be reached. /// /// Currently only a few of these are supported. The rest are reserved for future use. @@ -112,14 +115,26 @@ impl Endpoint { matches!(self, Endpoint::Nil) } - #[inline(always)] + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() < MAX_MARSHAL_SIZE { + let mut cursor = 0; + Self::unmarshal(&Buffer::::from_bytes(bytes).unwrap(), &mut cursor).map_or(None, |e| Some(e)) + } else { + None + } + } + pub fn to_bytes(&self) -> Vec { - let mut b: Buffer<256> = Buffer::new(); + let mut b: Buffer = Buffer::new(); self.marshal(&mut b).expect("internal error marshaling Endpoint"); b.as_bytes().to_vec() } +} - pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { +impl Marshalable for Endpoint { + const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE; + + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { match self { Endpoint::Nil => buf.append_u8(TYPE_NIL), Endpoint::ZeroTier(a, h) => { @@ -175,7 +190,7 @@ impl Endpoint { } } - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { let type_byte = buf.read_u8(cursor)?; if type_byte < 16 { if type_byte == 4 { @@ -188,29 +203,15 @@ impl Endpoint { Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream")) } } else { - let read_mac = |buf: &Buffer, cursor: &mut usize| { - let m = MAC::unmarshal(buf, cursor)?; - if m.is_some() { - Ok(m.unwrap()) - } else { - Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid MAC address")) - } - }; - match type_byte - 16 { TYPE_NIL => Ok(Endpoint::Nil), TYPE_ZEROTIER => { let zt = Address::unmarshal(buf, cursor)?; - if zt.is_some() { - let h = buf.read_bytes_fixed::(cursor)?; - Ok(Endpoint::ZeroTier(zt.unwrap(), h.clone())) - } else { - Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address")) - } + Ok(Endpoint::ZeroTier(zt, buf.read_bytes_fixed::(cursor)?.clone())) } - TYPE_ETHERNET => Ok(Endpoint::Ethernet(read_mac(buf, cursor)?)), - TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(read_mac(buf, cursor)?)), - TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(read_mac(buf, cursor)?)), + TYPE_ETHERNET => Ok(Endpoint::Ethernet(MAC::unmarshal(buf, cursor)?)), + TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(MAC::unmarshal(buf, cursor)?)), + TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(MAC::unmarshal(buf, cursor)?)), TYPE_IP => Ok(Endpoint::Ip(InetAddress::unmarshal(buf, cursor)?)), TYPE_IPUDP => Ok(Endpoint::IpUdp(InetAddress::unmarshal(buf, cursor)?)), TYPE_IPTCP => Ok(Endpoint::IpTcp(InetAddress::unmarshal(buf, cursor)?)), @@ -218,19 +219,13 @@ impl Endpoint { TYPE_WEBRTC => Ok(Endpoint::WebRTC(buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?.to_vec())), TYPE_ZEROTIER_ENCAP => { let zt = Address::unmarshal(buf, cursor)?; - if zt.is_some() { - let h = buf.read_bytes_fixed::(cursor)?; - Ok(Endpoint::ZeroTierEncap(zt.unwrap(), h.clone())) - } else { - Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address")) - } + Ok(Endpoint::ZeroTierEncap(zt, buf.read_bytes_fixed::(cursor)?.clone())) } _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream")), } } } } - impl Hash for Endpoint { fn hash(&self, state: &mut H) { match self { @@ -376,8 +371,6 @@ impl FromStr for Endpoint { } } -const TEMP_SERIALIZE_BUFFER_SIZE: usize = 1024; - impl Serialize for Endpoint { fn serialize(&self, serializer: S) -> Result where @@ -386,7 +379,7 @@ impl Serialize for Endpoint { if serializer.is_human_readable() { serializer.serialize_str(self.to_string().as_str()) } else { - let mut tmp: Buffer = Buffer::new(); + let mut tmp: Buffer = Buffer::new(); assert!(self.marshal(&mut tmp).is_ok()); serializer.serialize_bytes(tmp.as_bytes()) } @@ -406,8 +399,8 @@ impl<'de> serde::de::Visitor<'de> for EndpointVisitor { where E: serde::de::Error, { - if v.len() <= TEMP_SERIALIZE_BUFFER_SIZE { - let mut tmp: Buffer = Buffer::new(); + if v.len() <= MAX_MARSHAL_SIZE { + let mut tmp: Buffer = Buffer::new(); let _ = tmp.append_bytes(v); let mut cursor = 0; Endpoint::unmarshal(&tmp, &mut cursor).map_err(|e| E::custom(e.to_string())) diff --git a/zerotier-network-hypervisor/src/vl1/identity.rs b/zerotier-network-hypervisor/src/vl1/identity.rs index e7360469f..53367a779 100644 --- a/zerotier-network-hypervisor/src/vl1/identity.rs +++ b/zerotier-network-hypervisor/src/vl1/identity.rs @@ -28,6 +28,7 @@ use zerotier_core_crypto::x25519::*; use crate::error::{InvalidFormatError, InvalidParameterError}; use crate::util::buffer::Buffer; +use crate::util::marshalable::Marshalable; use crate::util::pool::{Pool, PoolFactory, Pooled}; use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_POW_THRESHOLD}; use crate::vl1::Address; @@ -43,7 +44,7 @@ pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff; /// Current sanity limit for the size of a marshaled Identity /// This is padded just a little up to 512 and can be increased if new key types are ever added. -pub const MAX_MARSHAL_SIZE: usize = 25 +pub(crate) const MAX_MARSHAL_SIZE: usize = 25 + ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE @@ -86,8 +87,10 @@ pub struct IdentitySecret { /// for human-readable formats and binary otherwise. /// /// SECURITY NOTE: for security reasons secret keys are NOT exported by default by to_string() -/// or by the serde serializer. If you want secrets you must use to_string_with_options() or -/// marshal(). This is to prevent accidental leakage of secrets by naive code. +/// or the default marshal() in Marshalable. You must use to_string_with_options() and +/// marshal_with_options() to get secrets. The clone() method on the other hand does duplicate +/// secrets so as not to violate the contract of creating an exact duplicate of the object. +/// There is a clone_without_secrets() if this isn't wanted. #[derive(Clone)] pub struct Identity { pub address: Address, @@ -121,7 +124,7 @@ fn concat_arrays_4 Self { - // First generate an identity with just x25519 keys. + // First generate an identity with just x25519 keys and derive its address. let mut sha = SHA512::new(); let ed25519 = Ed25519KeyPair::generate(); let ed25519_pub = ed25519.public_bytes(); @@ -185,13 +188,16 @@ impl Identity { let _ = self_sign_buf.write_all(p384_ecdh.public_key_bytes()); let _ = self_sign_buf.write_all(p384_ecdsa.public_key_bytes()); + // Fingerprint includes only the above fields, so calc before appending the ECDSA signature. + self.fingerprint = SHA512::hash(self_sign_buf.as_slice()); + // Sign all keys including the x25519 ones with the new P-384 keys. let ecdsa_self_signature = p384_ecdsa.sign(self_sign_buf.as_slice()); - // Sign everything with the original ed25519 key to bind the new key pairs. Include the ECDSA - // signature because these signatures are not deterministic. We don't want the ability to - // make a new identity with the same address but a different fingerprint by mangling the - // ECDSA signature in some way. + // Sign everything with the original ed25519 key to bind the new key pairs. Include ECDSA + // signature because ECDSA signatures are randomized and we want only this specific one. + // Identities should be rigid. (Ed25519 signatures are deterministic.) + let _ = self_sign_buf.write_all(&ecdsa_self_signature); let ed25519_self_signature = self.secret.as_ref().unwrap().ed25519.sign(self_sign_buf.as_slice()); let _ = self.p384.insert(IdentityP384Public { @@ -201,13 +207,24 @@ impl Identity { ed25519_self_signature, }); let _ = self.secret.as_mut().unwrap().p384.insert(IdentityP384Secret { ecdh: p384_ecdh, ecdsa: p384_ecdsa }); - self.fingerprint = SHA512::hash(self_sign_buf.as_slice()); return Ok(true); } return Ok(false); } + /// Create a clone minus any secret key it holds. + pub fn clone_without_secret(&self) -> Identity { + Self { + address: self.address, + c25519: self.c25519.clone(), + ed25519: self.ed25519.clone(), + p384: self.p384.clone(), + secret: None, + fingerprint: self.fingerprint.clone(), + } + } + /// Get a bit mask of algorithms present in this identity. #[inline(always)] pub fn algorithms(&self) -> u8 { @@ -237,12 +254,13 @@ impl Identity { return false; } + let _ = self_sign_buf.write_all(&p384.ecdsa_self_signature); if !ed25519_verify(&self.ed25519, &p384.ed25519_self_signature, self_sign_buf.as_slice()) { return false; } } - // NOTE: fingerprint is always computed locally, so no need to check it. + // NOTE: fingerprint is always computed on generation or deserialize so no need to check. let mut sha = SHA512::new(); sha.update(&self.c25519); @@ -349,16 +367,16 @@ impl Identity { } } - pub fn to_bytes(&self, include_algorithms: u8, include_private: bool) -> Buffer { + pub fn to_buffer_with_options(&self, include_algorithms: u8, include_private: bool) -> Buffer { let mut b: Buffer = Buffer::new(); - assert!(self.marshal(&mut b, include_algorithms, include_private).is_ok()); + assert!(self.marshal_with_options(&mut b, include_algorithms, include_private).is_ok()); b } const P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE) as u16; const P384_PUBLIC_ONLY_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u16; - pub fn marshal(&self, buf: &mut Buffer, include_algorithms: u8, include_private: bool) -> std::io::Result<()> { + pub fn marshal_with_options(&self, buf: &mut Buffer, include_algorithms: u8, include_private: bool) -> std::io::Result<()> { let algorithms = self.algorithms() & include_algorithms; let secret = self.secret.as_ref(); @@ -425,141 +443,6 @@ impl Identity { Ok(()) } - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { - let address = Address::from_bytes(buf.read_bytes_fixed::(cursor)?); - if !address.is_some() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid address")); - } - let address = address.unwrap(); - - let mut x25519_public: Option<([u8; C25519_PUBLIC_KEY_SIZE], [u8; ED25519_PUBLIC_KEY_SIZE])> = None; - let mut x25519_secret: Option<([u8; C25519_SECRET_KEY_SIZE], [u8; ED25519_SECRET_KEY_SIZE])> = None; - let mut p384_ecdh_ecdsa_public: Option<(P384PublicKey, P384PublicKey, [u8; P384_ECDSA_SIGNATURE_SIZE], [u8; ED25519_SIGNATURE_SIZE])> = None; - let mut p384_ecdh_ecdsa_secret: Option<([u8; P384_SECRET_KEY_SIZE], [u8; P384_SECRET_KEY_SIZE])> = None; - - loop { - let algorithm = buf.read_u8(cursor); - if algorithm.is_err() { - break; - } - match algorithm.unwrap() { - 0x00 | IDENTITY_ALGORITHM_X25519 => { - let a = buf.read_bytes_fixed::(cursor)?; - let b = buf.read_bytes_fixed::(cursor)?; - x25519_public = Some((a.clone(), b.clone())); - let sec_size = buf.read_u8(cursor)?; - if sec_size == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 { - let a = buf.read_bytes_fixed::(cursor)?; - let b = buf.read_bytes_fixed::(cursor)?; - x25519_secret = Some((a.clone(), b.clone())); - } else if sec_size != 0 { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid x25519 secret")); - } - } - 0x03 => { - // This isn't an algorithm; each algorithm is identified by just one bit. This - // indicates the total size of the section after the x25519 keys for backward - // compatibility. See comments in marshal(). New versions can ignore this field. - *cursor += 2; - } - IDENTITY_ALGORITHM_EC_NIST_P384 => { - let size = buf.read_u16(cursor)?; - if size < Self::P384_PUBLIC_ONLY_BUNDLE_SIZE { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key")); - } - let a = buf.read_bytes_fixed::(cursor)?; - let b = buf.read_bytes_fixed::(cursor)?; - let c = buf.read_bytes_fixed::(cursor)?; - let d = buf.read_bytes_fixed::(cursor)?; - let a = P384PublicKey::from_bytes(a); - let b = P384PublicKey::from_bytes(b); - if a.is_none() || b.is_none() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key")); - } - p384_ecdh_ecdsa_public = Some((a.unwrap(), b.unwrap(), c.clone(), d.clone())); - if size > Self::P384_PUBLIC_ONLY_BUNDLE_SIZE { - if size != Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 secret key")); - } - let a = buf.read_bytes_fixed::(cursor)?; - let b = buf.read_bytes_fixed::(cursor)?; - p384_ecdh_ecdsa_secret = Some((a.clone(), b.clone())); - } - } - _ => { - // Skip any unrecognized cipher suites, all of which will be prefixed by a size. - *cursor += buf.read_u16(cursor)? as usize; - if *cursor > buf.len() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid field length")); - } - } - } - } - - if x25519_public.is_none() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 key missing")); - } - let x25519_public = x25519_public.unwrap(); - - let mut sha = SHA512::new(); - sha.update(&address.to_bytes()); - sha.update(&x25519_public.0); - sha.update(&x25519_public.1); - if p384_ecdh_ecdsa_public.is_some() { - let p384 = p384_ecdh_ecdsa_public.as_ref().unwrap(); - sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]); - sha.update(p384.0.as_bytes()); - sha.update(p384.1.as_bytes()); - } - - Ok(Identity { - address, - c25519: x25519_public.0.clone(), - ed25519: x25519_public.1.clone(), - p384: if p384_ecdh_ecdsa_public.is_some() { - let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap(); - Some(IdentityP384Public { - ecdh: p384_ecdh_ecdsa_public.0.clone(), - ecdsa: p384_ecdh_ecdsa_public.1.clone(), - ecdsa_self_signature: p384_ecdh_ecdsa_public.2.clone(), - ed25519_self_signature: p384_ecdh_ecdsa_public.3.clone(), - }) - } else { - None - }, - secret: if x25519_secret.is_some() { - let x25519_secret = x25519_secret.unwrap(); - let c25519_secret = C25519KeyPair::from_bytes(&x25519_public.0, &x25519_secret.0); - let ed25519_secret = Ed25519KeyPair::from_bytes(&x25519_public.1, &x25519_secret.1); - if c25519_secret.is_none() || ed25519_secret.is_none() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 public key invalid")); - } - Some(IdentitySecret { - c25519: c25519_secret.unwrap(), - ed25519: ed25519_secret.unwrap(), - p384: if p384_ecdh_ecdsa_secret.is_some() && p384_ecdh_ecdsa_public.is_some() { - let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap(); - let p384_ecdh_ecdsa_secret = p384_ecdh_ecdsa_secret.as_ref().unwrap(); - let p384_ecdh_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.0.as_bytes(), &p384_ecdh_ecdsa_secret.0); - let p384_ecdsa_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.1.as_bytes(), &p384_ecdh_ecdsa_secret.1); - if p384_ecdh_secret.is_none() || p384_ecdsa_secret.is_none() { - return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p384 secret key invalid")); - } - Some(IdentityP384Secret { - ecdh: p384_ecdh_secret.unwrap(), - ecdsa: p384_ecdsa_secret.unwrap(), - }) - } else { - None - }, - }) - } else { - None - }, - fingerprint: sha.finish(), - }) - } - /// Marshal this identity as a string. /// /// The include_algorithms bitmap controls which algorithms will be included, provided we have them. @@ -751,6 +634,150 @@ impl FromStr for Identity { } } +impl Marshalable for Identity { + const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE; + + #[inline(always)] + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + self.marshal_with_options(buf, IDENTITY_ALGORITHM_ALL, false) + } + + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + let address = Address::from_bytes(buf.read_bytes_fixed::(cursor)?); + if !address.is_some() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid address")); + } + let address = address.unwrap(); + + let mut x25519_public: Option<([u8; C25519_PUBLIC_KEY_SIZE], [u8; ED25519_PUBLIC_KEY_SIZE])> = None; + let mut x25519_secret: Option<([u8; C25519_SECRET_KEY_SIZE], [u8; ED25519_SECRET_KEY_SIZE])> = None; + let mut p384_ecdh_ecdsa_public: Option<(P384PublicKey, P384PublicKey, [u8; P384_ECDSA_SIGNATURE_SIZE], [u8; ED25519_SIGNATURE_SIZE])> = None; + let mut p384_ecdh_ecdsa_secret: Option<([u8; P384_SECRET_KEY_SIZE], [u8; P384_SECRET_KEY_SIZE])> = None; + + loop { + let algorithm = buf.read_u8(cursor); + if algorithm.is_err() { + break; + } + match algorithm.unwrap() { + 0x00 | IDENTITY_ALGORITHM_X25519 => { + let a = buf.read_bytes_fixed::(cursor)?; + let b = buf.read_bytes_fixed::(cursor)?; + x25519_public = Some((a.clone(), b.clone())); + let sec_size = buf.read_u8(cursor)?; + if sec_size == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8 { + let a = buf.read_bytes_fixed::(cursor)?; + let b = buf.read_bytes_fixed::(cursor)?; + x25519_secret = Some((a.clone(), b.clone())); + } else if sec_size != 0 { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid x25519 secret")); + } + } + 0x03 => { + // This isn't an algorithm; each algorithm is identified by just one bit. This + // indicates the total size of the section after the x25519 keys for backward + // compatibility. See comments in marshal(). New versions can ignore this field. + *cursor += 2; + } + IDENTITY_ALGORITHM_EC_NIST_P384 => { + let size = buf.read_u16(cursor)?; + if size < Self::P384_PUBLIC_ONLY_BUNDLE_SIZE { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key")); + } + let a = buf.read_bytes_fixed::(cursor)?; + let b = buf.read_bytes_fixed::(cursor)?; + let c = buf.read_bytes_fixed::(cursor)?; + let d = buf.read_bytes_fixed::(cursor)?; + let a = P384PublicKey::from_bytes(a); + let b = P384PublicKey::from_bytes(b); + if a.is_none() || b.is_none() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key")); + } + p384_ecdh_ecdsa_public = Some((a.unwrap(), b.unwrap(), c.clone(), d.clone())); + if size > Self::P384_PUBLIC_ONLY_BUNDLE_SIZE { + if size != Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 secret key")); + } + let a = buf.read_bytes_fixed::(cursor)?; + let b = buf.read_bytes_fixed::(cursor)?; + p384_ecdh_ecdsa_secret = Some((a.clone(), b.clone())); + } + } + _ => { + // Skip any unrecognized cipher suites, all of which will be prefixed by a size. + *cursor += buf.read_u16(cursor)? as usize; + if *cursor > buf.len() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid field length")); + } + } + } + } + + if x25519_public.is_none() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 key missing")); + } + let x25519_public = x25519_public.unwrap(); + + let mut sha = SHA512::new(); + sha.update(&address.to_bytes()); + sha.update(&x25519_public.0); + sha.update(&x25519_public.1); + if p384_ecdh_ecdsa_public.is_some() { + let p384 = p384_ecdh_ecdsa_public.as_ref().unwrap(); + sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]); + sha.update(p384.0.as_bytes()); + sha.update(p384.1.as_bytes()); + } + + Ok(Identity { + address, + c25519: x25519_public.0.clone(), + ed25519: x25519_public.1.clone(), + p384: if p384_ecdh_ecdsa_public.is_some() { + let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap(); + Some(IdentityP384Public { + ecdh: p384_ecdh_ecdsa_public.0.clone(), + ecdsa: p384_ecdh_ecdsa_public.1.clone(), + ecdsa_self_signature: p384_ecdh_ecdsa_public.2.clone(), + ed25519_self_signature: p384_ecdh_ecdsa_public.3.clone(), + }) + } else { + None + }, + secret: if x25519_secret.is_some() { + let x25519_secret = x25519_secret.unwrap(); + let c25519_secret = C25519KeyPair::from_bytes(&x25519_public.0, &x25519_secret.0); + let ed25519_secret = Ed25519KeyPair::from_bytes(&x25519_public.1, &x25519_secret.1); + if c25519_secret.is_none() || ed25519_secret.is_none() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "x25519 public key invalid")); + } + Some(IdentitySecret { + c25519: c25519_secret.unwrap(), + ed25519: ed25519_secret.unwrap(), + p384: if p384_ecdh_ecdsa_secret.is_some() && p384_ecdh_ecdsa_public.is_some() { + let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap(); + let p384_ecdh_ecdsa_secret = p384_ecdh_ecdsa_secret.as_ref().unwrap(); + let p384_ecdh_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.0.as_bytes(), &p384_ecdh_ecdsa_secret.0); + let p384_ecdsa_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.1.as_bytes(), &p384_ecdh_ecdsa_secret.1); + if p384_ecdh_secret.is_none() || p384_ecdsa_secret.is_none() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p384 secret key invalid")); + } + Some(IdentityP384Secret { + ecdh: p384_ecdh_secret.unwrap(), + ecdsa: p384_ecdsa_secret.unwrap(), + }) + } else { + None + }, + }) + } else { + None + }, + fingerprint: sha.finish(), + }) + } +} + impl PartialEq for Identity { #[inline(always)] fn eq(&self, other: &Self) -> bool { @@ -789,7 +816,7 @@ impl Serialize for Identity { serializer.serialize_str(self.to_string_with_options(IDENTITY_ALGORITHM_ALL, false).as_str()) } else { let mut tmp: Buffer = Buffer::new(); - assert!(self.marshal(&mut tmp, IDENTITY_ALGORITHM_ALL, false).is_ok()); + assert!(self.marshal_with_options(&mut tmp, IDENTITY_ALGORITHM_ALL, false).is_ok()); serializer.serialize_bytes(tmp.as_bytes()) } } @@ -922,6 +949,7 @@ pub(crate) fn purge_verification_memory_pool() { #[cfg(test)] mod tests { + use crate::util::marshalable::Marshalable; use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL}; use std::str::FromStr; use std::time::{Duration, SystemTime}; @@ -941,16 +969,16 @@ mod tests { "aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626", ]; const GOOD_V1_IDENTITIES: [&'static str; 10] = [ - "a8f6e0566e:0:a13a6394de205384eb75eb62179ef11423295c5ecdccfeed7f2eff6c7a74f8059c99eed164c5dfaf2a4cf395ec7b72b68ee1c3c31916de4bc57c07abfe77f9c2::2:A1Cj2O0hKLlhDQ6guCCv5H1UgzbegZwse0iqTaaZov9LpKifyKH0e1VzmHrPmoKcvgJyzI-BAqRQzBiUjScXIjojneNKOywc0Gvq-zeDCYPcXN393xi3q25mB3ud9iEN-GN6wiXPWFjHy-CBD9tGDJzr-G3ZJZvrdiLGT5rZ5W2cZtx8ORYnp9L9HJJOeb8qgdfVr67B5pT9jPsxSsw8P4qlzFFOlX2WN9Hvvu0TO6S_N4yq173deyr-f-ehcBFiBXsSG96p44oU4uRRBEDZWhzHDuD22Vw8PhsB8mko9IRqVXCGbvlaKJ0vyAZ_PyVRM9n_Z-HAEvLveAT-f61mh4YP", - "d913346e57:0:afa3bb56d4f8aae1f91205e3629fa80db32f8a67698a0ba4e5a948105572b91f7f5368d4bcbf99424e359d8ac461ebd075d336a91651968d31c625d2c6dcd7ca::2:A_ApZGZgN_ImMs7FawasAsthZ5NIqcjp1vAj0Gjcrl8ugwY2CXbHIKFCertcdPNEuQM5HIYmfIB3iTXN3-CtjgBgI5KpJ_Jzm-BoTQKgSHlhVfiSbUQkgmlntXK8yG165rYDdAyA4U7vAipOT9kdeolr4WddCso11M0B_V7O72u0Nmquaw0KRF-GpS_LaM96LHJBKoTSwKrymmgelXV8VYnO-tY_YJPWtdtfXjoI9nz6q4cTHgQDw3NlUDMt1Zd7c9d7jrKI-FOca2GycVtDEHGq2yxZl7dPHWrpP96yr9NXrziXJo9UWaxJW0nMka7eTRwzzhhdIsM9Ra-2k4pZmvEH", - "ad52ee12b3:0:e8cafd1e4794f259a481b466009b3d1a267653246d21ba9c5feb803ff63cc829f6e45070585f069440cb5ad46b8f17ab90894be7efce7d3d1de6ee00ce7d3fe5::2:A_DC0dhns3PEzcdo6HKr83BSZ-Jc7R25HTkAq9r7HHAhC7Fg49ijzTJ9FT5p0_t-7AKZHxLpCOf8h4bwTCSqUDl5UT5_5m4X3aHCEyUwZR0CjbNaXL2lZVlPn3EnuBEq5w3zKHO8LeYPIXPgmsG0KkUk3eRXDjgZF24YNY4Iu10IMVLgiSAMWRveh5Dg_ShkEWWsbBK5IHkWm9tqkthm61sg9FgLbyWRsj3D-GF_X6g3Tz3a9iOKiTzTX2roR4OERbxnWV5lFggrlkoJFEUm0MsKTCX8ul_gO_bhYUIXSqibVn7XlIF_fj69frWgDgBB4ehCuBDuoMopRgbTZYWw9BEP", - "2e7864c663:0:6564af3f6feb9a8239ec9d7e4e8f1ded503dbac95d5f197c73103b4f9e325c322f82284a22e4eefa9a4077d7b25d335845858fdae85f2b0eef1fca27f45015e9::2:AyvRLX_lyzC6DTtK6JSd-Gxk954kHWDIuUwY8PFCUv-xrzmWKIJqNM_FWkFhzlon9wKaqWbTzHm3Frb39zzvC-24-AkYVnnvkoOlUaifnk8a0ndfvTHruYoBZ_ojJqsonErET-cZrRZ_QjeShHYmlggz1W_Ipkcut7R0vjLev5jHGQSZh7CYNo7FsGc7POjxUKgUD7F1DNnbb623Ecq9A7uZoTDcER5wjNhjR9ov1cVUExPHD_BLiszRMCFvTxqPDZB-_1HUw3KhRjUed0k_pF2HQkbfggiQ9ZPcGTNur17Y2fc8-P1hcH5LIZA6bt1AjXAM4R1bfTHUpiA01WJHe5QL", - "3ab7e39024:0:44f07b42dad081542e3c24f39b39fe6ca6b168236578a8360600682cd98bb904636a2f90ef5e3f09bb690362383e9a5fcd84a6b4cd74ac5fe73e2514eb701471::2:Ar7x1arjl5HnkLCufDF-QpD2n9UqfaMvZXCcxVGVyfDfWnwCzEkzPFJwmrRYXoRW2ANnkjijRfJI4aADvd0rxthp4NLkkXdWIyzifnLVgPO0ieRTv6wveAPYGHFHpi6wnKiUEq40zcAXe4XEdY1YHeMyTaArg33vQSw9lUvHmvxIsh1BErYe1_TSxkxqogVu_X_9QXiIu6fn1BXTuYcLDOKOQzPurl_1g2uILhow9wV_jlMsmEIIA6LNgcmQtR-Qyvl4j9--cmQyZ3GNw1Qx5EnTwCkiLyoLGnrynqxVFBriJTU6wYvZEHBgPGBy9AtkI9B7J4IMTNi6RM1fcu6Kv4QH", - "d29e7b84cf:0:b1d15bfd75bedfafe92b556deee8c9c820d6ef7688d4020f7032bee59a1d3a5f6afa74d06c6391f0c7dba12eb2501b737abc5a64c16dae54fe4b63b575930363::2:Apad_7rAJvntlpC1-ikl174cAHI7iHUOWiTk3XOMmAUVkmwWO-qUoDkpsYd6jrSCzAIyopjlJIlK0Fw8O8Y4n4njLzVqXsWTGE61CBouoYcMwm0qNVbu6R8RiYnWTg247dvfttQCzLu66ZKwsMTl7_iS52FnUdNC-nznYvqSHRQYOVTamVTWfMxIc0tsYcyRumkLn0q9bzvOarHCuxB2oV2Wu1sLpPQEqe3qNThtxObfFcTVsDcMCSq8gcii0jLJxSrK7Bv7ZkoIq4Q0lD_JH9bp-v_52l7CRye8gZaa-3wScenW9VnxAEWly7YiHgRIF2oNZMbavXyM2PJuv4CvAnUJ", - "2e9dc1b7fc:0:4bb7eda550d7fe1f50b9bf473d9141d086728f773755abb66c45ba35e134264e80e26d77944a1a7e2aecb3000117735f0b74a2efa4fb2e4f3ca8d2346ea39485::2:A6B4txc97pW0JR3bXm-90_OuOJBs54p0gD9crTVnHi6mMTAujELLh7_E3aNh6XKviwNi-UXZgI5ip21RayLH3EiUOErSWO2sxQWi-lUY5XyeSwp-sOqDuRtgZVXDdBUsWf4X5lW2K_Oly5gD7InS29QVu2kkHxnd_FEKvC0-EF_jCr_UTiR_Mki0MZIGoecP_5a_CRTsLuVg7i0c5_pEypNcys6flXtNSpzev0ramywTkIN7IU3b-9lL4nsxwMlTHS2qXR-NQP-Ic9yIZ-NR-wr6tW7IcsTR9D0jB5H8SZyBMzXF90zaRLL_YgUZKRJ6TFlvHu2moNHdqSjvnklf1UoE", - "37cab19425:0:d9c9e16a56dc1a7e51b575b9a2d63a39c4d2d4d3b7b6ec9120a434aff0f3d54e09ac0fc5452b78613154611944d4d0372a8cab42e29dd2c062375f21f750f12f::2:A9ACtecFby8mpN9nAGc_UE7AMBS035_z28KiXHfbqL7T2uoI2ig0HRYjAB_E_WYhzgJb6AykhZtQ6wUInzSZCttpxSgnbnvJQ56bkkKa-QIlE68KlG8DBt0rIvf9T30q3z_Yd9NVsqJhpJJyQMin1juqpQ7rKNNQLe95XXv28lWrELYgXroDnnF2RWJF4BBDcISx4mNLaXJgojAaT6EqoletIA-9_71rl0SeKDh6cV8leMGYkdkFsqPXhvf3-mx9_seuTOFmQvohXf3pGjWjK0jJGEiQHblo2h7KEMnuwDSsZNjx7UsiqyXEH98jP-haXmuLyWgOkSIj6FeaYBn8ir4D", - "d9b3ef808f:0:fff56156ccbb67cfbcb22f8e11360e1f72049c607b0d037ef0502d6332c1dc4ac52caad347fd1a797272b1afe1cdd78203e0672d50af3d349e1db3c8f73f815e::2:A2XJ4QNrEQC9sh_dNrhDre2YkX4IWesb9H6c466Tg99sWEpDPOxsSLkflPj0uLL3wQJHlCgBwpd6zQJRaxABCaQzri-ODlN_z58M1casR47Ez_E3ZoznKG3I3Dh4cTo0fchudJiPF2sE66cRI_in20-95LpbbQeP6_PquiG0z_C9zEbIOopfllM4GlsXx_KKV6pYHooWNpai6QEGU9KRyJJqOVY3wh3v0lYJNNN8BG00rj_TQBR0ZuPhEE5pM2KiOWkhpgC-P3AChn1oQp78fH08FVoW9TXyQMArsGp7Cj-j-jvG6PyRfLUxI90h_taqPrLPnGHYZ5wqkFlsRDs86pIE", - "637cb047d7:0:fe11b6f03dc441f6070b631d2f948d74381b7b6da83cbfbdc859bbc7c7ee6675963576322830f52d01929b47703151cb9462f82103781a172a5542aec3d69f88::2:AzLuKAy1qkieKWN6fYDduBzkK7m330NJoLByI6S8-LeyfsWNXxn6XLO1mFklAPwvnAOdatyaEUYE5S2zGIdRFkWOBiTXNnzCTNBVhVQC0VXua4tqIL8BlumH_unK4oAEIRGWlt5gyU9wmpFJtA27b0YUUwUzR9V1cSnze5oxwheWlsT5befZQbR8sNOP89Jh9fkYxoHCWTiaBT7LtmD_kGCahXZXt6EK5nypJkyN_Pkj7Csyk2Y7gCFjo4kz86UFa-wM88zDFzrxrM4cnQT2unatIoiTATTZBOg7hRM5elvtWt5P-4sUMHHmsQwukF6bgYuQkpgD2AZ3o6y7pIYWrUIE" + "b9553fc08f:0:e5b69b67aba3fbb35fb5b26e3f8d98bd081944c0153350c2fbd0e5d4d68c4d19b0c33c44c7933c9e7d02162a56abad2de4ebe9e2bf606d7021b92e7412e20270:487b70af891cfd47d847e12739623abd67ed1a3554c4bcaf73a7e44178b1c04a9e59f4b4cedc17b6de00f8f7b26880fea6f82fdc67e371f5cfcab7a1f44dd267:2:AjvDSgtrnnkvMheXcH8P42wXPjPNVYedfvFldPUr7Xn_icixyCAoNGkXwTNEMN9xpwNqq9mvfqQa-mOUJUz8yiWTNqnw8T1Esaf-OGZSu-leWOEbmCswVIl94qNsjS5g8Kx4BHzTtkx2t_quKMoelK5EOscwAwEiFS94r1nUau3H9QBQWNr9v44_8_Dbj-V6Eeo70ZwiU2bCUlsvoBS5ae1Aepktv6Usi1Yzl9L-6v22VH0RLLinY_b_r63sdf4LSay9YZ8b4GnMYkXb_0KqDTSZXxsiDUb1lhmE63ARsLo9b9X8oKm_Kog5ZXhnMUUjN6DObiDjJ5pdyjvPEXlxzOoD:IKPkJFOW9NbDYSl2Aagb1OQ4HyDDN6iQpDhECBE84nUQj5KbN-IGU5a-IVvO3K1UyYcDfKnb2PMycSungUWO9LKbWFykXDKDmoWL0Sz7yEmfshDn96KTevLNpCbRNiAj", + "2f67e50239:0:a79b755302a4fd4805bea5b4e3f12cbf75e9adee50dce2cff48252eeb5e9803b8cf677cc5488851e5c64796b251ac9eca78af01a1825966e3d7d1fc06da4779e:48815d7f81db7709d88067d14fc8cb8a91b24eea70b791603002812619c73f4dabbba724dfb76d188c9150daef9ff4d650a5825752840683027c569fa478ca41:2:AmVWC14Qx4OEERfjBRvECvsbPPgRmuJr2N6ypX-ewfyEsZwQ8kVX2c3mrTsyfnUSuQJrjLh9p7L7G2Fv6LI0T7AXlL8_Hczk_81UlhV9eG9Dj70p4NtPWcAeH-osDJPCDTZmNx4cke6DQ8XwDeU_NFLOm_GKniNj85oO9iVRt40BZLD89S-bloPxptd4vT4QP07YL3VnRyLNUEn1PLnvedBwfU9WSSbGtYVOTYNElr4Jgmt6WK78Yq4bgQogayu8eKCb5AeS0A4vwVf0Ii7nsYOHnEQE-g7MSmV01awR2PC9hL8_uviZQKdNROYx9LKQrXLeszxl0cHawmB8hoOAnxkH:D0lKhr2LBBY8wTlrI2RaxI3bq9fZeJuU6xTbCOfF442YFjTIrqtGokDzirUYpVfMsCyT2nS-xmYDGBmDDAIEdG3x9fWuCjD1trib9ZKCB-1bX21ZOOUzsux9q6EByox9", + "484d3aabbc:0:0574978881591073ecdfa9e48ae0dce17b285896695e12e7f4f1c76f4abf0a03e22d12d97363542cdb59c0387533f49295a5831de758901328392021e28488fb:58ae228ba918e8558e1f8167b0e3612d9f58e48ff3caaa5c1aafd9cc4e81c7634a7d6b6c5859f539836e28bcd6d7a0d14760134b643d89bdc8d369765501fce8:2:AwTUxHZP6YGmdUzno2_JW6RLRaR8Qr_jyUXcbRVXTismnvzr2ERwSqXMlc3I3qvTBgOUFhl2oNXDNdiuCPsDINJuSiez9sm5nx3yzfYdwP636VjoLzjdGBnh82Sp9jNsYx1ZlK8ZWl2zzlNtdLs30OKvm-xhSmr5Mpkx9VK12j5jm8VDsoTMwAW3kjOBjCIy7kQ7gd61I89W4f7KIntjz1ZW9yuon3XWfFDbskUqbe7sQIldAvOKcggaZQWWljVrnn6hT4k6UwjDPyA_i2CaUTJmn_lVxilsW6UGXeIFyo-opG62zpVeBQcOhblIu9ndkAXpK0KJLjdppvDAwmTk8cEH:grMfFb7ZGfz5-SBX-JLoiY2BW5DJ5rvAsO0Aop1npYMnSHL2VM_56eiYiBlwZ7zdKyYWdA_BRApPl5x0iCQcqXLhgQAVn-iL5edFHTGIF8HLRVcLn6_XIz7_u5TAydXp", + "210ae3e250:0:d3fb8d2c651a4b5d2cdfe8ac07ed3aa2c303ff5250990d08aa5998fa8017335d2aff2b82bb153cb349c91e4177e16e718bcc74586644a582d8702481e30a813d:f8d31b46693c013f392da1180806edfaa7c389c1040f8e772195c086fd0d44617b9eb809e21e9de0e44b06236a5997b04522dc78218eca76b6c66f6ab8034770:2:Ai6daBUu73oul4o4lpH-xt2jufhIOIeZFR34vrO4MlY8oC258FPdg-e57467BS8pfQKkSh_Qvrarwl4kHgpCfANqWhFS7jdnvRTwWXPNESwsP04y8ZoGtR3-p8eaq9qUYWiZg7qnfvcoqecvySpk3gZIthZOXEtchID0InjYmkbCipHZcyzUDP82hkbrWlZWFySMVwP2sDUc_WT6Lyx4aMP83R1ZMTVw1-Q-peK8Ihevw4yCfOuF_iJOMxsek-NAA4R9n2gYhQJIDOn8Hj7zuNIffjcgoDOSX-pJr0mke_elL0u5Hgnko9qAPIH6PGQJRt5U5tpa5E-beJ0_aGaBWMwK:YC-wYTJu8l_7ZfUoK4xXP6fdVjon4QPQXE92fVgvs7-B2Sw2bsbus9-QGmWcUmJPWvrnpmkT14sgoyf7mWWoJFo93DCHm8dWheC8-cDUMIQ12uHgBKL33r6Ka57kgY71", + "f947dd72f4:0:5221ae17483f5dfe954a0240ebf0d2b466ff7f7f67cdd702eeadbe2e510dd26a795e15b2c84f85e610becb2ca049c73687dbcc8c6407cfade6a191c6e7f70377:501adf90810f5cd095581dd0f2929184db36278fd6b48909e3a10d80560bf5603d2750143063730932b31f240ea1c30f0349d017a363a75c6c1b95b8d0c659e5:2:AvXfGGmMG2OZRaamtGYJkatdUSHv4Z1PfbdKExOGwWnOewYGcPTvBDChtlAg9IwqOgJ9-XSnSFxgskoQtI8wDFXRhrr659_FFAzM-oyQ9sj3ZrSXYnFNPpfoyiennT2nsQmP2MoahrQCMerQIIO86EXc98BWVZJO79LC5fxhTK1EWzE9APvOBFulv6c-W9dxVlD9CT-oIAjqIBL8hVUgKLdWPmtzGmAd4NnfrfiHDx4zaAkoSmFxyuUOCvfp8AioGXNLrHwIPqquOqQKw2tcIHnDaHA72JpjOV7787Eb_2pTwFhyzJVKixDrESNqhI-35SHrcsaJel1mUOGcweliqEkB:NHS7hGLadxiuFWRqT48kp1NvZ0jf4Po861scAhIFRP4hLMe6Uuk4TGmajBEzZhQjz-60as3nQN5VaHg1NjE5eFUbJuijAagi4dEyvzsQWg4HPUYn8hNe9Z95vz-n5DgO", + "e11c31a215:0:db25e87625513ba531ac19eff743bf6e769517776e964bec1eaed38c559de3518167075d21ea4d89d611628c559ee52336537583201dcf46f2546e3f8bcdc7ec:40a09a8a1bd5f071262a79ad52c5a6b485318ad58ec38bebf432236bece7624c96b9ef2737eb992ae15c0d63c439c384b63aa35467aee5c686c5201cf7617158:2:A4VDDuVuH4UpctYg5_-rDhLduiHcColHzGAVudbe9ri0kTx7D_paVFYG-YUZaJu4LALW8FgWbjoEYjXepWErZWrZJk-MxrA-8IfeotLDZQbBNC5V_UxpDlPt-gRdyYpz-mN5KLe21shP0JiO7eL-rnGqYS4lmw8Fk_vHDq0AL1rNCIQn6x0k0jVZsXE0SUq_8BfrC3ufI4-D-KuqpdozdR54E4vUxT6WZpoeahMFlT0w87XPbeIp4RerWw4tuuBMfvyvm9cQCqQkAaOPNT4TEzLQwlQasf1pFE3aCguvdU4i2SuIhGIXaukHoXP5nWLibQpILd71bEnwX7wBCgIrBjUM:7IXCtpXjI0RQ2I478353Ab-7iFXv9VNa9SPR18M8ra1CpZWgPD5hkfl0PRyaHkBdzvpb2VkZ0TFTIigh8ZkFUeJxgu84i_AXIaPEsTa9fevCagbR57caRa-xqLRe4Onl", + "69fc019ddf:0:ebb1b3c67b2c658e60afa1d14fc5586946aeb5f3abb42e3deb684d4eaa4ed04ff758e4af6770184e6aecc52d090a7992279886c88de1ca99ce80bf04d01a48e9:6011b9bed796b82bf2f981a92fdc94b62d6942a25323208ab193227935c429412a239889c8a5c07960706d936d3497db5c5bef80f5ca3f162d96d305374e3dd9:2:AiFYEjbjMSSYf8gXtV-r9YDnl6RVgKmckZi25ss3yLLi5hvdB92I8Vr80QiHcXbGkgO2YBJ1_EUMTQAeiL1VlaY3mSKf0Q892yjeoqe7sOJiWrvx1x2Xl4zD2kHi2pbLy1NA7NUkroJamJ4Z5J__rq8yNv4bsqEr2iaTiADWxKfpLRc29cjA11eiUq_Xjj72gBpr78DmT_-N63OAUhBPSHHrA60NannkvkRKcy7WsCr9pB9L0QSZd3BgoYE78VAfaOx9gJw4vihkel3lFEXEaIyoJhwcFxEBIsnfl0gagQ7NeCqRkEzeiXDVS1pFOY3BCu6zV5Q-Aqtw5yqozSXxa9QN:jmOd2wCLT5z09JXJMXOMPk91I1in7qhq0Rsdsi-xEYenHs3nPX5k___FZ3bpFYqjx914Yy65QrXKkThr3hysgiXztEtILYOr8yXf56O16Io-XB6L6CK0VrKnSb4p40Lf", + "74152a45b9:0:11a049ebf24d6e9911c68e1c849b4290c8b72bff2954699e54138446573e041346fa34e09b697ae3a9d7c6f94aeb1b21e54745fab10f7a56df22947ef20a5c7d:e8d19530a8ce8685ba39ca69eabfb3d0fad5ed63bf59f025a3658f33e6c629737e49e57b3c91026ef8648d6a473c8ee9f7a27a040c822a0fff44adc0079aa969:2:Ak86tzCx95V6u5t4Ty2OrAWN9PSOjIWQkk0g28VBEwo84HFVVLSl2gBKV2HYDpO2TAJlLNFxhDynea4XdHrbA6Cdv1nsNdWsaizHiClZ3qoBEf9riSimVLpwPI5BMC-1vznYivS-_gMKYmSX5D20UDKZwK3bM-p3wLwcwslurLzsv4KMk2DTzkExdWGjHkZhOC6754YLgdjM6UsYgohPHnqZNUCLo288BFxBhYshmOxrAjnFzapp67dzf5SfV9hiwhWhblEEFYa2FC1lm8WQC3dPVJwg6Bmkw8pMBzN_qoQZYAOVanzu2dUisCx0oLIVW4-AzPJOqHH1DkiUl4opkZEO:rNVgOQsylq8Xi-wnW__VvcHjqert2hl8mLhlHBu9IG4SDelD9c24Bs6skvwxV2QRBQwbvmv11f6MNkhA6PK5W22OMOmHMG8GsnXlORRH5HMfBzVBdffK_91lfBkB67pP", + "fe63ec5d9a:0:cb64b8f27bc6ed0c2875e98d988e11db3d1f2016d14787ed51fbc0493b58aa3b8329d49142fc9acce421b7d3a9b9908a5afed3367b589bc1b0c94ae04c8a383d:e81cc452f9f616210295f96d2034ac715c3b87ca95dbc1e77bd3dcc798719558a4ca82befa391aaffd8099787a60086b35cc6a2390c55720716ac05d2a7b116f:2:Aor2te5DUyKXFFrvOyegql5i0XOleTu4d1AfEl-SE0KZ_PiQeVsWiWsy57SXVZtFTQL7YNyV5dd3oRUmPqcFzNlfY7RnJnKwf-OfmFzin4VeCqqw-d4eAfgNaav2Y5qVC_uc-VuPdRONqSqd04n6RG9Gyz9XvMqXH8Dk6L2iVkP3y1TjyzapIbNQC8pRnsRJaPafN2QVG6qhu7clFAoOzsgkM8BKaxrTLvbznrTpjGAZk5ocb_gLZiDDwOzUsMoUI4Zi3Gky4TyHFAWX9ifJgeSh_D9gj_oGCvsQhF8xVkgsXDTkHAN4hmIEJM_myzL-XnpROFGcaN6m8y085HS7UCcB:6HVliM67dmXAgYPs-z75TTz3m6tSVX6-EpEsy6N-TsX2a_5czJImDXQ-Qhi3524tDIIktWJcUv8JGA7kTPHkqr4Jrnt6JJXG4DVf6xw-n-GpRje-EwyW4Q378tomPpw5", + "99ff6d1b10:0:20148e36a3dec6f91537e0bc0e2a852b6cc0fac6c664ef8ce453d7cc404b2b7c5f30bcd244d68ca009f4be0be6ad9a8ea51452d4bb8f7872cb8dacb5d1cdbc25:489fc570d4086ee0bf160d2dc8cc4547b4d3e4336c97b46d45b38064cb19087057adee0ecac1a3fc398627772407f7b814d1292db1ab8eecfdaf6177a7c09870:2:AlLlDYoNRBKH038FnkvZPXfQ1w9T19Df1AW8cav-mrJH524nbtxHI_t-cBorpm8i1wOPnYH96Bkr2LH9UeXaR0xHSNW-dqDTdWbu7uz7sqseULAq7izA8k5VcRQH9YesrcsgASmEOKxJZAc1CkjJpoCj8cwKHZECx5EE3rNFEF4ZxQth5AYg6P8isEpjMw7zBQOGlIRWV5sjVSml_JUKMnB3H9ZOE4UTxTXKVUEUJwHcp_9tXVv_RYuQOarzIRq0-jpod1kamOcLXFsYAiQbg_DwW7F2kQMdqUWJ7tHVwOxfb2CbwD40Yo_-VZ3ZJ8AGm-eY0ngO3PDBbUyKwct_ljwO:29hzAxGQd6oGze7c8XxqueXF_OnZ5WHtpAGQt3RhG2UPEl4uUximYMS25uAqARmZe4VIkwWJ16_IO7nTaizC3feRDWl1BpePoFVuUoFGsK303kUL6IH6xsqdXup2Uahg" ]; #[test] @@ -958,8 +986,9 @@ mod tests { let gen = Identity::generate(); assert!(gen.agree(&gen).is_some()); assert!(gen.validate_identity()); - let bytes = gen.to_bytes(IDENTITY_ALGORITHM_ALL, true); + let bytes = gen.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true); let string = gen.to_string_with_options(IDENTITY_ALGORITHM_ALL, true); + //println!("{}", string); assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen)); let mut cursor = 0_usize; assert!(Identity::unmarshal(&bytes, &mut cursor).unwrap().eq(&gen)); @@ -978,13 +1007,13 @@ mod tests { assert!(id.validate_identity()); assert!(id.p384.is_none()); - let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true); + let idb = id.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true); let mut cursor = 0; let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap(); assert!(id == id_unmarshal); assert!(id_unmarshal.secret.is_some()); - let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, false); + let idb2 = id_unmarshal.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, false); cursor = 0; let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap(); assert!(id_unmarshal2 == id_unmarshal); @@ -1004,18 +1033,18 @@ mod tests { } for id_str in GOOD_V1_IDENTITIES { let id = Identity::from_str(id_str).unwrap(); - assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, false).as_str(), id_str); + assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, true).as_str(), id_str); assert!(id.validate_identity()); assert!(id.p384.is_some()); - let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true); + let idb = id.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, true); let mut cursor = 0; let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).unwrap(); assert!(id == id_unmarshal); cursor = 0; - let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, false); + let idb2 = id_unmarshal.to_buffer_with_options(IDENTITY_ALGORITHM_ALL, false); let id_unmarshal2 = Identity::unmarshal(&idb2, &mut cursor).unwrap(); assert!(id_unmarshal2 == id_unmarshal); assert!(id_unmarshal2 == id); diff --git a/zerotier-network-hypervisor/src/vl1/inetaddress.rs b/zerotier-network-hypervisor/src/vl1/inetaddress.rs index c22a2bc01..9f57494e2 100644 --- a/zerotier-network-hypervisor/src/vl1/inetaddress.rs +++ b/zerotier-network-hypervisor/src/vl1/inetaddress.rs @@ -15,6 +15,8 @@ use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::util::marshalable::Marshalable; + #[cfg(windows)] use winapi::um::winsock2; @@ -647,8 +649,12 @@ impl InetAddress { } } } +} - pub fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { +impl Marshalable for InetAddress { + const MAX_MARSHAL_SIZE: usize = 19; + + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { unsafe { match self.sa.sa_family as u8 { AF_INET => { @@ -672,7 +678,7 @@ impl InetAddress { } } - pub fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { let t = buf.read_u8(cursor)?; if t == 4 { let b: &[u8; 6] = buf.read_bytes_fixed(cursor)?; diff --git a/zerotier-network-hypervisor/src/vl1/mac.rs b/zerotier-network-hypervisor/src/vl1/mac.rs index b17317eda..d9777432b 100644 --- a/zerotier-network-hypervisor/src/vl1/mac.rs +++ b/zerotier-network-hypervisor/src/vl1/mac.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::error::InvalidFormatError; use crate::util::buffer::Buffer; +use crate::util::marshalable::Marshalable; /// An Ethernet MAC address. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -57,15 +58,19 @@ impl MAC { pub fn to_u64(&self) -> u64 { self.0.get() } +} + +impl Marshalable for MAC { + const MAX_MARSHAL_SIZE: usize = 6; #[inline(always)] - pub(crate) fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { buf.append_bytes(&self.0.get().to_be_bytes()[2..]) } #[inline(always)] - pub(crate) fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result> { - buf.read_bytes_fixed::<6>(cursor).map(|b| Self::from_bytes_fixed(b)) + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + Self::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).map_or_else(|| Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "cannot be zero")), |a| Ok(a)) } } diff --git a/zerotier-network-hypervisor/src/vl1/mod.rs b/zerotier-network-hypervisor/src/vl1/mod.rs index e4dde6734..a8fe50293 100644 --- a/zerotier-network-hypervisor/src/vl1/mod.rs +++ b/zerotier-network-hypervisor/src/vl1/mod.rs @@ -15,6 +15,7 @@ mod dictionary; mod mac; mod path; mod peer; +mod rootcluster; pub(crate) mod fragmentedpacket; pub(crate) mod hybridkey; @@ -33,3 +34,4 @@ pub use mac::MAC; pub use node::{Node, SystemInterface}; pub use path::Path; pub use peer::Peer; +pub use rootcluster::{Root, RootCluster}; diff --git a/zerotier-network-hypervisor/src/vl1/node.rs b/zerotier-network-hypervisor/src/vl1/node.rs index 0054faf61..516a6dff9 100644 --- a/zerotier-network-hypervisor/src/vl1/node.rs +++ b/zerotier-network-hypervisor/src/vl1/node.rs @@ -104,11 +104,20 @@ pub trait InnerProtocolInterface: Sync + Send { fn has_trust_relationship(&self, id: &Identity) -> bool; } +/// Trait for objects that are serviced in the background loop (the actual loop is external). +pub(crate) trait BackgroundServicable { + /// How often in milliseconds to call service(). + const SERVICE_INTERVAL_MS: i64; + + /// Service object and return true if the object should be retained (if applicable). + fn service(&self, si: &SI, node: &Node, time_ticks: i64) -> bool; +} + #[derive(Default)] struct BackgroundTaskIntervals { - whois: IntervalGate<{ WhoisQueue::INTERVAL }>, - paths: IntervalGate<{ Path::CALL_EVERY_INTERVAL_MS }>, - peers: IntervalGate<{ Peer::CALL_EVERY_INTERVAL_MS }>, + whois: IntervalGate<{ WhoisQueue::SERVICE_INTERVAL_MS }>, + paths: IntervalGate<{ Path::SERVICE_INTERVAL_MS }>, + peers: IntervalGate<{ Peer::SERVICE_INTERVAL_MS }>, } pub struct Node { @@ -152,7 +161,7 @@ impl Node { } } else { let id_str = String::from_utf8_lossy(id_str.as_ref().unwrap().as_slice()); - let id = Identity::from_str(id_str.as_ref()); + let id = Identity::from_str(id_str.as_ref().trim()); if id.is_err() { return Err(InvalidParameterError("invalid identity")); } else { @@ -170,7 +179,7 @@ impl Node { instance_id: zerotier_core_crypto::random::next_u64_secure(), identity: id, intervals: Mutex::new(BackgroundTaskIntervals::default()), - paths: DashMap::with_capacity(128), + paths: DashMap::with_capacity(256), peers: DashMap::with_capacity(128), roots: Mutex::new(Vec::new()), whois: WhoisQueue::new(), @@ -208,27 +217,18 @@ impl Node { let tt = si.time_ticks(); if intervals.peers.gate(tt) { - self.peers.retain(|_, peer| { - peer.call_every_interval(si, tt); - todo!(); - true - }); + self.peers.retain(|_, peer| peer.service(si, self, tt)); } if intervals.paths.gate(tt) { - self.paths.retain(|_, path| { - path.upgrade().map_or(false, |p| { - p.call_every_interval(si, tt); - true - }) - }); + self.paths.retain(|_, path| path.upgrade().map_or(false, |p| p.service(si, self, tt))); } if intervals.whois.gate(tt) { - self.whois.call_every_interval(self, si, tt); + let _ = self.whois.service(si, self, tt); } - Duration::from_millis(WhoisQueue::INTERVAL.min(Path::CALL_EVERY_INTERVAL_MS).min(Peer::CALL_EVERY_INTERVAL_MS) as u64 / 4) + Duration::from_millis((WhoisQueue::SERVICE_INTERVAL_MS.min(Path::SERVICE_INTERVAL_MS).min(Peer::SERVICE_INTERVAL_MS) as u64) / 2) } /// Called when a packet is received on the physical wire. @@ -237,14 +237,16 @@ impl Node { if let Some(dest) = Address::from_bytes(&fragment_header.dest) { let time_ticks = si.time_ticks(); if dest == self.identity.address { - // Handle packets addressed to this node. + // Handle packets (seemingly) addressed to this node. - let path = self.path(source_endpoint, source_local_socket, source_local_interface); + let path = self.path_to_endpoint(source_endpoint, source_local_socket, source_local_interface); path.log_receive_anything(time_ticks); if fragment_header.is_fragment() { if let Some(assembled_packet) = path.receive_fragment(u64::from_ne_bytes(fragment_header.id), fragment_header.fragment_no(), fragment_header.total_fragments(), data, time_ticks) { if let Some(frag0) = assembled_packet.frags[0].as_ref() { + // Fragmented packet is fully assembled. + let packet_header = frag0.struct_at::(0); if packet_header.is_ok() { let packet_header = packet_header.unwrap(); @@ -260,6 +262,8 @@ impl Node { } } else { if let Ok(packet_header) = data.struct_at::(0) { + // Packet is not fragmented. + if let Some(source) = Address::from_bytes(&packet_header.src) { if let Some(peer) = self.peer(source) { peer.receive(self, si, ph, time_ticks, source_endpoint, &path, &packet_header, data.as_ref(), &[]); @@ -271,7 +275,6 @@ impl Node { } } else { // Forward packets not destined for this node. - // TODO: need to add check for whether this node should forward. Regular nodes should only forward if a trust relationship exists. if fragment_header.is_fragment() { if fragment_header.increment_hops() > FORWARD_MAX_HOPS { @@ -286,6 +289,7 @@ impl Node { return; } } + if let Some(peer) = self.peer(dest) { peer.forward(si, time_ticks, data.as_ref()); } @@ -308,7 +312,7 @@ impl Node { /// /// This is a canonicalizing function that returns a unique path object for every tuple /// of endpoint, local socket, and local interface. - pub fn path(&self, ep: &Endpoint, local_socket: Option, local_interface: Option) -> Arc { + pub fn path_to_endpoint(&self, ep: &Endpoint, local_socket: Option, local_interface: Option) -> Arc { let key = Path::local_lookup_key(ep, local_socket, local_interface); let mut path_entry = self.paths.entry(key).or_insert_with(|| Weak::new()); if let Some(path) = path_entry.value().upgrade() { diff --git a/zerotier-network-hypervisor/src/vl1/path.rs b/zerotier-network-hypervisor/src/vl1/path.rs index 0e1290bd4..d74e0d03a 100644 --- a/zerotier-network-hypervisor/src/vl1/path.rs +++ b/zerotier-network-hypervisor/src/vl1/path.rs @@ -18,14 +18,11 @@ use zerotier_core_crypto::hash::SHA512_HASH_SIZE; use crate::util::*; use crate::vl1::fragmentedpacket::FragmentedPacket; -use crate::vl1::node::SystemInterface; +use crate::vl1::node::*; use crate::vl1::protocol::*; use crate::vl1::Endpoint; use crate::PacketBuffer; -/// Keepalive interval for paths in milliseconds. -pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000; - // A bunch of random values used to randomize the local_lookup_key() function's mappings of addresses to 128-bit internal keys. lazy_static! { static ref RANDOM_64BIT_SALT_0: u64 = zerotier_core_crypto::random::next_u64_secure(); @@ -33,6 +30,9 @@ lazy_static! { static ref RANDOM_64BIT_SALT_2: u64 = zerotier_core_crypto::random::next_u64_secure(); static ref RANDOM_128BIT_SALT_0: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128); static ref RANDOM_128BIT_SALT_1: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128); + static ref RANDOM_128BIT_SALT_2: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128); + static ref RANDOM_128BIT_SALT_3: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128); + static ref RANDOM_128BIT_SALT_4: u128 = (zerotier_core_crypto::random::next_u64_secure().wrapping_shl(64) as u128) ^ (zerotier_core_crypto::random::next_u64_secure() as u128); } /// A remote endpoint paired with a local socket and a local interface. @@ -58,9 +58,9 @@ impl Path { match endpoint { Endpoint::Nil => 0, Endpoint::ZeroTier(_, h) => u128::from_ne_bytes(*byte_array_range::(h)), - Endpoint::Ethernet(m) => (m.to_u64() | 0x0100000000000000) as u128 ^ lsi, - Endpoint::WifiDirect(m) => (m.to_u64() | 0x0200000000000000) as u128 ^ lsi, - Endpoint::Bluetooth(m) => (m.to_u64() | 0x0400000000000000) as u128 ^ lsi, + Endpoint::Ethernet(m) => RANDOM_128BIT_SALT_0.wrapping_add(lsi as u128).wrapping_add(m.to_u64() as u128), + Endpoint::WifiDirect(m) => RANDOM_128BIT_SALT_1.wrapping_add(lsi as u128).wrapping_add(m.to_u64() as u128), + Endpoint::Bluetooth(m) => RANDOM_128BIT_SALT_2.wrapping_add(lsi as u128).wrapping_add(m.to_u64() as u128), Endpoint::Ip(ip) => ip.ip_as_native_u128().wrapping_sub(lsi), // naked IP has no port Endpoint::IpUdp(ip) => ip.ip_as_native_u128().wrapping_add(lsi), // UDP maintains one path per IP but merely learns the most recent port Endpoint::IpTcp(ip) => ip.ip_as_native_u128().wrapping_sub(crate::util::hash64_noncrypt((ip.port() as u64).wrapping_add(*RANDOM_64BIT_SALT_2)) as u128).wrapping_sub(lsi), @@ -69,14 +69,14 @@ impl Path { hh.write_u64(local_socket); hh.write_u64(local_interface); hh.write(s.as_bytes()); - RANDOM_128BIT_SALT_0.wrapping_add(hh.finish() as u128) + RANDOM_128BIT_SALT_3.wrapping_add(hh.finish() as u128) } Endpoint::WebRTC(b) => { let mut hh = std::collections::hash_map::DefaultHasher::new(); hh.write_u64(local_socket); hh.write_u64(local_interface); hh.write(b.as_slice()); - RANDOM_128BIT_SALT_1.wrapping_add(hh.finish() as u128) + RANDOM_128BIT_SALT_4.wrapping_add(hh.finish() as u128) } Endpoint::ZeroTierEncap(_, h) => u128::from_ne_bytes(*byte_array_range::(h)), } @@ -159,9 +159,14 @@ impl Path { pub(crate) fn log_send_anything(&self, time_ticks: i64) { self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed); } +} - pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL; - pub(crate) fn call_every_interval(&self, _si: &SI, time_ticks: i64) { +impl BackgroundServicable for Path { + const SERVICE_INTERVAL_MS: i64 = PATH_KEEPALIVE_INTERVAL; + + fn service(&self, si: &SI, node: &Node, time_ticks: i64) -> bool { self.fragmented_packets.lock().retain(|_, frag| (time_ticks - frag.ts_ticks) < PACKET_FRAGMENT_EXPIRATION); + // TODO: keepalives + true } } diff --git a/zerotier-network-hypervisor/src/vl1/peer.rs b/zerotier-network-hypervisor/src/vl1/peer.rs index 303ae04fa..cb928b04d 100644 --- a/zerotier-network-hypervisor/src/vl1/peer.rs +++ b/zerotier-network-hypervisor/src/vl1/peer.rs @@ -467,7 +467,7 @@ impl Peer { } // Add this node's identity. - assert!(self.identity.marshal(&mut packet, IDENTITY_ALGORITHM_ALL, false).is_ok()); + assert!(self.identity.marshal_with_options(&mut packet, IDENTITY_ALGORITHM_ALL, false).is_ok()); if self.identity.algorithms() == IDENTITY_ALGORITHM_X25519 { // LEGACY: append an extra zero when marshaling identities containing only x25519 keys. // See comments in Identity::marshal(). @@ -538,17 +538,11 @@ impl Peer { ) } - pub(crate) const CALL_EVERY_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10; - - /// Called every INTERVAL during background tasks. #[inline(always)] - pub(crate) fn call_every_interval(&self, si: &SI, time_ticks: i64) {} + fn receive_hello(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer) {} #[inline(always)] - fn receive_hello(&self, si: &SI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} - - #[inline(always)] - fn receive_error(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { + fn receive_error(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer) { let mut cursor: usize = 0; let _ = payload.read_struct::(&mut cursor).map(|error_header| { let in_re_message_id = u64::from_ne_bytes(error_header.in_re_message_id); @@ -564,7 +558,7 @@ impl Peer { } #[inline(always)] - fn receive_ok(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { + fn receive_ok(&self, si: &SI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer) { let mut cursor: usize = 0; let _ = payload.read_struct::(&mut cursor).map(|ok_header| { let in_re_message_id = u64::from_ne_bytes(ok_header.in_re_message_id); @@ -628,3 +622,12 @@ impl Peer { } } } + +impl BackgroundServicable for Peer { + const SERVICE_INTERVAL_MS: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME / 10; + + #[inline(always)] + fn service(&self, si: &SI, node: &Node, time_ticks: i64) -> bool { + true + } +} diff --git a/zerotier-network-hypervisor/src/vl1/protocol.rs b/zerotier-network-hypervisor/src/vl1/protocol.rs index 169e1664e..168125bbd 100644 --- a/zerotier-network-hypervisor/src/vl1/protocol.rs +++ b/zerotier-network-hypervisor/src/vl1/protocol.rs @@ -180,6 +180,9 @@ pub const WHOIS_RETRY_MAX: u16 = 3; /// Maximum number of packets to queue up behind a WHOIS. pub const WHOIS_MAX_WAITING_PACKETS: usize = 64; +/// Keepalive interval for paths in milliseconds. +pub const PATH_KEEPALIVE_INTERVAL: i64 = 20000; + /// Proof of work difficulty (threshold) for identity generation. pub const IDENTITY_POW_THRESHOLD: u8 = 17; diff --git a/zerotier-network-hypervisor/src/vl1/rootcluster.rs b/zerotier-network-hypervisor/src/vl1/rootcluster.rs new file mode 100644 index 000000000..b51fb3e40 --- /dev/null +++ b/zerotier-network-hypervisor/src/vl1/rootcluster.rs @@ -0,0 +1,267 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * (c)2021 ZeroTier, Inc. + * https://www.zerotier.com/ + */ + +use std::collections::BTreeSet; +use std::io::Write; + +use crate::util::buffer::Buffer; +use crate::util::marshalable::Marshalable; +use crate::vl1::identity::*; +use crate::vl1::protocol::PACKET_SIZE_MAX; +use crate::vl1::Endpoint; + +/// Description of a member of a root cluster. +#[derive(Clone, PartialEq, Eq)] +pub struct Root { + /// Full identity of this node. + pub identity: Identity, + + /// Addresses of this root or None if this is a former member attesting to an update that removes it. + pub addresses: Option>, + + /// Signature of entire cluster by this identity. + pub cluster_signature: Vec, + + /// Flags field (currently unused). + pub flags: u64, +} + +impl PartialOrd for Root { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + self.identity.partial_cmp(&other.identity) + } +} + +impl Ord for Root { + #[inline(always)] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.identity.cmp(&other.identity) + } +} + +/// Signed description of a cluster of root nodes. +/// +/// Root cluster definitions must be signed by all current participating nodes' identities. In addition +/// there is an update authorization model based on authorization by at least N-1 previous members. +/// See the documentation of should_replace(). +/// +/// To build a cluster definition first use new(), then use add() to add all members, then have each member +/// use sign() to sign its entry. All members must sign after all calls to add() have been made since everyone +/// must sign the same definition. +#[derive(Clone, PartialEq, Eq)] +pub struct RootCluster { + /// An arbitrary name, which could be something like a domain. + pub name: String, + + /// A monotonically increasing revision number (doesn't have to be sequential). + pub revision: u64, + + /// A set of Root nodes that are current or immediately former members of this cluster. + /// This will always be sorted by member identity. + pub members: Vec, +} + +impl RootCluster { + pub fn new(name: String, revision: u64) -> Self { + Self { name, revision, members: Vec::new() } + } + + fn marshal_internal(&self, buf: &mut Buffer, include_signatures: bool) -> std::io::Result<()> { + buf.append_u8(0)?; // version byte for future use + buf.append_varint(self.name.as_bytes().len() as u64)?; + buf.append_bytes(self.name.as_bytes())?; + buf.append_varint(self.revision)?; + buf.append_varint(self.members.len() as u64)?; + for m in self.members.iter() { + m.identity.marshal_with_options(buf, IDENTITY_ALGORITHM_ALL, false)?; + if m.addresses.is_some() { + let addresses = m.addresses.as_ref().unwrap(); + buf.append_varint(addresses.len() as u64)?; + for a in addresses.iter() { + a.marshal(buf)?; + } + } else { + buf.append_varint(0)?; + } + if include_signatures { + buf.append_varint(m.cluster_signature.len() as u64)?; + buf.append_bytes(m.cluster_signature.as_slice())?; + } + buf.append_varint(m.flags)?; + buf.append_varint(0)?; // size of additional fields for future use + } + buf.append_varint(0)?; // size of additional fields for future use + Ok(()) + } + + /// Internal method to marshal without signatures for use during sign and verify. + fn marshal_for_signing(&self) -> Buffer { + let mut tmp = Buffer::::new(); + assert!(self.marshal_internal(&mut tmp, false).is_ok()); + tmp + } + + /// Verify signatures present in this root cluster definition. + pub fn verify(&self) -> bool { + if self.members.is_empty() { + return false; + } + + let tmp = self.marshal_for_signing(); + for m in self.members.iter() { + if m.cluster_signature.is_empty() || !m.identity.verify(tmp.as_bytes(), m.cluster_signature.as_slice()) { + return false; + } + } + + return true; + } + + /// Add a member to this definition, replacing any current entry for this identity. + pub fn add<'a, I: Iterator>(&mut self, member_identity: &Identity, addresses: Option) { + self.members.retain(|m| !m.identity.eq(member_identity)); + let _ = self.members.push(Root { + identity: member_identity.clone_without_secret(), + addresses: addresses.map(|addresses| { + let mut tmp = BTreeSet::new(); + for a in addresses { + tmp.insert(a.clone()); + } + tmp + }), + cluster_signature: Vec::new(), + flags: 0, + }); + self.members.sort(); + } + + /// Sign this definition, returning true on success. + /// + /// A return value of false indicates that this member wasn't in the definition or this identity + /// did not have its secret to sign. + /// + /// All current members must sign whether they are disabled (witnessing) or active. The verify() + /// method will return true when signing is complete. + pub fn sign(&mut self, member_identity: &Identity) -> bool { + let signature = member_identity.sign(self.marshal_for_signing().as_bytes(), IDENTITY_ALGORITHM_ALL, false); + let unsigned_entry = self.members.iter().find_map(|m| if m.identity.eq(member_identity) { Some(m.clone()) } else { None }); + if unsigned_entry.is_some() && signature.is_some() { + let unsigned_entry = unsigned_entry.unwrap(); + self.members.retain(|m| !m.identity.eq(member_identity)); + let _ = self.members.push(Root { + identity: unsigned_entry.identity, + addresses: unsigned_entry.addresses, + cluster_signature: signature.unwrap(), + flags: unsigned_entry.flags, + }); + self.members.sort(); + return true; + } + return false; + } + + /// Check whether this root cluster definition should replace a previous one. + /// + /// A root cluster definition replaces an older version of (1) the name is equal, (2) + /// the revision is higher, and (3) the new definition was signed by at least N-1 active + /// (not disabled) members of the previous definition. So for example a cluster of four + /// root nodes can replace one member if three cluster members sign the update, but to + /// remove two at a time one of the exiting members would have to sign. This is done by + /// adding it with None as its address list, making it disabled. Disabled members function + /// only as signers (witnesses). + /// + /// There is one edge case though. If a cluster definition has only one member, that one + /// member must sign the next update. N-1 is not permitted to be less than one. If that was + /// not the case it would be possible for anyone to update a one-member definition! + /// + /// This DOES call verify() on itself prior to checking to avoid the disastrous error + /// of forgetting to verify signatures on a new definition. + /// + /// Be sure the semantics are right and this method is being called with 'self' being the + /// new root cluster definition and 'previous' being the current/old one. + pub fn should_replace(&self, previous: &Self) -> bool { + if self.name.eq(&previous.name) && self.revision > previous.revision && self.verify() { + let mut my_signers = BTreeSet::new(); + for m in self.members.iter() { + my_signers.insert(m.identity.fingerprint.clone()); + } + + let mut previous_count: isize = 0; + let mut witness_count: isize = 0; + for m in previous.members.iter() { + if m.addresses.is_some() { + previous_count += 1; + witness_count += my_signers.contains(&m.identity.fingerprint) as isize; + } + } + + witness_count >= (previous_count - 1).max(1) + } else { + false + } + } +} + +impl Marshalable for RootCluster { + const MAX_MARSHAL_SIZE: usize = PACKET_SIZE_MAX; + + #[inline(always)] + fn marshal(&self, buf: &mut Buffer) -> std::io::Result<()> { + self.marshal_internal(buf, true) + } + + fn unmarshal(buf: &Buffer, cursor: &mut usize) -> std::io::Result { + let mut rc = Self::new(String::new(), 0); + if buf.read_u8(cursor)? != 0 { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unsupported version")); + } + + let name_len = buf.read_varint(cursor)?; + rc.name = String::from_utf8(buf.read_bytes(name_len as usize, cursor)?.to_vec()).map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid UTF8"))?; + + rc.revision = buf.read_varint(cursor)?; + + let member_count = buf.read_varint(cursor)?; + for _ in 0..member_count { + let mut m = Root { + identity: Identity::unmarshal(buf, cursor)?, + addresses: None, + cluster_signature: Vec::new(), + flags: 0, + }; + + let address_count = buf.read_varint(cursor)?; + if address_count > 0 { + let mut addresses = BTreeSet::new(); + for _ in 0..address_count { + addresses.insert(Endpoint::unmarshal(buf, cursor)?); + } + let _ = m.addresses.insert(addresses); + } + + let signature_size = buf.read_varint(cursor)?; + let _ = m.cluster_signature.write_all(buf.read_bytes(signature_size as usize, cursor)?); + + m.flags = buf.read_varint(cursor)?; + + *cursor += buf.read_varint(cursor)? as usize; + + rc.members.push(m); + } + + *cursor += buf.read_varint(cursor)? as usize; + if *cursor > buf.len() { + return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid length")); + } + + rc.members.sort(); + + return Ok(rc); + } +} diff --git a/zerotier-network-hypervisor/src/vl1/whoisqueue.rs b/zerotier-network-hypervisor/src/vl1/whoisqueue.rs index 0dc25b68d..f4768231c 100644 --- a/zerotier-network-hypervisor/src/vl1/whoisqueue.rs +++ b/zerotier-network-hypervisor/src/vl1/whoisqueue.rs @@ -12,7 +12,7 @@ use parking_lot::Mutex; use crate::util::gate::IntervalGate; use crate::vl1::fragmentedpacket::FragmentedPacket; -use crate::vl1::node::{Node, SystemInterface}; +use crate::vl1::node::{BackgroundServicable, Node, SystemInterface}; use crate::vl1::protocol::{WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_INTERVAL, WHOIS_RETRY_MAX}; use crate::vl1::Address; use crate::PacketBuffer; @@ -31,8 +31,6 @@ struct WhoisQueueItem { pub(crate) struct WhoisQueue(Mutex>); impl WhoisQueue { - pub(crate) const INTERVAL: i64 = WHOIS_RETRY_INTERVAL; - pub fn new() -> Self { Self(Mutex::new(HashMap::new())) } @@ -65,8 +63,15 @@ impl WhoisQueue { let _ = qi.map(|mut qi| qi.packet_queue.iter_mut().for_each(packet_handler)); } - /// Called every INTERVAL during background tasks. - pub fn call_every_interval(&self, node: &Node, si: &SI, time_ticks: i64) { + fn send_whois(&self, node: &Node, si: &SI, targets: &[Address]) { + todo!() + } +} + +impl BackgroundServicable for WhoisQueue { + const SERVICE_INTERVAL_MS: i64 = WHOIS_RETRY_INTERVAL; + + fn service(&self, si: &SI, node: &Node, time_ticks: i64) -> bool { let mut targets: Vec
= Vec::new(); self.0.lock().retain(|target, qi| { if qi.retry_count < WHOIS_RETRY_MAX { @@ -82,9 +87,6 @@ impl WhoisQueue { if !targets.is_empty() { self.send_whois(node, si, targets.as_slice()); } - } - - fn send_whois(&self, node: &Node, si: &SI, targets: &[Address]) { - todo!() + true } } diff --git a/zerotier-system-service/Cargo.lock b/zerotier-system-service/Cargo.lock index 197dd4514..f1599c87a 100644 --- a/zerotier-system-service/Cargo.lock +++ b/zerotier-system-service/Cargo.lock @@ -147,12 +147,13 @@ dependencies = [ [[package]] name = "dashmap" -version = "4.0.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "391b56fbd302e585b7a9494fb70e40949567b1cf9003a8e4a6041a1687c26573" dependencies = [ "cfg-if", - "num_cpus", + "hashbrown", + "lock_api", ] [[package]] @@ -242,6 +243,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" + [[package]] name = "heapless" version = "0.7.10"