A whole heap of changes including: addresses are now 48 bits, base64 is gone, and rewrote Identity for simplification.

This commit is contained in:
Adam Ierymenko 2023-03-17 22:13:32 -04:00
parent c4cb7c4429
commit 3f047bc8dc
34 changed files with 1090 additions and 1790 deletions

View file

@ -496,7 +496,7 @@ impl InnerProtocolLayer for Controller {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
verb: u8,

View file

@ -1,4 +1,5 @@
mod bn;
#[allow(unused)]
mod cipher_ctx;
mod ec;
mod error;

View file

@ -16,8 +16,8 @@ lz4_flex = { version = "^0", features = ["safe-encode", "safe-decode", "checked-
serde = { version = "^1", features = ["derive"], default-features = false }
phf = { version = "^0", features = ["macros", "std"], default-features = false }
num-traits = "^0"
rmp-serde = "^1"
fastcdc = "^3"
serde_cbor = "^0"
[dev-dependencies]
rand = "*"

View file

@ -1,11 +1,11 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::time::Duration;
use zerotier_network_hypervisor::vl1::Identity;
use zerotier_network_hypervisor::vl1::identity::Identity;
pub fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("basic");
group.measurement_time(Duration::new(30, 0));
group.bench_function("identity generation", |b| b.iter(|| Identity::generate()));
group.bench_function("identity generation", |b| b.iter(|| Identity::generate(false)));
group.finish();
}

View file

@ -138,9 +138,6 @@ pub const ADDRESS_SIZE: usize = 5;
/// Length of an address in string format.
pub const ADDRESS_SIZE_STRING: usize = 10;
/// Prefix indicating reserved addresses (that can't actually be addresses).
pub const ADDRESS_RESERVED_PREFIX: u8 = 0xff;
/// Bit mask for address bits in a u64.
pub const ADDRESS_MASK: u64 = 0xffffffffff;
@ -307,10 +304,10 @@ pub mod v1 {
}
#[inline(always)]
pub fn get_packet_aad_bytes(destination: Address, source: Address, flags_cipher_hops: u8) -> [u8; 11] {
pub fn get_packet_aad_bytes(destination: &Address, source: &Address, flags_cipher_hops: u8) -> [u8; 11] {
let mut id = [0u8; 11];
id[0..5].copy_from_slice(&destination.to_bytes());
id[5..10].copy_from_slice(&source.to_bytes());
id[0..5].copy_from_slice(destination.as_bytes_v1());
id[5..10].copy_from_slice(source.as_bytes_v1());
id[10] = flags_cipher_hops & FLAGS_FIELD_MASK_HIDE_HOPS;
id
}
@ -559,9 +556,6 @@ pub(crate) const PEER_HELLO_INTERVAL_MAX: i64 = 300000;
/// Timeout for path association with peers and for peers themselves.
pub(crate) const PEER_EXPIRATION_TIME: i64 = (PEER_HELLO_INTERVAL_MAX * 2) + 10000;
/// Proof of work difficulty (threshold) for identity generation.
pub(crate) const IDENTITY_POW_THRESHOLD: u8 = 17;
// Multicast LIKE expire time in milliseconds.
pub const VL2_DEFAULT_MULTICAST_LIKE_EXPIRE: i64 = 600000;

View file

@ -1,79 +1,145 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::num::NonZeroU64;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
use zerotier_utils::memory;
/// A unique address on the global ZeroTier VL1 network.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
const BASE62_ALPHABET: &'static [u8; 62] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE62_ALPHABET_REVERSE: [u8; 256] = [0; 256];
#[derive(Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Address(NonZeroU64);
pub struct Address([u128; 3]);
impl Address {
/// Get an address from a 64-bit integer or return None if it is zero or reserved.
#[inline(always)]
pub fn from_u64(mut i: u64) -> Option<Address> {
i &= 0xffffffffff;
NonZeroU64::new(i).and_then(|ii| {
if (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 {
Some(Address(ii))
} else {
None
}
})
}
pub const V1_ADDRESS_SIZE: usize = 5;
pub const V1_ADDRESS_STRING_SIZE: usize = 10;
pub const V2_ADDRESS_SIZE: usize = 48;
pub const V2_ADDRESS_STRING_SIZE: usize = 65;
pub const RESERVED_PREFIX: u8 = 0xff;
#[inline(always)]
pub fn from_bytes(b: &[u8]) -> Option<Address> {
if b.len() >= ADDRESS_SIZE {
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
pub(crate) fn new_uninitialized() -> Self {
Self([0, 0, 0])
}
pub fn from_bytes(b: &[u8]) -> Result<Option<Self>, InvalidFormatError> {
if b.len() == Self::V1_ADDRESS_SIZE {
Ok(Self::from_bytes_v1(b))
} else if b.len() == Self::V2_ADDRESS_SIZE {
Ok(Self::from_bytes_v2(b))
} else {
Err(InvalidFormatError)
}
}
pub(crate) fn from_bytes_v1(b: &[u8]) -> Option<Self> {
let mut a = Self([0; 3]);
memory::as_byte_array_mut::<[u128; 3], 48>(&mut a.0)[..Self::V1_ADDRESS_SIZE].copy_from_slice(b);
if a.0[0] != 0 {
Some(a)
} else {
None
}
}
pub(crate) fn from_bytes_v2(b: &[u8]) -> Option<Self> {
let a = Self(memory::load_raw(b));
if a.0.iter().any(|i| *i != 0) {
Some(a)
} else {
None
}
}
#[inline(always)]
pub fn from_bytes_fixed(b: &[u8; ADDRESS_SIZE]) -> Option<Address> {
Self::from_u64((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
pub(crate) fn from_bytes_raw(b: &[u8; 48]) -> Option<Self> {
Self::from_bytes_v2(b)
}
#[inline(always)]
pub fn to_bytes(&self) -> [u8; ADDRESS_SIZE] {
let i = self.0.get();
[(i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
pub(crate) fn from_u64_v1(i: u64) -> Option<Self> {
if i != 0 {
Some(Self([i.wrapping_shl(24 + 64).to_be() as u128, 0, 0]))
} else {
None
}
}
}
impl From<Address> for u64 {
/// True if this address lacks extended hash information.
#[inline(always)]
fn from(a: Address) -> Self {
a.0.get()
pub fn is_v1_only(&self) -> bool {
self.0[1] == 0 && self.0[2] == 0
}
}
impl From<&Address> for u64 {
#[inline(always)]
fn from(a: &Address) -> Self {
a.0.get()
pub(crate) fn as_bytes_raw(&self) -> &[u8; 48] {
memory::as_byte_array::<[u128; 3], 48>(&self.0)
}
#[inline(always)]
pub(crate) fn as_bytes_raw_mut(&mut self) -> &mut [u8; 48] {
memory::as_byte_array_mut::<[u128; 3], 48>(&mut self.0)
}
#[inline(always)]
pub(crate) fn as_u64_v1(&self) -> u64 {
u128::from_be(self.0[0]).wrapping_shr(24 + 64) as u64
}
#[inline(always)]
pub(crate) fn as_bytes_v1(&self) -> &[u8; Self::V1_ADDRESS_SIZE] {
memory::array_range::<u8, 48, 0, 5>(memory::as_byte_array::<[u128; 3], 48>(&self.0))
}
/// Get all bits in this address (last 344 will be zero if this is only a V1 address).
#[inline(always)]
pub fn as_bytes_full(&self) -> &[u8; Self::V2_ADDRESS_SIZE] {
memory::as_byte_array::<[u128; 3], 48>(&self.0)
}
/// Get a byte serialized address.
/// This returns either a 40-bit short address or a full 384 bits depending on whether this
/// contains only a short V1 address or a full length V2 address. Use as_bytes_full() to
/// always get all 384 bits with the last 344 being zero for V1-only addresses.
pub fn as_bytes(&self) -> &[u8] {
if self.is_v1_only() {
&memory::as_byte_array::<[u128; 3], 48>(&self.0)[..Self::V1_ADDRESS_SIZE]
} else {
memory::as_byte_array::<[u128; 3], 48>(&self.0)
}
}
/// Get the short 10-digit address string for this address.
pub fn to_short_string(&self) -> String {
hex::to_string(&memory::as_byte_array::<[u128; 3], 48>(&self.0)[..Self::V1_ADDRESS_SIZE])
}
}
impl ToString for Address {
fn to_string(&self) -> String {
let mut v = self.0.get() << 24;
let mut s = String::with_capacity(ADDRESS_SIZE * 2);
for _ in 0..(ADDRESS_SIZE * 2) {
s.push(hex::HEX_CHARS[(v >> 60) as usize] as char);
v <<= 4;
let mut s = String::with_capacity(Self::V2_ADDRESS_STRING_SIZE);
let mut remainders = 0u16;
for qq in self.0.iter() {
let mut q = u128::from_be(*qq);
for _ in 0..21 {
let (x, y) = (q % 62, q / 62);
q = y;
s.push(BASE62_ALPHABET[x as usize] as char);
}
debug_assert!(q <= 7);
remainders = remainders.wrapping_shl(3);
remainders |= q as u16;
}
debug_assert!(remainders <= 511);
s.push(BASE62_ALPHABET[(remainders % 62) as usize] as char);
s.push(BASE62_ALPHABET[(remainders / 62) as usize] as char);
s
}
}
@ -82,19 +148,63 @@ impl FromStr for Address {
type Err = InvalidFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Address::from_bytes(hex::from_string(s).as_slice()).map_or_else(|| Err(InvalidFormatError), |a| Ok(a))
if s.len() == Self::V1_ADDRESS_STRING_SIZE {
return Ok(Self::from_bytes_v1(hex::from_string(s).as_slice()).ok_or(InvalidFormatError)?);
} else if s.len() == Self::V2_ADDRESS_STRING_SIZE {
let mut s = s.as_bytes();
let mut a = Self([0, 0, 0]);
for qi in 0..3 {
let mut q = 0u128;
for _ in 0..21 {
let r = BASE62_ALPHABET_REVERSE[s[0] as usize];
if r == 255 {
return Err(InvalidFormatError);
}
q += r as u128;
s = &s[1..];
q *= 62;
}
a.0[qi] = q;
}
let mut remainders = 0u16;
for _ in 0..2 {
let r = BASE62_ALPHABET_REVERSE[s[0] as usize];
if r == 255 {
return Err(InvalidFormatError);
}
remainders += r as u16;
s = &s[1..];
remainders *= 62;
}
if remainders > 511 {
return Err(InvalidFormatError);
}
a.0[0] += (remainders.wrapping_shr(6) & 7) as u128;
a.0[1] += (remainders.wrapping_shr(3) & 7) as u128;
a.0[2] += (remainders & 7) as u128;
return Ok(a);
}
return Err(InvalidFormatError);
}
}
impl Hash for Address {
impl PartialOrd for Address {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.0.get());
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Address {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
u128::from_be(self.0[0])
.cmp(&u128::from_be(other.0[0]))
.then(u128::from_be(self.0[1]).cmp(&u128::from_be(other.0[1])))
.then(u128::from_be(self.0[2]).cmp(&u128::from_be(other.0[2])))
}
}
impl Debug for Address {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_string().as_str())
}
@ -108,7 +218,7 @@ impl Serialize for Address {
if serializer.is_human_readable() {
serializer.serialize_str(self.to_string().as_str())
} else {
serializer.serialize_bytes(&self.to_bytes())
serializer.serialize_bytes(self.as_bytes())
}
}
}
@ -126,10 +236,10 @@ impl<'de> serde::de::Visitor<'de> for AddressVisitor {
where
E: serde::de::Error,
{
if v.len() == ADDRESS_SIZE {
Address::from_bytes(v).map_or_else(|| Err(E::custom("object too large")), |a| Ok(a))
if let Ok(Some(v)) = Address::from_bytes(v) {
Ok(v)
} else {
Err(E::custom("object size incorrect"))
Err(E::custom("invalid address"))
}
}
@ -153,127 +263,3 @@ impl<'de> Deserialize<'de> for Address {
}
}
}
#[cfg(test)]
mod tests {
fn safe_address() -> super::Address {
let mut addr: Option<super::Address>;
'retry: loop {
let rawaddr: u64 = rand::random();
addr = super::Address::from_u64(rawaddr);
if addr.is_some() {
break 'retry;
}
}
addr.unwrap()
}
#[test]
fn address_marshal_u64() {
let mut rawaddr: u64 = rand::random();
let addr = super::Address::from_u64(rawaddr);
assert!(addr.is_some());
let addr: u64 = addr.unwrap().into();
assert_eq!(addr, rawaddr & 0xffffffffff);
rawaddr = 0;
assert!(super::Address::from_u64(rawaddr).is_none());
rawaddr = (crate::protocol::ADDRESS_RESERVED_PREFIX as u64) << 32;
assert!(super::Address::from_u64(rawaddr).is_none());
}
#[test]
fn address_marshal_bytes() {
use crate::protocol::ADDRESS_SIZE;
let mut v: Vec<u8> = Vec::with_capacity(ADDRESS_SIZE);
let mut i = 0;
while i < ADDRESS_SIZE {
v.push(rand::random());
i += 1;
}
let addr = super::Address::from_bytes(v.as_slice());
assert!(addr.is_some());
assert_eq!(addr.unwrap().to_bytes(), v.as_slice());
let empty: Vec<u8> = Vec::new();
let emptyaddr = super::Address::from_bytes(empty.as_slice());
assert!(emptyaddr.is_none());
let mut v2: [u8; ADDRESS_SIZE] = [0u8; ADDRESS_SIZE];
let mut i = 0;
while i < ADDRESS_SIZE {
v2[i] = v[i];
i += 1;
}
let addr2 = super::Address::from_bytes_fixed(&v2);
assert!(addr2.is_some());
assert_eq!(addr2.unwrap().to_bytes(), v2);
assert_eq!(addr.unwrap(), addr2.unwrap());
}
#[test]
fn address_to_from_string() {
use std::str::FromStr;
for _ in 0..1000 {
let rawaddr: u64 = rand::random();
let addr = super::Address::from_u64(rawaddr);
// NOTE: a regression here is covered by other tests and should not break this test
// accidentally.
if addr.is_none() {
continue;
}
let addr = addr.unwrap();
assert_ne!(addr.to_string(), "");
assert_eq!(addr.to_string().len(), 10);
assert_eq!(super::Address::from_str(&addr.to_string()).unwrap(), addr);
}
}
#[test]
fn address_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
let addr = safe_address();
addr.hash(&mut hasher);
let result1 = hasher.finish();
// this loop is mostly to ensure that hash returns a consistent result every time.
for _ in 0..1000 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
addr.hash(&mut hasher);
let result2 = hasher.finish();
assert_ne!(result2.to_string(), "");
assert_eq!(result1.to_string(), result2.to_string());
}
}
#[test]
fn address_serialize() {
let addr = safe_address();
for _ in 0..1000 {
assert_eq!(
serde_json::from_str::<super::Address>(&serde_json::to_string(&addr).unwrap()).unwrap(),
addr
);
assert_eq!(
serde_cbor::from_slice::<super::Address>(&serde_cbor::to_vec(&addr).unwrap()).unwrap(),
addr
);
}
}
}

View file

@ -6,14 +6,13 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
use crate::vl1::inetaddress::InetAddress;
use crate::vl1::{Address, MAC};
use zerotier_utils::base64;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use zerotier_utils::str::{escape, unescape};
pub const TYPE_NIL: u8 = 0;
pub const TYPE_ZEROTIER: u8 = 1;
@ -39,7 +38,7 @@ pub enum Endpoint {
/// Via another node using unencapsulated relaying (e.g. via a root)
/// This is the address and the full identity fingerprint.
ZeroTier(Address, [u8; IDENTITY_FINGERPRINT_SIZE]),
ZeroTier(Address),
/// Direct L2 Ethernet
Ethernet(MAC),
@ -67,7 +66,7 @@ pub enum Endpoint {
/// Via another node using inner encapsulation via VERB_ENCAP.
/// This is the address and the full identity fingerprint.
ZeroTierEncap(Address, [u8; IDENTITY_FINGERPRINT_SIZE]),
ZeroTierEncap(Address),
}
impl Default for Endpoint {
@ -92,7 +91,7 @@ impl Endpoint {
pub fn type_id(&self) -> u8 {
match self {
Endpoint::Nil => TYPE_NIL,
Endpoint::ZeroTier(_, _) => TYPE_ZEROTIER,
Endpoint::ZeroTier(_) => TYPE_ZEROTIER,
Endpoint::Ethernet(_) => TYPE_ETHERNET,
Endpoint::WifiDirect(_) => TYPE_WIFIDIRECT,
Endpoint::Bluetooth(_) => TYPE_BLUETOOTH,
@ -101,7 +100,7 @@ impl Endpoint {
Endpoint::IpTcp(_) => TYPE_IPTCP,
Endpoint::Http(_) => TYPE_HTTP,
Endpoint::WebRTC(_) => TYPE_WEBRTC,
Endpoint::ZeroTierEncap(_, _) => TYPE_ZEROTIER_ENCAP,
Endpoint::ZeroTierEncap(_) => TYPE_ZEROTIER_ENCAP,
}
}
@ -134,15 +133,14 @@ impl Endpoint {
impl Marshalable for Endpoint {
const MAX_MARSHAL_SIZE: usize = MAX_MARSHAL_SIZE;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError> {
match self {
Endpoint::Nil => {
buf.append_u8(16 + TYPE_NIL)?;
}
Endpoint::ZeroTier(a, h) => {
Endpoint::ZeroTier(a) => {
buf.append_u8(16 + TYPE_ZEROTIER)?;
buf.append_bytes_fixed(&a.to_bytes())?;
buf.append_bytes_fixed(h)?;
buf.append_bytes_fixed(a.as_bytes_raw())?;
}
Endpoint::Ethernet(m) => {
buf.append_u8(16 + TYPE_ETHERNET)?;
@ -184,10 +182,9 @@ impl Marshalable for Endpoint {
buf.append_varint(b.len() as u64)?;
buf.append_bytes(b)?;
}
Endpoint::ZeroTierEncap(a, h) => {
Endpoint::ZeroTierEncap(a) => {
buf.append_u8(16 + TYPE_ZEROTIER_ENCAP)?;
buf.append_bytes_fixed(&a.to_bytes())?;
buf.append_bytes_fixed(h)?;
buf.append_bytes_fixed(a.as_bytes_raw())?;
}
}
Ok(())
@ -214,10 +211,9 @@ impl Marshalable for Endpoint {
} else {
match type_byte - 16 {
TYPE_NIL => Ok(Endpoint::Nil),
TYPE_ZEROTIER => {
let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?;
Ok(Endpoint::ZeroTier(zt, buf.read_bytes_fixed::<IDENTITY_FINGERPRINT_SIZE>(cursor)?.clone()))
}
TYPE_ZEROTIER => Ok(Endpoint::ZeroTier(
Address::from_bytes_raw(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?,
)),
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)?)),
@ -228,10 +224,9 @@ impl Marshalable for Endpoint {
String::from_utf8_lossy(buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?).to_string(),
)),
TYPE_WEBRTC => Ok(Endpoint::WebRTC(buf.read_bytes(buf.read_varint(cursor)? as usize, cursor)?.to_vec())),
TYPE_ZEROTIER_ENCAP => {
let zt = Address::from_bytes_fixed(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?;
Ok(Endpoint::ZeroTierEncap(zt, buf.read_bytes_fixed(cursor)?.clone()))
}
TYPE_ZEROTIER_ENCAP => Ok(Endpoint::ZeroTier(
Address::from_bytes_raw(buf.read_bytes_fixed(cursor)?).ok_or(UnmarshalError::InvalidData)?,
)),
_ => Err(UnmarshalError::InvalidData),
}
}
@ -244,9 +239,9 @@ impl Hash for Endpoint {
Endpoint::Nil => {
state.write_u8(TYPE_NIL);
}
Endpoint::ZeroTier(a, _) => {
Endpoint::ZeroTier(a) => {
state.write_u8(TYPE_ZEROTIER);
state.write_u64(a.into())
a.hash(state);
}
Endpoint::Ethernet(m) => {
state.write_u8(TYPE_ETHERNET);
@ -280,9 +275,9 @@ impl Hash for Endpoint {
state.write_u8(TYPE_WEBRTC);
offer.hash(state);
}
Endpoint::ZeroTierEncap(a, _) => {
Endpoint::ZeroTierEncap(a) => {
state.write_u8(TYPE_ZEROTIER_ENCAP);
state.write_u64(a.into())
a.hash(state);
}
}
}
@ -293,7 +288,7 @@ impl Ord for Endpoint {
// Manually implement Ord to ensure that sort order is known and consistent.
match (self, other) {
(Endpoint::Nil, Endpoint::Nil) => Ordering::Equal,
(Endpoint::ZeroTier(a, ah), Endpoint::ZeroTier(b, bh)) => a.cmp(b).then_with(|| ah.cmp(bh)),
(Endpoint::ZeroTier(a), Endpoint::ZeroTier(b)) => a.cmp(b),
(Endpoint::Ethernet(a), Endpoint::Ethernet(b)) => a.cmp(b),
(Endpoint::WifiDirect(a), Endpoint::WifiDirect(b)) => a.cmp(b),
(Endpoint::Bluetooth(a), Endpoint::Bluetooth(b)) => a.cmp(b),
@ -302,7 +297,7 @@ impl Ord for Endpoint {
(Endpoint::IpTcp(a), Endpoint::IpTcp(b)) => a.cmp(b),
(Endpoint::Http(a), Endpoint::Http(b)) => a.cmp(b),
(Endpoint::WebRTC(a), Endpoint::WebRTC(b)) => a.cmp(b),
(Endpoint::ZeroTierEncap(a, ah), Endpoint::ZeroTierEncap(b, bh)) => a.cmp(b).then_with(|| ah.cmp(bh)),
(Endpoint::ZeroTierEncap(a), Endpoint::ZeroTierEncap(b)) => a.cmp(b),
_ => self.type_id().cmp(&other.type_id()),
}
}
@ -319,7 +314,7 @@ impl ToString for Endpoint {
fn to_string(&self) -> String {
match self {
Endpoint::Nil => format!("nil"),
Endpoint::ZeroTier(a, ah) => format!("zt:{}-{}", a.to_string(), base64::encode_url_nopad(ah)),
Endpoint::ZeroTier(a) => format!("zt:{}", a.to_string()),
Endpoint::Ethernet(m) => format!("eth:{}", m.to_string()),
Endpoint::WifiDirect(m) => format!("wifip2p:{}", m.to_string()),
Endpoint::Bluetooth(m) => format!("bt:{}", m.to_string()),
@ -327,8 +322,8 @@ impl ToString for Endpoint {
Endpoint::IpUdp(ip) => format!("udp:{}", ip.to_string()),
Endpoint::IpTcp(ip) => format!("tcp:{}", ip.to_string()),
Endpoint::Http(url) => format!("url:{}", url.clone()), // http or https
Endpoint::WebRTC(offer) => format!("webrtc:{}", base64::encode_url_nopad(offer.as_slice())),
Endpoint::ZeroTierEncap(a, ah) => format!("zte:{}-{}", a.to_string(), base64::encode_url_nopad(ah)),
Endpoint::WebRTC(offer) => format!("webrtc:{}", escape(offer.as_slice())),
Endpoint::ZeroTierEncap(a) => format!("zte:{}", a.to_string()),
}
}
}
@ -348,18 +343,10 @@ impl FromStr for Endpoint {
let (endpoint_type, endpoint_data) = ss.unwrap();
match endpoint_type {
"zt" | "zte" => {
let address_and_hash = endpoint_data.split_once("-");
if address_and_hash.is_some() {
let (address, hash) = address_and_hash.unwrap();
if let Some(hash) = base64::decode_url_nopad(hash) {
if hash.len() == IDENTITY_FINGERPRINT_SIZE {
if endpoint_type == "zt" {
return Ok(Endpoint::ZeroTier(Address::from_str(address)?, hash.as_slice().try_into().unwrap()));
} else {
return Ok(Endpoint::ZeroTierEncap(Address::from_str(address)?, hash.as_slice().try_into().unwrap()));
}
}
}
if endpoint_type == "zt" {
return Ok(Endpoint::ZeroTier(Address::from_str(endpoint_data)?));
} else {
return Ok(Endpoint::ZeroTierEncap(Address::from_str(endpoint_data)?));
}
}
"eth" => return Ok(Endpoint::Ethernet(MAC::from_str(endpoint_data)?)),
@ -369,11 +356,7 @@ impl FromStr for Endpoint {
"udp" => return Ok(Endpoint::IpUdp(InetAddress::from_str(endpoint_data)?)),
"tcp" => return Ok(Endpoint::IpTcp(InetAddress::from_str(endpoint_data)?)),
"url" => return Ok(Endpoint::Http(endpoint_data.into())),
"webrtc" => {
if let Some(offer) = base64::decode_url_nopad(endpoint_data) {
return Ok(Endpoint::WebRTC(offer));
}
}
"webrtc" => return Ok(Endpoint::WebRTC(unescape(endpoint_data))),
_ => {}
}
return Err(InvalidFormatError);
@ -438,278 +421,3 @@ impl<'de> Deserialize<'de> for Endpoint {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::*;
fn randstring(len: u8) -> String {
(0..len)
.map(|_| (rand::random::<u8>() % 26) + 'a' as u8)
.map(|c| {
if rand::random::<bool>() {
(c as char).to_ascii_uppercase()
} else {
c as char
}
})
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join("")
}
#[test]
fn endpoint_default() {
let e: Endpoint = Default::default();
assert!(matches!(e, Endpoint::Nil))
}
#[test]
fn endpoint_from_bytes() {
let v = [0u8; MAX_MARSHAL_SIZE];
assert!(Endpoint::from_bytes(&v).is_none());
}
#[test]
fn endpoint_marshal_nil() {
let n = Endpoint::Nil;
let mut buf = Buffer::<1>::new();
let res = n.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let n2 = res.unwrap();
assert_eq!(n, n2);
}
#[test]
fn endpoint_marshal_zerotier() {
for _ in 0..1000 {
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
let zte = Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash);
const TMP: usize = IDENTITY_FINGERPRINT_SIZE + 8;
let mut buf = Buffer::<TMP>::new();
let res = zte.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let zte2 = res.unwrap();
assert_eq!(zte, zte2);
}
}
#[test]
fn endpoint_marshal_zerotier_encap() {
for _ in 0..1000 {
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
let zte = Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash);
const TMP: usize = IDENTITY_FINGERPRINT_SIZE + 8;
let mut buf = Buffer::<TMP>::new();
let res = zte.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let zte2 = res.unwrap();
assert_eq!(zte, zte2);
}
}
#[test]
fn endpoint_marshal_mac() {
for _ in 0..1000 {
let mac = crate::vl1::MAC::from_u64(rand::random()).unwrap();
for e in [
Endpoint::Ethernet(mac.clone()),
Endpoint::WifiDirect(mac.clone()),
Endpoint::Bluetooth(mac.clone()),
] {
let mut buf = Buffer::<7>::new();
let res = e.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let e2 = res.unwrap();
assert_eq!(e, e2);
}
}
}
#[test]
fn endpoint_marshal_inetaddress() {
for _ in 0..1000 {
let mut v = [0u8; 16];
v.fill_with(|| rand::random());
let inet = crate::vl1::InetAddress::from_ip_port(&v, 1234);
for e in [Endpoint::Icmp(inet.clone()), Endpoint::IpTcp(inet.clone()), Endpoint::IpUdp(inet.clone())] {
let mut buf = Buffer::<20>::new();
let res = e.marshal(&mut buf);
assert!(res.is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let e2 = res.unwrap();
assert_eq!(e, e2);
}
}
}
#[test]
fn endpoint_marshal_http() {
for _ in 0..1000 {
let http = Endpoint::Http(randstring(30));
let mut buf = Buffer::<33>::new();
assert!(http.marshal(&mut buf).is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let http2 = res.unwrap();
assert_eq!(http, http2);
}
}
#[test]
fn endpoint_marshal_webrtc() {
for _ in 0..1000 {
let mut v = Vec::with_capacity(100);
v.fill_with(|| rand::random());
let rtc = Endpoint::WebRTC(v);
let mut buf = Buffer::<102>::new();
assert!(rtc.marshal(&mut buf).is_ok());
let res = Endpoint::unmarshal(&buf, &mut 0);
assert!(res.is_ok());
let rtc2 = res.unwrap();
assert_eq!(rtc, rtc2);
}
}
#[test]
fn endpoint_to_from_string() {
use std::str::FromStr;
for _ in 0..1000 {
let mut v = Vec::with_capacity(100);
v.fill_with(|| rand::random());
let rtc = Endpoint::WebRTC(v);
assert_ne!(rtc.to_string().len(), 0);
assert!(rtc.to_string().starts_with("webrtc"));
let rtc2 = Endpoint::from_str(&rtc.to_string()).unwrap();
assert_eq!(rtc, rtc2);
let http = Endpoint::Http(randstring(30));
assert_ne!(http.to_string().len(), 0);
assert!(http.to_string().starts_with("url"));
let http2 = Endpoint::from_str(&http.to_string()).unwrap();
assert_eq!(http, http2);
let mut v = [0u8; 16];
v.fill_with(|| rand::random());
let inet = crate::vl1::InetAddress::from_ip_port(&v, 0);
let ip = Endpoint::Icmp(inet.clone());
assert_ne!(ip.to_string().len(), 0);
assert!(ip.to_string().starts_with("icmp"));
let ip2 = Endpoint::from_str(&ip.to_string()).unwrap();
assert_eq!(ip, ip2);
let inet = crate::vl1::InetAddress::from_ip_port(&v, 1234);
for e in [(Endpoint::IpTcp(inet.clone()), "tcp"), (Endpoint::IpUdp(inet.clone()), "udp")] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
let mac = crate::vl1::MAC::from_u64(rand::random()).unwrap();
for e in [
(Endpoint::Ethernet(mac.clone()), "eth"),
(Endpoint::WifiDirect(mac.clone()), "wifip2p"),
(Endpoint::Bluetooth(mac.clone()), "bt"),
] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
let mut hash = [0u8; IDENTITY_FINGERPRINT_SIZE];
hash.fill_with(|| rand::random());
let mut v = [0u8; ADDRESS_SIZE];
v.fill_with(|| rand::random());
// correct for situations where RNG generates a prefix which generates a None value.
while v[0] == ADDRESS_RESERVED_PREFIX {
v[0] = rand::random()
}
for e in [
(Endpoint::ZeroTier(Address::from_bytes(&v).unwrap(), hash), "zt"),
(Endpoint::ZeroTierEncap(Address::from_bytes(&v).unwrap(), hash), "zte"),
] {
assert_ne!(e.0.to_string().len(), 0);
assert!(e.0.to_string().starts_with(e.1));
let e2 = Endpoint::from_str(&e.0.to_string()).unwrap();
assert_eq!(e.0, e2);
}
assert_eq!(Endpoint::Nil.to_string(), "nil");
}
}
}

View file

@ -1,6 +1,6 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md.
use crate::vl1::*;
use crate::vl1::identity::Identity;
#[derive(Clone)]
pub enum Event {

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@ use num_traits::AsPrimitive;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
@ -879,7 +879,7 @@ impl InetAddress {
impl Marshalable for InetAddress {
const MAX_MARSHAL_SIZE: usize = 19;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError> {
unsafe {
match self.sa.sa_family as AddressFamilyType {
AF_INET => {

View file

@ -7,7 +7,7 @@ use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
@ -75,9 +75,8 @@ impl Marshalable for MAC {
const MAX_MARSHAL_SIZE: usize = 6;
#[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError> {
buf.append_bytes(&self.0.get().to_be_bytes()[2..])
.map_err(|_| UnmarshalError::OutOfBounds)
}
#[inline(always)]

View file

@ -15,7 +15,6 @@ pub mod inetaddress;
pub use address::Address;
pub use endpoint::Endpoint;
pub use event::Event;
pub use identity::Identity;
pub use inetaddress::InetAddress;
pub use mac::MAC;
pub use node::{ApplicationLayer, InnerProtocolLayer, Node, PacketHandlerResult};

View file

@ -3,7 +3,6 @@
use std::collections::HashMap;
use std::convert::Infallible;
use std::hash::Hash;
use std::io::Write;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, RwLock, Weak};
use std::time::Duration;
@ -13,17 +12,15 @@ use crate::vl1::address::Address;
use crate::vl1::debug_event;
use crate::vl1::endpoint::Endpoint;
use crate::vl1::event::Event;
use crate::vl1::identity::Identity;
use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::path::{Path, PathServiceResult};
use crate::vl1::peer::Peer;
use crate::vl1::rootset::RootSet;
use zerotier_crypto::random;
use zerotier_crypto::typestate::{Valid, Verified};
use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::gate::IntervalGate;
use zerotier_utils::hex;
use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::ringbuffer::RingBuffer;
/// Interface trait to be implemented by code that's using the ZeroTier network hypervisor.
@ -40,12 +37,6 @@ pub trait ApplicationLayer: Sync + Send + 'static {
/// A VL1 level event occurred.
fn event(&self, event: Event);
/// Load this node's identity from the data store.
fn load_node_identity(&self) -> Option<Valid<Identity>>;
/// Save this node's identity to the data store, returning true on success.
fn save_node_identity(&self, id: &Valid<Identity>) -> bool;
/// Get a pooled packet buffer for internal use.
fn get_buffer(&self) -> PooledPacketBuffer;
@ -141,7 +132,7 @@ pub trait InnerProtocolLayer: Sync + Send {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
verb: u8,
@ -158,7 +149,7 @@ pub trait InnerProtocolLayer: Sync + Send {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
in_re_verb: u8,
@ -177,7 +168,7 @@ pub trait InnerProtocolLayer: Sync + Send {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
in_re_verb: u8,
@ -221,70 +212,30 @@ struct BackgroundTaskIntervals {
}
struct WhoisQueueItem<Application: ApplicationLayer + ?Sized> {
v1_proto_waiting_packets:
RingBuffer<(Weak<Path<Application::LocalSocket, Application::LocalInterface>>, PooledPacketBuffer), WHOIS_MAX_WAITING_PACKETS>,
v1_proto_waiting_packets: RingBuffer<(Weak<Path<Application>>, PooledPacketBuffer), WHOIS_MAX_WAITING_PACKETS>,
last_retry_time: i64,
retry_count: u16,
}
/// A ZeroTier VL1 node that can communicate securely with the ZeroTier peer-to-peer network.
pub struct Node<Application: ApplicationLayer + ?Sized> {
/// A random ID generated to identify this particular running instance.
pub instance_id: [u8; 16],
/// This node's identity and permanent keys.
pub identity: Valid<Identity>,
/// Interval latches for periodic background tasks.
identity_secret: IdentitySecret,
intervals: Mutex<BackgroundTaskIntervals>,
/// Canonicalized network paths, held as Weak<> to be automatically cleaned when no longer in use.
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application::LocalSocket, Application::LocalInterface>>>>,
/// Peers with which we are currently communicating.
paths: RwLock<HashMap<PathKey<'static, 'static, Application::LocalSocket>, Arc<Path<Application>>>>,
peers: RwLock<HashMap<Address, Arc<Peer<Application>>>>,
/// This node's trusted roots, sorted in ascending order of quality/preference, and cluster definitions.
roots: RwLock<RootInfo<Application>>,
/// Current best root.
best_root: RwLock<Option<Arc<Peer<Application>>>>,
/// Queue of identities being looked up.
whois_queue: Mutex<HashMap<Address, WhoisQueueItem<Application>>>,
}
impl<Application: ApplicationLayer + ?Sized> Node<Application> {
pub fn new(app: &Application, auto_generate_identity: bool, auto_upgrade_identity: bool) -> Result<Self, InvalidParameterError> {
let mut id = {
let id = app.load_node_identity();
if id.is_none() {
if !auto_generate_identity {
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
} else {
let id = Identity::generate();
app.event(Event::IdentityAutoGenerated(id.as_ref().clone()));
app.save_node_identity(&id);
id
}
} else {
id.unwrap()
}
};
if auto_upgrade_identity {
let old = id.clone();
if id.upgrade()? {
app.save_node_identity(&id);
app.event(Event::IdentityAutoUpgraded(old.remove_typestate(), id.as_ref().clone()));
}
}
debug_event!(app, "[vl1] loaded identity {}", id.to_string());
Ok(Self {
pub fn new(identity: Valid<Identity>, identity_secret: IdentitySecret) -> Self {
Self {
instance_id: random::get_bytes_secure(),
identity: id,
identity,
identity_secret,
intervals: Mutex::new(BackgroundTaskIntervals::default()),
paths: RwLock::new(HashMap::new()),
peers: RwLock::new(HashMap::new()),
@ -297,11 +248,11 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
}),
best_root: RwLock::new(None),
whois_queue: Mutex::new(HashMap::new()),
})
}
}
#[inline]
pub fn peer(&self, a: Address) -> Option<Arc<Peer<Application>>> {
pub fn peer(&self, a: &Address) -> Option<Arc<Peer<Application>>> {
self.peers.read().unwrap().get(&a).cloned()
}
@ -398,7 +349,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
} {
debug_event!(app, "[vl1] root sets modified, synchronizing internal data structures");
let (mut old_root_identities, address_collisions, new_roots, bad_identities, my_root_sets) = {
let (mut old_root_identities, new_roots, bad_identities, my_root_sets) = {
let roots = self.roots.read().unwrap();
let old_root_identities: Vec<Identity> = roots.roots.iter().map(|(p, _)| p.identity.as_ref().clone()).collect();
@ -406,35 +357,9 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
let mut bad_identities = Vec::new();
let mut my_root_sets: Option<Vec<u8>> = None;
// This is a sanity check to make sure we don't have root sets that contain roots with the same address
// but a different identity. If we do, the offending address is blacklisted. This would indicate something
// weird and possibly nasty happening with whomever is making your root set definitions.
let mut address_collisions = Vec::new();
{
let mut address_collision_check = HashMap::with_capacity(roots.sets.len() * 8);
for (_, rs) in roots.sets.iter() {
for m in rs.members.iter() {
if m.identity.eq(&self.identity) {
let _ = my_root_sets.get_or_insert_with(|| Vec::new()).write_all(rs.to_bytes().as_slice());
} else if self
.peers
.read()
.unwrap()
.get(&m.identity.address)
.map_or(false, |p| !p.identity.as_ref().eq(&m.identity))
|| address_collision_check
.insert(m.identity.address, &m.identity)
.map_or(false, |old_id| !old_id.eq(&m.identity))
{
address_collisions.push(m.identity.address);
}
}
}
}
for (_, rs) in roots.sets.iter() {
for m in rs.members.iter() {
if m.endpoints.is_some() && !address_collisions.contains(&m.identity.address) && !m.identity.eq(&self.identity) {
if m.endpoints.is_some() && !m.identity.eq(&self.identity) {
debug_event!(
app,
"[vl1] examining root {} with {} endpoints",
@ -445,13 +370,13 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
if let Some(peer) = peers.get(&m.identity.address) {
new_roots.insert(peer.clone(), m.endpoints.as_ref().unwrap().iter().cloned().collect());
} else {
if let Some(peer) = Peer::new(&self.identity, Valid::mark_valid(m.identity.clone()), time_ticks) {
if let Some(peer) = Peer::new(&self.identity_secret, Valid::mark_valid(m.identity.clone()), time_ticks) {
drop(peers);
new_roots.insert(
self.peers
.write()
.unwrap()
.entry(m.identity.address)
.entry(m.identity.address.clone())
.or_insert_with(|| Arc::new(peer))
.clone(),
m.endpoints.as_ref().unwrap().iter().cloned().collect(),
@ -464,15 +389,9 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
}
}
(old_root_identities, address_collisions, new_roots, bad_identities, my_root_sets)
(old_root_identities, new_roots, bad_identities, my_root_sets)
};
for c in address_collisions.iter() {
app.event(Event::SecurityWarning(format!(
"address/identity collision in root sets! address {} collides across root sets or with an existing peer and is being ignored as a root!",
c.to_string()
)));
}
for i in bad_identities.iter() {
app.event(Event::SecurityWarning(format!(
"bad identity detected for address {} in at least one root set, ignoring (error creating peer object)",
@ -586,7 +505,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
let roots = self.roots.read().unwrap();
for (a, peer) in self.peers.read().unwrap().iter() {
if !peer.service(app, self, time_ticks) && !roots.roots.contains_key(peer) {
dead_peers.push(*a);
dead_peers.push(a.clone());
}
}
}
@ -633,7 +552,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
if (time_ticks - qi.last_retry_time) >= WHOIS_RETRY_INTERVAL {
qi.retry_count += 1;
qi.last_retry_time = time_ticks;
need_whois.push(*address);
need_whois.push(address.clone());
}
}
need_whois
@ -672,7 +591,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
// Legacy ZeroTier V1 packet handling
if let Ok(fragment_header) = packet.struct_mut_at::<v1::FragmentHeader>(0) {
if let Some(dest) = Address::from_bytes_fixed(&fragment_header.dest) {
if let Some(dest) = Address::from_bytes_v1(&fragment_header.dest) {
// Packet is addressed to this node.
if dest == self.identity.address {
@ -703,8 +622,8 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
debug_event!(app, "[vl1] [v1] #{:0>16x} packet fully assembled!", fragment_header_id);
if let Ok(packet_header) = frag0.struct_at::<v1::PacketHeader>(0) {
if let Some(source) = Address::from_bytes(&packet_header.src) {
if let Some(peer) = self.peer(source) {
if let Some(source) = Address::from_bytes_v1(&packet_header.src) {
if let Some(peer) = self.peer(&source) {
peer.v1_proto_receive(
self,
app,
@ -728,7 +647,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
}
}
if ok {
self.whois(app, source, Some((Arc::downgrade(&path), combined_packet)), time_ticks);
self.whois(app, source.clone(), Some((Arc::downgrade(&path), combined_packet)), time_ticks);
}
}
} // else source address invalid
@ -738,8 +657,8 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
} else if let Ok(packet_header) = packet.struct_at::<v1::PacketHeader>(0) {
debug_event!(app, "[vl1] [v1] #{:0>16x} is unfragmented", u64::from_be_bytes(packet_header.id));
if let Some(source) = Address::from_bytes(&packet_header.src) {
if let Some(peer) = self.peer(source) {
if let Some(source) = Address::from_bytes_v1(&packet_header.src) {
if let Some(peer) = self.peer(&source) {
peer.v1_proto_receive(self, app, inner, time_ticks, &path, packet_header, packet.as_ref(), &[]);
} else {
self.whois(app, source, Some((Arc::downgrade(&path), packet)), time_ticks);
@ -788,7 +707,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
return;
}
if let Some(peer) = self.peer(dest) {
if let Some(peer) = self.peer(&dest) {
if let Some(forward_path) = peer.direct_path() {
app.wire_send(
&forward_path.endpoint,
@ -810,16 +729,10 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
}
/// Enqueue and send a WHOIS query for a given address, adding the supplied packet (if any) to the list to be processed on reply.
fn whois(
&self,
app: &Application,
address: Address,
waiting_packet: Option<(Weak<Path<Application::LocalSocket, Application::LocalInterface>>, PooledPacketBuffer)>,
time_ticks: i64,
) {
fn whois(&self, app: &Application, address: Address, waiting_packet: Option<(Weak<Path<Application>>, PooledPacketBuffer)>, time_ticks: i64) {
{
let mut whois_queue = self.whois_queue.lock().unwrap();
let qi = whois_queue.entry(address).or_insert_with(|| WhoisQueueItem {
let qi = whois_queue.entry(address.clone()).or_insert_with(|| WhoisQueueItem {
v1_proto_waiting_packets: RingBuffer::new(),
last_retry_time: 0,
retry_count: 0,
@ -856,7 +769,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
.send(app, self, None, time_ticks, |packet| -> Result<(), Infallible> {
assert!(packet.append_u8(message_type::VL1_WHOIS).is_ok());
while !addresses.is_empty() && (packet.len() + ADDRESS_SIZE) <= UDP_DEFAULT_MTU {
assert!(packet.append_bytes_fixed(&addresses[0].to_bytes()).is_ok());
assert!(packet.append_bytes_fixed(addresses[0].as_bytes_v1()).is_ok());
addresses = &addresses[1..];
}
Ok(())
@ -882,13 +795,13 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
if let Some(received_identity) = received_identity.validate() {
let mut whois_queue = self.whois_queue.lock().unwrap();
if let Some(qi) = whois_queue.get_mut(&received_identity.address) {
let address = received_identity.address;
let address = received_identity.address.clone();
if app.should_respond_to(&received_identity) {
let mut peers = self.peers.write().unwrap();
if let Some(peer) = peers.get(&address).cloned().or_else(|| {
Peer::new(&self.identity, received_identity, time_ticks)
Peer::new(&self.identity_secret, received_identity, time_ticks)
.map(|p| Arc::new(p))
.and_then(|peer| Some(peers.entry(address).or_insert(peer).clone()))
.and_then(|peer| Some(peers.entry(address.clone()).or_insert(peer).clone()))
}) {
drop(peers);
for p in qi.v1_proto_waiting_packets.iter() {
@ -928,7 +841,7 @@ impl<Application: ApplicationLayer + ?Sized> Node<Application> {
local_socket: &Application::LocalSocket,
local_interface: &Application::LocalInterface,
time_ticks: i64,
) -> Arc<Path<Application::LocalSocket, Application::LocalInterface>> {
) -> Arc<Path<Application>> {
let paths = self.paths.read().unwrap();
if let Some(path) = paths.get(&PathKey::Ref(ep, local_socket)) {
path.clone()

View file

@ -11,6 +11,8 @@ use crate::vl1::endpoint::Endpoint;
use zerotier_crypto::random;
use zerotier_utils::NEVER_HAPPENED_TICKS;
use super::ApplicationLayer;
pub(crate) const SERVICE_INTERVAL_MS: i64 = protocol::PATH_KEEPALIVE_INTERVAL;
pub(crate) enum PathServiceResult {
@ -24,18 +26,23 @@ pub(crate) enum PathServiceResult {
/// These are maintained in Node and canonicalized so that all unique paths have
/// one and only one unique path object. That enables statistics to be tracked
/// for them and uniform application of things like keepalives.
pub struct Path<LocalSocket, LocalInterface> {
pub struct Path<Application: ApplicationLayer + ?Sized> {
pub endpoint: Endpoint,
pub local_socket: LocalSocket,
pub local_interface: LocalInterface,
pub local_socket: Application::LocalSocket,
pub local_interface: Application::LocalInterface,
last_send_time_ticks: AtomicI64,
last_receive_time_ticks: AtomicI64,
create_time_ticks: i64,
fragmented_packets: Mutex<HashMap<u64, protocol::v1::FragmentedPacket, PacketIdHasher>>,
}
impl<LocalSocket, LocalInterface> Path<LocalSocket, LocalInterface> {
pub(crate) fn new(endpoint: Endpoint, local_socket: LocalSocket, local_interface: LocalInterface, time_ticks: i64) -> Self {
impl<Application: ApplicationLayer + ?Sized> Path<Application> {
pub(crate) fn new(
endpoint: Endpoint,
local_socket: Application::LocalSocket,
local_interface: Application::LocalInterface,
time_ticks: i64,
) -> Self {
Self {
endpoint,
local_socket,

View file

@ -17,9 +17,10 @@ use zerotier_utils::NEVER_HAPPENED_TICKS;
use crate::protocol::*;
use crate::vl1::address::Address;
use crate::vl1::debug_event;
use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::node::*;
use crate::vl1::Valid;
use crate::vl1::{Endpoint, Identity, Path};
use crate::vl1::{Endpoint, Path};
use crate::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
pub(crate) const SERVICE_INTERVAL_MS: i64 = 10000;
@ -42,7 +43,7 @@ pub struct Peer<Application: ApplicationLayer + ?Sized> {
}
struct PeerPath<Application: ApplicationLayer + ?Sized> {
path: Weak<Path<Application::LocalSocket, Application::LocalInterface>>,
path: Weak<Path<Application>>,
last_receive_time_ticks: i64,
}
@ -62,8 +63,8 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
///
/// This only returns None if this_node_identity does not have its secrets or if some
/// fatal error occurs performing key agreement between the two identities.
pub(crate) fn new(this_node_identity: &Valid<Identity>, id: Valid<Identity>, time_ticks: i64) -> Option<Self> {
this_node_identity.agree(&id).map(|static_secret| -> Self {
pub(crate) fn new(this_node_identity: &IdentitySecret, id: Valid<Identity>, time_ticks: i64) -> Option<Self> {
this_node_identity.x25519.agree(&id).map(|static_secret| -> Self {
Self {
identity: id,
v1_proto_static_secret: v1::SymmetricSecret::new(static_secret),
@ -113,7 +114,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
/// Get current best path or None if there are no direct paths to this peer.
#[inline]
pub fn direct_path(&self) -> Option<Arc<Path<Application::LocalSocket, Application::LocalInterface>>> {
pub fn direct_path(&self) -> Option<Arc<Path<Application>>> {
for p in self.paths.lock().unwrap().iter() {
let pp = p.path.upgrade();
if pp.is_some() {
@ -125,7 +126,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
/// Get either the current best direct path or an indirect path via e.g. a root.
#[inline]
pub fn path(&self, node: &Node<Application>) -> Option<Arc<Path<Application::LocalSocket, Application::LocalInterface>>> {
pub fn path(&self, node: &Node<Application>) -> Option<Arc<Path<Application>>> {
let direct_path = self.direct_path();
if direct_path.is_some() {
return direct_path;
@ -136,7 +137,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
return None;
}
fn learn_path(&self, app: &Application, new_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>, time_ticks: i64) {
fn learn_path(&self, app: &Application, new_path: &Arc<Path<Application>>, time_ticks: i64) {
let mut paths = self.paths.lock().unwrap();
// TODO: check path filter
@ -285,7 +286,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
&self,
app: &Application,
node: &Node<Application>,
path: Option<&Arc<Path<Application::LocalSocket, Application::LocalInterface>>>,
path: Option<&Arc<Path<Application>>>,
time_ticks: i64,
builder_function: BuilderFunction,
) -> Option<Result<R, E>> {
@ -324,7 +325,11 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
let mut aes_gmac_siv = self.v1_proto_static_secret.aes_gmac_siv.get();
aes_gmac_siv.encrypt_init(&self.v1_proto_next_message_id().to_be_bytes());
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(self.identity.address, node.identity.address, flags_cipher_hops));
aes_gmac_siv.encrypt_set_aad(&v1::get_packet_aad_bytes(
&self.identity.address,
&node.identity.address,
flags_cipher_hops,
));
let payload = packet.as_bytes_starting_at_mut(v1::HEADER_SIZE).unwrap();
aes_gmac_siv.encrypt_first_pass(payload);
aes_gmac_siv.encrypt_first_pass_finish();
@ -333,8 +338,8 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id.copy_from_slice(&tag[0..8]);
header.dest = self.identity.address.to_bytes();
header.src = node.identity.address.to_bytes();
header.dest = *self.identity.address.as_bytes_v1();
header.src = *node.identity.address.as_bytes_v1();
header.flags_cipher_hops = flags_cipher_hops;
header.mac.copy_from_slice(&tag[8..16]);
} else {
@ -350,8 +355,8 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
{
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
header.id = self.v1_proto_next_message_id().to_be_bytes();
header.dest = self.identity.address.to_bytes();
header.src = node.identity.address.to_bytes();
header.dest = *self.identity.address.as_bytes_v1();
header.src = *node.identity.address.as_bytes_v1();
header.flags_cipher_hops = flags_cipher_hops;
header
},
@ -408,8 +413,8 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
{
let f: &mut (v1::PacketHeader, v1::message_component_structs::HelloFixedHeaderFields) = packet.append_struct_get_mut().unwrap();
f.0.id = message_id.to_ne_bytes();
f.0.dest = self.identity.address.to_bytes();
f.0.src = node.identity.address.to_bytes();
f.0.dest = *self.identity.address.as_bytes_v1();
f.0.src = *node.identity.address.as_bytes_v1();
f.0.flags_cipher_hops = v1::CIPHER_NOCRYPT_POLY1305;
f.1.verb = message_type::VL1_HELLO;
f.1.version_proto = PROTOCOL_VERSION;
@ -420,7 +425,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
}
debug_assert_eq!(packet.len(), 41);
assert!(node.identity.write_public(packet.as_mut(), !self.is_v2()).is_ok());
assert!(node.identity.write_bytes(packet.as_mut(), !self.is_v2()).is_ok());
let (_, poly1305_key) = v1_proto_salsa_poly_create(
&self.v1_proto_static_secret,
@ -470,7 +475,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
app: &Application,
inner: &Inner,
time_ticks: i64,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
packet_header: &v1::PacketHeader,
frag0: &PacketBuffer,
fragments: &[Option<PooledPacketBuffer>],
@ -564,7 +569,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
node: &Node<Application>,
time_ticks: i64,
message_id: MessageId,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
payload: &PacketBuffer,
) -> PacketHandlerResult {
if !(app.should_respond_to(&self.identity) || node.this_node_is_root() || node.is_peer_root(self)) {
@ -617,7 +622,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
inner: &Inner,
node: &Node<Application>,
_time_ticks: i64,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
payload: &PacketBuffer,
@ -655,7 +660,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
inner: &Inner,
node: &Node<Application>,
time_ticks: i64,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
path_is_known: bool,
@ -765,9 +770,9 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
if !self
.send(app, node, None, time_ticks, |packet| {
while addresses.len() >= ADDRESS_SIZE && (packet.len() + Identity::MAX_MARSHAL_SIZE) <= UDP_DEFAULT_MTU {
if let Some(zt_address) = Address::from_bytes(&addresses[..ADDRESS_SIZE]) {
if let Some(peer) = node.peer(zt_address) {
peer.identity.write_public(packet, !self.is_v2())?;
if let Some(zt_address) = Address::from_bytes_v1(&addresses[..ADDRESS_SIZE]) {
if let Some(peer) = node.peer(&zt_address) {
peer.identity.write_bytes(packet, !self.is_v2())?;
}
}
addresses = &addresses[ADDRESS_SIZE..];
@ -789,7 +794,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
node: &Node<Application>,
_time_ticks: i64,
_message_id: MessageId,
_source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
_source_path: &Arc<Path<Application>>,
_payload: &PacketBuffer,
) -> PacketHandlerResult {
if node.is_peer_root(self) {}
@ -827,7 +832,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
_app: &Application,
_node: &Node<Application>,
_time_ticks: i64,
_source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
_source_path: &Arc<Path<Application>>,
_payload: &PacketBuffer,
) -> PacketHandlerResult {
PacketHandlerResult::Ok
@ -838,7 +843,7 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
_app: &Application,
_node: &Node<Application>,
_time_ticks: i64,
_source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
_source_path: &Arc<Path<Application>>,
_payload: &PacketBuffer,
) -> PacketHandlerResult {
PacketHandlerResult::Ok
@ -848,14 +853,14 @@ impl<Application: ApplicationLayer + ?Sized> Peer<Application> {
impl<Application: ApplicationLayer + ?Sized> Hash for Peer<Application> {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u64(self.identity.address.into());
self.identity.address.hash(state)
}
}
impl<Application: ApplicationLayer + ?Sized> PartialEq for Peer<Application> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.identity.fingerprint.eq(&other.identity.fingerprint)
self.identity.eq(&other.identity)
}
}

View file

@ -3,16 +3,18 @@
use std::collections::BTreeSet;
use std::io::Write;
use crate::vl1::identity::{Identity, IDENTITY_MAX_SIGNATURE_SIZE};
use crate::vl1::identity::Identity;
use crate::vl1::Endpoint;
use zerotier_crypto::typestate::Verified;
use zerotier_utils::arrayvec::ArrayVec;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use serde::{Deserialize, Serialize};
use super::identity::IdentitySecret;
/// Description of a member of a root cluster.
///
/// Natural sort order is in order of identity address.
@ -32,7 +34,7 @@ pub struct Root {
/// This is populated by the sign() method when the completed root set is signed by each member.
/// All member roots must sign.
#[serde(default)]
pub signature: ArrayVec<u8, IDENTITY_MAX_SIGNATURE_SIZE>,
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
/// Priority (higher number is lower priority, 0 is default).
///
@ -132,7 +134,7 @@ impl RootSet {
) {
self.members.retain(|m| m.identity.address != member_identity.address);
let _ = self.members.push(Root {
identity: member_identity.clone_without_secret(),
identity: member_identity.clone(),
endpoints: endpoints.map(|endpoints| {
let mut tmp = BTreeSet::new();
for a in endpoints {
@ -154,8 +156,8 @@ impl RootSet {
///
/// 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(), false);
pub fn sign(&mut self, member_identity: &Identity, member_identity_secret: &IdentitySecret) -> bool {
let signature = member_identity_secret.sign(self.marshal_for_signing().as_bytes());
let unsigned_entry = self.members.iter().find_map(|m| {
if m.identity.eq(member_identity) {
Some(m.clone())
@ -163,13 +165,13 @@ impl RootSet {
None
}
});
if unsigned_entry.is_some() && signature.is_some() {
if unsigned_entry.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,
endpoints: unsigned_entry.endpoints,
signature: signature.unwrap(),
signature: signature,
priority: unsigned_entry.priority,
protocol_version: unsigned_entry.protocol_version,
});
@ -197,9 +199,9 @@ impl RootSet {
/// 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 {
let mut my_signers = BTreeSet::new();
let mut my_signers = Vec::with_capacity(self.members.len());
for m in self.members.iter() {
my_signers.insert(m.identity.fingerprint.clone());
my_signers.push(&m.identity);
}
let mut previous_count: isize = 0;
@ -207,7 +209,7 @@ impl RootSet {
for m in previous.members.iter() {
if m.endpoints.is_some() {
previous_count += 1;
witness_count += my_signers.contains(&m.identity.fingerprint) as isize;
witness_count += my_signers.iter().any(|id| (*id).eq(&m.identity)) as isize;
}
}
@ -217,7 +219,7 @@ impl RootSet {
}
}
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_signatures: bool) -> Result<(), UnmarshalError> {
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_signatures: bool) -> Result<(), OutOfBoundsError> {
buf.append_u8(0)?; // version byte for future use
buf.append_varint(self.name.as_bytes().len() as u64)?;
@ -265,7 +267,7 @@ impl Marshalable for RootSet {
const MAX_MARSHAL_SIZE: usize = crate::protocol::v1::SIZE_MAX;
#[inline(always)]
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError> {
self.marshal_internal(buf, true)
}

View file

@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex, RwLock};
use crate::protocol;
use crate::protocol::PacketBuffer;
use crate::vl1::identity::Identity;
use crate::vl1::*;
use crate::vl2::{MulticastGroup, NetworkId};
@ -59,7 +60,7 @@ impl MulticastAuthority {
if auth(network_id, &source.identity) {
let sub_key = (network_id, MulticastGroup { mac, adi: payload.read_u32(&mut cursor).unwrap() });
if let Some(sub) = subscriptions.read().get(&sub_key) {
let _ = sub.lock().unwrap().insert(source.identity.address, time_ticks);
let _ = sub.lock().unwrap().insert(source.identity.address.clone(), time_ticks);
} else {
let _ = subscriptions
.write(&self.subscriptions)
@ -67,7 +68,7 @@ impl MulticastAuthority {
.or_insert_with(|| Mutex::new(HashMap::new()))
.lock()
.unwrap()
.insert(source.identity.address, time_ticks);
.insert(source.identity.address.clone(), time_ticks);
}
}
}
@ -103,7 +104,7 @@ impl MulticastAuthority {
if let Some(sub) = subscriptions.get(&(network_id, MulticastGroup { mac, adi })) {
let sub = sub.lock().unwrap();
for a in sub.keys() {
gathered.push(*a);
gathered.push(a.clone());
}
}
@ -126,7 +127,7 @@ impl MulticastAuthority {
packet.append_u16(in_this_packet as u16)?;
for _ in 0..in_this_packet {
packet.append_bytes_fixed(&gathered.pop().unwrap().to_bytes())?;
packet.append_bytes_fixed(gathered.pop().unwrap().as_bytes_v1())?;
}
Ok(())

View file

@ -11,7 +11,6 @@ use zerotier_utils::error::InvalidFormatError;
use zerotier_utils::hex;
use zerotier_utils::hex::HEX_CHARS;
use crate::protocol::ADDRESS_MASK;
use crate::vl1::Address;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@ -23,7 +22,7 @@ impl NetworkId {
pub fn from_u64(i: u64) -> Option<NetworkId> {
// Note that we check both that 'i' is non-zero and that the address of the controller is valid.
if let Some(ii) = NonZeroU64::new(i) {
if Address::from_u64(i & ADDRESS_MASK).is_some() {
if Address::from_u64_v1(i).is_some() {
return Some(Self(ii));
}
}
@ -32,7 +31,7 @@ impl NetworkId {
#[inline]
pub fn from_controller_and_network_no(controller: Address, network_no: u64) -> Option<NetworkId> {
Self::from_u64(u64::from(controller).wrapping_shl(24) | (network_no & 0xffffff))
Self::from_u64(controller.as_u64_v1().wrapping_shl(24) | (network_no & 0xffffff))
}
#[inline]
@ -57,12 +56,12 @@ impl NetworkId {
/// Get the network controller ID for this network, which is the most significant 40 bits.
#[inline]
pub fn network_controller(&self) -> Address {
Address::from_u64(self.0.get()).unwrap()
Address::from_u64_v1(self.0.get()).unwrap()
}
/// Consume this network ID and return one with the same network number but a different controller ID.
pub fn change_network_controller(self, new_controller: Address) -> NetworkId {
Self(NonZeroU64::new((self.network_no() as u64) | u64::from(new_controller).wrapping_shl(24)).unwrap())
Self(NonZeroU64::new((self.network_no() as u64) | new_controller.as_u64_v1().wrapping_shl(24)).unwrap())
}
/// Get the 24-bit local network identifier minus the 40-bit controller address portion.

View file

@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use phf::phf_map;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
use crate::protocol;
@ -239,7 +239,7 @@ impl Rule {
Self {
t: action::TEE,
v: RuleValue {
forward: rule_value::Forward { address: address.into(), flags, length },
forward: rule_value::Forward { address: address.as_u64_v1(), flags, length },
},
}
}
@ -248,7 +248,7 @@ impl Rule {
Self {
t: action::TEE,
v: RuleValue {
forward: rule_value::Forward { address: address.into(), flags, length },
forward: rule_value::Forward { address: address.as_u64_v1(), flags, length },
},
}
}
@ -257,7 +257,7 @@ impl Rule {
Self {
t: action::TEE,
v: RuleValue {
forward: rule_value::Forward { address: address.into(), flags, length },
forward: rule_value::Forward { address: address.as_u64_v1(), flags, length },
},
}
}
@ -273,14 +273,14 @@ impl Rule {
pub fn match_source_zerotier_address(not: bool, or: bool, address: Address) -> Self {
Self {
t: t(not, or, match_cond::SOURCE_ZEROTIER_ADDRESS),
v: RuleValue { zt: address.into() },
v: RuleValue { zt: address.as_u64_v1() },
}
}
pub fn match_dest_zerotier_address(not: bool, or: bool, address: Address) -> Self {
Self {
t: t(not, or, match_cond::DEST_ZEROTIER_ADDRESS),
v: RuleValue { zt: address.into() },
v: RuleValue { zt: address.as_u64_v1() },
}
}
@ -306,21 +306,21 @@ impl Rule {
return v.action_accept();
}
action::TEE => {
if let Some(a) = Address::from_u64(self.v.forward.address) {
if let Some(a) = Address::from_u64_v1(self.v.forward.address) {
return v.action_tee(a, self.v.forward.flags, self.v.forward.length);
} else {
return v.invalid_rule();
}
}
action::WATCH => {
if let Some(a) = Address::from_u64(self.v.forward.address) {
if let Some(a) = Address::from_u64_v1(self.v.forward.address) {
return v.action_watch(a, self.v.forward.flags, self.v.forward.length);
} else {
return v.invalid_rule();
}
}
action::REDIRECT => {
if let Some(a) = Address::from_u64(self.v.forward.address) {
if let Some(a) = Address::from_u64_v1(self.v.forward.address) {
return v.action_redirect(a, self.v.forward.flags, self.v.forward.length);
} else {
return v.invalid_rule();
@ -333,14 +333,14 @@ impl Rule {
return v.action_priority(self.v.qos_bucket);
}
match_cond::SOURCE_ZEROTIER_ADDRESS => {
if let Some(a) = Address::from_u64(self.v.zt) {
if let Some(a) = Address::from_u64_v1(self.v.zt) {
v.match_source_zerotier_address(not, or, a);
} else {
return v.invalid_rule();
}
}
match_cond::DEST_ZEROTIER_ADDRESS => {
if let Some(a) = Address::from_u64(self.v.zt) {
if let Some(a) = Address::from_u64_v1(self.v.zt) {
v.match_dest_zerotier_address(not, or, a);
} else {
return v.invalid_rule();
@ -447,7 +447,7 @@ impl Rule {
impl Marshalable for Rule {
const MAX_MARSHAL_SIZE: usize = 21;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError> {
buf.append_u8(self.t)?;
unsafe {
match self.t & 0x3f {
@ -837,7 +837,7 @@ impl<'a> HumanReadableRule<'a> {
unsafe {
match *t {
action::TEE | action::WATCH | action::REDIRECT => {
r.v.forward.address = self.address?.into();
r.v.forward.address = self.address.as_ref()?.as_u64_v1();
r.v.forward.flags = self.flags?;
r.v.forward.length = self.length?;
}
@ -845,7 +845,7 @@ impl<'a> HumanReadableRule<'a> {
r.v.qos_bucket = self.qosBucket?;
}
match_cond::SOURCE_ZEROTIER_ADDRESS | match_cond::DEST_ZEROTIER_ADDRESS => {
r.v.zt = self.address?.into();
r.v.zt = self.address.as_ref()?.as_u64_v1();
}
match_cond::VLAN_ID => {
r.v.vlan_id = self.vlanId?;

View file

@ -4,7 +4,7 @@ use fastcdc::v2020;
use zerotier_crypto::hash::{SHA384, SHA384_HASH_SIZE};
use zerotier_utils::error::{InvalidFormatError, InvalidParameterError};
use zerotier_utils::memory::byte_array_chunks_exact;
use zerotier_utils::memory::array_chunks_exact;
const MAX_RECURSION_DEPTH: u8 = 64; // sanity limit, object would have to be quite huge to hit this
@ -30,7 +30,7 @@ impl ScatteredObject {
if (hl.len() % SHA384_HASH_SIZE) != 0 || hl.is_empty() {
return Err(InvalidFormatError);
}
for h in byte_array_chunks_exact::<SHA384_HASH_SIZE>(hl) {
for h in array_chunks_exact::<u8, SHA384_HASH_SIZE>(hl) {
if (h[SHA384_HASH_SIZE - 1] & 0x01) != 0 {
if let Some(chunk) = get_chunk(h) {
if depth < MAX_RECURSION_DEPTH {
@ -72,7 +72,7 @@ impl ScatteredObject {
let mut chunk_no = 0;
let mut missing_chunks = false;
for h in byte_array_chunks_exact::<SHA384_HASH_SIZE>(self.need.as_slice()) {
for h in array_chunks_exact::<u8, SHA384_HASH_SIZE>(self.need.as_slice()) {
let dc = self.data_chunks.get_mut(chunk_no).unwrap();
if dc.is_empty() {
debug_assert_eq!(h.len(), SHA384_HASH_SIZE);
@ -110,7 +110,7 @@ impl ScatteredObject {
/// This list can get longer through the course of object retrival since incoming chunks can
/// be chunks of hashes instead of chunks of data.
pub fn need(&self) -> impl Iterator<Item = &[u8; SHA384_HASH_SIZE]> {
byte_array_chunks_exact::<SHA384_HASH_SIZE>(self.need.as_slice())
array_chunks_exact::<u8, SHA384_HASH_SIZE>(self.need.as_slice())
}
}

View file

@ -16,7 +16,7 @@ impl InnerProtocolLayer for Switch {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
verb: u8,
@ -31,7 +31,7 @@ impl InnerProtocolLayer for Switch {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
in_re_verb: u8,
@ -48,7 +48,7 @@ impl InnerProtocolLayer for Switch {
app: &Application,
node: &Node<Application>,
source: &Arc<Peer<Application>>,
source_path: &Arc<Path<Application::LocalSocket, Application::LocalInterface>>,
source_path: &Arc<Path<Application>>,
source_hops: u8,
message_id: u64,
in_re_verb: u8,

View file

@ -1,12 +1,11 @@
use std::borrow::Cow;
use zerotier_utils::blob::Blob;
use zerotier_utils::flatsortedmap::FlatSortedMap;
use serde::{Deserialize, Serialize};
use crate::vl1::identity::IDENTITY_FINGERPRINT_SIZE;
use crate::vl1::inetaddress::InetAddress;
use crate::vl1::Address;
use crate::vl2::rule::Rule;
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)]
@ -47,7 +46,7 @@ pub struct Topology<'a> {
#[serde(skip_serializing_if = "FlatSortedMap::is_empty")]
#[serde(default)]
pub members: FlatSortedMap<'a, Blob<IDENTITY_FINGERPRINT_SIZE>, Member<'a>>,
pub members: FlatSortedMap<'a, Address, Member<'a>>,
}
#[inline(always)]

View file

@ -1,6 +1,6 @@
use std::io::Write;
use crate::vl1::identity::Identity;
use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::Address;
use crate::vl2::NetworkId;
@ -35,23 +35,18 @@ pub struct CertificateOfMembership {
impl CertificateOfMembership {
/// Create a new signed certificate of membership.
/// None is returned if an error occurs, such as the issuer missing its secrets.
pub fn new(issuer: &Identity, network_id: NetworkId, issued_to: &Identity, timestamp: i64, max_delta: u64) -> Option<Self> {
pub fn new(issuer: &IdentitySecret, network_id: NetworkId, issued_to: &Identity, timestamp: i64, max_delta: u64) -> Self {
let mut com = CertificateOfMembership {
network_id,
timestamp,
max_delta,
issued_to: issued_to.address,
issued_to: issued_to.address.clone(),
issued_to_fingerprint: Blob::default(),
signature: ArrayVec::new(),
};
com.issued_to_fingerprint = Blob::from(Self::v1_proto_issued_to_fingerprint(issued_to));
if let Some(signature) = issuer.sign(&com.v1_proto_get_qualifier_bytes(), true) {
com.signature = signature;
Some(com)
} else {
None
}
com.signature = issuer.sign(&com.v1_proto_get_qualifier_bytes());
com
}
fn v1_proto_get_qualifier_bytes(&self) -> [u8; 168] {
@ -64,7 +59,7 @@ impl CertificateOfMembership {
q[4] = u64::from(self.network_id).to_be();
q[5] = 0; // no disagreement permitted
q[6] = 2u64.to_be();
q[7] = u64::from(self.issued_to).to_be();
q[7] = self.issued_to.as_u64_v1().to_be();
q[8] = u64::MAX; // no to_be needed for all-1s
// This is a fix for a security issue in V1 in which an attacker could (with much CPU use)
@ -91,9 +86,9 @@ impl CertificateOfMembership {
/// Get the identity fingerprint used in V1, which only covers the curve25519 keys.
fn v1_proto_issued_to_fingerprint(issued_to: &Identity) -> [u8; 32] {
let mut v1_signee_hasher = SHA384::new();
v1_signee_hasher.update(&issued_to.address.to_bytes());
v1_signee_hasher.update(&issued_to.x25519);
v1_signee_hasher.update(&issued_to.ed25519);
v1_signee_hasher.update(issued_to.address.as_bytes_v1());
v1_signee_hasher.update(&issued_to.x25519.ecdh);
v1_signee_hasher.update(&issued_to.x25519.eddsa);
(&v1_signee_hasher.finish()[..32]).try_into().unwrap()
}
@ -104,7 +99,7 @@ impl CertificateOfMembership {
v.push(0);
v.push(7); // 7 qualifiers, big-endian 16-bit
let _ = v.write_all(&self.v1_proto_get_qualifier_bytes());
let _ = v.write_all(&controller_address.to_bytes());
let _ = v.write_all(controller_address.as_bytes_v1());
let _ = v.write_all(self.signature.as_bytes());
v
}
@ -160,7 +155,7 @@ impl CertificateOfMembership {
network_id: NetworkId::from_u64(network_id).ok_or(InvalidParameterError("invalid network ID"))?,
timestamp,
max_delta,
issued_to: Address::from_u64(issued_to).ok_or(InvalidParameterError("invalid issued to address"))?,
issued_to: Address::from_u64_v1(issued_to).ok_or(InvalidParameterError("invalid issued to address"))?,
issued_to_fingerprint: Blob::from(v1_fingerprint),
signature: {
let mut s = ArrayVec::new();

View file

@ -1,7 +1,8 @@
use std::collections::HashSet;
use std::io::Write;
use crate::vl1::{Address, Identity, InetAddress, MAC};
use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::{Address, InetAddress, MAC};
use crate::vl2::NetworkId;
use serde::{Deserialize, Serialize};
@ -33,7 +34,7 @@ pub struct CertificateOfOwnership {
pub timestamp: i64,
pub things: HashSet<Thing>,
pub issued_to: Address,
pub signature: ArrayVec<u8, { crate::vl1::identity::IDENTITY_MAX_SIGNATURE_SIZE }>,
pub signature: ArrayVec<u8, { Identity::MAX_SIGNATURE_SIZE }>,
}
impl CertificateOfOwnership {
@ -62,7 +63,7 @@ impl CertificateOfOwnership {
let _ = self.things.insert(Thing::Mac(mac));
}
fn internal_to_bytes(&self, for_sign: bool, signed_by: Address) -> Option<Vec<u8>> {
fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> Option<Vec<u8>> {
if self.things.len() > 0xffff {
return None;
}
@ -93,8 +94,8 @@ impl CertificateOfOwnership {
}
}
}
let _ = v.write_all(&self.issued_to.to_bytes());
let _ = v.write_all(&signed_by.to_bytes());
let _ = v.write_all(self.issued_to.as_bytes_v1());
let _ = v.write_all(signed_by.as_bytes_v1());
if for_sign {
v.push(0);
v.push(0);
@ -111,7 +112,7 @@ impl CertificateOfOwnership {
}
#[inline(always)]
pub fn to_bytes(&self, signed_by: Address) -> Option<Vec<u8>> {
pub fn to_bytes(&self, signed_by: &Address) -> Option<Vec<u8>> {
self.internal_to_bytes(false, signed_by)
}
@ -155,7 +156,7 @@ impl CertificateOfOwnership {
network_id: NetworkId::from_u64(network_id).ok_or(InvalidParameterError("invalid network ID"))?,
timestamp,
things,
issued_to: Address::from_bytes(&b[..5]).ok_or(InvalidParameterError("invalid address"))?,
issued_to: Address::from_bytes_v1(&b[..5]).ok_or(InvalidParameterError("invalid address"))?,
signature: {
let mut s = ArrayVec::new();
s.push_slice(&b[13..109]);
@ -167,13 +168,11 @@ impl CertificateOfOwnership {
}
/// Sign certificate of ownership for use by V1 nodes.
pub fn sign(&mut self, issuer: &Identity, issued_to: &Identity) -> bool {
self.issued_to = issued_to.address;
if let Some(to_sign) = self.internal_to_bytes(true, issuer.address) {
if let Some(signature) = issuer.sign(&to_sign.as_slice(), true) {
self.signature = signature;
return true;
}
pub fn sign(&mut self, issuer_address: &Address, issuer: &IdentitySecret, issued_to: &Identity) -> bool {
self.issued_to = issued_to.address.clone();
if let Some(to_sign) = self.internal_to_bytes(true, issuer_address) {
self.signature = issuer.sign(&to_sign.as_slice());
return true;
}
return false;
}

View file

@ -6,16 +6,17 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::vl1::{Address, Identity, InetAddress};
use crate::vl1::identity::Identity;
use crate::vl1::{Address, InetAddress};
use crate::vl2::iproute::IpRoute;
use crate::vl2::rule::Rule;
use crate::vl2::v1::{CertificateOfMembership, CertificateOfOwnership, Tag};
use crate::vl2::NetworkId;
use zerotier_utils::buffer::Buffer;
use zerotier_utils::buffer::{Buffer, OutOfBoundsError};
use zerotier_utils::dictionary::Dictionary;
use zerotier_utils::error::InvalidParameterError;
use zerotier_utils::marshalable::Marshalable;
use zerotier_utils::marshalable::{Marshalable, UnmarshalError};
/// Network configuration object sent to nodes by network controllers.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@ -130,24 +131,18 @@ impl NetworkConfig {
if !self.routes.is_empty() {
let r: Vec<IpRoute> = self.routes.iter().cloned().collect();
d.set_bytes(
proto_v1_field_name::network_config::ROUTES,
IpRoute::marshal_multiple_to_bytes(r.as_slice()).unwrap(),
);
d.set_bytes(proto_v1_field_name::network_config::ROUTES, marshal_multiple_to_bytes(r.as_slice()));
}
if !self.static_ips.is_empty() {
let ips: Vec<InetAddress> = self.static_ips.iter().cloned().collect();
d.set_bytes(
proto_v1_field_name::network_config::STATIC_IPS,
InetAddress::marshal_multiple_to_bytes(ips.as_slice()).unwrap(),
);
d.set_bytes(proto_v1_field_name::network_config::STATIC_IPS, marshal_multiple_to_bytes(ips.as_slice()));
}
if !self.rules.is_empty() {
d.set_bytes(
proto_v1_field_name::network_config::RULES,
Rule::marshal_multiple_to_bytes(self.rules.as_slice()).unwrap(),
marshal_multiple_to_bytes(self.rules.as_slice()),
);
}
@ -188,7 +183,7 @@ impl NetworkConfig {
if !v1cred.certificates_of_ownership.is_empty() {
let mut certs = Vec::with_capacity(v1cred.certificates_of_ownership.len() * 256);
for c in v1cred.certificates_of_ownership.iter() {
let _ = certs.write_all(c.to_bytes(controller_identity.address)?.as_slice());
let _ = certs.write_all(c.to_bytes(&controller_identity.address)?.as_slice());
}
d.set_bytes(proto_v1_field_name::network_config::CERTIFICATES_OF_OWNERSHIP, certs);
}
@ -196,7 +191,7 @@ impl NetworkConfig {
if !v1cred.tags.is_empty() {
let mut tags = Vec::with_capacity(v1cred.tags.len() * 256);
for (_, t) in v1cred.tags.iter() {
let _ = tags.write_all(t.to_bytes(controller_identity.address).as_ref());
let _ = tags.write_all(t.to_bytes(&controller_identity.address).as_ref());
}
d.set_bytes(proto_v1_field_name::network_config::TAGS, tags);
}
@ -256,7 +251,7 @@ impl NetworkConfig {
nc.multicast_limit = d.get_u64(proto_v1_field_name::network_config::MULTICAST_LIMIT).unwrap_or(0) as u32;
if let Some(routes_bin) = d.get_bytes(proto_v1_field_name::network_config::ROUTES) {
for r in IpRoute::unmarshal_multiple_from_bytes(routes_bin)
for r in unmarshal_multiple_from_bytes(routes_bin)
.map_err(|_| InvalidParameterError("invalid route object(s)"))?
.drain(..)
{
@ -265,7 +260,7 @@ impl NetworkConfig {
}
if let Some(static_ips_bin) = d.get_bytes(proto_v1_field_name::network_config::STATIC_IPS) {
for ip in InetAddress::unmarshal_multiple_from_bytes(static_ips_bin)
for ip in unmarshal_multiple_from_bytes(static_ips_bin)
.map_err(|_| InvalidParameterError("invalid route object(s)"))?
.drain(..)
{
@ -274,7 +269,7 @@ impl NetworkConfig {
}
if let Some(rules_bin) = d.get_bytes(proto_v1_field_name::network_config::RULES) {
nc.rules = Rule::unmarshal_multiple_from_bytes(rules_bin).map_err(|_| InvalidParameterError("invalid route object(s)"))?;
nc.rules = unmarshal_multiple_from_bytes(rules_bin).map_err(|_| InvalidParameterError("invalid route object(s)"))?;
}
if let Some(dns_bin) = d.get_bytes(proto_v1_field_name::network_config::DNS) {
@ -434,7 +429,7 @@ pub struct V1Credentials {
impl Marshalable for IpRoute {
const MAX_MARSHAL_SIZE: usize = (InetAddress::MAX_MARSHAL_SIZE * 2) + 2 + 2;
fn marshal<const BL: usize>(&self, buf: &mut zerotier_utils::buffer::Buffer<BL>) -> Result<(), zerotier_utils::marshalable::UnmarshalError> {
fn marshal<const BL: usize>(&self, buf: &mut zerotier_utils::buffer::Buffer<BL>) -> Result<(), OutOfBoundsError> {
self.target.marshal(buf)?;
if let Some(via) = self.via.as_ref() {
via.marshal(buf)?;
@ -477,3 +472,34 @@ impl Marshalable for IpRoute {
})
}
}
const TEMP_BUF_SIZE: usize = 1024;
fn marshal_multiple_to_bytes<M: Marshalable>(multiple: &[M]) -> Vec<u8> {
debug_assert!(M::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp = Vec::with_capacity(M::MAX_MARSHAL_SIZE * multiple.len());
for m in multiple.iter() {
let _ = tmp.write_all(m.to_buffer::<TEMP_BUF_SIZE>().unwrap().as_bytes());
}
tmp
}
fn unmarshal_multiple_from_bytes<M: Marshalable>(mut bytes: &[u8]) -> Result<Vec<M>, UnmarshalError> {
debug_assert!(M::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp: Buffer<TEMP_BUF_SIZE> = Buffer::new();
let mut v: Vec<M> = Vec::new();
while bytes.len() > 0 {
let chunk_size = bytes.len().min(M::MAX_MARSHAL_SIZE);
if tmp.append_bytes(&bytes[..chunk_size]).is_err() {
return Err(UnmarshalError::OutOfBounds);
}
let mut cursor = 0;
v.push(M::unmarshal(&mut tmp, &mut cursor)?);
if cursor == 0 {
return Err(UnmarshalError::InvalidData);
}
let _ = tmp.erase_first_n(cursor);
bytes = &bytes[chunk_size..];
}
Ok(v)
}

View file

@ -1,11 +1,11 @@
use std::io::Write;
use zerotier_crypto::typestate::Valid;
use zerotier_utils::arrayvec::ArrayVec;
use serde::{Deserialize, Serialize};
use crate::vl1::{Address, Identity};
use crate::vl1::identity::IdentitySecret;
use crate::vl1::Address;
use crate::vl2::v1::CredentialType;
use crate::vl2::NetworkId;
@ -26,9 +26,10 @@ impl Revocation {
threshold: i64,
target: Address,
issued_to: Address,
signer: &Valid<Identity>,
signer_address: &Address,
signer: &IdentitySecret,
fast_propagate: bool,
) -> Option<Self> {
) -> Self {
let mut r = Self {
network_id,
threshold,
@ -37,28 +38,24 @@ impl Revocation {
signature: ArrayVec::new(),
fast_propagate,
};
if let Some(sig) = signer.sign(r.internal_to_bytes(true, signer.address).as_bytes(), true) {
r.signature.as_mut().copy_from_slice(sig.as_bytes());
Some(r)
} else {
None
}
r.signature = signer.sign(r.internal_to_bytes(true, signer_address).as_bytes());
r
}
fn internal_to_bytes(&self, for_sign: bool, signed_by: Address) -> ArrayVec<u8, 256> {
fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> ArrayVec<u8, 256> {
let mut v = ArrayVec::new();
if for_sign {
let _ = v.write_all(&[0x7f; 8]);
}
let _ = v.write_all(&[0; 4]);
let _ = v.write_all(&((self.threshold as u32) ^ (u64::from(self.target) as u32)).to_be_bytes()); // ID only used in V1, arbitrary
let _ = v.write_all(&((self.threshold as u32) ^ (self.target.as_u64_v1() as u32)).to_be_bytes()); // ID only used in V1, arbitrary
let _ = v.write_all(&self.network_id.to_bytes());
let _ = v.write_all(&[0; 8]);
let _ = v.write_all(&self.threshold.to_be_bytes());
let _ = v.write_all(&(self.fast_propagate as u64).to_be_bytes()); // 0x1 is the flag for this
let _ = v.write_all(&self.target.to_bytes());
let _ = v.write_all(&signed_by.to_bytes());
let _ = v.write_all(self.target.as_bytes_v1());
let _ = v.write_all(signed_by.as_bytes_v1());
v.push(CredentialType::CertificateOfMembership as u8);
if for_sign {
@ -74,7 +71,7 @@ impl Revocation {
}
#[inline(always)]
pub fn v1_proto_to_bytes(&self, controller_address: Address) -> ArrayVec<u8, 256> {
pub fn v1_proto_to_bytes(&self, controller_address: &Address) -> ArrayVec<u8, 256> {
self.internal_to_bytes(false, controller_address)
}
}

View file

@ -1,6 +1,6 @@
use std::io::Write;
use crate::vl1::identity::Identity;
use crate::vl1::identity::{Identity, IdentitySecret};
use crate::vl1::Address;
use crate::vl2::NetworkId;
@ -21,24 +21,29 @@ pub struct Tag {
}
impl Tag {
pub fn new(id: u32, value: u32, issuer: &Identity, network_id: NetworkId, issued_to: &Identity, timestamp: i64) -> Option<Self> {
pub fn new(
id: u32,
value: u32,
issuer_address: &Address,
issuer: &IdentitySecret,
network_id: NetworkId,
issued_to: &Identity,
timestamp: i64,
) -> Self {
let mut tag = Self {
network_id,
timestamp,
issued_to: issued_to.address,
issued_to: issued_to.address.clone(),
id,
value,
signature: Blob::default(),
};
let to_sign = tag.internal_to_bytes(true, issuer.address);
if let Some(signature) = issuer.sign(to_sign.as_ref(), true) {
tag.signature.as_mut().copy_from_slice(signature.as_bytes());
return Some(tag);
}
return None;
let to_sign = tag.internal_to_bytes(true, issuer_address);
tag.signature.as_mut().copy_from_slice(issuer.sign(to_sign.as_ref()).as_bytes());
tag
}
fn internal_to_bytes(&self, for_sign: bool, signed_by: Address) -> ArrayVec<u8, 256> {
fn internal_to_bytes(&self, for_sign: bool, signed_by: &Address) -> ArrayVec<u8, 256> {
let mut v = ArrayVec::new();
if for_sign {
let _ = v.write_all(&[0x7f; 8]);
@ -47,8 +52,8 @@ impl Tag {
let _ = v.write_all(&self.timestamp.to_be_bytes());
let _ = v.write_all(&self.id.to_be_bytes());
let _ = v.write_all(&self.value.to_be_bytes());
let _ = v.write_all(&self.issued_to.to_bytes());
let _ = v.write_all(&signed_by.to_bytes());
let _ = v.write_all(self.issued_to.as_bytes_v1());
let _ = v.write_all(signed_by.as_bytes_v1());
if !for_sign {
v.push(1);
v.push(0);
@ -64,7 +69,7 @@ impl Tag {
}
#[inline(always)]
pub fn to_bytes(&self, signed_by: Address) -> ArrayVec<u8, 256> {
pub fn to_bytes(&self, signed_by: &Address) -> ArrayVec<u8, 256> {
self.internal_to_bytes(false, signed_by)
}
@ -77,7 +82,7 @@ impl Tag {
Self {
network_id: NetworkId::from_bytes(&b[0..8]).ok_or(InvalidParameterError("invalid network ID"))?,
timestamp: i64::from_be_bytes(b[8..16].try_into().unwrap()),
issued_to: Address::from_bytes(&b[24..29]).ok_or(InvalidParameterError("invalid address"))?,
issued_to: Address::from_bytes_v1(&b[24..29]).ok_or(InvalidParameterError("invalid address"))?,
id: u32::from_be_bytes(b[16..20].try_into().unwrap()),
value: u32::from_be_bytes(b[20..24].try_into().unwrap()),
signature: {

View file

@ -14,7 +14,6 @@ serde = { version = "^1", features = ["derive"], default-features = false }
serde_json = { version = "^1", features = ["std"], default-features = false }
tokio = { version = "^1", default-features = false, features = ["fs", "io-util", "io-std", "net", "process", "rt", "rt-multi-thread", "signal", "sync", "time"], optional = true }
futures-util = { version = "^0", optional = true }
base64 = "0.20.0"
[target."cfg(windows)".dependencies]
winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }

View file

@ -1,20 +0,0 @@
/* 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) ZeroTier, Inc.
* https://www.zerotier.com/
*/
const BASE64_URL_SAFE_NO_PAD_ENGINE: base64::engine::fast_portable::FastPortable =
base64::engine::fast_portable::FastPortable::from(&base64::alphabet::URL_SAFE, base64::engine::fast_portable::NO_PAD);
/// Encode base64 using URL-safe alphabet and no padding.
pub fn encode_url_nopad(bytes: &[u8]) -> String {
base64::encode_engine(bytes, &BASE64_URL_SAFE_NO_PAD_ENGINE)
}
/// Decode base64 using URL-safe alphabet and no padding, or None on error.
pub fn decode_url_nopad(b64: &str) -> Option<Vec<u8>> {
base64::decode_engine(b64, &BASE64_URL_SAFE_NO_PAD_ENGINE).ok()
}

View file

@ -7,7 +7,6 @@
*/
pub mod arrayvec;
pub mod base64;
pub mod blob;
pub mod buffer;
pub mod cast;
@ -24,9 +23,11 @@ pub mod json;
pub mod marshalable;
pub mod memory;
pub mod pool;
pub mod proquint;
#[cfg(feature = "tokio")]
pub mod reaper;
pub mod ringbuffer;
pub mod str;
pub mod sync;
pub mod varint;

View file

@ -8,20 +8,15 @@
use std::error::Error;
use std::fmt::{Debug, Display};
use std::io::Write;
use crate::buffer::Buffer;
/// Must be larger than any object we want to use with to_bytes() or from_bytes().
/// This hack can go away once Rust allows us to reference trait consts as generics.
const TEMP_BUF_SIZE: usize = 8192;
use crate::buffer::{Buffer, OutOfBoundsError};
/// A super-lightweight zero-allocation serialization interface.
pub trait Marshalable: Sized {
const MAX_MARSHAL_SIZE: usize;
/// Write this object into a buffer.
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), UnmarshalError>;
fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> Result<(), OutOfBoundsError>;
/// Read this object from a buffer.
///
@ -35,88 +30,101 @@ pub trait Marshalable: Sized {
/// 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]
fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, UnmarshalError> {
fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, OutOfBoundsError> {
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]
fn from_buffer<const BL: usize>(buf: &Buffer<BL>) -> Result<Self, UnmarshalError> {
let mut tmp = 0;
Self::unmarshal(buf, &mut tmp)
}
/// Marshal and convert to a Rust vector.
#[inline]
fn to_bytes(&self) -> Vec<u8> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new();
assert!(self.marshal(&mut tmp).is_ok()); // panics if TEMP_BUF_SIZE is too small
tmp.as_bytes().to_vec()
}
/// Unmarshal from a raw slice.
#[inline]
fn from_bytes(b: &[u8]) -> Result<Self, UnmarshalError> {
if b.len() <= TEMP_BUF_SIZE {
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
assert!(tmp.append_bytes(b).is_ok());
let mut cursor = 0;
Self::unmarshal(&tmp, &mut cursor)
} else {
Err(UnmarshalError::OutOfBounds)
/*
/// 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]
fn to_buffer<const BL: usize>(&self) -> Result<Buffer<BL>, UnmarshalError> {
let mut tmp = Buffer::new();
self.marshal(&mut tmp)?;
Ok(tmp)
}
}
/// Marshal a slice of marshalable objects to a concatenated byte vector.
#[inline]
fn marshal_multiple_to_bytes(objects: &[Self]) -> Result<Vec<u8>, UnmarshalError> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
let mut v: Vec<u8> = Vec::with_capacity(objects.len() * Self::MAX_MARSHAL_SIZE);
for i in objects.iter() {
i.marshal(&mut tmp)?;
let _ = v.write_all(tmp.as_bytes());
tmp.clear();
/// Unmarshal this object from a buffer.
///
/// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor.
#[inline]
fn from_buffer<const BL: usize>(buf: &Buffer<BL>) -> Result<Self, UnmarshalError> {
let mut tmp = 0;
Self::unmarshal(buf, &mut tmp)
}
Ok(v)
}
/// Unmarshal a concatenated byte slice of marshalable objects.
#[inline]
fn unmarshal_multiple_from_bytes(mut bytes: &[u8]) -> Result<Vec<Self>, UnmarshalError> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
let mut v: Vec<Self> = Vec::new();
while bytes.len() > 0 {
let chunk_size = bytes.len().min(Self::MAX_MARSHAL_SIZE);
if tmp.append_bytes(&bytes[..chunk_size]).is_err() {
return Err(UnmarshalError::OutOfBounds);
/// Marshal and convert to a Rust vector.
#[inline]
fn to_bytes(&self) -> Vec<u8> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new();
assert!(self.marshal(&mut tmp).is_ok()); // panics if TEMP_BUF_SIZE is too small
tmp.as_bytes().to_vec()
}
/// Unmarshal from a raw slice.
#[inline]
fn from_bytes(b: &[u8]) -> Result<Self, UnmarshalError> {
if b.len() <= TEMP_BUF_SIZE {
let mut tmp = Buffer::<TEMP_BUF_SIZE>::new_boxed();
assert!(tmp.append_bytes(b).is_ok());
let mut cursor = 0;
Self::unmarshal(&tmp, &mut cursor)
} else {
Err(UnmarshalError::OutOfBounds)
}
let mut cursor = 0;
v.push(Self::unmarshal(&mut tmp, &mut cursor)?);
if cursor == 0 {
return Err(UnmarshalError::InvalidData);
}
let _ = tmp.erase_first_n(cursor);
bytes = &bytes[chunk_size..];
}
Ok(v)
}
/// Unmarshal a buffer with a byte slice of marshalable objects.
#[inline]
fn unmarshal_multiple<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize, eof: usize) -> Result<Vec<Self>, UnmarshalError> {
let mut v: Vec<Self> = Vec::new();
while *cursor < eof {
v.push(Self::unmarshal(buf, cursor)?);
/// Marshal a slice of marshalable objects to a concatenated byte vector.
#[inline]
fn marshal_multiple_to_bytes(objects: &[Self]) -> Result<Vec<u8>, UnmarshalError> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
let mut v: Vec<u8> = Vec::with_capacity(objects.len() * Self::MAX_MARSHAL_SIZE);
for i in objects.iter() {
i.marshal(&mut tmp)?;
let _ = v.write_all(tmp.as_bytes());
tmp.clear();
}
Ok(v)
}
Ok(v)
}
/// Unmarshal a concatenated byte slice of marshalable objects.
#[inline]
fn unmarshal_multiple_from_bytes(mut bytes: &[u8]) -> Result<Vec<Self>, UnmarshalError> {
assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE);
let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new();
let mut v: Vec<Self> = Vec::new();
while bytes.len() > 0 {
let chunk_size = bytes.len().min(Self::MAX_MARSHAL_SIZE);
if tmp.append_bytes(&bytes[..chunk_size]).is_err() {
return Err(UnmarshalError::OutOfBounds);
}
let mut cursor = 0;
v.push(Self::unmarshal(&mut tmp, &mut cursor)?);
if cursor == 0 {
return Err(UnmarshalError::InvalidData);
}
let _ = tmp.erase_first_n(cursor);
bytes = &bytes[chunk_size..];
}
Ok(v)
}
/// Unmarshal a buffer with a byte slice of marshalable objects.
#[inline]
fn unmarshal_multiple<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize, eof: usize) -> Result<Vec<Self>, UnmarshalError> {
let mut v: Vec<Self> = Vec::new();
while *cursor < eof {
v.push(Self::unmarshal(buf, cursor)?);
}
Ok(v)
}
*/
}
pub enum UnmarshalError {

View file

@ -59,9 +59,9 @@ pub fn load_raw<T: Copy>(src: &[u8]) -> T {
}
}
/// Our version of the not-yet-stable array_chunks method in slice, but only for byte arrays.
/// Our version of the not-yet-stable array_chunks method in slice.
#[inline(always)]
pub fn byte_array_chunks_exact<const S: usize>(a: &[u8]) -> impl Iterator<Item = &[u8; S]> {
pub fn array_chunks_exact<T, const S: usize>(a: &[T]) -> impl Iterator<Item = &[T; S]> {
let mut i = 0;
let l = a.len();
std::iter::from_fn(move || {

98
utils/src/proquint.rs Normal file
View file

@ -0,0 +1,98 @@
// This is a trimmed down version of: https://github.com/christian-blades-cb/proquint-rs
// BSD license
const UINT2CONSONANT: [char; 16] = ['b', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'z'];
const UINT2VOWEL: &'static [char] = &['a', 'i', 'o', 'u'];
const MASK_FIRST4_U16: u16 = 0xF000;
const MASK_FIRST2_U16: u16 = 0xC000;
macro_rules! decons {
($res:ident, $bitcounter:ident, $x:expr) => {{
$bitcounter += 4;
$res = $res.wrapping_shl(4);
$res += $x;
}};
}
macro_rules! devowel {
($res:ident, $bitcounter:ident, $x:expr) => {{
$bitcounter += 2;
$res = $res.wrapping_shl(2);
$res += $x;
}};
}
macro_rules! cons_u16 {
($i:ident, $out:ident) => {
let j: u16 = ($i & MASK_FIRST4_U16).wrapping_shr(12);
$i = $i.wrapping_shl(4);
$out.push(UINT2CONSONANT[j as usize]);
};
}
macro_rules! vowel_u16 {
($i:ident, $out:ident) => {
let j: u16 = ($i & MASK_FIRST2_U16).wrapping_shr(14);
$i = $i.wrapping_shl(2);
$out.push(UINT2VOWEL[j as usize]);
};
}
pub fn u16_from_quint(quint: &str) -> Option<u16> {
let mut bitcounter = 0usize;
let mut res = 0u16;
for c in quint.chars() {
match c {
'b' => decons!(res, bitcounter, 0u16),
'd' => decons!(res, bitcounter, 1u16),
'f' => decons!(res, bitcounter, 2u16),
'g' => decons!(res, bitcounter, 3u16),
'h' => decons!(res, bitcounter, 4u16),
'j' => decons!(res, bitcounter, 5u16),
'k' => decons!(res, bitcounter, 6u16),
'l' => decons!(res, bitcounter, 7u16),
'm' => decons!(res, bitcounter, 8u16),
'n' => decons!(res, bitcounter, 9u16),
'p' => decons!(res, bitcounter, 10u16),
'r' => decons!(res, bitcounter, 11u16),
's' => decons!(res, bitcounter, 12u16),
't' => decons!(res, bitcounter, 13u16),
'v' => decons!(res, bitcounter, 14u16),
'z' => decons!(res, bitcounter, 15u16),
'a' => devowel!(res, bitcounter, 0u16),
'i' => devowel!(res, bitcounter, 1u16),
'o' => devowel!(res, bitcounter, 2u16),
'u' => devowel!(res, bitcounter, 3u16),
_ => {}
}
}
if bitcounter == 16 {
Some(res)
} else {
None
}
}
pub fn u16_to_quint(mut i: u16, out: &mut String) {
cons_u16!(i, out);
vowel_u16!(i, out);
cons_u16!(i, out);
vowel_u16!(i, out);
out.push(UINT2CONSONANT[(i & MASK_FIRST4_U16).wrapping_shr(12) as usize]);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quint_u16() {
let mut s = String::with_capacity(16);
for i in u16::MIN..=u16::MAX {
s.clear();
u16_to_quint(i, &mut s);
assert_eq!(s.len(), 5);
assert_eq!(u16_from_quint(s.as_str()).unwrap(), i);
}
}
}

56
utils/src/str.rs Normal file
View file

@ -0,0 +1,56 @@
/* 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) ZeroTier, Inc.
* https://www.zerotier.com/
*/
use crate::hex::HEX_CHARS;
/// Escape non-ASCII-printable characters in a string.
/// This also escapes quotes and other sensitive characters that cause issues on terminals.
pub fn escape(b: &[u8]) -> String {
let mut s = String::with_capacity(b.len() * 2);
for b in b.iter() {
let b = *b;
if b >= 43 && b <= 126 && b != 92 && b != 96 {
s.push(b as char);
} else {
s.push('\\');
s.push(HEX_CHARS[(b.wrapping_shr(4) & 0xf) as usize] as char);
s.push(HEX_CHARS[(b & 0xf) as usize] as char);
}
}
s
}
/// Unescape a string with \XX hexadecimal escapes.
pub fn unescape(s: &str) -> Vec<u8> {
let mut b = Vec::with_capacity(s.len());
let mut s = s.as_bytes();
while let Some(c) = s.first() {
let c = *c;
if c == b'\\' {
if s.len() < 3 {
break;
}
let mut cc = 0u8;
for c in [s[1], s[2]] {
if c >= 48 && c <= 57 {
cc = cc.wrapping_shl(4) | (c - 48);
} else if c >= 65 && c <= 70 {
cc = cc.wrapping_shl(4) | (c - 55);
} else if c >= 97 && c <= 102 {
cc = cc.wrapping_shl(4) | (c - 87);
}
}
b.push(cc);
s = &s[3..];
} else {
b.push(c);
s = &s[1..];
}
}
b
}