mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
A bunch of cleanup, and make MAC and Address idiomatic in the sense that they have no native nil value and use NonZero for zero-cost use of Option<>
This commit is contained in:
parent
40941a25f7
commit
bd4367014e
15 changed files with 375 additions and 452 deletions
|
@ -1,54 +1,73 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::num::NonZeroU64;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::hex::HEX_CHARS;
|
||||
use crate::vl1::protocol::{ADDRESS_RESERVED_PREFIX, ADDRESS_SIZE};
|
||||
use crate::vl1::buffer::Buffer;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Address(u64);
|
||||
#[repr(transparent)]
|
||||
pub struct Address(NonZeroU64);
|
||||
|
||||
impl Address {
|
||||
/// Get an address from a 64-bit integer or return None if it is zero or reserved.
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<Address, InvalidFormatError> {
|
||||
if b.len() >= ADDRESS_SIZE {
|
||||
Ok(Address((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 fn from_u64(i: u64) -> Option<Address> {
|
||||
if i != 0 && (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 {
|
||||
Some(Address(unsafe { NonZeroU64::new_unchecked(i & 0xffffffffff) }))
|
||||
} else {
|
||||
Err(InvalidFormatError)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an address from a byte slice or return None if it is zero or reserved.
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Option<Address> {
|
||||
if b.len() >= ADDRESS_SIZE {
|
||||
let i = (b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64;
|
||||
if i != 0 && (i >> 32) != ADDRESS_RESERVED_PREFIX as u64 {
|
||||
Some(Address(unsafe { NonZeroU64::new_unchecked(i) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_reserved(&self) -> bool {
|
||||
(self.0 >> 32) as usize == ADDRESS_RESERVED_PREFIX as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
!self.is_nil() && !self.is_reserved()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_bytes(&self) -> [u8; ADDRESS_SIZE] {
|
||||
[(self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
|
||||
let i = self.0.get();
|
||||
[(i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_u64(&self) -> u64 {
|
||||
self.0
|
||||
pub fn to_u64(&self) -> u64 { self.0.get() }
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||
buf.append_and_init_bytes_fixed(|b: &mut [u8; ADDRESS_SIZE]| {
|
||||
let i = self.0.get();
|
||||
b[0] = (i >> 32) as u8;
|
||||
b[1] = (i >> 24) as u8;
|
||||
b[2] = (i >> 16) as u8;
|
||||
b[3] = (i >> 8) as u8;
|
||||
b[4] = i as u8;
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Option<Self>> {
|
||||
buf.read_bytes_fixed::<{ ADDRESS_SIZE }>(cursor).map(|b| Self::from_bytes(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Address {
|
||||
fn to_string(&self) -> String {
|
||||
let mut v = self.0 << 24;
|
||||
let mut s = String::new();
|
||||
s.reserve(ADDRESS_SIZE * 2);
|
||||
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_CHARS[(v >> 60) as usize] as char);
|
||||
v <<= 4;
|
||||
|
@ -61,42 +80,13 @@ impl FromStr for Address {
|
|||
type Err = InvalidFormatError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Address::from_bytes(crate::util::hex::from_string(s).as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Address {
|
||||
#[inline(always)]
|
||||
fn default() -> Address {
|
||||
Address(0)
|
||||
Address::from_bytes(crate::util::hex::from_string(s).as_slice()).map_or_else(|| Err(InvalidFormatError), |a| Ok(a))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Address {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8; ADDRESS_SIZE]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(b: &[u8; 5]) -> Address {
|
||||
Address((b[0] as u64) << 32 | (b[1] as u64) << 24 | (b[2] as u64) << 16 | (b[3] as u64) << 8 | b[4] as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; ADDRESS_SIZE]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(b: [u8; 5]) -> Address {
|
||||
Self::from(&b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Address {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> Address {
|
||||
debug_assert!((i >> 24) == 0);
|
||||
Address(i)
|
||||
state.write_u64(self.0.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::io::Write;
|
||||
use std::mem::{size_of, MaybeUninit};
|
||||
use std::ptr::write_bytes;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::util::pool::PoolFactory;
|
||||
|
||||
|
@ -19,18 +18,14 @@ unsafe impl<const L: usize> RawObject for Buffer<L> {}
|
|||
|
||||
impl<const L: usize> Default for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self(0, [0_u8; L])
|
||||
}
|
||||
fn default() -> Self { Self(0, [0_u8; L]) }
|
||||
}
|
||||
|
||||
const OVERFLOW_ERR_MSG: &'static str = "overflow";
|
||||
|
||||
impl<const L: usize> Buffer<L> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(0, [0_u8; L])
|
||||
}
|
||||
pub fn new() -> Self { Self(0, [0_u8; L]) }
|
||||
|
||||
/// Get a Buffer initialized with a copy of a byte slice.
|
||||
#[inline(always)]
|
||||
|
@ -47,14 +42,10 @@ impl<const L: usize> Buffer<L> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.1[0..self.0]
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] { &self.1[0..self.0] }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.1[0..self.0]
|
||||
}
|
||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.1[0..self.0] }
|
||||
|
||||
/// Get all bytes after a given position.
|
||||
#[inline(always)]
|
||||
|
@ -74,14 +65,10 @@ impl<const L: usize> Buffer<L> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
pub fn len(&self) -> usize { self.0 }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.0 == 0 }
|
||||
|
||||
/// Append a packed structure and call a function to initialize it in place.
|
||||
/// Anything not initialized will be zero.
|
||||
|
@ -159,6 +146,22 @@ impl<const L: usize> Buffer<L> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Append a variable length integer to this buffer.
|
||||
///
|
||||
/// Varints are encoded as a series of 7-bit bytes terminated by a final 7-bit byte whose
|
||||
/// most significant bit is set. Unlike fixed size integers varints are written in little
|
||||
/// endian order (in 7-bit chunks).
|
||||
///
|
||||
/// They are slower than fixed size values so they should not be used in formats that are
|
||||
/// created or parsed in very speed-critical paths.
|
||||
pub fn append_varint(&mut self, mut i: u64) -> std::io::Result<()> {
|
||||
while i >= 0x80 {
|
||||
self.append_u8((i as u8) & 0x7f)?;
|
||||
i >>= 7;
|
||||
}
|
||||
self.append_u8((i as u8) | 0x80)
|
||||
}
|
||||
|
||||
/// Append a byte
|
||||
#[inline(always)]
|
||||
pub fn append_u8(&mut self, i: u8) -> std::io::Result<()> {
|
||||
|
@ -295,6 +298,23 @@ impl<const L: usize> Buffer<L> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the next variable length integer and advance the cursor by its length in bytes.
|
||||
pub fn read_varint(&self, cursor: &mut usize) -> std::io::Result<u64> {
|
||||
let mut i = 0_u64;
|
||||
let mut p = 0;
|
||||
loop {
|
||||
let b = self.read_u8(cursor)?;
|
||||
if (b & 0x80) == 0 {
|
||||
i |= (b as u64) << p;
|
||||
p += 7;
|
||||
} else {
|
||||
i |= ((b & 0x7f) as u64) << p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Get the next u8 and advance the cursor.
|
||||
#[inline(always)]
|
||||
pub fn read_u8(&self, cursor: &mut usize) -> std::io::Result<u8> {
|
||||
|
@ -366,35 +386,25 @@ impl<const L: usize> Write for Buffer<L> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsRef<[u8]> for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||
}
|
||||
|
||||
impl<const L: usize> AsMut<[u8]> for Buffer<L> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.as_bytes_mut()
|
||||
}
|
||||
fn as_mut(&mut self) -> &mut [u8] { self.as_bytes_mut() }
|
||||
}
|
||||
|
||||
pub struct PooledBufferFactory<const L: usize>;
|
||||
|
||||
impl<const L: usize> PoolFactory<Buffer<L>> for PooledBufferFactory<L> {
|
||||
#[inline(always)]
|
||||
fn create(&self) -> Buffer<L> {
|
||||
Buffer::new()
|
||||
}
|
||||
fn create(&self) -> Buffer<L> { Buffer::new() }
|
||||
|
||||
#[inline(always)]
|
||||
fn reset(&self, obj: &mut Buffer<L>) {
|
||||
obj.clear();
|
||||
}
|
||||
fn reset(&self, obj: &mut Buffer<L>) { obj.clear(); }
|
||||
}
|
||||
|
|
|
@ -44,25 +44,15 @@ fn append_printable(s: &mut String, b: &[u8]) {
|
|||
}
|
||||
|
||||
impl Dictionary {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(BTreeMap::new())
|
||||
}
|
||||
pub fn new() -> Self { Self(BTreeMap::new()) }
|
||||
|
||||
pub fn clear(&mut self) { self.0.clear() }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||
|
||||
pub fn get_str(&self, k: &str) -> Option<&str> {
|
||||
self.0.get(k).map_or(None, |v| std::str::from_utf8(v.as_slice()).map_or(None, |s| Some(s)))
|
||||
|
|
|
@ -117,13 +117,13 @@ impl Endpoint {
|
|||
Endpoint::Http(url) => {
|
||||
buf.append_u8(16 + TYPE_HTTP)?;
|
||||
let b = url.as_bytes();
|
||||
buf.append_u16(b.len() as u16)?;
|
||||
buf.append_varint(b.len() as u64)?;
|
||||
buf.append_bytes(b)
|
||||
}
|
||||
Endpoint::WebRTC(offer) => {
|
||||
buf.append_u8(16 + TYPE_WEBRTC)?;
|
||||
let b = offer.as_slice();
|
||||
buf.append_u16(b.len() as u16)?;
|
||||
buf.append_varint(b.len() as u64)?;
|
||||
buf.append_bytes(b)
|
||||
}
|
||||
}
|
||||
|
@ -139,24 +139,33 @@ impl Endpoint {
|
|||
Ok(Endpoint::IpUdp(ip))
|
||||
}
|
||||
} else {
|
||||
let read_mac = |buf: &Buffer<BL>, cursor: &mut usize| {
|
||||
let m = MAC::unmarshal(buf, cursor)?;
|
||||
if m.is_some() {
|
||||
Ok(m.unwrap())
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid MAC address"))
|
||||
}
|
||||
};
|
||||
match type_byte - 16 {
|
||||
TYPE_NIL => Ok(Endpoint::Nil),
|
||||
TYPE_ZEROTIER => Ok(Endpoint::ZeroTier(Address::from(buf.read_bytes_fixed(cursor)?))),
|
||||
TYPE_ETHERNET => Ok(Endpoint::Ethernet(MAC::from(buf.read_bytes_fixed(cursor)?))),
|
||||
TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(MAC::from(buf.read_bytes_fixed(cursor)?))),
|
||||
TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(MAC::from(buf.read_bytes_fixed(cursor)?))),
|
||||
TYPE_ZEROTIER => {
|
||||
let zt = Address::unmarshal(buf, cursor)?;
|
||||
if zt.is_some() {
|
||||
Ok(Endpoint::ZeroTier(zt.unwrap()))
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid ZeroTier address"))
|
||||
}
|
||||
},
|
||||
TYPE_ETHERNET => Ok(Endpoint::Ethernet(read_mac(buf, cursor)?)),
|
||||
TYPE_WIFIDIRECT => Ok(Endpoint::WifiDirect(read_mac(buf, cursor)?)),
|
||||
TYPE_BLUETOOTH => Ok(Endpoint::Bluetooth(read_mac(buf, cursor)?)),
|
||||
TYPE_IP => Ok(Endpoint::Ip(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_IPUDP => Ok(Endpoint::IpUdp(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_IPTCP => Ok(Endpoint::IpTcp(InetAddress::unmarshal(buf, cursor)?)),
|
||||
TYPE_HTTP => {
|
||||
let l = buf.read_u16(cursor)?;
|
||||
Ok(Endpoint::Http(String::from_utf8_lossy(buf.read_bytes(l as usize, cursor)?).to_string()))
|
||||
}
|
||||
TYPE_WEBRTC => {
|
||||
let l = buf.read_u16(cursor)?;
|
||||
Ok(Endpoint::WebRTC(buf.read_bytes(l as usize, cursor)?.to_vec()))
|
||||
}
|
||||
_ => std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream"))
|
||||
TYPE_HTTP => Ok(Endpoint::Http(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())),
|
||||
_ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unrecognized endpoint type in stream"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,16 +219,14 @@ impl Hash for Endpoint {
|
|||
|
||||
impl PartialOrd for Endpoint {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
// We manually implement Ord and PartialOrd to ensure that ordering is always the same, since I'm not
|
||||
// sure if Rust's derivations for this are guaranteed to remain constant forever. Endpoint ordering
|
||||
// is important in the reproducibility of digital signatures any time one is signing a vector of them.
|
||||
impl Ord for Endpoint {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// This ordering is done explicitly instead of using derive(Ord) so it will be certain
|
||||
// to be consistent with the integer order in the Type enum. Make sure it stays this
|
||||
// way if new types are added in future revisions.
|
||||
match self {
|
||||
Endpoint::Nil => {
|
||||
match other {
|
||||
|
|
|
@ -109,11 +109,11 @@ impl Identity {
|
|||
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
if digest[0] < 17 {
|
||||
let addr = Address::from_bytes(&digest[59..64]).unwrap();
|
||||
if addr.is_valid() {
|
||||
let addr = Address::from_bytes(&digest[59..64]);
|
||||
if addr.is_some() {
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
return Identity {
|
||||
address: addr,
|
||||
address: addr.unwrap(),
|
||||
c25519: c25519_pub_bytes,
|
||||
ed25519: ed25519_pub_bytes,
|
||||
v1: None,
|
||||
|
@ -143,12 +143,12 @@ impl Identity {
|
|||
let sig = p521_ecdsa.sign(&sign_buf).unwrap();
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&sig, V1_BALLOON_SALT);
|
||||
if bh[0] < 7 {
|
||||
let addr = Address::from_bytes(&bh[43..48]).unwrap();
|
||||
if addr.is_valid() {
|
||||
let addr = Address::from_bytes(&bh[43..48]);
|
||||
if addr.is_some() {
|
||||
let p521_ecdh_pub = p521_ecdh.public_key().clone();
|
||||
let p521_ecdsa_pub = p521_ecdsa.public_key().clone();
|
||||
return Identity {
|
||||
address: addr,
|
||||
address: addr.unwrap(),
|
||||
c25519: c25519_pub_bytes,
|
||||
ed25519: ed25519_pub_bytes,
|
||||
v1: Some((p521_ecdh_pub, p521_ecdsa_pub, sig, bh)),
|
||||
|
@ -179,9 +179,7 @@ impl Identity {
|
|||
|
||||
/// Get this identity's 40-bit address.
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
pub fn address(&self) -> Address { self.address }
|
||||
|
||||
/// Compute a SHA384 hash of this identity's keys, including private keys if present.
|
||||
pub fn hash_all_keys(&self) -> [u8; 48] {
|
||||
|
@ -208,37 +206,33 @@ impl Identity {
|
|||
/// This can take a few milliseconds, especially on slower systems. V0 identities are slower
|
||||
/// to fully validate than V1 identities.
|
||||
pub fn locally_validate(&self) -> bool {
|
||||
if self.address.is_valid() {
|
||||
if self.v1.is_none() {
|
||||
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
|
||||
let genmem_ptr = unsafe { alloc(genmem_layout) };
|
||||
if !genmem_ptr.is_null() {
|
||||
let mut sha = SHA512::new();
|
||||
sha.update(&self.c25519);
|
||||
sha.update(&self.ed25519);
|
||||
let mut digest = sha.finish();
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
(digest[0] < 17) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if self.v1.is_none() {
|
||||
let genmem_layout = Layout::from_size_align(V0_IDENTITY_GEN_MEMORY, 8).unwrap();
|
||||
let genmem_ptr = unsafe { alloc(genmem_layout) };
|
||||
if !genmem_ptr.is_null() {
|
||||
let mut sha = SHA512::new();
|
||||
sha.update(&self.c25519);
|
||||
sha.update(&self.ed25519);
|
||||
let mut digest = sha.finish();
|
||||
v0_frankenhash(&mut digest, genmem_ptr);
|
||||
unsafe { dealloc(genmem_ptr, genmem_layout) };
|
||||
(digest[0] < 17) && Address::from_bytes(&digest[59..64]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
let p521 = self.v1.as_ref().unwrap();
|
||||
let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE];
|
||||
signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&self.c25519);
|
||||
signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519);
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).0.public_key_bytes());
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).1.public_key_bytes());
|
||||
if (*p521).1.verify(&signing_buf, &(*p521).2) {
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2, V1_BALLOON_SALT);
|
||||
(bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
let p521 = self.v1.as_ref().unwrap();
|
||||
let mut signing_buf = [0_u8; C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE];
|
||||
signing_buf[0..C25519_PUBLIC_KEY_SIZE].copy_from_slice(&self.c25519);
|
||||
signing_buf[C25519_PUBLIC_KEY_SIZE..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)].copy_from_slice(&self.ed25519);
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).0.public_key_bytes());
|
||||
signing_buf[(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)..(C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE)].copy_from_slice((*p521).1.public_key_bytes());
|
||||
if (*p521).1.verify(&signing_buf, &(*p521).2) {
|
||||
let bh = balloon::hash::<{ V1_BALLOON_SPACE_COST }, { V1_BALLOON_TIME_COST }, { V1_BALLOON_DELTA }>(&(*p521).2, V1_BALLOON_SALT);
|
||||
(bh[0] < 7) && bh.eq(&(*p521).3) && Address::from_bytes(&bh[43..48]).unwrap().eq(&self.address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,7 +558,6 @@ impl FromStr for Identity {
|
|||
return Err(InvalidFormatError);
|
||||
}
|
||||
} else {
|
||||
println!("foo");
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use std::mem::{zeroed, size_of, MaybeUninit};
|
||||
use std::ptr::{write_bytes, copy_nonoverlapping, null};
|
||||
use std::str::FromStr;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::{MaybeUninit, size_of, transmute_copy, zeroed};
|
||||
use std::net::{IpAddr, Ipv6Addr};
|
||||
use std::ptr::{copy_nonoverlapping, null, slice_from_raw_parts, write_bytes};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winsock2 as winsock2;
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::util::equal_ptr;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winsock2 as winsock2;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[cfg(not(windows))]
|
||||
type sockaddr = libc::sockaddr;
|
||||
|
@ -67,28 +67,18 @@ pub union InetAddress {
|
|||
|
||||
impl Clone for InetAddress {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let mut c = MaybeUninit::<InetAddress>::uninit().assume_init();
|
||||
copy_nonoverlapping((self as *const Self).cast::<u8>(), (&mut c as *mut Self).cast::<u8>(), size_of::<Self>());
|
||||
c
|
||||
}
|
||||
}
|
||||
fn clone(&self) -> Self { unsafe { transmute_copy(self) } }
|
||||
}
|
||||
|
||||
impl Default for InetAddress {
|
||||
#[inline(always)]
|
||||
fn default() -> InetAddress {
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
fn default() -> InetAddress { unsafe { zeroed() } }
|
||||
}
|
||||
|
||||
impl InetAddress {
|
||||
/// Get a new zero/nil InetAddress.
|
||||
#[inline(always)]
|
||||
pub fn new() -> InetAddress {
|
||||
unsafe { zeroed() }
|
||||
}
|
||||
pub fn new() -> InetAddress { unsafe { zeroed() } }
|
||||
|
||||
/// Construct from IP and port.
|
||||
/// If the IP is not either 4 or 16 bytes in length, a nil/0 InetAddress is returned.
|
||||
|
@ -103,9 +93,7 @@ impl InetAddress {
|
|||
|
||||
/// Zero the contents of this InetAddress.
|
||||
#[inline(always)]
|
||||
pub fn zero(&mut self) {
|
||||
unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) };
|
||||
}
|
||||
pub fn zero(&mut self) { unsafe { write_bytes((self as *mut Self).cast::<u8>(), 0, size_of::<Self>()) }; }
|
||||
|
||||
/// Get an instance of 127.0.0.1/port
|
||||
pub fn ipv4_loopback(port: u16) -> InetAddress {
|
||||
|
@ -143,31 +131,23 @@ impl InetAddress {
|
|||
|
||||
/// Returns true if this InetAddress is the nil value (zero).
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
unsafe { self.sa.sa_family == 0 }
|
||||
}
|
||||
pub fn is_nil(&self) -> bool { unsafe { self.sa.sa_family == 0 } }
|
||||
|
||||
/// Check if this is an IPv4 address.
|
||||
#[inline(always)]
|
||||
pub fn is_ipv4(&self) -> bool {
|
||||
unsafe { self.sa.sa_family as u8 == AF_INET }
|
||||
}
|
||||
pub fn is_ipv4(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET } }
|
||||
|
||||
/// Check if this is an IPv6 address.
|
||||
#[inline(always)]
|
||||
pub fn is_ipv6(&self) -> bool {
|
||||
unsafe { self.sa.sa_family as u8 == AF_INET6 }
|
||||
}
|
||||
pub fn is_ipv6(&self) -> bool { unsafe { self.sa.sa_family as u8 == AF_INET6 } }
|
||||
|
||||
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if nil.
|
||||
/// Get the address family of this InetAddress: AF_INET, AF_INET6, or 0 if uninitialized.
|
||||
#[inline(always)]
|
||||
pub fn family(&self) -> u8 {
|
||||
unsafe { self.sa.sa_family }
|
||||
}
|
||||
pub fn family(&self) -> u8 { unsafe { self.sa.sa_family } }
|
||||
|
||||
/// Get a pointer to the C "sockaddr" structure and the size of the returned structure in bytes.
|
||||
/// This is useful for interacting with C-level socket APIs. If this is a nil InetAddress this
|
||||
/// returns a null pointer and 0 for the size.
|
||||
/// This is useful for interacting with C-level socket APIs. This returns a null pointer if
|
||||
/// the address is not initialized.
|
||||
#[inline(always)]
|
||||
pub fn c_sockaddr(&self) -> (*const (), usize) {
|
||||
unsafe {
|
||||
|
@ -203,7 +183,7 @@ impl InetAddress {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get raw IP bytes, with length dependent on address family.
|
||||
/// Get raw IP bytes, with length dependent on address family (4 or 16).
|
||||
#[inline(always)]
|
||||
pub fn ip_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
|
@ -219,15 +199,18 @@ impl InetAddress {
|
|||
#[inline(always)]
|
||||
pub fn port(&self) -> u16 {
|
||||
unsafe {
|
||||
match self.sa.sa_family as u8 {
|
||||
AF_INET => u16::from_be(self.sin.sin_port as u16),
|
||||
AF_INET6 => u16::from_be(self.sin6.sin6_port as u16),
|
||||
u16::from_be(match self.sa.sa_family as u8 {
|
||||
AF_INET => self.sin.sin_port as u16,
|
||||
AF_INET6 => self.sin6.sin6_port as u16,
|
||||
_ => 0
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IP port.
|
||||
///
|
||||
/// This does nothing on uninitialized InetAddress objects. An address must first
|
||||
/// be initialized with an IP to select the correct address type.
|
||||
#[inline(always)]
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
let port = port.to_be();
|
||||
|
@ -498,16 +481,19 @@ impl Eq for InetAddress {}
|
|||
|
||||
impl PartialOrd for InetAddress {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
// Manually implement Ord to ensure consistent sort order across platforms, since we don't know exactly
|
||||
// how sockaddr structs will be laid out.
|
||||
impl Ord for InetAddress {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
unsafe {
|
||||
if self.sa.sa_family == other.sa.sa_family {
|
||||
match self.sa.sa_family as u8 {
|
||||
0 => {
|
||||
Ordering::Equal
|
||||
}
|
||||
AF_INET => {
|
||||
let ip_ordering = u32::from_be(self.sin.sin_addr.s_addr as u32).cmp(&u32::from_be(other.sin.sin_addr.s_addr as u32));
|
||||
if ip_ordering == Ordering::Equal {
|
||||
|
@ -527,11 +513,15 @@ impl Ord for InetAddress {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
Ordering::Equal
|
||||
// This shouldn't be possible, but handle it for correctness.
|
||||
(*slice_from_raw_parts((self as *const Self).cast::<u8>(), size_of::<Self>())).cmp(&*slice_from_raw_parts((other as *const Self).cast::<u8>(), size_of::<Self>()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.sa.sa_family as u8 {
|
||||
0 => {
|
||||
Ordering::Less
|
||||
}
|
||||
AF_INET => {
|
||||
if other.sa.sa_family as u8 == AF_INET6 {
|
||||
Ordering::Less
|
||||
|
@ -547,6 +537,7 @@ impl Ord for InetAddress {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
// This likewise should not be possible.
|
||||
self.sa.sa_family.cmp(&other.sa.sa_family)
|
||||
}
|
||||
}
|
||||
|
@ -576,9 +567,17 @@ impl Hash for InetAddress {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use crate::vl1::inetaddress::{InetAddress, sockaddr_storage};
|
||||
use std::mem::size_of;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::vl1::inetaddress::*;
|
||||
|
||||
#[test]
|
||||
fn values() {
|
||||
assert_ne!(AF_INET, 0);
|
||||
assert_ne!(AF_INET6, 0);
|
||||
assert_ne!(AF_INET, AF_INET6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layout() {
|
||||
|
|
|
@ -4,9 +4,6 @@ use crate::vl1::{Address, Endpoint, Identity};
|
|||
use crate::vl1::buffer::Buffer;
|
||||
use crate::vl1::protocol::PACKET_SIZE_MAX;
|
||||
|
||||
/// Maximum number of endpoints allowed in a Locator.
|
||||
pub const LOCATOR_MAX_ENDPOINTS: usize = 32;
|
||||
|
||||
/// A signed object generated by nodes to inform the network where they may be found.
|
||||
///
|
||||
/// By default this will just enumerate the roots used by this node, but nodes with
|
||||
|
@ -32,10 +29,6 @@ impl Locator {
|
|||
/// This returns None if an error occurs, which can only be something indicating a
|
||||
/// bug like too many endpoints or the identity lacking its secret keys.
|
||||
pub fn create(signer_identity: &Identity, subject: Address, ts: i64, endpoints: &[Endpoint]) -> Option<Locator> {
|
||||
if endpoints.len() > LOCATOR_MAX_ENDPOINTS {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut loc = Locator {
|
||||
subject,
|
||||
signer: signer_identity.address(),
|
||||
|
@ -68,29 +61,19 @@ impl Locator {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn subject(&self) -> Address {
|
||||
self.subject
|
||||
}
|
||||
pub fn subject(&self) -> Address { self.subject }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn signer(&self) -> Address {
|
||||
self.signer
|
||||
}
|
||||
pub fn signer(&self) -> Address { self.signer }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_proxy_signed(&self) -> bool {
|
||||
self.subject != self.signer
|
||||
}
|
||||
pub fn is_proxy_signed(&self) -> bool { self.subject != self.signer }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn timestamp(&self) -> i64 {
|
||||
self.timestamp
|
||||
}
|
||||
pub fn timestamp(&self) -> i64 { self.timestamp }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn endpoints(&self) -> &[Endpoint] {
|
||||
self.endpoints.as_slice()
|
||||
}
|
||||
pub fn endpoints(&self) -> &[Endpoint] { self.endpoints.as_slice() }
|
||||
|
||||
pub fn verify_signature(&self, signer_identity: &Identity) -> bool {
|
||||
let mut buf: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new();
|
||||
|
@ -106,47 +89,42 @@ impl Locator {
|
|||
}
|
||||
|
||||
fn marshal_internal<const BL: usize>(&self, buf: &mut Buffer<BL>, exclude_signature: bool) -> std::io::Result<()> {
|
||||
buf.append_u64(self.subject.to_u64())?;
|
||||
buf.append_u64(self.signer.to_u64())?;
|
||||
self.subject.marshal(buf)?;
|
||||
self.signer.marshal(buf)?;
|
||||
buf.append_u64(self.timestamp as u64)?;
|
||||
debug_assert!(self.endpoints.len() < 65536);
|
||||
buf.append_u16(self.endpoints.len() as u16)?;
|
||||
buf.append_varint(self.endpoints.len() as u64);
|
||||
for e in self.endpoints.iter() {
|
||||
e.marshal(buf)?;
|
||||
}
|
||||
buf.append_u16(0)?;
|
||||
buf.append_varint(0); // length of any additional fields
|
||||
if !exclude_signature {
|
||||
debug_assert!(self.signature.len() < 65536);
|
||||
buf.append_u16(self.signature.len() as u16)?;
|
||||
buf.append_varint(self.signature.len() as u64);
|
||||
buf.append_bytes(self.signature.as_slice())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||
self.marshal_internal(buf, false)
|
||||
}
|
||||
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> { self.marshal_internal(buf, false) }
|
||||
|
||||
pub fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
||||
let subject = Address::from(buf.read_u64(cursor)?);
|
||||
let signer = Address::from(buf.read_u64(cursor)?);
|
||||
let timestamp = buf.read_u64(cursor)? as i64;
|
||||
let endpoint_count = buf.read_u16(cursor)? as usize;
|
||||
if endpoint_count > LOCATOR_MAX_ENDPOINTS {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "too many endpoints"));
|
||||
pub(crate) fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Self> {
|
||||
let subject = Address::unmarshal(buf, cursor)?;
|
||||
let signer = Address::unmarshal(buf, cursor)?;
|
||||
if subject.is_none() || signer.is_none() {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid subject or signer address"));
|
||||
}
|
||||
let timestamp = buf.read_u64(cursor)? as i64;
|
||||
let endpoint_count = buf.read_varint(cursor)? as usize;
|
||||
let mut endpoints: Vec<Endpoint> = Vec::new();
|
||||
endpoints.reserve(endpoint_count);
|
||||
for _ in 0..endpoint_count {
|
||||
endpoints.push(Endpoint::unmarshal(buf, cursor)?);
|
||||
}
|
||||
*cursor += buf.read_u16(cursor)? as usize;
|
||||
let signature_len = buf.read_u16(cursor)? as usize;
|
||||
*cursor += buf.read_varint(cursor)? as usize;
|
||||
let signature_len = buf.read_varint(cursor)? as usize;
|
||||
let signature = buf.read_bytes(signature_len, cursor)?;
|
||||
Ok(Locator {
|
||||
subject,
|
||||
signer,
|
||||
subject: subject.unwrap(),
|
||||
signer: signer.unwrap(),
|
||||
timestamp,
|
||||
endpoints,
|
||||
signature: signature.to_vec(),
|
||||
|
|
|
@ -1,80 +1,84 @@
|
|||
use std::num::NonZeroU64;
|
||||
use std::str::FromStr;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::vl1::buffer::Buffer;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MAC(u64);
|
||||
#[repr(transparent)]
|
||||
pub struct MAC(NonZeroU64);
|
||||
|
||||
impl MAC {
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Result<MAC, InvalidFormatError> {
|
||||
if b.len() >= 6 {
|
||||
Ok(MAC((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64))
|
||||
pub fn from_u64(i: u64) -> Option<MAC> {
|
||||
if i != 0 {
|
||||
Some(MAC(unsafe { NonZeroU64::new_unchecked(i & 0xffffffffffff) }))
|
||||
} else {
|
||||
Err(InvalidFormatError)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Option<MAC> {
|
||||
if b.len() >= 6 {
|
||||
let i = (b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64;
|
||||
if i != 0 {
|
||||
Some(MAC(unsafe { NonZeroU64::new_unchecked(i) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_bytes(&self) -> [u8; 6] {
|
||||
[(self.0 >> 40) as u8, (self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
|
||||
let i = self.0.get();
|
||||
[(i >> 40) as u8, (i >> 32) as u8, (i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_u64(&self) -> u64 {
|
||||
self.0
|
||||
pub fn to_u64(&self) -> u64 { self.0.get() }
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>) -> std::io::Result<()> {
|
||||
buf.append_and_init_bytes_fixed(|b: &mut [u8; 6]| {
|
||||
let i = self.0.get();
|
||||
b[0] = (i >> 40) as u8;
|
||||
b[1] = (i >> 32) as u8;
|
||||
b[2] = (i >> 24) as u8;
|
||||
b[3] = (i >> 16) as u8;
|
||||
b[4] = (i >> 8) as u8;
|
||||
b[5] = i as u8;
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn unmarshal<const BL: usize>(buf: &Buffer<BL>, cursor: &mut usize) -> std::io::Result<Option<Self>> {
|
||||
buf.read_bytes_fixed::<6>(cursor).map(|b| Self::from_bytes(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for MAC {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
let b: [u8; 6] = self.to_bytes();
|
||||
format!("{}:{}:{}:{}:{}:{}", b[0], b[1], b[2], b[3], b[4], b[5])
|
||||
format!("{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}", b[0], b[1], b[2], b[3], b[4], b[5])
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MAC {
|
||||
type Err = InvalidFormatError;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
MAC::from_bytes(crate::util::hex::from_string(s).as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MAC {
|
||||
#[inline(always)]
|
||||
fn default() -> MAC {
|
||||
MAC(0)
|
||||
MAC::from_bytes(crate::util::hex::from_string(s).as_slice()).map_or_else(|| Err(InvalidFormatError), |m| Ok(m))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MAC {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8; 6]> for MAC {
|
||||
#[inline(always)]
|
||||
fn from(b: &[u8; 6]) -> MAC {
|
||||
MAC((b[0] as u64) << 40 | (b[1] as u64) << 32 | (b[2] as u64) << 24 | (b[3] as u64) << 16 as u64 | (b[4] as u64) << 8 | b[5] as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 6]> for MAC {
|
||||
#[inline(always)]
|
||||
fn from(b: [u8; 6]) -> MAC {
|
||||
Self::from(&b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for MAC {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> MAC {
|
||||
MAC(i)
|
||||
state.write_u64(self.0.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
// Only things that should be used from outside network-hypervisor should be full "pub."
|
||||
pub mod identity;
|
||||
pub mod inetaddress;
|
||||
pub mod endpoint;
|
||||
pub mod locator;
|
||||
pub mod rootset;
|
||||
|
||||
// These are either only used inside network-hypervisor or are selectively exported below.
|
||||
pub(crate) mod protocol;
|
||||
pub(crate) mod buffer;
|
||||
pub(crate) mod node;
|
||||
|
@ -9,12 +17,7 @@ pub(crate) mod mac;
|
|||
pub(crate) mod fragmentedpacket;
|
||||
pub(crate) mod whois;
|
||||
|
||||
pub mod identity;
|
||||
pub mod inetaddress;
|
||||
pub mod endpoint;
|
||||
pub mod locator;
|
||||
pub mod rootset;
|
||||
|
||||
// Export some core objects into the root namespace, since these are what other code will driectly deal with.
|
||||
pub use address::Address;
|
||||
pub use mac::MAC;
|
||||
pub use identity::Identity;
|
||||
|
|
|
@ -170,32 +170,22 @@ impl Node {
|
|||
|
||||
/// Get address, short for .identity().address()
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
self.identity.address()
|
||||
}
|
||||
pub fn address(&self) -> Address { self.identity.address() }
|
||||
|
||||
/// Get identity, which includes secret keys.
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> &Identity {
|
||||
&self.identity
|
||||
}
|
||||
pub fn identity(&self) -> &Identity { &self.identity }
|
||||
|
||||
/// Get this node's current locator or None if no locator created.
|
||||
#[inline(always)]
|
||||
pub fn locator(&self) -> Option<Arc<Locator>> {
|
||||
self.locator.lock().clone()
|
||||
}
|
||||
pub fn locator(&self) -> Option<Arc<Locator>> { self.locator.lock().clone() }
|
||||
|
||||
/// Get a reusable packet buffer.
|
||||
/// The buffer will automatically be returned to the pool if it is dropped.
|
||||
pub fn get_packet_buffer(&self) -> PacketBuffer {
|
||||
self.buffer_pool.get()
|
||||
}
|
||||
pub fn get_packet_buffer(&self) -> PacketBuffer { self.buffer_pool.get() }
|
||||
|
||||
/// Get a peer by address.
|
||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> {
|
||||
self.peers.get(&a).map(|peer| peer.value().clone())
|
||||
}
|
||||
pub fn peer(&self, a: Address) -> Option<Arc<Peer>> { self.peers.get(&a).map(|peer| peer.value().clone()) }
|
||||
|
||||
/// Get all peers currently in the peer cache.
|
||||
pub fn peers(&self) -> Vec<Arc<Peer>> {
|
||||
|
@ -207,14 +197,6 @@ impl Node {
|
|||
v
|
||||
}
|
||||
|
||||
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
||||
pub(crate) fn root(&self) -> Option<Arc<Peer>> {
|
||||
self.roots.lock().first().map(|p| p.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_root_set(&self) {
|
||||
}
|
||||
|
||||
/// Run background tasks and return desired delay until next call in milliseconds.
|
||||
///
|
||||
/// This should only be called once at a time. It technically won't hurt anything to
|
||||
|
@ -252,68 +234,83 @@ impl Node {
|
|||
if fragment_header.is_ok() {
|
||||
let fragment_header = fragment_header.unwrap();
|
||||
let time_ticks = ci.time_ticks();
|
||||
let dest = Address::from(&fragment_header.dest);
|
||||
if dest == self.identity.address() {
|
||||
let dest = Address::from_bytes(&fragment_header.dest);
|
||||
if dest.is_some() {
|
||||
let dest = dest.unwrap();
|
||||
if dest == self.identity.address() {
|
||||
|
||||
let path = self.path(source_endpoint, source_local_socket, source_local_interface);
|
||||
path.log_receive(time_ticks);
|
||||
if fragment_header.is_fragment() {
|
||||
let path = self.path(source_endpoint, source_local_socket, source_local_interface);
|
||||
path.log_receive(time_ticks);
|
||||
if fragment_header.is_fragment() {
|
||||
|
||||
let _ = path.receive_fragment(fragment_header.id, fragment_header.fragment_no(), fragment_header.total_fragments(), data, time_ticks).map(|assembled_packet| {
|
||||
if assembled_packet.frags[0].is_some() {
|
||||
let frag0 = assembled_packet.frags[0].as_ref().unwrap();
|
||||
let packet_header = frag0.struct_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
let packet_header = packet_header.unwrap();
|
||||
let source = Address::from(&packet_header.src);
|
||||
let _ = path.receive_fragment(fragment_header.id, fragment_header.fragment_no(), fragment_header.total_fragments(), data, time_ticks).map(|assembled_packet| {
|
||||
if assembled_packet.frags[0].is_some() {
|
||||
let frag0 = assembled_packet.frags[0].as_ref().unwrap();
|
||||
let packet_header = frag0.struct_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
let packet_header = packet_header.unwrap();
|
||||
let source = Address::from_bytes(&packet_header.src);
|
||||
if source.is_some() {
|
||||
let source = source.unwrap();
|
||||
let peer = self.peer(source);
|
||||
if peer.is_some() {
|
||||
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, frag0, &assembled_packet.frags[1..(assembled_packet.have as usize)]);
|
||||
} else {
|
||||
self.whois.query(self, ci, source, Some(QueuedPacket::Fragmented(assembled_packet)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
let packet_header = data.struct_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
let packet_header = packet_header.unwrap();
|
||||
let source = Address::from_bytes(&packet_header.src);
|
||||
if source.is_some() {
|
||||
let source = source.unwrap();
|
||||
let peer = self.peer(source);
|
||||
if peer.is_some() {
|
||||
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, frag0, &assembled_packet.frags[1..(assembled_packet.have as usize)]);
|
||||
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, data.as_ref(), &[]);
|
||||
} else {
|
||||
self.whois.query(self, ci, source, Some(QueuedPacket::Fragmented(assembled_packet)));
|
||||
self.whois.query(self, ci, source, Some(QueuedPacket::Singular(data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
let packet_header = data.struct_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
let packet_header = packet_header.unwrap();
|
||||
let source = Address::from(&packet_header.src);
|
||||
let peer = self.peer(source);
|
||||
if peer.is_some() {
|
||||
peer.unwrap().receive(self, ci, ph, time_ticks, &path, &packet_header, data.as_ref(), &[]);
|
||||
} else {
|
||||
self.whois.query(self, ci, source, Some(QueuedPacket::Singular(data)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if fragment_header.is_fragment() {
|
||||
if fragment_header.increment_hops() > FORWARD_MAX_HOPS {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let packet_header = data.struct_mut_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
if packet_header.unwrap().increment_hops() > FORWARD_MAX_HOPS {
|
||||
if fragment_header.is_fragment() {
|
||||
if fragment_header.increment_hops() > FORWARD_MAX_HOPS {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
let packet_header = data.struct_mut_at::<PacketHeader>(0);
|
||||
if packet_header.is_ok() {
|
||||
if packet_header.unwrap().increment_hops() > FORWARD_MAX_HOPS {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = self.peer(dest).map(|peer| peer.forward(ci, time_ticks, data.as_ref()));
|
||||
let _ = self.peer(dest).map(|peer| peer.forward(ci, time_ticks, data.as_ref()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current best root peer that we should use for WHOIS, relaying, etc.
|
||||
pub(crate) fn root(&self) -> Option<Arc<Peer>> { self.roots.lock().first().map(|p| p.clone()) }
|
||||
|
||||
/// Return true if a peer is a root.
|
||||
pub(crate) fn is_peer_root(&self, peer: &Peer) -> bool { self.roots.lock().iter().any(|p| Arc::as_ptr(p) == (peer as *const Peer)) }
|
||||
|
||||
/// Get the canonical Path object for a given endpoint and local socket information.
|
||||
///
|
||||
/// This is a canonicalizing function that returns a unique path object for every tuple
|
||||
|
|
|
@ -5,9 +5,9 @@ use parking_lot::Mutex;
|
|||
|
||||
use crate::util::U64PassThroughHasher;
|
||||
use crate::vl1::Endpoint;
|
||||
use crate::vl1::fragmentedpacket::{FragmentedPacket, FRAGMENT_EXPIRATION, FRAGMENT_MAX_INBOUND_PACKETS_PER_PATH};
|
||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||
use crate::vl1::node::{PacketBuffer, VL1CallerInterface};
|
||||
use crate::vl1::protocol::PacketID;
|
||||
use crate::vl1::protocol::*;
|
||||
|
||||
/// Keepalive interval for paths in milliseconds.
|
||||
pub(crate) const PATH_KEEPALIVE_INTERVAL: i64 = 20000;
|
||||
|
@ -41,29 +41,19 @@ impl Path {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn endpoint(&self) -> &Endpoint {
|
||||
&self.endpoint
|
||||
}
|
||||
pub fn endpoint(&self) -> &Endpoint { &self.endpoint }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn local_socket(&self) -> i64 {
|
||||
self.local_socket
|
||||
}
|
||||
pub fn local_socket(&self) -> i64 { self.local_socket }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn local_interface(&self) -> i64 {
|
||||
self.local_interface
|
||||
}
|
||||
pub fn local_interface(&self) -> i64 { self.local_interface }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn last_send_time_ticks(&self) -> i64 {
|
||||
self.last_send_time_ticks.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn last_send_time_ticks(&self) -> i64 { self.last_send_time_ticks.load(Ordering::Relaxed) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn last_receive_time_ticks(&self) -> i64 {
|
||||
self.last_receive_time_ticks.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn last_receive_time_ticks(&self) -> i64 { self.last_receive_time_ticks.load(Ordering::Relaxed) }
|
||||
|
||||
/// Receive a fragment and return a FragmentedPacket if the entire packet was assembled.
|
||||
/// This returns None if more fragments are needed to assemble the packet.
|
||||
|
|
|
@ -443,7 +443,7 @@ impl Peer {
|
|||
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_C25519, ephemeral_pair.c25519.public_bytes().to_vec());
|
||||
dict.set_bytes(HELLO_DICT_KEY_EPHEMERAL_P521, ephemeral_pair.p521.public_key_bytes().to_vec());
|
||||
});
|
||||
if node.is_root(self) {
|
||||
if node.is_peer_root(self) {
|
||||
// If the peer is a root we include some extra information for diagnostic and statistics
|
||||
// purposes such as the CPU type, bits, and OS info. This is not sent to other peers.
|
||||
dict.set_str(HELLO_DICT_KEY_SYS_ARCH, std::env::consts::ARCH);
|
||||
|
@ -480,7 +480,7 @@ impl Peer {
|
|||
self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed);
|
||||
self.total_bytes_sent.fetch_add(packet.len() as u64, Ordering::Relaxed);
|
||||
|
||||
path.map_or_else(|| {
|
||||
path.as_ref().map_or_else(|| {
|
||||
self.send_to_endpoint(ci, endpoint, None, None, packet_id, &packet)
|
||||
}, |path| {
|
||||
path.log_send(time_ticks);
|
||||
|
|
|
@ -149,6 +149,15 @@ pub const FORWARD_MAX_HOPS: u8 = 3;
|
|||
/// Maximum difference between an OK in-re packet ID and the current packet ID counter.
|
||||
pub const OK_PACKET_SEQUENCE_CUTOFF: u64 = 1000;
|
||||
|
||||
/// Frequency for WHOIS retries
|
||||
pub const WHOIS_RETRY_INTERVAL: i64 = 1000;
|
||||
|
||||
/// Maximum number of WHOIS retries
|
||||
pub const WHOIS_RETRY_MAX: u16 = 3;
|
||||
|
||||
/// Maximum number of packets to queue up behind a WHOIS.
|
||||
pub const WHOIS_MAX_WAITING_PACKETS: usize = 64;
|
||||
|
||||
/// A unique packet identifier, also the cryptographic nonce.
|
||||
///
|
||||
/// Packet IDs are stored as u64s for efficiency but they should be treated as
|
||||
|
@ -174,14 +183,10 @@ unsafe impl RawObject for PacketHeader {}
|
|||
|
||||
impl PacketHeader {
|
||||
#[inline(always)]
|
||||
pub fn cipher(&self) -> u8 {
|
||||
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER
|
||||
}
|
||||
pub fn cipher(&self) -> u8 { self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_CIPHER }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn hops(&self) -> u8 {
|
||||
self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
||||
}
|
||||
pub fn hops(&self) -> u8 { self.flags_cipher_hops & HEADER_FLAGS_FIELD_MASK_HOPS }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn increment_hops(&mut self) -> u8 {
|
||||
|
@ -192,29 +197,13 @@ impl PacketHeader {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_fragmented(&self) -> bool {
|
||||
(self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0
|
||||
}
|
||||
pub fn is_fragmented(&self) -> bool { (self.flags_cipher_hops & HEADER_FLAG_FRAGMENTED) != 0 }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn destination(&self) -> Address {
|
||||
Address::from(&self.dest)
|
||||
}
|
||||
pub fn id_bytes(&self) -> &[u8; 8] { unsafe { &*(self as *const Self).cast::<[u8; 8]>() } }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn source(&self) -> Address {
|
||||
Address::from(&self.src)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn id_bytes(&self) -> &[u8; 8] {
|
||||
unsafe { &*(self as *const Self).cast::<[u8; 8]>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; PACKET_HEADER_SIZE] {
|
||||
unsafe { &*(self as *const Self).cast::<[u8; PACKET_HEADER_SIZE]>() }
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8; PACKET_HEADER_SIZE] { unsafe { &*(self as *const Self).cast::<[u8; PACKET_HEADER_SIZE]>() } }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn aad_bytes(&self) -> [u8; 11] {
|
||||
|
@ -253,24 +242,16 @@ unsafe impl RawObject for FragmentHeader {}
|
|||
|
||||
impl FragmentHeader {
|
||||
#[inline(always)]
|
||||
pub fn is_fragment(&self) -> bool {
|
||||
self.fragment_indicator == FRAGMENT_INDICATOR
|
||||
}
|
||||
pub fn is_fragment(&self) -> bool { self.fragment_indicator == FRAGMENT_INDICATOR }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn total_fragments(&self) -> u8 {
|
||||
self.total_and_fragment_no >> 4
|
||||
}
|
||||
pub fn total_fragments(&self) -> u8 { self.total_and_fragment_no >> 4 }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fragment_no(&self) -> u8 {
|
||||
self.total_and_fragment_no & 0x0f
|
||||
}
|
||||
pub fn fragment_no(&self) -> u8 { self.total_and_fragment_no & 0x0f }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn hops(&self) -> u8 {
|
||||
self.reserved_hops & HEADER_FLAGS_FIELD_MASK_HOPS
|
||||
}
|
||||
pub fn hops(&self) -> u8 { self.reserved_hops & HEADER_FLAGS_FIELD_MASK_HOPS }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn increment_hops(&mut self) -> u8 {
|
||||
|
@ -281,14 +262,7 @@ impl FragmentHeader {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] {
|
||||
unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn destination(&self) -> Address {
|
||||
Address::from(&self.dest)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8; FRAGMENT_HEADER_SIZE] { unsafe { &*(self as *const Self).cast::<[u8; FRAGMENT_HEADER_SIZE]>() } }
|
||||
}
|
||||
|
||||
pub(crate) mod message_component_structs {
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::io::Write;
|
|||
|
||||
use concat_arrays::concat_arrays;
|
||||
|
||||
use crate::crypto::c25519::{ED25519_PUBLIC_KEY_SIZE, ED25519_SECRET_KEY_SIZE, ED25519_SIGNATURE_SIZE, ed25519_verify, Ed25519KeyPair};
|
||||
use crate::crypto::c25519::*;
|
||||
use crate::crypto::hash::SHA384;
|
||||
use crate::crypto::p521::{P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE, P521KeyPair};
|
||||
use crate::crypto::p521::*;
|
||||
use crate::crypto::secret::Secret;
|
||||
use crate::error::InvalidFormatError;
|
||||
use crate::vl1::{Endpoint, Identity};
|
||||
|
@ -253,11 +253,8 @@ impl RootSet {
|
|||
let type_id = buf.read_u8(cursor)?;
|
||||
match type_id {
|
||||
ROOT_SET_TYPE_LEGACY_PLANET | ROOT_SET_TYPE_LEGACY_MOON => {
|
||||
let root_set_type = if type_id == ROOT_SET_TYPE_LEGACY_PLANET {
|
||||
Type::LegacyPlanet(buf.read_u64(cursor)?)
|
||||
} else {
|
||||
Type::LegacyMoon(buf.read_u64(cursor)?)
|
||||
};
|
||||
let root_set_type = buf.read_u64(cursor)?;
|
||||
let root_set_type = if type_id == ROOT_SET_TYPE_LEGACY_PLANET { Type::LegacyPlanet(root_set_type) } else { Type::LegacyMoon(root_set_type) };
|
||||
let timestamp = buf.read_u64(cursor)?;
|
||||
let signer = buf.read_bytes(64, cursor)?.to_vec();
|
||||
let signature = buf.read_bytes(96, cursor)?.to_vec();
|
||||
|
|
|
@ -4,18 +4,9 @@ use parking_lot::Mutex;
|
|||
|
||||
use crate::util::gate::IntervalGate;
|
||||
use crate::vl1::Address;
|
||||
use crate::vl1::constants::*;
|
||||
use crate::vl1::fragmentedpacket::FragmentedPacket;
|
||||
use crate::vl1::node::{Node, PacketBuffer, VL1CallerInterface};
|
||||
|
||||
/// Frequency for WHOIS retries
|
||||
pub const WHOIS_RETRY_INTERVAL: i64 = 1000;
|
||||
|
||||
/// Maximum number of WHOIS retries
|
||||
pub const WHOIS_RETRY_MAX: u16 = 3;
|
||||
|
||||
/// Maximum number of packets to queue up behind a WHOIS.
|
||||
pub const WHOIS_MAX_WAITING_PACKETS: usize = 64;
|
||||
use crate::vl1::protocol::{WHOIS_RETRY_INTERVAL, WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_MAX};
|
||||
|
||||
pub(crate) enum QueuedPacket {
|
||||
Singular(PacketBuffer),
|
||||
|
|
Loading…
Add table
Reference in a new issue