mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-19 13:36:54 +02:00
A whole heap of changes including: addresses are now 48 bits, base64 is gone, and rewrote Identity for simplification.
This commit is contained in:
parent
c4cb7c4429
commit
3f047bc8dc
34 changed files with 1090 additions and 1790 deletions
|
@ -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,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod bn;
|
||||
#[allow(unused)]
|
||||
mod cipher_ctx;
|
||||
mod ec;
|
||||
mod error;
|
||||
|
|
|
@ -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 = "*"
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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 => {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
98
utils/src/proquint.rs
Normal 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
56
utils/src/str.rs
Normal 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
|
||||
}
|
Loading…
Add table
Reference in a new issue