mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 00:17:22 +02:00
A bunch of reorg, and add a safety valve to nuke the send key in ZSSP if it is hard-expired.
This commit is contained in:
parent
54d6fba6c5
commit
2649ce7571
13 changed files with 252 additions and 116 deletions
|
@ -11,3 +11,5 @@ path = "src/main.rs"
|
|||
zerotier-crypto = { path = "../crypto" }
|
||||
zerotier-utils = { path = "../utils" }
|
||||
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
||||
serde = { version = "^1", features = ["derive"], default-features = false }
|
||||
serde_json = { version = "^1", features = ["std"], default-features = false }
|
||||
|
|
1
controller/src/lib.rs
Normal file
1
controller/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod model;
|
127
controller/src/model.rs
Normal file
127
controller/src/model.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use zerotier_network_hypervisor::vl1::Address;
|
||||
use zerotier_network_hypervisor::vl1::InetAddress;
|
||||
use zerotier_network_hypervisor::vl2::NetworkId;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ObjectType {
|
||||
#[serde(rename = "network")]
|
||||
Network,
|
||||
#[serde(rename = "member")]
|
||||
Member,
|
||||
}
|
||||
|
||||
impl ObjectType {
|
||||
fn network() -> ObjectType {
|
||||
Self::Network
|
||||
}
|
||||
fn member() -> ObjectType {
|
||||
Self::Member
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Ipv4AssignMode {
|
||||
pub zt: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Ipv6AssignMode {
|
||||
pub zt: bool,
|
||||
pub rfc4193: bool,
|
||||
#[serde(rename = "6plane")]
|
||||
pub _6plane: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct IpAssignmentPool {
|
||||
#[serde(rename = "ipRangeStart")]
|
||||
ip_range_start: InetAddress,
|
||||
#[serde(rename = "ipRangeEnd")]
|
||||
ip_range_end: InetAddress,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Network {
|
||||
pub id: NetworkId,
|
||||
pub name: String,
|
||||
|
||||
#[serde(rename = "creationTime")]
|
||||
pub creation_time: i64,
|
||||
|
||||
#[serde(rename = "multicastLimit")]
|
||||
pub multicast_limit: u64,
|
||||
#[serde(rename = "enableBroadcast")]
|
||||
pub enable_broadcast: bool,
|
||||
|
||||
#[serde(rename = "v4AssignMode")]
|
||||
pub v4_assign_mode: Ipv4AssignMode,
|
||||
#[serde(rename = "v6AssignMode")]
|
||||
pub v6_assign_mode: Ipv6AssignMode,
|
||||
#[serde(rename = "ipAssignmentPools")]
|
||||
pub ip_assignment_pools: Vec<IpAssignmentPool>,
|
||||
|
||||
#[serde(rename = "rulesSource")]
|
||||
pub rules_source: String,
|
||||
|
||||
pub mtu: u16,
|
||||
pub private: bool,
|
||||
|
||||
#[serde(default = "ObjectType::network")]
|
||||
pub objtype: ObjectType,
|
||||
}
|
||||
|
||||
impl Hash for Network {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Member {
|
||||
#[serde(rename = "networkId")]
|
||||
pub network_id: NetworkId,
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
|
||||
#[serde(rename = "creationTime")]
|
||||
pub creation_time: i64,
|
||||
|
||||
#[serde(rename = "ipAssignments")]
|
||||
pub ip_assignments: Vec<InetAddress>,
|
||||
#[serde(rename = "noAutoAssignIps")]
|
||||
pub no_auto_assign_ips: bool,
|
||||
|
||||
#[serde(rename = "vMajor")]
|
||||
pub version_major: u16,
|
||||
#[serde(rename = "vMinor")]
|
||||
pub version_minor: u16,
|
||||
#[serde(rename = "vRev")]
|
||||
pub version_revision: u16,
|
||||
#[serde(rename = "vProto")]
|
||||
pub version_protocol: u16,
|
||||
|
||||
pub authorized: bool,
|
||||
#[serde(rename = "activeBridge")]
|
||||
pub bridge: bool,
|
||||
|
||||
#[serde(rename = "ssoExempt")]
|
||||
pub sso_exempt: bool,
|
||||
|
||||
#[serde(default = "ObjectType::member")]
|
||||
pub objtype: ObjectType,
|
||||
}
|
||||
|
||||
impl Hash for Member {
|
||||
#[inline(always)]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.network_id.hash(state);
|
||||
self.address.hash(state);
|
||||
}
|
||||
}
|
|
@ -20,12 +20,13 @@ extern "C" {
|
|||
pub struct Secret<const L: usize>(pub [u8; L]);
|
||||
|
||||
impl<const L: usize> Secret<L> {
|
||||
/// Create a new all-zero secret.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self([0_u8; L])
|
||||
}
|
||||
|
||||
/// Copy bytes into secret, will panic if size does not match.
|
||||
/// Copy bytes into secret, will panic if the slice does not match the size of this secret.
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self(b.try_into().unwrap())
|
||||
|
@ -36,16 +37,26 @@ impl<const L: usize> Secret<L> {
|
|||
&self.0
|
||||
}
|
||||
|
||||
/// Get the first N bytes of this secret as a fixed length array.
|
||||
#[inline(always)]
|
||||
pub fn first_n<const N: usize>(&self) -> &[u8; N] {
|
||||
assert!(N <= L);
|
||||
unsafe { &*self.0.as_ptr().cast() }
|
||||
}
|
||||
|
||||
/// Clone the first N bytes of this secret as another secret.
|
||||
#[inline(always)]
|
||||
pub fn first_n_clone<const N: usize>(&self) -> Secret<N> {
|
||||
Secret::<N>(self.first_n().clone())
|
||||
}
|
||||
|
||||
/// Destroy the contents of this secret, ignoring normal Rust mutability constraints.
|
||||
///
|
||||
/// This can be used to force a secret to be forgotten under e.g. key lifetime exceeded or error conditions.
|
||||
#[inline(always)]
|
||||
pub fn nuke(&self) {
|
||||
unsafe { OPENSSL_cleanse(std::mem::transmute(self.0.as_ptr().cast::<c_void>()), L) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L: usize> Drop for Secret<L> {
|
||||
|
|
|
@ -1604,6 +1604,11 @@ impl SessionKey {
|
|||
.pop()
|
||||
.unwrap_or_else(|| Box::new(AesGcm::new(self.send_key.as_bytes(), true))))
|
||||
} else {
|
||||
// Not only do we return an error, but we also destroy the key.
|
||||
let mut scp = self.send_cipher_pool.lock();
|
||||
scp.clear();
|
||||
self.send_key.nuke();
|
||||
|
||||
Err(Error::MaxKeyLifetimeExceeded)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,27 +26,3 @@ macro_rules! debug_event {
|
|||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use debug_event;
|
||||
|
||||
/// Obtain a view into a byte array cast as another byte array.
|
||||
#[inline(always)]
|
||||
pub(crate) fn byte_array_range<const A: usize, const START: usize, const LEN: usize>(a: &[u8; A]) -> &[u8; LEN] {
|
||||
assert!((START + LEN) <= A);
|
||||
unsafe { &*a.as_ptr().add(START).cast::<[u8; LEN]>() }
|
||||
}
|
||||
|
||||
/// View a flat (Copy) object as a byte array.
|
||||
#[inline(always)]
|
||||
pub(crate) fn flat_object_as_bytes<T: Copy>(t: &T) -> &[u8] {
|
||||
unsafe { &*std::ptr::slice_from_raw_parts((t as *const T).cast::<u8>(), std::mem::size_of::<T>()) }
|
||||
}
|
||||
|
||||
/// Trait that annotates a type as being alignment neutral, such as a packed struct of all bytes and byte arrays.
|
||||
pub(crate) unsafe trait AlignmentNeutral: Copy {}
|
||||
|
||||
/// View a byte array as a flat (Copy) object.
|
||||
/// To be safe this can only be used with annotated alignment-neutral structs.
|
||||
#[inline(always)]
|
||||
pub(crate) fn bytes_as_flat_object<T: Copy + AlignmentNeutral>(b: &[u8]) -> &T {
|
||||
assert!(b.len() >= std::mem::size_of::<T>());
|
||||
unsafe { &*b.as_ptr().cast() }
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ use zerotier_crypto::secret::Secret;
|
|||
use zerotier_crypto::x25519::*;
|
||||
|
||||
use zerotier_utils::hex;
|
||||
use zerotier_utils::memory::{as_byte_array, as_flat_object};
|
||||
|
||||
use crate::error::{InvalidFormatError, InvalidParameterError};
|
||||
use crate::util::{bytes_as_flat_object, flat_object_as_bytes, AlignmentNeutral};
|
||||
use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_FINGERPRINT_SIZE, IDENTITY_POW_THRESHOLD};
|
||||
use crate::vl1::Address;
|
||||
|
||||
|
@ -394,7 +394,7 @@ impl Identity {
|
|||
pub fn to_public_bytes(&self) -> IdentityBytes {
|
||||
if let Some(p384) = self.p384.as_ref() {
|
||||
IdentityBytes::X25519P384Public(
|
||||
flat_object_as_bytes(&packed::V1 {
|
||||
as_byte_array(&packed::V1 {
|
||||
v0: packed::V0 {
|
||||
address: self.address.to_bytes(),
|
||||
key_type: 0,
|
||||
|
@ -410,12 +410,11 @@ impl Identity {
|
|||
ecdsa_self_signature: p384.ecdsa_self_signature,
|
||||
ed25519_self_signature: p384.ed25519_self_signature,
|
||||
})
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
.clone(),
|
||||
)
|
||||
} else {
|
||||
IdentityBytes::X25519Public(
|
||||
flat_object_as_bytes(&packed::V0 {
|
||||
as_byte_array(&packed::V0 {
|
||||
address: self.address.to_bytes(),
|
||||
key_type: 0,
|
||||
x25519: self.x25519,
|
||||
|
@ -424,8 +423,7 @@ impl Identity {
|
|||
reserved: 0x03,
|
||||
ext_len: [0; 2],
|
||||
})
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -434,7 +432,7 @@ impl Identity {
|
|||
self.secret.as_ref().map(|s| {
|
||||
if let Some(p384) = s.p384.as_ref() {
|
||||
IdentityBytes::X25519P384Secret(
|
||||
flat_object_as_bytes(&packed::V1S {
|
||||
as_byte_array(&packed::V1S {
|
||||
v0s: packed::V0S {
|
||||
address: self.address.to_bytes(),
|
||||
key_type: 0,
|
||||
|
@ -454,12 +452,11 @@ impl Identity {
|
|||
ecdh_secret: p384.ecdh.secret_key_bytes().0.clone(),
|
||||
ecdsa_secret: p384.ecdsa.secret_key_bytes().0.clone(),
|
||||
})
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
.clone(),
|
||||
)
|
||||
} else {
|
||||
IdentityBytes::X25519Secret(
|
||||
flat_object_as_bytes(&packed::V0S {
|
||||
as_byte_array(&packed::V0S {
|
||||
address: self.address.to_bytes(),
|
||||
key_type: 0,
|
||||
x25519: self.x25519,
|
||||
|
@ -470,8 +467,7 @@ impl Identity {
|
|||
reserved: 0x03,
|
||||
ext_len: [0; 2],
|
||||
})
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -483,7 +479,7 @@ impl Identity {
|
|||
pub fn from_bytes(bytes: &IdentityBytes) -> Option<Self> {
|
||||
let mut id = match bytes {
|
||||
IdentityBytes::X25519Public(b) => {
|
||||
let b: &packed::V0 = bytes_as_flat_object(b);
|
||||
let b: &packed::V0 = as_flat_object(b);
|
||||
if b.key_type == 0 && b.secret_length == 0 && b.reserved == 0x03 && u16::from_be_bytes(b.ext_len) == 0 {
|
||||
Some(Self {
|
||||
address: Address::from_bytes_fixed(&b.address)?,
|
||||
|
@ -498,7 +494,7 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
IdentityBytes::X25519Secret(b) => {
|
||||
let b: &packed::V0S = bytes_as_flat_object(b);
|
||||
let b: &packed::V0S = as_flat_object(b);
|
||||
if b.key_type == 0
|
||||
&& b.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
||||
&& b.reserved == 0x03
|
||||
|
@ -521,7 +517,7 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
IdentityBytes::X25519P384Public(b) => {
|
||||
let b: &packed::V1 = bytes_as_flat_object(b);
|
||||
let b: &packed::V1 = as_flat_object(b);
|
||||
if b.v0.key_type == 0
|
||||
&& b.v0.secret_length == 0
|
||||
&& b.v0.reserved == 0x03
|
||||
|
@ -546,7 +542,7 @@ impl Identity {
|
|||
}
|
||||
}
|
||||
IdentityBytes::X25519P384Secret(b) => {
|
||||
let b: &packed::V1S = bytes_as_flat_object(b);
|
||||
let b: &packed::V1S = as_flat_object(b);
|
||||
if b.v0s.key_type == 0
|
||||
&& b.v0s.secret_length == (C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8
|
||||
&& b.v0s.reserved == 0x03
|
||||
|
@ -587,7 +583,7 @@ impl Identity {
|
|||
pub fn read_bytes<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||
let mut buf = [0_u8; 512];
|
||||
r.read_exact(&mut buf[..Self::BYTE_LENGTH_X25519_PUBLIC])?;
|
||||
let x25519_public = bytes_as_flat_object::<packed::V0>(&buf);
|
||||
let x25519_public: &packed::V0 = as_flat_object(&buf);
|
||||
let ext_len = u16::from_be_bytes(x25519_public.ext_len) as usize;
|
||||
let obj_len = if x25519_public.secret_length == 0 {
|
||||
let obj_len = ext_len + Self::BYTE_LENGTH_X25519_PUBLIC;
|
||||
|
@ -910,11 +906,6 @@ mod packed {
|
|||
pub ecdh_secret: [u8; P384_SECRET_KEY_SIZE],
|
||||
pub ecdsa_secret: [u8; P384_SECRET_KEY_SIZE],
|
||||
}
|
||||
|
||||
unsafe impl AlignmentNeutral for V0 {}
|
||||
unsafe impl AlignmentNeutral for V0S {}
|
||||
unsafe impl AlignmentNeutral for V1 {}
|
||||
unsafe impl AlignmentNeutral for V1S {}
|
||||
}
|
||||
|
||||
/// Identity rendered as a flat byte array.
|
||||
|
|
|
@ -11,9 +11,9 @@ use zerotier_crypto::poly1305;
|
|||
use zerotier_crypto::random::next_u64_secure;
|
||||
use zerotier_crypto::salsa::Salsa;
|
||||
use zerotier_crypto::secret::Secret;
|
||||
use zerotier_utils::memory::array_range;
|
||||
|
||||
use crate::util::buffer::BufferReader;
|
||||
use crate::util::byte_array_range;
|
||||
use crate::util::debug_event;
|
||||
use crate::util::marshalable::Marshalable;
|
||||
use crate::vl1::address::Address;
|
||||
|
@ -142,7 +142,7 @@ fn try_aead_decrypt(
|
|||
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
|
||||
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
|
||||
// decrypted tag.
|
||||
Some(u64::from_ne_bytes(*byte_array_range::<16, 0, 8>(tag)))
|
||||
Some(u64::from_ne_bytes(*array_range::<u8, 16, 0, 8>(tag)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -446,11 +446,11 @@ impl<SI: SystemInterface> Peer<SI> {
|
|||
aes_gmac_siv.encrypt_second_pass_in_place(payload);
|
||||
let tag = aes_gmac_siv.encrypt_second_pass_finish();
|
||||
let header = packet.struct_mut_at::<v1::PacketHeader>(0).unwrap();
|
||||
header.id = *byte_array_range::<16, 0, 8>(tag);
|
||||
header.id = *array_range::<u8, 16, 0, 8>(tag);
|
||||
header.dest = self.identity.address.to_bytes();
|
||||
header.src = node.identity.address.to_bytes();
|
||||
header.flags_cipher_hops = flags_cipher_hops;
|
||||
header.mac = *byte_array_range::<16, 8, 8>(tag);
|
||||
header.mac = *array_range::<u8, 16, 8, 8>(tag);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ path = "src/main.rs"
|
|||
[dependencies]
|
||||
zerotier-network-hypervisor = { path = "../network-hypervisor" }
|
||||
zerotier-crypto = { path = "../crypto" }
|
||||
zerotier-utils = { path = "../utils" }
|
||||
async-trait = "^0"
|
||||
num-traits = "^0"
|
||||
tokio = { version = "^1", features = ["fs", "io-util", "io-std", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "sync", "time"], default-features = false }
|
||||
|
|
|
@ -12,6 +12,7 @@ use zerotier_network_hypervisor::vl2::*;
|
|||
use zerotier_network_hypervisor::*;
|
||||
|
||||
use zerotier_crypto::random;
|
||||
use zerotier_utils::{ms_monotonic, ms_since_epoch};
|
||||
|
||||
use tokio::time::Duration;
|
||||
|
||||
|
@ -19,7 +20,6 @@ use crate::datadir::DataDir;
|
|||
use crate::localinterface::LocalInterface;
|
||||
use crate::localsocket::LocalSocket;
|
||||
use crate::udp::*;
|
||||
use crate::utils::{ms_monotonic, ms_since_epoch};
|
||||
|
||||
/// Interval between scans of system network interfaces to update port bindings.
|
||||
const UDP_UPDATE_BINDINGS_INTERVAL_MS: Duration = Duration::from_millis(5000);
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
|
@ -19,20 +16,32 @@ use zerotier_network_hypervisor::vl1::Identity;
|
|||
/// Default sanity limit parameter for read_limit() used throughout the service.
|
||||
pub const DEFAULT_FILE_IO_READ_LIMIT: usize = 1048576;
|
||||
|
||||
lazy_static! {
|
||||
static ref STARTUP_INSTANT: Instant = Instant::now();
|
||||
/// Convenience function to read up to limit bytes from a file.
|
||||
///
|
||||
/// If the file is larger than limit, the excess is not read.
|
||||
pub async fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||
let mut f = File::open(path).await?;
|
||||
let bytes = f.metadata().await?.len().min(limit as u64) as usize;
|
||||
let mut v: Vec<u8> = Vec::with_capacity(bytes);
|
||||
v.resize(bytes, 0);
|
||||
f.read_exact(v.as_mut_slice()).await?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Get milliseconds since unix epoch.
|
||||
#[inline(always)]
|
||||
pub fn ms_since_epoch() -> i64 {
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64
|
||||
}
|
||||
|
||||
/// Get milliseconds since an arbitrary time in the past, guaranteed to monotonically increase.
|
||||
#[inline(always)]
|
||||
pub fn ms_monotonic() -> i64 {
|
||||
Instant::now().duration_since(*STARTUP_INSTANT).as_millis() as i64
|
||||
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
|
||||
#[cfg(unix)]
|
||||
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
|
||||
unsafe {
|
||||
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
||||
libc::chmod(
|
||||
c_path.as_ptr(),
|
||||
if path.as_ref().is_dir() {
|
||||
0o700
|
||||
} else {
|
||||
0o600
|
||||
},
|
||||
) == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
|
||||
|
@ -143,34 +152,6 @@ pub fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience function to read up to limit bytes from a file.
|
||||
///
|
||||
/// If the file is larger than limit, the excess is not read.
|
||||
pub async fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||
let mut f = File::open(path).await?;
|
||||
let bytes = f.metadata().await?.len().min(limit as u64) as usize;
|
||||
let mut v: Vec<u8> = Vec::with_capacity(bytes);
|
||||
v.resize(bytes, 0);
|
||||
f.read_exact(v.as_mut_slice()).await?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
|
||||
#[cfg(unix)]
|
||||
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
|
||||
unsafe {
|
||||
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
|
||||
libc::chmod(
|
||||
c_path.as_ptr(),
|
||||
if path.as_ref().is_dir() {
|
||||
0o700
|
||||
} else {
|
||||
0o600
|
||||
},
|
||||
) == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Read an identity as either a literal or from a file.
|
||||
pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||
let parse_func = |s: &str| {
|
||||
|
@ -201,22 +182,3 @@ pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity,
|
|||
//pub fn c_strerror() -> String {
|
||||
// unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() }
|
||||
//}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::ms_monotonic;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn monotonic_clock_sanity_check() {
|
||||
let start = ms_monotonic();
|
||||
std::thread::sleep(Duration::from_millis(500));
|
||||
let end = ms_monotonic();
|
||||
// per docs:
|
||||
//
|
||||
// The thread may sleep longer than the duration specified due to scheduling specifics or
|
||||
// platform-dependent functionality. It will never sleep less.
|
||||
//
|
||||
assert!((end - start).abs() >= 500);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
||||
|
||||
pub mod arrayvec;
|
||||
pub mod gatherarray;
|
||||
pub mod hex;
|
||||
|
@ -5,3 +7,43 @@ pub mod memory;
|
|||
pub mod pool;
|
||||
pub mod ringbuffermap;
|
||||
pub mod varint;
|
||||
|
||||
/// Get milliseconds since unix epoch.
|
||||
pub fn ms_since_epoch() -> i64 {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as i64
|
||||
}
|
||||
|
||||
/// Get milliseconds since an arbitrary time in the past, guaranteed to monotonically increase.
|
||||
pub fn ms_monotonic() -> i64 {
|
||||
static STARTUP_INSTANT: parking_lot::RwLock<Option<std::time::Instant>> = parking_lot::RwLock::new(None);
|
||||
let si = *STARTUP_INSTANT.read();
|
||||
let instant_zero = if let Some(si) = si {
|
||||
si
|
||||
} else {
|
||||
*STARTUP_INSTANT.write().get_or_insert(std::time::Instant::now())
|
||||
};
|
||||
std::time::Instant::now().duration_since(instant_zero).as_millis() as i64
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ms_monotonic;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn monotonic_clock_sanity_check() {
|
||||
let start = ms_monotonic();
|
||||
std::thread::sleep(Duration::from_millis(500));
|
||||
let end = ms_monotonic();
|
||||
// per docs:
|
||||
//
|
||||
// The thread may sleep longer than the duration specified due to scheduling specifics or
|
||||
// platform-dependent functionality. It will never sleep less.
|
||||
//
|
||||
assert!((end - start).abs() >= 500);
|
||||
assert!((end - start).abs() < 750);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::mem::size_of;
|
||||
|
||||
// Version for architectures that definitely don't care about unaligned memory access.
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||
#[allow(unused)]
|
||||
mod fast_int_memory_access {
|
||||
|
@ -66,6 +67,7 @@ mod fast_int_memory_access {
|
|||
}
|
||||
}
|
||||
|
||||
// Version for architectures that might care about unaligned memory access.
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
#[allow(unused)]
|
||||
mod fast_int_memory_access {
|
||||
|
@ -122,10 +124,26 @@ mod fast_int_memory_access {
|
|||
|
||||
pub use fast_int_memory_access::*;
|
||||
|
||||
/// Obtain a view into an array cast as another array.
|
||||
/// This will panic if the template parameters would result in out of bounds access.
|
||||
#[inline(always)]
|
||||
pub fn array_range<T: Copy, const S: usize, const START: usize, const LEN: usize>(a: &[T; S]) -> &[T; LEN] {
|
||||
assert!((START + LEN) <= S);
|
||||
unsafe { &*a.as_ptr().add(START).cast::<[T; LEN]>() }
|
||||
}
|
||||
|
||||
/// Get a reference to a raw object as a byte array.
|
||||
/// The template parameter S must equal the size of the object in bytes or this will panic.
|
||||
#[inline(always)]
|
||||
pub fn as_byte_array<T: Copy, const S: usize>(o: &T) -> &[u8; S] {
|
||||
assert_eq!(S, size_of::<T>());
|
||||
unsafe { &*(o as *const T).cast::<[u8; S]>() }
|
||||
unsafe { &*(o as *const T).cast() }
|
||||
}
|
||||
|
||||
/// Get a byte array as a flat object.
|
||||
///
|
||||
/// WARNING: while this is technically safe, care must be taken if the object requires aligned access.
|
||||
pub fn as_flat_object<T: Copy, const S: usize>(b: &[u8; S]) -> &T {
|
||||
assert!(std::mem::size_of::<T>() <= S);
|
||||
unsafe { &*b.as_ptr().cast() }
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue