mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-08 05:23:44 +02:00
move to rustsvc branch
This commit is contained in:
parent
4f70873a46
commit
1fc4bc96d1
23 changed files with 8 additions and 4380 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -6,3 +6,11 @@
|
|||
*.so
|
||||
*.dylib
|
||||
*.a
|
||||
/.idea
|
||||
|
||||
# Added by cargo
|
||||
#
|
||||
# already existing elements were commented out
|
||||
|
||||
#/target
|
||||
Cargo.lock
|
||||
|
|
116
attic/rust-zerotier-core/Cargo.lock
generated
116
attic/rust-zerotier-core/Cargo.lock
generated
|
@ -1,116 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "base64-serde"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e964e3e0a930303c7c0bdb28ebf691dd98d9eee4b8b68019d2c995710b58a18"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "zerotier-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64-serde",
|
||||
"hex",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "zerotier-core"
|
||||
version = "0.1.0"
|
||||
authors = ["Adam Ierymenko <adam.ierymenko@zerotier.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 'z'
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = 'abort'
|
||||
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
hex = "0.4"
|
||||
base64-serde = "0"
|
||||
base64 = "0"
|
|
@ -1,13 +0,0 @@
|
|||
#[allow(unused_assignments)]
|
||||
#[allow(unused_mut)]
|
||||
fn main() {
|
||||
let d = env!("CARGO_MANIFEST_DIR");
|
||||
println!("cargo:rustc-link-search=native={}/../build/core", d);
|
||||
println!("cargo:rustc-link-lib=static=zt_core");
|
||||
|
||||
let mut cpplib = "c++";
|
||||
#[cfg(target_os = "linux")] {
|
||||
cpplib = "stdc++";
|
||||
}
|
||||
println!("cargo:rustc-link-lib={}", cpplib);
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
|
||||
pub struct Address(pub u64);
|
||||
|
||||
impl Default for Address {
|
||||
#[inline(always)]
|
||||
fn default() -> Address {
|
||||
Address(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
#[inline(always)]
|
||||
fn to_bytes(&self) -> [u8; 5] {
|
||||
[(self.0 >> 32) as u8, (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Address {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Address {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{:0>10x}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Address {
|
||||
#[inline(always)]
|
||||
fn from(i: u64) -> Self {
|
||||
Address(i & 0xffffffffff)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Address {
|
||||
#[inline(always)]
|
||||
fn from(s: &str) -> Self {
|
||||
Address::from(u64::from_str_radix(s, 16).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Address {
|
||||
#[inline(always)]
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
if bytes.len() >= 5 {
|
||||
Address(((bytes[0] as u64) << 32) | ((bytes[1] as u64) << 24) | ((bytes[2] as u64) << 16) | ((bytes[3] as u64) << 8) | (bytes[4] as u64))
|
||||
} else {
|
||||
Address(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Address {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} else {
|
||||
let b = self.to_bytes();
|
||||
serializer.serialize_bytes(b.as_ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
||||
type Value = Address;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") }
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) }
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Address {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(AddressVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(AddressVisitor)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
/// A reusable buffer for I/O to/from the ZeroTier core.
|
||||
///
|
||||
/// The core allocates and manages a pool of these. This provides a Rust
|
||||
/// interface to that pool. ZT core buffers are used to reduce the need for
|
||||
/// memory copying by passing buffers around instead of memcpy'ing when
|
||||
/// packet data is passed into and out of the core.
|
||||
///
|
||||
/// IMPORTANT NOTE: when these are fed into the ZeroTier core, drop() is
|
||||
/// elided via std::mem::forget(). Node does this automatically so usually
|
||||
/// users of this API do not need to be aware of it, but it's worth mentioning
|
||||
/// in case someone re-implements calls directly into the core. Dropping this
|
||||
/// after handing it back to the core could result in mytserious corruption
|
||||
/// bugs or double-free.
|
||||
///
|
||||
/// This does not implement copy or clone because that would result in this
|
||||
/// memory being dropped more than once. Use Rc or Arc to share.
|
||||
pub struct Buffer {
|
||||
pub(crate) zt_core_buf: *mut u8,
|
||||
pub(crate) data_size: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
/// Maximum capacity of a ZeroTier reusable buffer.
|
||||
pub const CAPACITY: usize = ztcore::ZT_BUF_SIZE as usize;
|
||||
|
||||
/// Obtain a new buffer from the core and set the size of its data to CAPACITY.
|
||||
/// The contents of the buffer are not defined.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Buffer {
|
||||
let b = unsafe { ztcore::ZT_getBuffer() as *mut u8 };
|
||||
if b.is_null() {
|
||||
panic!("out of memory calling ZT_getBuffer()");
|
||||
}
|
||||
Buffer {
|
||||
zt_core_buf: b,
|
||||
data_size: ztcore::ZT_BUF_SIZE as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current size of the data held by this buffer.
|
||||
/// Initially this is equal to CAPACITY.
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.data_size
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.zt_core_buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.zt_core_buf
|
||||
}
|
||||
|
||||
/// Set the size of the data held by this buffer.
|
||||
/// This is usually called after writing data into the buffer.
|
||||
#[inline(always)]
|
||||
pub fn set_len(&mut self, s: usize) {
|
||||
// CAPACITY will always be a power of two, so we can just mask this
|
||||
// to make this safe. This is a sanity check to make it impossible to
|
||||
// set this to an invalid size.
|
||||
self.data_size = s & (Buffer::CAPACITY - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Buffer {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
unsafe { ztcore::ZT_freeBuffer(self.zt_core_buf as *mut c_void); }
|
||||
}
|
||||
}
|
|
@ -1,818 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::zeroed;
|
||||
use std::os::raw::{c_char, c_uint, c_void};
|
||||
use std::pin::Pin;
|
||||
use std::ptr::{copy_nonoverlapping, null, null_mut, read_unaligned};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
#[allow(unused_imports)]
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
/// Maximum length of a string in a certificate (mostly for the certificate name fields).
|
||||
pub const CERTIFICATE_MAX_STRING_LENGTH: isize = ztcore::ZT_CERTIFICATE_MAX_STRING_LENGTH as isize;
|
||||
|
||||
/// Certificate local trust bit field flag: this certificate self-signs a root CA.
|
||||
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
|
||||
pub const CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG;
|
||||
|
||||
pub const CERTIFICATE_USAGE_DIGITAL_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE as u64;
|
||||
pub const CERTIFICATE_USAGE_NON_REPUDIATION: u64 = ztcore::ZT_CERTIFICATE_USAGE_NON_REPUDIATION as u64;
|
||||
pub const CERTIFICATE_USAGE_KEY_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_DATA_ENCIPHERMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_KEY_AGREEMENT: u64 = ztcore::ZT_CERTIFICATE_USAGE_KEY_AGREEMENT as u64;
|
||||
pub const CERTIFICATE_USAGE_CERTIFICATE_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING as u64;
|
||||
pub const CERTIFICATE_USAGE_CRL_SIGNING: u64 = ztcore::ZT_CERTIFICATE_USAGE_CRL_SIGNING as u64;
|
||||
pub const CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE as u64;
|
||||
pub const CERTIFICATE_USAGE_TIMESTAMPING: u64 = ztcore::ZT_CERTIFICATE_USAGE_TIMESTAMPING as u64;
|
||||
pub const CERTIFICATE_USAGE_ZEROTIER_ROOT_SET: u64 = ztcore::ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET as u64;
|
||||
|
||||
#[inline(always)]
|
||||
fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] {
|
||||
let mut a = [0_u8; L];
|
||||
let vl = v.len();
|
||||
if vl >= L {
|
||||
a.copy_from_slice(&v.as_slice()[0..L]);
|
||||
} else {
|
||||
a.copy_from_slice(v.as_slice());
|
||||
}
|
||||
a
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum CertificatePublicKeyAlgorithm {
|
||||
None = ztcore::ZT_CertificatePublicKeyAlgorithm_ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE as isize,
|
||||
ECDSANistP384 = ztcore::ZT_CertificatePublicKeyAlgorithm_ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384 as isize,
|
||||
}
|
||||
|
||||
impl From<i32> for CertificatePublicKeyAlgorithm {
|
||||
#[inline(always)]
|
||||
fn from(n: i32) -> CertificatePublicKeyAlgorithm {
|
||||
CertificatePublicKeyAlgorithm::from_i32(n).unwrap_or(CertificatePublicKeyAlgorithm::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct CertificateSerialNo(pub [u8; 48]);
|
||||
|
||||
impl CertificateSerialNo {
|
||||
pub const SIZE: usize = 48;
|
||||
pub const SIZE_HEX: usize = 96;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0_u8; 48]) }
|
||||
|
||||
/// Create a new certificate serial from a hex string, returning None if invalid.
|
||||
/// The from() alternative for converting from a string returns an a nil (all zero) serial on error.
|
||||
pub fn new_from_string(s: &str) -> Option<CertificateSerialNo> {
|
||||
hex::decode(s).map_or(None, |b| {
|
||||
if b.len() == Self::SIZE {
|
||||
Some(CertificateSerialNo(vec_to_array::<48>(&b)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if serial is all zeroes.
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
is_all_zeroes(self.0)
|
||||
}
|
||||
|
||||
/// Returns true if this serial indicates a self-signed certificate.
|
||||
/// This is meaningful for the serial in the issuer field of Certificate,
|
||||
/// which will be all 0xff in self-signed certificates.
|
||||
#[inline(always)]
|
||||
pub fn is_self_signed(&self) -> bool {
|
||||
for x in self.0.iter() {
|
||||
if *x != 0xff {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Set this to all 0xff which in the issuer field means self-signed.
|
||||
#[inline(always)]
|
||||
pub fn set_self_signed(&mut self) {
|
||||
self.0.fill(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Serials are SHA384 hashes, so we can just use the first 8 bytes as-is.
|
||||
state.write_u64(unsafe { read_unaligned::<u64>(self.0.as_ptr().cast()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> From<S> for CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
fn from(s: S) -> CertificateSerialNo {
|
||||
Self::new_from_string(s.as_ref()).unwrap_or_else(|| CertificateSerialNo::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CertificateSerialNo {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String { hex::encode(self.0) }
|
||||
}
|
||||
|
||||
impl serde::Serialize for CertificateSerialNo {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||
}
|
||||
|
||||
struct CertificateSerialNoVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for CertificateSerialNoVisitor {
|
||||
type Value = CertificateSerialNo;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("object") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) }, |serial| { Ok(serial as Self::Value) }) }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for CertificateSerialNo {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateSerialNoVisitor) }
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum CertificateError {
|
||||
None = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_NONE as isize,
|
||||
InvalidFormat = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_FORMAT as isize,
|
||||
InvalidIdentity = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_IDENTITY as isize,
|
||||
InvalidPrimarySignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE as isize,
|
||||
InvalidChain = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_CHAIN as isize,
|
||||
InvalidComponentSignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE as isize,
|
||||
InvalidUniqueIdProof = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF as isize,
|
||||
MissingRequiredFields = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS as isize,
|
||||
OutOfValidTimeWindow = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW as isize,
|
||||
Revoked = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_REVOKED as isize,
|
||||
}
|
||||
|
||||
impl CertificateError {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
CertificateError::None => "None",
|
||||
CertificateError::InvalidFormat => "InvalidFormat",
|
||||
CertificateError::InvalidIdentity => "InvalidIdentity",
|
||||
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
|
||||
CertificateError::InvalidChain => "InvalidChain",
|
||||
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
|
||||
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
|
||||
CertificateError::MissingRequiredFields => "MissingRequiredFields",
|
||||
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow",
|
||||
CertificateError::Revoked => "Revoked",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CertificateError {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
String::from(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> From<S> for CertificateError {
|
||||
fn from(s: S) -> CertificateError {
|
||||
match s.as_ref().to_ascii_lowercase().as_str() {
|
||||
"invalidformat" => CertificateError::InvalidFormat,
|
||||
"invalididentity" => CertificateError::InvalidIdentity,
|
||||
"invalidprimarysignature" => CertificateError::InvalidPrimarySignature,
|
||||
"invalidchain" => CertificateError::InvalidChain,
|
||||
"invalidcomponentsignature" => CertificateError::InvalidComponentSignature,
|
||||
"invaliduniqueidproof" => CertificateError::InvalidUniqueIdProof,
|
||||
"missingrequiredfields" => CertificateError::MissingRequiredFields,
|
||||
"outofvalidtimewindow" => CertificateError::OutOfValidTimeWindow,
|
||||
"revoked" => CertificateError::Revoked,
|
||||
_ => CertificateError::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for CertificateError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||
}
|
||||
|
||||
struct CertificateErrorVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for CertificateErrorVisitor {
|
||||
type Value = CertificateError;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("object") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { return Ok(CertificateError::from(s)); }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for CertificateError {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateErrorVisitor) }
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct CertificateName {
|
||||
#[serde(rename = "serialNo")]
|
||||
pub serial_no: String,
|
||||
#[serde(rename = "commonName")]
|
||||
pub common_name: String,
|
||||
pub country: String,
|
||||
pub organization: String,
|
||||
pub unit: String,
|
||||
pub locality: String,
|
||||
pub province: String,
|
||||
#[serde(rename = "streetAddress")]
|
||||
pub street_address: String,
|
||||
#[serde(rename = "postalCode")]
|
||||
pub postal_code: String,
|
||||
pub email: String,
|
||||
pub url: String,
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
impl CertificateName {
|
||||
pub fn new() -> CertificateName {
|
||||
CertificateName {
|
||||
serial_no: String::new(),
|
||||
common_name: String::new(),
|
||||
country: String::new(),
|
||||
organization: String::new(),
|
||||
unit: String::new(),
|
||||
locality: String::new(),
|
||||
province: String::new(),
|
||||
street_address: String::new(),
|
||||
postal_code: String::new(),
|
||||
email: String::new(),
|
||||
url: String::new(),
|
||||
host: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn new_from_capi(cn: &ztcore::ZT_Certificate_Name) -> CertificateName {
|
||||
return CertificateName {
|
||||
serial_no: cstr_to_string(cn.serialNo.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
common_name: cstr_to_string(cn.commonName.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
country: cstr_to_string(cn.country.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
organization: cstr_to_string(cn.organization.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
unit: cstr_to_string(cn.unit.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
locality: cstr_to_string(cn.locality.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
province: cstr_to_string(cn.province.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
street_address: cstr_to_string(cn.streetAddress.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
postal_code: cstr_to_string(cn.postalCode.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
email: cstr_to_string(cn.email.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
url: cstr_to_string(cn.url.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
host: cstr_to_string(cn.host.as_ptr(), CERTIFICATE_MAX_STRING_LENGTH - 1),
|
||||
};
|
||||
}
|
||||
|
||||
fn str_to_cert_cstr(s: &String, cs: &mut [c_char; 128]) {
|
||||
let mut l = s.len();
|
||||
if l == 0 {
|
||||
cs[0] = 0;
|
||||
return;
|
||||
}
|
||||
if l > 126 {
|
||||
l = 126;
|
||||
}
|
||||
unsafe { copy_nonoverlapping(s.as_ptr(), cs.as_mut_ptr() as *mut u8, l); }
|
||||
cs[l + 1] = 0;
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn to_capi(&self) -> ztcore::ZT_Certificate_Name {
|
||||
let mut cn: ztcore::ZT_Certificate_Name = zeroed();
|
||||
Self::str_to_cert_cstr(&self.serial_no, &mut cn.serialNo);
|
||||
Self::str_to_cert_cstr(&self.common_name, &mut cn.commonName);
|
||||
Self::str_to_cert_cstr(&self.country, &mut cn.country);
|
||||
Self::str_to_cert_cstr(&self.organization, &mut cn.organization);
|
||||
Self::str_to_cert_cstr(&self.unit, &mut cn.unit);
|
||||
Self::str_to_cert_cstr(&self.locality, &mut cn.locality);
|
||||
Self::str_to_cert_cstr(&self.province, &mut cn.province);
|
||||
Self::str_to_cert_cstr(&self.street_address, &mut cn.streetAddress);
|
||||
Self::str_to_cert_cstr(&self.postal_code, &mut cn.postalCode);
|
||||
Self::str_to_cert_cstr(&self.email, &mut cn.email);
|
||||
Self::str_to_cert_cstr(&self.url, &mut cn.url);
|
||||
Self::str_to_cert_cstr(&self.host, &mut cn.host);
|
||||
return cn;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct CertificateNetwork {
|
||||
pub id: NetworkId,
|
||||
pub controller: Option<Fingerprint>,
|
||||
}
|
||||
|
||||
impl CertificateNetwork {
|
||||
pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Network) -> CertificateNetwork {
|
||||
if is_all_zeroes(cn.controller.hash) {
|
||||
CertificateNetwork {
|
||||
id: NetworkId(cn.id),
|
||||
controller: None,
|
||||
}
|
||||
} else {
|
||||
CertificateNetwork {
|
||||
id: NetworkId(cn.id),
|
||||
controller: Some(Fingerprint {
|
||||
address: Address(cn.controller.address),
|
||||
hash: cn.controller.hash,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_capi(&self) -> ztcore::ZT_Certificate_Network {
|
||||
self.controller.as_ref().map_or_else(|| {
|
||||
ztcore::ZT_Certificate_Network {
|
||||
id: self.id.0,
|
||||
controller: ztcore::ZT_Fingerprint {
|
||||
address: 0,
|
||||
hash: [0_u8; 48],
|
||||
},
|
||||
}
|
||||
}, |controller| {
|
||||
ztcore::ZT_Certificate_Network {
|
||||
id: self.id.0,
|
||||
controller: ztcore::ZT_Fingerprint {
|
||||
address: controller.address.0,
|
||||
hash: controller.hash,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct CertificateIdentity {
|
||||
pub identity: Identity,
|
||||
pub locator: Option<Locator>,
|
||||
}
|
||||
|
||||
impl CertificateIdentity {
|
||||
pub(crate) unsafe fn new_from_capi(ci: &ztcore::ZT_Certificate_Identity) -> Option<CertificateIdentity> {
|
||||
if ci.identity.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(CertificateIdentity {
|
||||
identity: Identity::new_from_capi(ci.identity, false).clone(),
|
||||
locator: if ci.locator.is_null() { None } else { Some(Locator::new_from_capi(ci.locator, false).clone()) },
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn to_capi(&self) -> ztcore::ZT_Certificate_Identity {
|
||||
ztcore::ZT_Certificate_Identity {
|
||||
identity: self.identity.capi,
|
||||
locator: if self.locator.is_some() { self.locator.as_ref().unwrap().capi } else { null() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct CertificateSubject {
|
||||
pub timestamp: i64,
|
||||
pub identities: Vec<CertificateIdentity>,
|
||||
pub networks: Vec<CertificateNetwork>,
|
||||
#[serde(rename = "updateURLs")]
|
||||
pub update_urls: Vec<String>,
|
||||
pub name: CertificateName,
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
#[serde(rename = "uniqueId")]
|
||||
pub unique_id: Vec<u8>,
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
#[serde(rename = "uniqueIdSignature")]
|
||||
pub unique_id_signature: Vec<u8>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct CertificateSubjectCAPIContainer {
|
||||
pub(crate) subject: ztcore::ZT_Certificate_Subject,
|
||||
subject_identities: Option<Pin<Box<[ztcore::ZT_Certificate_Identity]>>>,
|
||||
subject_networks: Option<Pin<Box<[ztcore::ZT_Certificate_Network]>>>,
|
||||
subject_urls: Option<Pin<Box<[*const c_char]>>>,
|
||||
subject_urls_strs: Option<Pin<Box<[CString]>>>,
|
||||
}
|
||||
|
||||
impl CertificateSubject {
|
||||
pub fn new() -> CertificateSubject {
|
||||
CertificateSubject {
|
||||
timestamp: 0,
|
||||
identities: Vec::new(),
|
||||
networks: Vec::new(),
|
||||
update_urls: Vec::new(),
|
||||
name: CertificateName::new(),
|
||||
unique_id: Vec::new(),
|
||||
unique_id_signature: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn new_from_capi(cs: &ztcore::ZT_Certificate_Subject) -> CertificateSubject {
|
||||
let mut identities: Vec<CertificateIdentity> = Vec::new();
|
||||
if !cs.identities.is_null() && cs.identityCount > 0 {
|
||||
let cidentities: &[ztcore::ZT_Certificate_Identity] = std::slice::from_raw_parts(cs.identities, cs.identityCount as usize);
|
||||
for i in cidentities.iter() {
|
||||
let ci = CertificateIdentity::new_from_capi(i);
|
||||
if ci.is_some() {
|
||||
identities.push(ci.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut networks: Vec<CertificateNetwork> = Vec::new();
|
||||
if !cs.networks.is_null() && cs.networkCount > 0 {
|
||||
let cnetworks: &[ztcore::ZT_Certificate_Network] = std::slice::from_raw_parts(cs.networks, cs.networkCount as usize);
|
||||
for i in cnetworks.iter() {
|
||||
networks.push(CertificateNetwork::new_from_capi(i));
|
||||
}
|
||||
}
|
||||
|
||||
let mut update_urls: Vec<String> = Vec::new();
|
||||
if !cs.updateURLs.is_null() && cs.updateURLCount > 0 {
|
||||
let cupdate_urls: &[*const c_char] = std::slice::from_raw_parts(cs.updateURLs, cs.updateURLCount as usize);
|
||||
for i in cupdate_urls.iter() {
|
||||
update_urls.push(cstr_to_string(*i, CERTIFICATE_MAX_STRING_LENGTH - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return CertificateSubject {
|
||||
timestamp: cs.timestamp,
|
||||
identities,
|
||||
networks,
|
||||
update_urls,
|
||||
name: CertificateName::new_from_capi(&cs.name),
|
||||
unique_id: cs.uniqueId[0..(cs.uniqueIdSize as usize)].to_vec(),
|
||||
unique_id_signature: cs.uniqueIdSignature[0..cs.uniqueIdSignatureSize as usize].to_vec(),
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn to_capi(&self) -> CertificateSubjectCAPIContainer {
|
||||
let mut identity_count: c_uint = 0;
|
||||
let mut network_count: c_uint = 0;
|
||||
let mut update_url_count: c_uint = 0;
|
||||
|
||||
let mut capi_identities = if self.identities.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut capi_identities: Vec<ztcore::ZT_Certificate_Identity> = Vec::new();
|
||||
capi_identities.reserve(self.identities.len());
|
||||
for i in self.identities.iter() {
|
||||
capi_identities.push((*i).to_capi());
|
||||
}
|
||||
identity_count = capi_identities.len() as c_uint;
|
||||
Some(Pin::from(capi_identities.into_boxed_slice()))
|
||||
};
|
||||
|
||||
let mut capi_networks = if self.networks.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut capi_networks: Vec<ztcore::ZT_Certificate_Network> = Vec::new();
|
||||
capi_networks.reserve(self.networks.len());
|
||||
for i in self.networks.iter() {
|
||||
capi_networks.push((*i).to_capi());
|
||||
}
|
||||
network_count = capi_networks.len() as c_uint;
|
||||
Some(Pin::from(capi_networks.into_boxed_slice()))
|
||||
};
|
||||
|
||||
let (mut capi_urls, capi_urls_strs) = if self.update_urls.is_empty() {
|
||||
(None, None)
|
||||
} else {
|
||||
let mut capi_urls_strs: Vec<CString> = Vec::new();
|
||||
let mut capi_urls: Vec<*const c_char> = Vec::new();
|
||||
capi_urls_strs.reserve(self.update_urls.len());
|
||||
capi_urls.reserve(self.update_urls.len());
|
||||
for i in self.update_urls.iter() {
|
||||
let cs = CString::new((*i).as_str());
|
||||
if cs.is_ok() {
|
||||
capi_urls_strs.push(cs.unwrap());
|
||||
}
|
||||
}
|
||||
let capi_urls_strs = Pin::from(capi_urls_strs.into_boxed_slice());
|
||||
for i in capi_urls_strs.iter() {
|
||||
capi_urls.push((*i).as_ptr());
|
||||
}
|
||||
update_url_count = capi_urls.len() as c_uint;
|
||||
(Some(Pin::from(capi_urls.into_boxed_slice())), Some(capi_urls_strs))
|
||||
};
|
||||
|
||||
CertificateSubjectCAPIContainer {
|
||||
subject: ztcore::ZT_Certificate_Subject {
|
||||
timestamp: self.timestamp,
|
||||
identities: capi_identities.as_mut().map_or(null_mut(), |v| v.as_mut_ptr()),
|
||||
networks: capi_networks.as_mut().map_or(null_mut(), |v| v.as_mut_ptr()),
|
||||
updateURLs: capi_urls.as_mut().map_or(null_mut(), |v| v.as_mut_ptr()),
|
||||
identityCount: identity_count,
|
||||
networkCount: network_count,
|
||||
updateURLCount: update_url_count,
|
||||
name: self.name.to_capi(),
|
||||
uniqueId: vec_to_array(&self.unique_id),
|
||||
uniqueIdSignature: vec_to_array(&self.unique_id_signature),
|
||||
uniqueIdSize: self.unique_id.len() as c_uint,
|
||||
uniqueIdSignatureSize: self.unique_id_signature.len() as c_uint,
|
||||
},
|
||||
subject_identities: capi_identities,
|
||||
subject_networks: capi_networks,
|
||||
subject_urls: capi_urls,
|
||||
subject_urls_strs: capi_urls_strs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new certificate signing request.
|
||||
/// A CSR is a Certificate containing only the subject (with optional unique ID and signature)
|
||||
/// and its private key. Other fields must be filled in by the owner of the signing certificate.
|
||||
pub fn new_csr(&self, certifcate_private_key: &[u8], subject_unique_id_private_key: Option<&[u8]>) -> Result<Vec<u8>, ResultCode> {
|
||||
let mut csr: Vec<u8> = Vec::new();
|
||||
csr.resize(65536, 0);
|
||||
let mut csr_size: c_int = 65536;
|
||||
|
||||
let (uid, uid_size) = subject_unique_id_private_key.map_or((null::<u8>(), 0 as c_int), |b| (b.as_ptr(), b.len() as c_int));
|
||||
let r = unsafe {
|
||||
let s = self.to_capi();
|
||||
ztcore::ZT_Certificate_newCSR(&s.subject, certifcate_private_key.as_ptr().cast(), certifcate_private_key.len() as c_int, uid.cast(), uid_size, csr.as_mut_ptr().cast(), &mut csr_size)
|
||||
};
|
||||
|
||||
if r == 0 {
|
||||
csr.resize(csr_size as usize, 0);
|
||||
csr.shrink_to_fit();
|
||||
Ok(csr)
|
||||
} else {
|
||||
Err(ResultCode::from_i32(r as i32).unwrap_or(ResultCode::ErrorInternalNonFatal))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Certificate {
|
||||
#[serde(rename = "serialNo")]
|
||||
pub serial_no: CertificateSerialNo,
|
||||
#[serde(rename = "usageFlags")]
|
||||
pub usage_flags: u64,
|
||||
pub timestamp: i64,
|
||||
pub validity: [i64; 2],
|
||||
pub subject: CertificateSubject,
|
||||
pub issuer: CertificateSerialNo,
|
||||
#[serde(rename = "issuerPublicKey")]
|
||||
pub issuer_public_key: Vec<u8>,
|
||||
#[serde(rename = "publicKey")]
|
||||
pub public_key: Vec<u8>,
|
||||
#[serde(rename = "subjectSignature")]
|
||||
pub subject_signature: Vec<u8>,
|
||||
#[serde(rename = "extendedAttributes")]
|
||||
pub extended_attributes: Vec<u8>,
|
||||
#[serde(with = "Base64URLSafeNoPad")]
|
||||
pub signature: Vec<u8>,
|
||||
#[serde(rename = "maxPathLength")]
|
||||
pub max_path_length: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct CertificateCAPIContainer {
|
||||
pub(crate) certificate: ztcore::ZT_Certificate,
|
||||
subject_container: CertificateSubjectCAPIContainer,
|
||||
}
|
||||
|
||||
impl Certificate {
|
||||
/// Create a new public/private key pair for use in certificate signing or subject unique IDs.
|
||||
/// This returns a pair of (public, private) or an error. The first byte of both the
|
||||
/// public and private are the type.
|
||||
pub fn new_key_pair(alg: CertificatePublicKeyAlgorithm) -> Result<(Vec<u8>, Vec<u8>), ResultCode> {
|
||||
let mut public_key = [0_u8; ztcore::ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE as usize];
|
||||
let mut private_key = [0_u8; ztcore::ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE as usize];
|
||||
let mut public_key_size: c_int = 0;
|
||||
let mut private_key_size: c_int = 0;
|
||||
let r = unsafe { ztcore::ZT_Certificate_newKeyPair(alg.to_i32().unwrap() as ztcore::ZT_CertificatePublicKeyAlgorithm, public_key.as_mut_ptr(), &mut public_key_size, private_key.as_mut_ptr(), &mut private_key_size) };
|
||||
if r == 0 {
|
||||
if public_key_size > 0 && private_key_size > 0 {
|
||||
Ok((public_key[0..public_key_size as usize].to_vec(), private_key[0..private_key_size as usize].to_vec()))
|
||||
} else {
|
||||
Err(ResultCode::ErrorInternalNonFatal)
|
||||
}
|
||||
} else {
|
||||
Err(ResultCode::from_i32(r as i32).unwrap_or(ResultCode::ErrorBadParameter))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an empty certificate structure.
|
||||
pub fn new() -> Certificate {
|
||||
Certificate {
|
||||
serial_no: CertificateSerialNo::new(),
|
||||
usage_flags: 0,
|
||||
timestamp: 0,
|
||||
validity: [0, i64::MAX],
|
||||
subject: CertificateSubject::new(),
|
||||
issuer: CertificateSerialNo::new(),
|
||||
issuer_public_key: Vec::new(),
|
||||
public_key: Vec::new(),
|
||||
subject_signature: Vec::new(),
|
||||
extended_attributes: Vec::new(),
|
||||
max_path_length: 0,
|
||||
signature: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate {
|
||||
return Certificate {
|
||||
serial_no: CertificateSerialNo(c.serialNo),
|
||||
usage_flags: c.usageFlags,
|
||||
timestamp: c.timestamp,
|
||||
validity: c.validity,
|
||||
subject: CertificateSubject::new_from_capi(&c.subject),
|
||||
issuer: CertificateSerialNo(c.issuer),
|
||||
issuer_public_key: c.issuerPublicKey[0..(c.issuerPublicKeySize as usize)].to_vec(),
|
||||
public_key: c.publicKey[0..(c.publicKeySize as usize)].to_vec(),
|
||||
subject_signature: c.subjectSignature[0..(c.subjectSignatureSize as usize)].to_vec(),
|
||||
extended_attributes: Vec::from(std::slice::from_raw_parts(c.extendedAttributes, c.extendedAttributesSize as usize)),
|
||||
max_path_length: c.maxPathLength as u32,
|
||||
signature: c.signature[0..(c.signatureSize as usize)].to_vec(),
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn to_capi(&self) -> CertificateCAPIContainer {
|
||||
let subject = self.subject.to_capi();
|
||||
CertificateCAPIContainer {
|
||||
certificate: ztcore::ZT_Certificate {
|
||||
serialNo: self.serial_no.0,
|
||||
usageFlags: self.usage_flags,
|
||||
timestamp: self.timestamp,
|
||||
validity: self.validity,
|
||||
subject: subject.subject,
|
||||
issuer: self.issuer.0,
|
||||
issuerPublicKey: vec_to_array(&self.issuer_public_key),
|
||||
publicKey: vec_to_array(&self.public_key),
|
||||
subjectSignature: vec_to_array(&self.subject_signature),
|
||||
issuerPublicKeySize: self.issuer_public_key.len() as c_uint,
|
||||
publicKeySize: self.public_key.len() as c_uint,
|
||||
subjectSignatureSize: self.subject_signature.len() as c_uint,
|
||||
extendedAttributes: self.extended_attributes.as_ptr(),
|
||||
extendedAttributesSize: self.extended_attributes.len() as c_uint,
|
||||
maxPathLength: self.max_path_length as c_uint,
|
||||
signature: vec_to_array(&self.signature),
|
||||
signatureSize: self.signature.len() as c_uint,
|
||||
},
|
||||
subject_container: subject,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_bytes(b: &[u8], verify: bool) -> Result<Certificate, CertificateError> {
|
||||
let mut capi_cert: *const ztcore::ZT_Certificate = null_mut();
|
||||
let capi_verify: c_int = if verify { 1 } else { 0 };
|
||||
let result = unsafe { ztcore::ZT_Certificate_decode(&mut capi_cert as *mut *const ztcore::ZT_Certificate, b.as_ptr() as *const c_void, b.len() as c_int, capi_verify) };
|
||||
if result != ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_NONE {
|
||||
return Err(CertificateError::from_i32(result as i32).unwrap_or(CertificateError::InvalidFormat));
|
||||
}
|
||||
if capi_cert.is_null() {
|
||||
return Err(CertificateError::InvalidFormat);
|
||||
}
|
||||
unsafe {
|
||||
let cert = Certificate::new_from_capi(&*capi_cert);
|
||||
ztcore::ZT_Certificate_delete(capi_cert);
|
||||
return Ok(cert);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, ResultCode> {
|
||||
let mut cert: Vec<u8> = Vec::new();
|
||||
cert.resize(16384, 0);
|
||||
let mut cert_size: c_int = 16384;
|
||||
unsafe {
|
||||
let capi = self.to_capi();
|
||||
if ztcore::ZT_Certificate_encode(&capi.certificate as *const ztcore::ZT_Certificate, cert.as_mut_ptr() as *mut c_void, &mut cert_size) != 0 {
|
||||
return Err(ResultCode::ErrorInternalNonFatal);
|
||||
}
|
||||
}
|
||||
cert.resize(cert_size as usize, 0);
|
||||
return Ok(cert);
|
||||
}
|
||||
|
||||
/// Sign this certificate, returning new signed certificate.
|
||||
pub fn sign(&self, issuer: &CertificateSerialNo, issuer_private_key: &[u8]) -> Result<Certificate, ResultCode> {
|
||||
let signed_cert = unsafe {
|
||||
let c = self.to_capi();
|
||||
ztcore::ZT_Certificate_sign(&c.certificate, issuer.0.as_ptr(), issuer_private_key.as_ptr().cast(), issuer_private_key.len() as c_int)
|
||||
};
|
||||
if signed_cert.is_null() {
|
||||
Err(ResultCode::ErrorBadParameter)
|
||||
} else {
|
||||
let signed_cert2 = unsafe { Certificate::new_from_capi(&*signed_cert) };
|
||||
unsafe { ztcore::ZT_Certificate_delete(signed_cert) };
|
||||
Ok(signed_cert2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify certificate structure and signatures.
|
||||
/// This does not verify the full certificate chain, just what can be verified
|
||||
/// by looking at the certificate itself.
|
||||
pub fn verify(&self, clock: i64) -> CertificateError {
|
||||
unsafe {
|
||||
let capi = self.to_capi();
|
||||
return CertificateError::from_i32(ztcore::ZT_Certificate_verify(&capi.certificate as *const ztcore::ZT_Certificate, clock) as i32).unwrap_or(CertificateError::InvalidFormat);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this is a self-signed certificate.
|
||||
pub fn is_self_signed(&self) -> bool {
|
||||
for b in self.issuer.0.iter() {
|
||||
if *b != 0xff {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn generate_key_pair() {
|
||||
let (pubk, privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
|
||||
println!("key pair public: {}", hex::encode(pubk).as_str());
|
||||
println!("key pair private: {}", hex::encode(privk).as_str());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cert() {
|
||||
let (issuer_pubk, issuer_privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
|
||||
let (pubk, _) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
|
||||
let (_, unique_id_private) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
|
||||
let id0 = Identity::new_generate(IdentityType::Curve25519).ok().unwrap();
|
||||
|
||||
let mut cert = Certificate {
|
||||
serial_no: CertificateSerialNo::new(),
|
||||
usage_flags: 1,
|
||||
timestamp: 2,
|
||||
validity: [1, 10],
|
||||
subject: CertificateSubject::new(),
|
||||
issuer: CertificateSerialNo::new(),
|
||||
issuer_public_key: issuer_pubk,
|
||||
public_key: pubk.clone(),
|
||||
subject_signature: Vec::new(),
|
||||
extended_attributes: Vec::new(),
|
||||
max_path_length: 123,
|
||||
signature: Vec::new(),
|
||||
};
|
||||
cert.serial_no.0[1] = 99;
|
||||
cert.issuer.0[1] = 199;
|
||||
cert.subject.timestamp = 5;
|
||||
cert.subject.identities.push(CertificateIdentity {
|
||||
identity: id0.clone(),
|
||||
locator: None,
|
||||
});
|
||||
cert.subject.networks.push(CertificateNetwork {
|
||||
id: NetworkId(0xdeadbeef),
|
||||
controller: Some(id0.fingerprint()),
|
||||
});
|
||||
cert.subject.update_urls.push(String::from("http://foo.bar"));
|
||||
cert.subject.name = CertificateName {
|
||||
serial_no: String::from("12345"),
|
||||
common_name: String::from("foo"),
|
||||
country: String::from("bar"),
|
||||
organization: String::from("baz"),
|
||||
unit: String::from("asdf"),
|
||||
locality: String::from("qwerty"),
|
||||
province: String::from("province"),
|
||||
street_address: String::from("street address"),
|
||||
postal_code: String::from("postal code"),
|
||||
email: String::from("nobody@nowhere.org"),
|
||||
url: String::from("https://www.zerotier.com/"),
|
||||
host: String::from("zerotier.com"),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let cert_capi = cert.to_capi();
|
||||
let cert2 = Certificate::new_from_capi(&cert_capi.certificate);
|
||||
assert!(cert == cert2);
|
||||
}
|
||||
|
||||
let csr = cert.subject.new_csr(pubk.as_ref(), Some(unique_id_private.as_ref()));
|
||||
assert!(csr.is_ok());
|
||||
let csr = csr.ok().unwrap();
|
||||
|
||||
let csr_decoded = Certificate::new_from_bytes(csr.as_ref(), false);
|
||||
assert!(csr_decoded.is_ok());
|
||||
let mut csr_decoded = csr_decoded.ok().unwrap();
|
||||
|
||||
csr_decoded.validity = cert.validity;
|
||||
|
||||
let cert_signed = csr_decoded.sign(&cert.issuer, issuer_privk.as_ref());
|
||||
assert!(cert_signed.is_ok());
|
||||
let cert_signed = cert_signed.ok().unwrap();
|
||||
|
||||
assert!(cert_signed.verify(-1) == CertificateError::None);
|
||||
assert!(cert_signed.verify(5) == CertificateError::None);
|
||||
assert!(cert_signed.verify(15) != CertificateError::None);
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_char, c_uint};
|
||||
|
||||
use crate::{cstr_to_string, ResultCode};
|
||||
|
||||
/// Rust interface to the Dictionary data structure.
|
||||
#[derive(Clone)]
|
||||
pub struct Dictionary {
|
||||
data: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
// Callback called by ZeroTier core to parse a Dictionary, populates a Rust Dictionary object.
|
||||
extern "C" fn populate_dict_callback(arg: *mut c_void, c_key: *const c_char, key_len: c_uint, c_value: *const c_void, value_len: c_uint) {
|
||||
let d = unsafe { &mut *(arg.cast::<Dictionary>()) };
|
||||
let k = unsafe { cstr_to_string(c_key, key_len as isize) };
|
||||
if !k.is_empty() {
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
if value_len > 0 {
|
||||
let vp = c_value.cast::<u8>();
|
||||
v.reserve(value_len as usize);
|
||||
for i in 0..(value_len as isize) {
|
||||
unsafe { v.push(*(vp.offset(i))) };
|
||||
}
|
||||
}
|
||||
let _ = d.data.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
impl Dictionary {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Dictionary {
|
||||
Dictionary { data: HashMap::new() }
|
||||
}
|
||||
|
||||
pub fn new_from_bytes(dict: &[u8]) -> Result<Dictionary, ResultCode> {
|
||||
let mut d = Dictionary{ data: HashMap::new() };
|
||||
if unsafe { crate::capi::ZT_Dictionary_parse(dict.as_ptr().cast(), dict.len() as c_uint, (&mut d as *mut Dictionary).cast(), Some(populate_dict_callback)) != 0 } {
|
||||
Ok(d)
|
||||
} else {
|
||||
Err(ResultCode::ErrorBadParameter)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<K: AsRef<[u8]>>(&self, k: K) -> Option<&Vec<u8>> {
|
||||
let ks = String::from(String::from_utf8_lossy(k.as_ref()));
|
||||
self.data.get(&ks)
|
||||
}
|
||||
|
||||
pub fn get_or_empty<K: AsRef<[u8]>>(&self, k: K) -> Vec<u8> {
|
||||
let ks = String::from(String::from_utf8_lossy(k.as_ref()));
|
||||
self.data.get(&ks).map_or_else(|| -> Vec<u8> { Vec::new() }, |d| -> Vec<u8> { d.clone() })
|
||||
}
|
||||
|
||||
pub fn get_str<K: AsRef<[u8]>>(&self, k: K) -> Option<&str> {
|
||||
let ks = String::from(String::from_utf8_lossy(k.as_ref()));
|
||||
let v = self.data.get(&ks);
|
||||
v.map_or(None, |v: &Vec<u8>| {
|
||||
let vs = std::str::from_utf8(v.as_slice());
|
||||
vs.map_or(None, |v: &str| {
|
||||
Some(v)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_string_or_empty<K: AsRef<[u8]>>(&self, k: K) -> String {
|
||||
self.get_str(k).map_or_else(|| { String::new() },|s| { String::from(s) })
|
||||
}
|
||||
|
||||
pub fn get_ui<K: AsRef<[u8]>>(&self, k: K) -> Option<u64> {
|
||||
let v = self.get_str(k);
|
||||
v.map_or(None, |v: &str| {
|
||||
let vi = u64::from_str_radix(v, 16);
|
||||
vi.map_or(None, |i: u64| {
|
||||
Some(i)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_uint};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum EndpointType {
|
||||
Nil = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_NIL as isize,
|
||||
ZeroTier = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_ZEROTIER as isize,
|
||||
Ethernet = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_ETHERNET as isize,
|
||||
WifiDirect = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_WIFI_DIRECT as isize,
|
||||
Bluetooth = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_BLUETOOTH as isize,
|
||||
Ip = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_IP as isize,
|
||||
IpUdp = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_IP_UDP as isize,
|
||||
IpTcp = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_IP_TCP as isize,
|
||||
IpTcpWs = ztcore::ZT_EndpointType_ZT_ENDPOINT_TYPE_IP_TCP_WS as isize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Endpoint {
|
||||
pub type_: EndpointType,
|
||||
pub(crate) capi: ztcore::ZT_Endpoint
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
pub(crate) fn new_from_capi(ep: &ztcore::ZT_Endpoint) -> Endpoint {
|
||||
return Endpoint{
|
||||
type_: EndpointType::from_i32(ep.type_ as i32).unwrap(),
|
||||
capi: *ep
|
||||
};
|
||||
}
|
||||
|
||||
pub fn new_from_bytes(bytes: &[u8]) -> Result<Endpoint, ResultCode> {
|
||||
unsafe {
|
||||
let mut cep: MaybeUninit<ztcore::ZT_Endpoint> = MaybeUninit::uninit();
|
||||
let ec = ztcore::ZT_Endpoint_fromBytes(cep.as_mut_ptr(), bytes.as_ptr().cast(), bytes.len() as c_uint);
|
||||
if ec == 0 {
|
||||
let epi = cep.assume_init();
|
||||
return Ok(Endpoint{
|
||||
type_: EndpointType::from_i32(epi.type_ as i32).unwrap(),
|
||||
capi: epi
|
||||
});
|
||||
}
|
||||
return Err(ResultCode::from_i32(ec).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_string(s: &str) -> Result<Endpoint, ResultCode> {
|
||||
let cs = CString::new(s);
|
||||
if cs.is_err() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
let cs = cs.unwrap();
|
||||
unsafe {
|
||||
let mut cep: MaybeUninit<ztcore::ZT_Endpoint> = MaybeUninit::uninit();
|
||||
let ec = ztcore::ZT_Endpoint_fromString(cep.as_mut_ptr(), cs.as_ptr()) as i32;
|
||||
if ec == 0 {
|
||||
let epi = cep.assume_init();
|
||||
return Ok(Endpoint{
|
||||
type_: EndpointType::from_i32(epi.type_ as i32).unwrap(),
|
||||
capi: epi
|
||||
});
|
||||
}
|
||||
return Err(ResultCode::from_i32(ec).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the InetAddress in this endpoint or None if this is not of a relevant type.
|
||||
pub fn as_inetaddress(&self) -> Option<&InetAddress> {
|
||||
match self.type_ {
|
||||
EndpointType::Ip | EndpointType::IpUdp | EndpointType::IpTcp | EndpointType::IpTcpWs => {
|
||||
unsafe {
|
||||
Some(InetAddress::transmute_capi(&self.capi.value.ia))
|
||||
}
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Endpoint {
|
||||
fn to_string(&self) -> String {
|
||||
unsafe {
|
||||
let mut buf: MaybeUninit<[c_char; 1024]> = MaybeUninit::uninit();
|
||||
if ztcore::ZT_Endpoint_toString(&(self.capi) as *const ztcore::ZT_Endpoint, (*buf.as_mut_ptr()).as_mut_ptr(), 1024).is_null() {
|
||||
return String::from("(invalid)");
|
||||
}
|
||||
return cstr_to_string((*buf.as_ptr()).as_ptr(), 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Endpoint {
|
||||
fn eq(&self, other: &Endpoint) -> bool {
|
||||
if self.type_ == other.type_ {
|
||||
self.to_string() == other.to_string()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Endpoint {}
|
||||
|
||||
impl serde::Serialize for Endpoint {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||
}
|
||||
struct EndpointVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for EndpointVisitor {
|
||||
type Value = Endpoint;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Endpoint value in string form") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
let id = Endpoint::new_from_string(s);
|
||||
if id.is_err() {
|
||||
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
|
||||
}
|
||||
return Ok(id.ok().unwrap() as Self::Value);
|
||||
}
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for Endpoint {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(EndpointVisitor) }
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct Fingerprint {
|
||||
pub address: Address,
|
||||
pub hash: [u8; 48],
|
||||
}
|
||||
|
||||
impl Fingerprint {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_from_capi(fp: &ztcore::ZT_Fingerprint) -> Fingerprint {
|
||||
Fingerprint {
|
||||
address: Address(fp.address),
|
||||
hash: fp.hash,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_string(s: &str) -> Result<Fingerprint, ResultCode> {
|
||||
let cs = CString::new(s);
|
||||
if cs.is_err() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
let cs = cs.unwrap();
|
||||
let mut cfp: MaybeUninit<ztcore::ZT_Fingerprint> = MaybeUninit::uninit();
|
||||
unsafe {
|
||||
if ztcore::ZT_Fingerprint_fromString(cfp.as_mut_ptr(), cs.as_ptr()) != 0 {
|
||||
let fp = cfp.assume_init();
|
||||
return Ok(Fingerprint {
|
||||
address: Address(fp.address),
|
||||
hash: fp.hash,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
|
||||
pub fn new_from_bytes(bytes: &[u8]) -> Result<Fingerprint, ResultCode> {
|
||||
if bytes.len() >= (5 + 48) {
|
||||
Ok(Fingerprint {
|
||||
address: Address::from(bytes),
|
||||
hash: {
|
||||
let mut h: MaybeUninit<[u8; 48]> = MaybeUninit::uninit();
|
||||
unsafe {
|
||||
copy_nonoverlapping(bytes.as_ptr().offset(5), h.as_mut_ptr().cast::<u8>(), 48);
|
||||
h.assume_init()
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Err(ResultCode::ErrorBadParameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Fingerprint {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.address.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Fingerprint {
|
||||
fn to_string(&self) -> String {
|
||||
let mut buf: [u8; 256] = [0; 256];
|
||||
unsafe {
|
||||
if ztcore::ZT_Fingerprint_toString(&ztcore::ZT_Fingerprint { address: self.address.0, hash: self.hash }, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int).is_null() {
|
||||
return String::from("(invalid)");
|
||||
}
|
||||
return cstr_to_string(buf.as_ptr() as *const c_char, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Fingerprint {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct FingerprintVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
|
||||
type Value = Fingerprint;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Fingerprint in string format") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
let id = Fingerprint::new_from_string(s);
|
||||
if id.is_err() {
|
||||
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
|
||||
}
|
||||
return Ok(id.ok().unwrap() as Self::Value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Fingerprint {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
deserializer.deserialize_str(FingerprintVisitor)
|
||||
}
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::CString;
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
use std::os::raw::*;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum IdentityType {
|
||||
Curve25519 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_C25519 as isize,
|
||||
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 as isize,
|
||||
}
|
||||
|
||||
impl IdentityType {
|
||||
fn to_str(&self) -> &'static str {
|
||||
if *self == IdentityType::Curve25519 { "c25519" } else { "p384" }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IdentityType {
|
||||
fn to_string(&self) -> String {
|
||||
String::from(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Identity {
|
||||
pub type_: IdentityType,
|
||||
pub address: Address,
|
||||
pub(crate) capi: *const ztcore::ZT_Identity,
|
||||
requires_delete: bool,
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
pub(crate) fn new_from_capi(id: *const ztcore::ZT_Identity, requires_delete: bool) -> Identity {
|
||||
unsafe {
|
||||
Identity {
|
||||
type_: FromPrimitive::from_i32(ztcore::ZT_Identity_type(id) as i32).unwrap(),
|
||||
address: Address(ztcore::ZT_Identity_address(id)),
|
||||
capi: id,
|
||||
requires_delete,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync type and address fields with C API.
|
||||
/// This isn't truly unsafe because these are primitive types, but it's marked as such
|
||||
/// because it breaks the rules slightly. It's used in Node to make sure its identity
|
||||
/// wrapper matches the one in C++-land even if the latter changes.
|
||||
pub(crate) unsafe fn sync_type_and_address_with_capi(&self) {
|
||||
let s: *mut Identity = transmute(self as *const Identity);
|
||||
(*s).type_ = FromPrimitive::from_i32(ztcore::ZT_Identity_type(self.capi) as i32).unwrap();
|
||||
(*s).address.0 = ztcore::ZT_Identity_address(self.capi);
|
||||
}
|
||||
|
||||
/// Generate a new identity.
|
||||
/// This is time consuming due to one time proof of work. It can take several seconds.
|
||||
pub fn new_generate(id_type: IdentityType) -> Result<Identity, ResultCode> {
|
||||
unsafe {
|
||||
let id = ztcore::ZT_Identity_new(id_type.to_u32().unwrap());
|
||||
if id.is_null() {
|
||||
return Err(ResultCode::ErrorBadParameter); // this only really happens if type is invalid
|
||||
}
|
||||
return Ok(Identity::new_from_capi(id, true));
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct from a string representation of this identity.
|
||||
pub fn new_from_string(s: &str) -> Result<Identity, ResultCode> {
|
||||
unsafe {
|
||||
let cs = CString::new(s);
|
||||
if cs.is_err() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
let cs = cs.unwrap();
|
||||
let id = ztcore::ZT_Identity_fromString(cs.as_ptr());
|
||||
if id.is_null() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
return Ok(Identity::new_from_capi(id, true));
|
||||
}
|
||||
}
|
||||
|
||||
fn intl_to_string(&self, include_private: bool) -> String {
|
||||
let mut buf: MaybeUninit<[c_char; 4096]> = MaybeUninit::uninit();
|
||||
unsafe {
|
||||
let bufptr = (*buf.as_mut_ptr()).as_mut_ptr();
|
||||
if ztcore::ZT_Identity_toString(self.capi, bufptr, 4096, if include_private { 1 } else { 0 }).is_null() {
|
||||
return String::from("(invalid)");
|
||||
}
|
||||
return cstr_to_string(bufptr, 4096);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to a string and include the private key if present.
|
||||
/// If the private key is not present this is the same as to_string().
|
||||
#[inline(always)]
|
||||
pub fn to_secret_string(&self) -> String {
|
||||
self.intl_to_string(true)
|
||||
}
|
||||
|
||||
/// Validate this identity, which can be slightly time consuming in some cases (20-40ms).
|
||||
#[inline(always)]
|
||||
pub fn validate(&self) -> bool {
|
||||
unsafe { ztcore::ZT_Identity_validate(self.capi) != 0 }
|
||||
}
|
||||
|
||||
/// Returns true if this Identity includes its corresponding private key.
|
||||
#[inline(always)]
|
||||
pub fn has_private(&self) -> bool {
|
||||
unsafe { ztcore::ZT_Identity_hasPrivate(self.capi) != 0 }
|
||||
}
|
||||
|
||||
/// Obtain the full fingerprint of this identity, which includes a SHA384 hash of the public key.
|
||||
pub fn fingerprint(&self) -> Fingerprint {
|
||||
unsafe {
|
||||
let cfp = ztcore::ZT_Identity_fingerprint(self.capi);
|
||||
return Fingerprint {
|
||||
address: Address((*cfp).address),
|
||||
hash: (*cfp).hash,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign some data with this identity.
|
||||
pub fn sign(&self, data: &[u8]) -> Result<Box<[u8]>, ResultCode> {
|
||||
unsafe {
|
||||
let mut sig: Vec<u8> = vec!(0; 128);
|
||||
let siglen = ztcore::ZT_Identity_sign(self.capi, data.as_ptr() as *const c_void, data.len() as c_uint, sig.as_mut_ptr() as *mut c_void, sig.len() as u32);
|
||||
if siglen > 0 {
|
||||
sig.resize(siglen as usize, 0);
|
||||
return Ok(sig.into_boxed_slice());
|
||||
}
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a signature by this identity.
|
||||
#[inline(always)]
|
||||
pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
|
||||
unsafe { signature.len() > 0 && ztcore::ZT_Identity_verify(self.capi, data.as_ptr() as *const c_void, data.len() as c_uint, signature.as_ptr() as *const c_void, signature.len() as c_uint) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Identity {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.address.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Identity {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { ztcore::ZT_Identity_compare(self.capi, other.capi) == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Identity {}
|
||||
|
||||
impl Ord for Identity {
|
||||
fn cmp(&self, b: &Self) -> Ordering {
|
||||
let c = unsafe { ztcore::ZT_Identity_compare(self.capi, b.capi) };
|
||||
if c < 0 {
|
||||
Ordering::Less
|
||||
} else if c > 0 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Identity {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, b: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Identity {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Identity {
|
||||
unsafe {
|
||||
return Identity::new_from_capi(ztcore::ZT_Identity_clone(self.capi), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Identity {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
if self.requires_delete {
|
||||
unsafe {
|
||||
ztcore::ZT_Identity_delete(self.capi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Identity {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
self.intl_to_string(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Identity {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
serializer.serialize_str(self.intl_to_string(false).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentityVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
|
||||
type Value = Identity;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Identity in string format") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
let id = Identity::new_from_string(s);
|
||||
if id.is_err() {
|
||||
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
|
||||
}
|
||||
return Ok(id.ok().unwrap() as Self::Value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Identity {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
deserializer.deserialize_str(IdentityVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn identity() {
|
||||
let test1 = Identity::new_generate(IdentityType::Curve25519);
|
||||
assert!(test1.is_ok());
|
||||
let test1 = test1.ok().unwrap();
|
||||
assert!(test1.has_private());
|
||||
|
||||
let test2 = Identity::new_generate(IdentityType::NistP384);
|
||||
assert!(test2.is_ok());
|
||||
let test2 = test2.ok().unwrap();
|
||||
|
||||
println!("test type 0: {}", test1.to_secret_string());
|
||||
println!("test type 1: {}", test2.to_secret_string());
|
||||
|
||||
assert!(test1.clone() == test1);
|
||||
|
||||
let test12 = Identity::new_from_string(test1.to_string().as_str());
|
||||
assert!(test12.is_ok());
|
||||
let test12 = test12.ok().unwrap();
|
||||
assert!(!test12.has_private());
|
||||
let test22 = Identity::new_from_string(test2.to_string().as_str());
|
||||
assert!(test22.is_ok());
|
||||
let test22 = test22.ok().unwrap();
|
||||
assert!(test1 == test12);
|
||||
assert!(test2 == test22);
|
||||
|
||||
println!("test type 0, from string: {}", test12.to_string());
|
||||
println!("test type 1, from string: {}", test22.to_string());
|
||||
|
||||
let from_str_fail = Identity::new_from_string("asdf:foo:invalid");
|
||||
assert!(from_str_fail.is_err());
|
||||
|
||||
let mut to_sign: [u8; 4] = [1, 2, 3, 4];
|
||||
|
||||
let signed = test1.sign(&to_sign);
|
||||
assert!(signed.is_ok());
|
||||
let signed = signed.ok().unwrap();
|
||||
assert!(test1.verify(&to_sign, signed.as_ref()));
|
||||
to_sign[0] = 2;
|
||||
assert!(!test1.verify(&to_sign, signed.as_ref()));
|
||||
to_sign[0] = 1;
|
||||
|
||||
let signed = test2.sign(&to_sign);
|
||||
assert!(signed.is_ok());
|
||||
let signed = signed.ok().unwrap();
|
||||
assert!(test2.verify(&to_sign, signed.as_ref()));
|
||||
to_sign[0] = 2;
|
||||
assert!(!test2.verify(&to_sign, signed.as_ref()));
|
||||
}
|
||||
}
|
|
@ -1,375 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::CString;
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::os::raw::{c_uint, c_void};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
// WARNING: here be dragons! This defines an opaque blob in Rust that shadows
|
||||
// and is of the exact size as an opaque blob in C that shadows and is the
|
||||
// exact size of struct sockaddr_storage. This Rust code makes use of a good
|
||||
// deal of transmute() magic to save copying and allow these identically sized
|
||||
// blobs to be freely cast to one another. That the sizes are correct is
|
||||
// checked statically in the C++ code and in the tests in the Rust code.
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
pub enum IpScope {
|
||||
None = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_NONE as isize,
|
||||
Multicast = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_MULTICAST as isize,
|
||||
Loopback = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_LOOPBACK as isize,
|
||||
PseudoPrivate = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_PSEUDOPRIVATE as isize,
|
||||
Global = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_GLOBAL as isize,
|
||||
LinkLocal = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_LINK_LOCAL as isize,
|
||||
Shared = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_SHARED as isize,
|
||||
Private = ztcore::ZT_InetAddress_IpScope_ZT_IP_SCOPE_PRIVATE as isize
|
||||
}
|
||||
|
||||
impl IpScope {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
IpScope::None => "None",
|
||||
IpScope::Multicast => "Multicast",
|
||||
IpScope::Loopback => "Loopback",
|
||||
IpScope::PseudoPrivate => "PseudoPrivate",
|
||||
IpScope::Global => "Global",
|
||||
IpScope::LinkLocal => "LinkLocal",
|
||||
IpScope::Shared => "Shared",
|
||||
IpScope::Private => "Private",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum InetAddressFamily {
|
||||
Nil,
|
||||
IPv4,
|
||||
IPv6
|
||||
}
|
||||
|
||||
impl InetAddressFamily {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
InetAddressFamily::Nil => "Nil",
|
||||
InetAddressFamily::IPv4 => "IPv4",
|
||||
InetAddressFamily::IPv6 => "IPv6",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const IPV4_INADDR_ANY: [u8; 4] = [0; 4];
|
||||
pub const IPV6_INADDR_ANY: [u8; 16] = [0; 16];
|
||||
pub const IPV4_LOOPBACK: [u8; 4] = [127, 0, 0, 1];
|
||||
pub const IPV6_LOOPBACK: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
|
||||
|
||||
/// Opaque structure that can hold an IPv4 or IPv6 address.
|
||||
pub struct InetAddress {
|
||||
// This must be the same size as ZT_InetAddress in zerotier.h. This is
|
||||
// checked in tests.
|
||||
bits: [u64; (ztcore::ZT_SOCKADDR_STORAGE_SIZE / 8) as usize]
|
||||
}
|
||||
|
||||
impl InetAddress {
|
||||
/// Create a new empty and "nil" InetAddress.
|
||||
pub fn new() -> InetAddress {
|
||||
InetAddress {
|
||||
bits: [0; (ztcore::ZT_SOCKADDR_STORAGE_SIZE / 8) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an IPv4 0.0.0.0 InetAddress
|
||||
pub fn new_ipv4_any(port: u16) -> InetAddress {
|
||||
let mut ia = InetAddress::new();
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setIpBytes(ia.as_capi_mut_ptr(), IPV4_INADDR_ANY.as_ptr().cast(), 4, port as c_uint);
|
||||
}
|
||||
ia
|
||||
}
|
||||
|
||||
/// Create an IPv6 ::0 InetAddress
|
||||
pub fn new_ipv6_any(port: u16) -> InetAddress {
|
||||
let mut ia = InetAddress::new();
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setIpBytes(ia.as_capi_mut_ptr(), IPV6_INADDR_ANY.as_ptr().cast(), 16, port as c_uint);
|
||||
}
|
||||
ia
|
||||
}
|
||||
|
||||
/// Create 127.0.0.1/port
|
||||
pub fn new_ipv4_loopback(port: u16) -> InetAddress {
|
||||
let mut ia = InetAddress::new();
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setIpBytes(ia.as_capi_mut_ptr(), IPV4_LOOPBACK.as_ptr().cast(), 4, port as c_uint);
|
||||
}
|
||||
ia
|
||||
}
|
||||
|
||||
/// Create ::1/port
|
||||
pub fn new_ipv6_loopback(port: u16) -> InetAddress {
|
||||
let mut ia = InetAddress::new();
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setIpBytes(ia.as_capi_mut_ptr(), IPV6_LOOPBACK.as_ptr().cast(), 16, port as c_uint);
|
||||
}
|
||||
ia
|
||||
}
|
||||
|
||||
/// Create from a 4-byte IPv4 IP or a 16-byte IPv6 IP.
|
||||
/// Returns None if ip is not 4 or 16 bytes.
|
||||
pub fn new_from_ip_bytes(ip: &[u8], port: u16) -> Option<InetAddress> {
|
||||
if ip.len() != 4 && ip.len() != 16 {
|
||||
return None;
|
||||
}
|
||||
let mut a = InetAddress::new();
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setIpBytes(a.as_capi_mut_ptr(), ip.as_ptr() as *const c_void, ip.len() as c_uint, port as c_uint);
|
||||
}
|
||||
Some(a)
|
||||
}
|
||||
|
||||
/// Create from an InetAddress in string form.
|
||||
/// Returns None if the string is not valid.
|
||||
pub fn new_from_string(s: &str) -> Option<InetAddress> {
|
||||
let mut a = InetAddress::new();
|
||||
let cs = CString::new(s);
|
||||
if cs.is_ok() {
|
||||
let cs = cs.unwrap();
|
||||
unsafe {
|
||||
if ztcore::ZT_InetAddress_fromString(a.as_capi_mut_ptr(), cs.as_ptr()) == 0 {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(a)
|
||||
}
|
||||
|
||||
/// Unsafely transmute a raw sockaddr_storage structure into an InetAddress.
|
||||
/// The type S MUST have a size equal to the size of this type and the
|
||||
/// OS's sockaddr_storage. If not, this may crash.
|
||||
#[inline(always)]
|
||||
pub unsafe fn transmute_raw_sockaddr_storage<S>(ss: &S) -> &InetAddress {
|
||||
transmute(ss)
|
||||
}
|
||||
|
||||
/// Transmute a ZT_InetAddress from the core into a reference to a Rust
|
||||
/// InetAddress containing exactly the same data. The returned reference
|
||||
/// of course only remains valid so long as the ZT_InetAddress remains
|
||||
/// valid.
|
||||
#[inline(always)]
|
||||
pub(crate) fn transmute_capi(a: &ztcore::ZT_InetAddress) -> &InetAddress {
|
||||
unsafe {
|
||||
transmute(a)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an InetAddress by copying a ZT_InetAddress instead of transmuting.
|
||||
/// Returns None if the input is a nil address.
|
||||
pub(crate) fn new_from_capi(a: &ztcore::ZT_InetAddress) -> Option<InetAddress> {
|
||||
if a.bits[0] != 0 {
|
||||
Some(InetAddress {
|
||||
bits: a.bits
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear and set to the "nil" value.
|
||||
pub fn clear(&mut self) {
|
||||
for i in self.bits.iter_mut() {
|
||||
*i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this InetAddress holds nothing.
|
||||
#[inline(always)]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self.bits[0] == 0 // if ss_family != 0, this will not be zero
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn as_capi_ptr(&self) -> *const ztcore::ZT_InetAddress {
|
||||
unsafe {
|
||||
transmute(self as *const InetAddress)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn as_capi_mut_ptr(&mut self) -> *mut ztcore::ZT_InetAddress {
|
||||
unsafe {
|
||||
transmute(self as *mut InetAddress)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn port(&self) -> u16 {
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_port(self.as_capi_ptr()) as u16
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_setPort(self.as_capi_mut_ptr(), port as c_uint);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the network scope of the IP in this object.
|
||||
#[inline(always)]
|
||||
pub fn ip_scope(&self) -> IpScope {
|
||||
unsafe {
|
||||
IpScope::from_i32(ztcore::ZT_InetAddress_ipScope(self.as_capi_ptr()) as i32).unwrap_or(IpScope::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_v4(&self) -> bool {
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_isV4(self.as_capi_ptr()) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_v6(&self) -> bool {
|
||||
unsafe {
|
||||
ztcore::ZT_InetAddress_isV6(self.as_capi_ptr()) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the address family of this InetAddress.
|
||||
pub fn family(&self) -> InetAddressFamily {
|
||||
if !self.is_nil() {
|
||||
unsafe {
|
||||
if ztcore::ZT_InetAddress_isV4(self.as_capi_ptr()) != 0 {
|
||||
return InetAddressFamily::IPv4;
|
||||
}
|
||||
if ztcore::ZT_InetAddress_isV6(self.as_capi_ptr()) != 0 {
|
||||
return InetAddressFamily::IPv6;
|
||||
}
|
||||
}
|
||||
}
|
||||
InetAddressFamily::Nil
|
||||
}
|
||||
|
||||
/// Convert to std::net::SocketAddr for use with std::net APIs
|
||||
pub fn to_socketaddr(&self) -> Option<SocketAddr> {
|
||||
let mut buf: MaybeUninit<[u8; 16]> = MaybeUninit::uninit();
|
||||
let len;
|
||||
let buf = unsafe {
|
||||
len = ztcore::ZT_InetAddress_ipBytes(self.as_capi_ptr(), buf.as_mut_ptr().cast());
|
||||
buf.assume_init()
|
||||
};
|
||||
if len == 4 {
|
||||
Some(SocketAddr::new(IpAddr::from(Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3])), self.port()))
|
||||
} else if len == 16 {
|
||||
Some(SocketAddr::new(IpAddr::from(buf), self.port()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for InetAddress {
|
||||
fn to_string(&self) -> String {
|
||||
let mut buf: MaybeUninit<[c_char; 128]> = MaybeUninit::uninit();
|
||||
unsafe {
|
||||
return cstr_to_string(ztcore::ZT_InetAddress_toString(self.as_capi_ptr(), (*buf.as_mut_ptr()).as_mut_ptr(), 128), 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for InetAddress {
|
||||
fn from(s: &str) -> InetAddress {
|
||||
let a = InetAddress::new_from_string(s);
|
||||
if a.is_none() {
|
||||
return InetAddress::new();
|
||||
}
|
||||
a.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for InetAddress {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
InetAddress{
|
||||
bits: self.bits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for InetAddress {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let c = unsafe { ztcore::ZT_InetAddress_compare(self.as_capi_ptr(), other.as_capi_ptr()) };
|
||||
if c < 0 {
|
||||
Ordering::Less
|
||||
} else if c > 0 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for InetAddress {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InetAddress {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { ztcore::ZT_InetAddress_compare(self.as_capi_ptr(), other.as_capi_ptr()) == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for InetAddress {}
|
||||
|
||||
impl serde::Serialize for InetAddress {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||
}
|
||||
struct InetAddressVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for InetAddressVisitor {
|
||||
type Value = InetAddress;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("InetAddress value in string form") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
let id = InetAddress::new_from_string(s);
|
||||
if id.is_none() {
|
||||
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
|
||||
}
|
||||
return Ok(id.unwrap() as Self::Value);
|
||||
}
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for InetAddress {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(InetAddressVisitor) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn type_sizes() {
|
||||
assert_eq!(size_of::<ztcore::ZT_InetAddress>(), size_of::<InetAddress>());
|
||||
}
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
#[macro_use] extern crate base64_serde;
|
||||
|
||||
#[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)]
|
||||
mod capi; // bindgen generated
|
||||
|
||||
mod identity;
|
||||
mod address;
|
||||
mod fingerprint;
|
||||
mod endpoint;
|
||||
mod certificate;
|
||||
mod networkid;
|
||||
mod inetaddress;
|
||||
mod locator;
|
||||
mod path;
|
||||
mod peer;
|
||||
mod node;
|
||||
mod mac;
|
||||
mod buffer;
|
||||
mod portableatomici64;
|
||||
mod virtualnetworkconfig;
|
||||
mod multicastgroup;
|
||||
mod dictionary;
|
||||
pub mod trace;
|
||||
|
||||
use crate::capi as ztcore;
|
||||
|
||||
pub use identity::*;
|
||||
pub use address::Address;
|
||||
pub use fingerprint::Fingerprint;
|
||||
pub use endpoint::*;
|
||||
pub use certificate::*;
|
||||
pub use networkid::NetworkId;
|
||||
pub use inetaddress::*;
|
||||
pub use locator::*;
|
||||
pub use path::Path;
|
||||
pub use peer::Peer;
|
||||
pub use node::*;
|
||||
pub use mac::MAC;
|
||||
pub use buffer::Buffer;
|
||||
pub use portableatomici64::PortableAtomicI64;
|
||||
pub use virtualnetworkconfig::*;
|
||||
pub use multicastgroup::MulticastGroup;
|
||||
pub use dictionary::*;
|
||||
|
||||
base64_serde_type!(Base64URLSafeNoPad, base64::URL_SAFE_NO_PAD);
|
||||
|
||||
/// Recommended minimum thread stack size for background threads.
|
||||
pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 524288;
|
||||
|
||||
/// Default TCP and UDP port.
|
||||
pub const DEFAULT_PORT: u16 = ztcore::ZT_DEFAULT_PORT as u16;
|
||||
|
||||
/// Default secondary UDP port if enabled.
|
||||
pub const DEFAULT_SECONDARY_PORT: u16 = ztcore::ZT_DEFAULT_SECONDARY_PORT as u16;
|
||||
|
||||
/// Size of a ZeroTier core "Buffer" in bytes.
|
||||
pub const BUF_SIZE: usize = ztcore::ZT_BUF_SIZE as usize;
|
||||
|
||||
/// Minimum physical MTU.
|
||||
pub const MIN_MTU: u32 = ztcore::ZT_MIN_MTU;
|
||||
|
||||
/// Maximum physical MTU.
|
||||
pub const MAX_MTU: u32 = ztcore::ZT_MAX_MTU;
|
||||
|
||||
/// Default physica UDP MTU (not including IP or UDP headers).
|
||||
pub const DEFAULT_UDP_MTU: u32 = ztcore::ZT_DEFAULT_UDP_MTU;
|
||||
|
||||
/// Maximum UDP MTU (we never actually get this high).
|
||||
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU;
|
||||
|
||||
/// Base64 encode using the URL-safe alphabet with no padding.
|
||||
pub fn base64_encode<T: AsRef<[u8]>>(t: &T) -> String {
|
||||
base64::encode_config(t, base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
/// Base64 decode using the URL-safe alphabet with no padding.
|
||||
pub fn base64_decode<T: AsRef<[u8]>>(t: &T) -> Result<Vec<u8>, base64::DecodeError> {
|
||||
base64::decode_config(t, base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case,non_upper_case_globals)]
|
||||
pub mod RulePacketCharacteristicFlags {
|
||||
pub const Inbound: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_INBOUND as u64;
|
||||
pub const Multicast: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST as u64;
|
||||
pub const Broadcast: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST as u64;
|
||||
pub const SenderIpAuthenticated: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED as u64;
|
||||
pub const SenderMacAuthenticated: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED as u64;
|
||||
pub const TcpFlagNS: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_NS as u64;
|
||||
pub const TcpFlagCWR: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_CWR as u64;
|
||||
pub const TcpFlagECE: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_ECE as u64;
|
||||
pub const TcpFlagURG: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_URG as u64;
|
||||
pub const TcpFlagACK: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK as u64;
|
||||
pub const TcpFlagPSH: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_PSH as u64;
|
||||
pub const TcpFlagRST: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_RST as u64;
|
||||
pub const TcpFlagSYN: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN as u64;
|
||||
pub const TcpFlagFIN: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN as u64;
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
pub enum CredentialType {
|
||||
Null = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_NULL as isize,
|
||||
CertificateOfMembership = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_COM as isize,
|
||||
Capability = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_CAPABILITY as isize,
|
||||
Tag = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_TAG as isize,
|
||||
CertificateOfOwnership = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_COO as isize,
|
||||
Revocation = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_REVOCATION as isize,
|
||||
}
|
||||
|
||||
impl CredentialType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
CredentialType::Null => "Null",
|
||||
CredentialType::CertificateOfMembership => "CertificateOfMembership",
|
||||
CredentialType::Capability => "Capability",
|
||||
CredentialType::Tag => "Tag",
|
||||
CredentialType::CertificateOfOwnership => "CertificateOfOwnership",
|
||||
CredentialType::Revocation => "Revocation",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
pub enum ResultCode {
|
||||
Ok = ztcore::ZT_ResultCode_ZT_RESULT_OK as isize,
|
||||
FatalErrorOutOfMemory = ztcore::ZT_ResultCode_ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY as isize,
|
||||
FatalErrorDataStoreFailed = ztcore::ZT_ResultCode_ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED as isize,
|
||||
FatalErrorInternal = ztcore::ZT_ResultCode_ZT_RESULT_FATAL_ERROR_INTERNAL as isize,
|
||||
ErrorNetworkNotFound = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_NETWORK_NOT_FOUND as isize,
|
||||
ErrorUnsupportedOperation = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_UNSUPPORTED_OPERATION as isize,
|
||||
ErrorBadParameter = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_BAD_PARAMETER as isize,
|
||||
ErrorInvalidCredential = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_INVALID_CREDENTIAL as isize,
|
||||
ErrorCollidingObject = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_COLLIDING_OBJECT as isize,
|
||||
ErrorInternalNonFatal = ztcore::ZT_ResultCode_ZT_RESULT_ERROR_INTERNAL as isize,
|
||||
}
|
||||
|
||||
impl ResultCode {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
ResultCode::Ok => "Ok",
|
||||
ResultCode::FatalErrorOutOfMemory => "FatalErrorOutOfMemory",
|
||||
ResultCode::FatalErrorDataStoreFailed => "FatalErrorDataStoreFailed",
|
||||
ResultCode::FatalErrorInternal => "FatalErrorInternal",
|
||||
ResultCode::ErrorNetworkNotFound => "ErrorNetworkNotFound",
|
||||
ResultCode::ErrorUnsupportedOperation => "ErrorUnsupportedOperation",
|
||||
ResultCode::ErrorBadParameter => "ErrorBadParameter",
|
||||
ResultCode::ErrorInvalidCredential => "ErrorInvalidCredential",
|
||||
ResultCode::ErrorCollidingObject => "ErrorCollidingObject",
|
||||
ResultCode::ErrorInternalNonFatal => "ErrorInternalNonFatal",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a tuple of major, minor, revision, and build version numbers from the ZeroTier core.
|
||||
pub fn version() -> (i32, i32, i32, i32) {
|
||||
let mut major: c_int = 0;
|
||||
let mut minor: c_int = 0;
|
||||
let mut revision: c_int = 0;
|
||||
let mut build: c_int = 0;
|
||||
unsafe {
|
||||
ztcore::ZT_version(&mut major as *mut c_int, &mut minor as *mut c_int, &mut revision as *mut c_int, &mut build as *mut c_int);
|
||||
}
|
||||
(major as i32, minor as i32, revision as i32, build as i32)
|
||||
}
|
||||
|
||||
/// Get a random 64-bit integer using the non-cryptographic PRNG in the ZeroTier core.
|
||||
#[inline(always)]
|
||||
pub fn random() -> u64 {
|
||||
unsafe {
|
||||
return ztcore::ZT_random();
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether this byte array or slice is all zeroes.
|
||||
pub fn is_all_zeroes<B: AsRef<[u8]>>(b: B) -> bool {
|
||||
for c in b.as_ref().iter() {
|
||||
if *c != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// The CStr stuff is cumbersome, so this is an easier to use function to turn a C string into a String.
|
||||
/// This returns an empty string on a null pointer or invalid UTF-8. It's unsafe because it can crash if
|
||||
/// the string is not zero-terminated. A size limit can be passed in if available to reduce this risk, or
|
||||
/// the max_len parameter can be -1 if there is no known limit.
|
||||
pub unsafe fn cstr_to_string(cstr: *const c_char, max_len: isize) -> String {
|
||||
if !cstr.is_null() {
|
||||
let mut cstr_len: isize = 0;
|
||||
while max_len < 0 || cstr_len < max_len {
|
||||
if *cstr.offset(cstr_len) == 0 {
|
||||
break;
|
||||
}
|
||||
cstr_len += 1;
|
||||
}
|
||||
return String::from(std::str::from_utf8(std::slice::from_raw_parts(cstr as *const u8, cstr_len as usize)).unwrap_or(""));
|
||||
}
|
||||
String::new()
|
||||
}
|
||||
|
||||
/*
|
||||
#[macro_export(crate)]
|
||||
macro_rules! enum_str {
|
||||
(enum $name:ident {
|
||||
$($variant:ident = $val:expr),*,
|
||||
}) => {
|
||||
enum $name {
|
||||
$($variant = $val),*
|
||||
}
|
||||
impl $name {
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
$($name::$variant => stringify!($variant)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
*/
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_int, c_uint};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::null;
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
pub struct Locator {
|
||||
pub(crate) capi: *const ztcore::ZT_Locator,
|
||||
requires_delete: bool
|
||||
}
|
||||
|
||||
impl Locator {
|
||||
/// Create and sign a new locator.
|
||||
/// The signer must include its secret key.
|
||||
pub fn new(signer: &Identity, revision: i64, endpoints: &Vec<Endpoint>) -> Result<Locator, ResultCode> {
|
||||
let mut capi_endpoints: Vec<ztcore::ZT_Endpoint> = Vec::new();
|
||||
capi_endpoints.reserve(endpoints.len());
|
||||
for ep in endpoints.iter() {
|
||||
capi_endpoints.push(ep.capi);
|
||||
}
|
||||
let loc = unsafe { ztcore::ZT_Locator_create(revision, capi_endpoints.as_ptr(), null(), capi_endpoints.len() as c_uint, signer.capi) };
|
||||
if loc.is_null() {
|
||||
Err(ResultCode::ErrorBadParameter)
|
||||
} else {
|
||||
Ok(Locator::new_from_capi(loc, true))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_from_capi(l: *const ztcore::ZT_Locator, requires_delete: bool) -> Locator {
|
||||
Locator{
|
||||
capi: l,
|
||||
requires_delete
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_string(s: &str) -> Result<Locator, ResultCode> {
|
||||
let cs = CString::new(s);
|
||||
if cs.is_err() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
let cs = cs.unwrap();
|
||||
let l = unsafe { ztcore::ZT_Locator_fromString(cs.as_ptr()) };
|
||||
if l.is_null() {
|
||||
return Err(ResultCode::ErrorBadParameter);
|
||||
}
|
||||
return Ok(Locator::new_from_capi(l, true));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn revision(&self) -> i64 {
|
||||
unsafe { ztcore::ZT_Locator_revision(self.capi) as i64 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn signer(&self) -> Address {
|
||||
unsafe { Address(ztcore::ZT_Locator_signer(self.capi)) }
|
||||
}
|
||||
|
||||
pub fn endpoints(&self) -> Vec<Endpoint> {
|
||||
let mut eps: Vec<Endpoint> = Vec::new();
|
||||
let ep_count = unsafe { ztcore::ZT_Locator_endpointCount(self.capi) as usize };
|
||||
eps.reserve(ep_count as usize);
|
||||
for i in 0..ep_count {
|
||||
eps.push(Endpoint::new_from_capi(unsafe { &*ztcore::ZT_Locator_endpoint(self.capi, i as c_uint) }));
|
||||
}
|
||||
eps
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn verify(&self, id: &Identity) -> bool {
|
||||
unsafe { ztcore::ZT_Locator_verify(self.capi, id.capi) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Locator {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
if self.requires_delete {
|
||||
unsafe { ztcore::ZT_Locator_delete(self.capi); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Locator {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Locator {
|
||||
Locator::new_from_string(self.to_string().as_str()).ok().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Locator {
|
||||
fn to_string(&self) -> String {
|
||||
const LOCATOR_STRING_BUF_LEN: usize = 16384;
|
||||
let mut buf: MaybeUninit<[u8; LOCATOR_STRING_BUF_LEN]> = MaybeUninit::uninit();
|
||||
if unsafe { ztcore::ZT_Locator_toString(self.capi, buf.as_mut_ptr().cast(), LOCATOR_STRING_BUF_LEN as c_int).is_null() }{
|
||||
"(invalid)".to_owned()
|
||||
} else {
|
||||
unsafe { cstr_to_string(buf.as_ptr().cast(), LOCATOR_STRING_BUF_LEN as isize) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Locator {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Locator) -> bool {
|
||||
unsafe { ztcore::ZT_Locator_equals(self.capi, other.capi) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Locator {}
|
||||
|
||||
impl serde::Serialize for Locator {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { serializer.serialize_str(self.to_string().as_str()) }
|
||||
}
|
||||
struct LocatorVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for LocatorVisitor {
|
||||
type Value = Locator;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Locator value in string form") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
let id = Locator::new_from_string(s);
|
||||
if id.is_err() {
|
||||
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
|
||||
}
|
||||
return Ok(id.ok().unwrap() as Self::Value);
|
||||
}
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for Locator {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(LocatorVisitor) }
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||
pub struct MAC(pub u64);
|
||||
|
||||
impl Default for MAC {
|
||||
#[inline(always)]
|
||||
fn default() -> MAC {
|
||||
MAC(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MAC {
|
||||
#[inline(always)]
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MAC {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for MAC {
|
||||
fn to_string(&self) -> String {
|
||||
let x = self.0;
|
||||
format!("{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}:{:0>2x}", (x >> 40) & 0xff, (x >> 32) & 0xff, (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for MAC {
|
||||
#[inline(always)]
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
if bytes.len() >= 6 {
|
||||
MAC(((bytes[0] as u64) << 40) | ((bytes[1] as u64) << 32) | ((bytes[2] as u64) << 24) | ((bytes[3] as u64) << 16) | ((bytes[4] as u64) << 8) | (bytes[5] as u64))
|
||||
} else {
|
||||
MAC(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MAC {
|
||||
fn from(s: &str) -> MAC {
|
||||
MAC(u64::from_str_radix(s.replace(":", "").as_str(), 16).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for MAC {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} else {
|
||||
let b = self.to_bytes();
|
||||
serializer.serialize_bytes(b.as_ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
|
||||
type Value = MAC;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Ethernet MAC address in string format (with or without : separators)") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(MAC::from(s)) }
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(MAC::from(v)) }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for MAC {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(AddressVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_bytes(AddressVisitor)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use crate::MAC;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct MulticastGroup {
|
||||
pub mac: MAC,
|
||||
pub adi: u32,
|
||||
}
|
||||
|
||||
impl ToString for MulticastGroup {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}/{}", self.mac.to_string(), self.adi)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MulticastGroup {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.mac.0.hash(state);
|
||||
self.adi.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for MulticastGroup {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.mac.0 < other.mac.0 {
|
||||
Ordering::Less
|
||||
} else if self.mac.0 > other.mac.0 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
if self.adi < other.adi {
|
||||
Ordering::Less
|
||||
} else if self.adi > other.adi {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for MulticastGroup {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
|
||||
pub struct NetworkId(pub u64);
|
||||
|
||||
impl Default for NetworkId {
|
||||
#[inline(always)]
|
||||
fn default() -> NetworkId {
|
||||
NetworkId(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for NetworkId {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for NetworkId {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{:0>16x}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for NetworkId {
|
||||
#[inline(always)]
|
||||
fn from(n: u64) -> Self {
|
||||
NetworkId(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for NetworkId {
|
||||
#[inline(always)]
|
||||
fn from(s: &str) -> Self {
|
||||
NetworkId(u64::from_str_radix(s, 16).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for NetworkId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
} else {
|
||||
serializer.serialize_u64(self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NetworkIdVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
|
||||
type Value = NetworkId;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier network ID") }
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId::from(s)) }
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(NetworkId(v)) }
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for NetworkId {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
if deserializer.is_human_readable() {
|
||||
deserializer.deserialize_str(NetworkIdVisitor)
|
||||
} else {
|
||||
deserializer.deserialize_u64(NetworkIdVisitor)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,557 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::collections::hash_map::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
|
||||
use std::pin::Pin;
|
||||
use std::ptr::{null_mut, slice_from_raw_parts, copy_nonoverlapping};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: i64 = 200;
|
||||
|
||||
const EMPTY_BYTE_ARRAY: [u8; 0] = [];
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Event {
|
||||
Up = ztcore::ZT_Event_ZT_EVENT_UP as isize,
|
||||
Offline = ztcore::ZT_Event_ZT_EVENT_OFFLINE as isize,
|
||||
Online = ztcore::ZT_Event_ZT_EVENT_ONLINE as isize,
|
||||
Down = ztcore::ZT_Event_ZT_EVENT_DOWN as isize,
|
||||
Trace = ztcore::ZT_Event_ZT_EVENT_TRACE as isize,
|
||||
UserMessage = ztcore::ZT_Event_ZT_EVENT_USER_MESSAGE as isize,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum StateObjectType {
|
||||
IdentityPublic = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_IDENTITY_PUBLIC as isize,
|
||||
IdentitySecret = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_IDENTITY_SECRET as isize,
|
||||
Locator = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_LOCATOR as isize,
|
||||
Peer = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_PEER as isize,
|
||||
NetworkConfig = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_NETWORK_CONFIG as isize,
|
||||
TrustStore = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_TRUST_STORE as isize,
|
||||
}
|
||||
|
||||
impl StateObjectType {
|
||||
/// True if this state object should be protected in a data store.
|
||||
/// This could mean its file permissions should be locked down so they're only readable by the service, for example.
|
||||
#[inline(always)]
|
||||
pub fn is_secret(&self) -> bool {
|
||||
*self == StateObjectType::IdentitySecret || *self == StateObjectType::TrustStore
|
||||
}
|
||||
}
|
||||
|
||||
/// The status of a ZeroTier node.
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct NodeStatus {
|
||||
pub address: Address,
|
||||
pub identity: Identity,
|
||||
#[serde(rename = "publicIdentity")]
|
||||
pub public_identity: String,
|
||||
#[serde(rename = "secretIdentity")]
|
||||
pub secret_identity: String,
|
||||
pub online: bool,
|
||||
}
|
||||
|
||||
/// An event handler that receives events, frames, and packets from the core.
|
||||
/// Note that if multiple threads are calling into Node these may be called by any of these threads
|
||||
/// at any time and must be thread safe.
|
||||
pub trait NodeEventHandler<N: Sync + Send + 'static> {
|
||||
/// Called when a configuration change or update should be applied to a network.
|
||||
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &N, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>);
|
||||
|
||||
/// Called when a frame should be injected into the virtual network (physical -> virtual).
|
||||
fn virtual_network_frame(&self, network_id: NetworkId, network_obj: &N, source_mac: MAC, dest_mac: MAC, ethertype: u16, vlan_id: u16, data: &[u8]);
|
||||
|
||||
/// Called when a core ZeroTier event occurs.
|
||||
fn event(&self, event: Event, event_data: &[u8]);
|
||||
|
||||
/// Called to store an object into the object store.
|
||||
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()>;
|
||||
|
||||
/// Called to retrieve an object from the object store.
|
||||
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result<Vec<u8>>;
|
||||
|
||||
/// Called to send a packet over the physical network (virtual -> physical).
|
||||
fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32;
|
||||
|
||||
/// Called to check and see if a physical address should be used for ZeroTier traffic.
|
||||
fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool;
|
||||
|
||||
/// Called to look up a path to a known node, allowing out of band lookup methods for physical paths to nodes.
|
||||
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>;
|
||||
}
|
||||
|
||||
pub(crate) struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
|
||||
event_handler: T,
|
||||
capi: *mut ztcore::ZT_Node,
|
||||
networks_by_id: Mutex<HashMap<u64, Pin<Box<N>>>>,
|
||||
recent_clock: PortableAtomicI64,
|
||||
recent_ticks: PortableAtomicI64,
|
||||
event_handler_placeholder: PhantomData<H>,
|
||||
}
|
||||
|
||||
/// An instance of the ZeroTier core.
|
||||
///
|
||||
/// The event handler is templated as AsRef<H> where H is the concrete type of the actual
|
||||
/// handler. This allows the handler to be an Arc<>, Box<>, or similar. We do this instead
|
||||
/// of templating it on "dyn NodeEventHandler" because we want the types to all be concrete
|
||||
/// to avoid dynamic call overhead. Unfortunately it makes the types here a tad more
|
||||
/// verbose.
|
||||
///
|
||||
/// In most cases you will want the handler to be an Arc<> anyway since most uses will be
|
||||
/// multithreaded or async.
|
||||
pub struct Node<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
|
||||
intl: Pin<Box<NodeIntl<T, N, H>>>,
|
||||
identity_wrapper: Option<Identity>,
|
||||
event_handler_placeholder: PhantomData<H>,
|
||||
}
|
||||
|
||||
/********************************************************************************************************************/
|
||||
|
||||
extern "C" fn zt_virtual_network_config_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
nwid: u64,
|
||||
nptr: *mut *mut c_void,
|
||||
op: ztcore::ZT_VirtualNetworkConfigOperation,
|
||||
conf: *const ztcore::ZT_VirtualNetworkConfig,
|
||||
) {
|
||||
let _ = VirtualNetworkConfigOperation::from_i32(op as i32).map(|op| {
|
||||
let n = unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) };
|
||||
if conf.is_null() {
|
||||
n.event_handler.as_ref().virtual_network_config(NetworkId(nwid), unsafe { &*(nptr.cast::<N>()) }, op, None);
|
||||
} else {
|
||||
let conf2 = unsafe { VirtualNetworkConfig::new_from_capi(&*conf) };
|
||||
n.event_handler.as_ref().virtual_network_config(NetworkId(nwid), unsafe { &*(nptr.cast::<N>()) }, op, Some(&conf2));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn zt_virtual_network_frame_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
nwid: u64,
|
||||
nptr: *mut *mut c_void,
|
||||
source_mac: u64,
|
||||
dest_mac: u64,
|
||||
ethertype: c_uint,
|
||||
vlan_id: c_uint,
|
||||
data: *const c_void,
|
||||
data_size: c_uint,
|
||||
) {
|
||||
if !nptr.is_null() {
|
||||
unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) }.event_handler.as_ref().virtual_network_frame(
|
||||
NetworkId(nwid),
|
||||
unsafe { &*(nptr.cast::<N>()) },
|
||||
MAC(source_mac),
|
||||
MAC(dest_mac),
|
||||
ethertype as u16,
|
||||
vlan_id as u16,
|
||||
unsafe { &*slice_from_raw_parts(data.cast::<u8>(), data_size as usize) });
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn zt_event_callback<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
ev: ztcore::ZT_Event,
|
||||
data: *const c_void,
|
||||
data_size: c_uint,
|
||||
) {
|
||||
let _ = Event::from_i32(ev as i32).map(|ev: Event| {
|
||||
let n = unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) };
|
||||
if data.is_null() {
|
||||
n.event_handler.as_ref().event(ev, &EMPTY_BYTE_ARRAY);
|
||||
} else {
|
||||
n.event_handler.as_ref().event(ev, unsafe { &*slice_from_raw_parts(data.cast::<u8>(), data_size as usize) });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn zt_state_put_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
obj_type: ztcore::ZT_StateObjectType,
|
||||
obj_id: *const u64,
|
||||
obj_id_len: c_uint,
|
||||
obj_data: *const c_void,
|
||||
obj_data_len: c_int,
|
||||
) {
|
||||
let _ = StateObjectType::from_i32(obj_type as i32).map(|obj_type| {
|
||||
let n = unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) };
|
||||
let _ = n.event_handler.as_ref().state_put(obj_type, unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) }, unsafe { &*slice_from_raw_parts(obj_data.cast::<u8>(), obj_data_len as usize) });
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn zt_state_get_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
obj_type: ztcore::ZT_StateObjectType,
|
||||
obj_id: *const u64,
|
||||
obj_id_len: c_uint,
|
||||
obj_data: *mut *mut c_void,
|
||||
obj_data_free_function: *mut *mut c_void,
|
||||
) -> c_int {
|
||||
if obj_data.is_null() || obj_data_free_function.is_null() {
|
||||
-1 as c_int
|
||||
} else {
|
||||
unsafe {
|
||||
*obj_data = null_mut();
|
||||
*obj_data_free_function = transmute(ztcore::free as *const ());
|
||||
}
|
||||
StateObjectType::from_i32(obj_type as i32).map_or_else(|| {
|
||||
-1 as c_int
|
||||
}, |obj_type| {
|
||||
unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) }.event_handler.as_ref().state_get(obj_type, unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) }).map_or_else(|_| {
|
||||
-1 as c_int
|
||||
}, |obj_data_result| {
|
||||
let obj_data_len = obj_data_result.len() as c_int;
|
||||
if obj_data_len > 0 {
|
||||
unsafe {
|
||||
let obj_data_raw = ztcore::malloc(obj_data_len as c_ulong);
|
||||
if obj_data_raw.is_null() {
|
||||
-1 as c_int
|
||||
} else {
|
||||
copy_nonoverlapping(obj_data_result.as_ptr(), obj_data_raw.cast::<u8>(), obj_data_len as usize);
|
||||
*obj_data = obj_data_raw;
|
||||
obj_data_len
|
||||
}
|
||||
}
|
||||
} else {
|
||||
-1 as c_int
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn zt_wire_packet_send_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
local_socket: i64,
|
||||
sock_addr: *const ztcore::ZT_InetAddress,
|
||||
data: *const c_void,
|
||||
data_size: c_uint,
|
||||
packet_ttl: c_uint,
|
||||
) -> c_int {
|
||||
unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) }.event_handler.as_ref().wire_packet_send(local_socket, InetAddress::transmute_capi(unsafe { &*sock_addr }), unsafe { &*slice_from_raw_parts(data.cast::<u8>(), data_size as usize) }, packet_ttl as u32) as c_int
|
||||
}
|
||||
|
||||
extern "C" fn zt_path_check_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
address: u64,
|
||||
identity: *const ztcore::ZT_Identity,
|
||||
local_socket: i64,
|
||||
sock_addr: *const ztcore::ZT_InetAddress,
|
||||
) -> c_int {
|
||||
let id = Identity::new_from_capi(identity, false);
|
||||
unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) }.event_handler.as_ref().path_check(Address(address), &id, local_socket, InetAddress::transmute_capi(unsafe { &*sock_addr })) as c_int
|
||||
}
|
||||
|
||||
extern "C" fn zt_path_lookup_function<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>>(
|
||||
_: *mut ztcore::ZT_Node,
|
||||
uptr: *mut c_void,
|
||||
_: *mut c_void,
|
||||
address: u64,
|
||||
identity: *const ztcore::ZT_Identity,
|
||||
sock_family: c_int,
|
||||
sock_addr: *mut ztcore::ZT_InetAddress,
|
||||
) -> c_int {
|
||||
if sock_addr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let sock_family2: InetAddressFamily;
|
||||
unsafe {
|
||||
if sock_family == ztcore::ZT_AF_INET {
|
||||
sock_family2 = InetAddressFamily::IPv4;
|
||||
} else if sock_family == ztcore::ZT_AF_INET6 {
|
||||
sock_family2 = InetAddressFamily::IPv6;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let id = Identity::new_from_capi(identity, false);
|
||||
unsafe { &*(uptr.cast::<NodeIntl<T, N, H>>()) }.event_handler.as_ref().path_lookup(Address(address), &id, sock_family2).map_or_else(|| {
|
||||
0 as c_int
|
||||
}, |result| {
|
||||
let result_ptr = &result as *const InetAddress;
|
||||
unsafe {
|
||||
copy_nonoverlapping(result_ptr.cast::<ztcore::ZT_InetAddress>(), sock_addr, 1);
|
||||
}
|
||||
1 as c_int
|
||||
})
|
||||
}
|
||||
|
||||
/********************************************************************************************************************/
|
||||
|
||||
impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> Node<T, N, H> {
|
||||
/// Create a new Node with a given event handler.
|
||||
#[allow(unused_mut)]
|
||||
pub fn new(event_handler: T, clock: i64, ticks: i64) -> Result<Node<T, N, H>, ResultCode> {
|
||||
let mut n = Node {
|
||||
intl: Box::pin(NodeIntl {
|
||||
event_handler: event_handler.clone(),
|
||||
capi: null_mut(),
|
||||
networks_by_id: Mutex::new(HashMap::new()),
|
||||
event_handler_placeholder: PhantomData::default(),
|
||||
recent_clock: PortableAtomicI64::new(clock),
|
||||
recent_ticks: PortableAtomicI64::new(ticks),
|
||||
}),
|
||||
identity_wrapper: None,
|
||||
event_handler_placeholder: PhantomData::default(),
|
||||
};
|
||||
|
||||
let rc = unsafe {
|
||||
let callbacks = ztcore::ZT_Node_Callbacks {
|
||||
statePutFunction: transmute(zt_state_put_function::<T, N, H> as *const ()),
|
||||
stateGetFunction: transmute(zt_state_get_function::<T, N, H> as *const ()),
|
||||
wirePacketSendFunction: transmute(zt_wire_packet_send_function::<T, N, H> as *const ()),
|
||||
virtualNetworkFrameFunction: transmute(zt_virtual_network_frame_function::<T, N, H> as *const ()),
|
||||
virtualNetworkConfigFunction: transmute(zt_virtual_network_config_function::<T, N, H> as *const ()),
|
||||
eventCallback: transmute(zt_event_callback::<T, N, H> as *const ()),
|
||||
pathCheckFunction: transmute(zt_path_check_function::<T, N, H> as *const ()),
|
||||
pathLookupFunction: transmute(zt_path_lookup_function::<T, N, H> as *const ()),
|
||||
};
|
||||
ztcore::ZT_Node_new(transmute(&(n.intl.capi) as *const *mut ztcore::ZT_Node), clock, ticks, null_mut(), transmute(&*n.intl as *const NodeIntl<T, N, H>), &callbacks)
|
||||
};
|
||||
|
||||
if rc == 0 {
|
||||
assert!(!n.intl.capi.is_null());
|
||||
n.identity_wrapper.replace(Identity::new_from_capi(unsafe { ztcore::ZT_Node_identity(n.intl.capi) }, false));
|
||||
Ok(n)
|
||||
} else {
|
||||
Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal))
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform periodic background tasks.
|
||||
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds
|
||||
/// since the node was created, and after this runs it returns the amount of time the caller
|
||||
/// should wait before calling it again.
|
||||
#[inline(always)]
|
||||
pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 {
|
||||
self.intl.recent_clock.set(clock);
|
||||
self.intl.recent_ticks.set(ticks);
|
||||
let mut next_task_deadline: i64 = ticks;
|
||||
unsafe {
|
||||
ztcore::ZT_Node_processBackgroundTasks(self.intl.capi, clock, ticks, null_mut(), (&mut next_task_deadline as *mut i64).cast());
|
||||
}
|
||||
(next_task_deadline - ticks).clamp(1_i64, NODE_BACKGROUND_TASKS_MAX_INTERVAL)
|
||||
}
|
||||
|
||||
/// Join a network, associating network_obj with it.
|
||||
/// If a fingerprint is supplied it will be used as a full sha384 fingerprint of the
|
||||
/// network's controller.
|
||||
pub fn join(&self, clock: i64, ticks: i64, nwid: NetworkId, controller_fingerprint: Option<Fingerprint>, network_obj: N) -> ResultCode {
|
||||
let mut cfp: MaybeUninit<ztcore::ZT_Fingerprint> = MaybeUninit::uninit();
|
||||
let mut cfpp: *mut ztcore::ZT_Fingerprint = null_mut();
|
||||
if controller_fingerprint.is_some() {
|
||||
let cfp2 = controller_fingerprint.unwrap();
|
||||
cfpp = cfp.as_mut_ptr();
|
||||
unsafe {
|
||||
(*cfpp).address = cfp2.address.0;
|
||||
(*cfpp).hash = cfp2.hash;
|
||||
}
|
||||
}
|
||||
|
||||
let network_obj = Box::pin(network_obj);
|
||||
let rc = unsafe { ztcore::ZT_Node_join(self.intl.capi, clock, ticks, null_mut(), transmute((&*network_obj) as *const N), nwid.0, cfpp) };
|
||||
if rc == ztcore::ZT_ResultCode_ZT_RESULT_OK {
|
||||
self.intl.networks_by_id.lock().unwrap().insert(nwid.0, network_obj);
|
||||
ResultCode::Ok
|
||||
} else {
|
||||
ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::ErrorInternalNonFatal)
|
||||
}
|
||||
}
|
||||
|
||||
/// Leave a network.
|
||||
pub fn leave(&self, clock: i64, ticks: i64, nwid: NetworkId) -> ResultCode {
|
||||
self.intl.networks_by_id.lock().unwrap().remove(&nwid.0).map_or_else(|| {
|
||||
ResultCode::ErrorNetworkNotFound
|
||||
}, |_| {
|
||||
unsafe {
|
||||
ResultCode::from_i32(ztcore::ZT_Node_leave(self.intl.capi, clock, ticks, null_mut(), null_mut(), nwid.0) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Access a network's associated network object.
|
||||
/// This executes the supplied function or closure if we are joined to
|
||||
/// this network, providing its associated network object as a parameter.
|
||||
/// This happens while the internal data structure is locked, so do not
|
||||
/// do anything time consuming while inside this function. The return value
|
||||
/// (if any) of this function is returned, or None if we are not joined to
|
||||
/// this network.
|
||||
#[inline(always)]
|
||||
pub fn with_network<F: FnOnce(&N) -> R, R>(&self, nwid: NetworkId, f: F) -> Option<R> {
|
||||
self.intl.networks_by_id.lock().unwrap().get(&nwid.0).map_or(None, |nw| {
|
||||
Some(f(&*nw))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the address of this node.
|
||||
#[inline(always)]
|
||||
pub fn address(&self) -> Address {
|
||||
unsafe { Address(ztcore::ZT_Node_address(self.intl.capi) as u64) }
|
||||
}
|
||||
|
||||
/// Handle a wire packet read from the physical network.
|
||||
/// This is physical -> virtual.
|
||||
#[inline(always)]
|
||||
pub fn process_wire_packet(&self, clock: i64, ticks: i64, local_socket: i64, remote_address: &InetAddress, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
|
||||
let intl = &*self.intl;
|
||||
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processWirePacket(intl.capi, clock, ticks, null_mut(), local_socket, remote_address.as_capi_ptr(), data.zt_core_buf as *const c_void, data.data_size as u32, 1, (next_task_deadline as *mut i64).cast()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
|
||||
std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs
|
||||
rc
|
||||
}
|
||||
|
||||
/// Handle a packet sent via a virtual network interface such as a tun/tap device.
|
||||
/// This is virtual -> physical.
|
||||
#[inline(always)]
|
||||
pub fn process_virtual_network_frame(&self, clock: i64, ticks: i64, nwid: &NetworkId, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: Buffer, next_task_deadline: &mut i64) -> ResultCode {
|
||||
let intl = &*self.intl;
|
||||
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processVirtualNetworkFrame(intl.capi, clock, ticks, null_mut(), nwid.0, source_mac.0, dest_mac.0, ethertype as c_uint, vlan_id as c_uint, data.zt_core_buf as *const c_void, data.data_size as u32, 1, (next_task_deadline as *mut i64).cast()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
|
||||
std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs
|
||||
rc
|
||||
}
|
||||
|
||||
/// Subscribe to a multicast group on a given network.
|
||||
#[inline(always)]
|
||||
pub fn multicast_subscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
|
||||
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
|
||||
}
|
||||
|
||||
/// Unsubscribe from a multicast group on a given network.
|
||||
#[inline(always)]
|
||||
pub fn multicast_unsubscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
|
||||
unsafe { ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.intl.capi, clock, ticks, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }
|
||||
}
|
||||
|
||||
/// Get this node's identity.
|
||||
#[inline(always)]
|
||||
pub fn identity(&self) -> &Identity {
|
||||
let id = self.identity_wrapper.as_ref().unwrap();
|
||||
unsafe { id.sync_type_and_address_with_capi() };
|
||||
id
|
||||
}
|
||||
|
||||
/// Get status information for this node.
|
||||
pub fn status(&self, clock: i64, ticks: i64) -> NodeStatus {
|
||||
let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed();
|
||||
unsafe {
|
||||
ztcore::ZT_Node_status(self.intl.capi, clock, ticks, null_mut(), ns.as_mut_ptr());
|
||||
let ns = ns.assume_init();
|
||||
if ns.identity.is_null() {
|
||||
panic!("ZT_Node_status() returned null identity");
|
||||
}
|
||||
NodeStatus {
|
||||
address: Address(ns.address),
|
||||
identity: Identity::new_from_capi(&*ns.identity, false).clone(),
|
||||
public_identity: cstr_to_string(ns.publicIdentity, -1),
|
||||
secret_identity: cstr_to_string(ns.secretIdentity, -1),
|
||||
online: ns.online != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of all peers with which this node is communicating or has very recently communicated.
|
||||
/// The presence of a peer on this list doesn't mean it has actual access to any network.
|
||||
pub fn peers(&self, clock: i64, ticks: i64) -> Vec<Peer> {
|
||||
let mut p: Vec<Peer> = Vec::new();
|
||||
unsafe {
|
||||
let pl = ztcore::ZT_Node_peers(self.intl.capi, clock, ticks, null_mut());
|
||||
if !pl.is_null() {
|
||||
let peer_count = (*pl).peerCount as usize;
|
||||
p.reserve(peer_count);
|
||||
for i in 0..peer_count as isize {
|
||||
p.push(Peer::new_from_capi(&*(*pl).peers.offset(i)));
|
||||
}
|
||||
ztcore::ZT_freeQueryResult(pl as *const c_void);
|
||||
}
|
||||
}
|
||||
p
|
||||
}
|
||||
|
||||
/// Get the networks this node has joined.
|
||||
pub fn networks(&self) -> Vec<VirtualNetworkConfig> {
|
||||
let mut n: Vec<VirtualNetworkConfig> = Vec::new();
|
||||
unsafe {
|
||||
let nl = ztcore::ZT_Node_networks(self.intl.capi);
|
||||
if !nl.is_null() {
|
||||
let net_count = (*nl).networkCount as usize;
|
||||
n.reserve(net_count);
|
||||
for i in 0..net_count as isize {
|
||||
n.push(VirtualNetworkConfig::new_from_capi(&*(*nl).networks.offset(i)));
|
||||
}
|
||||
ztcore::ZT_freeQueryResult(nl as *const c_void);
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
/// Get the certificates in this node's trust store and their local trust flags.
|
||||
pub fn certificates(&self, clock: i64, ticks: i64) -> Vec<(Certificate, u32)> {
|
||||
let mut c: Vec<(Certificate, u32)> = Vec::new();
|
||||
unsafe {
|
||||
let cl = ztcore::ZT_Node_listCertificates(self.intl.capi, clock, ticks, null_mut());
|
||||
if !cl.is_null() {
|
||||
let cert_count = (*cl).certCount as usize;
|
||||
c.reserve(cert_count);
|
||||
for i in 0..cert_count as isize {
|
||||
c.push((Certificate::new_from_capi(&**(*cl).certs.offset(i)), *(*cl).localTrust.offset(i)));
|
||||
}
|
||||
ztcore::ZT_freeQueryResult(cl as *const c_void);
|
||||
}
|
||||
}
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + Clone + 'static, H: NodeEventHandler<N>> Node<T, N, H> {
|
||||
/// Get a copy of this network's associated object.
|
||||
/// This is only available if N implements Clone.
|
||||
pub fn network(&self, nwid: NetworkId) -> Option<N> {
|
||||
self.intl.networks_by_id.lock().unwrap().get(&nwid.0).map_or(None, |nw| Some((**nw).clone()))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> Sync for Node<T, N, H> {}
|
||||
|
||||
unsafe impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> Send for Node<T, N, H> {}
|
||||
|
||||
impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> Drop for Node<T, N, H> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ztcore::ZT_Node_delete(self.intl.capi, self.intl.recent_clock.get(), self.intl.recent_ticks.get(), null_mut());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Endpoint;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||
pub struct Path {
|
||||
pub endpoint: Endpoint,
|
||||
#[serde(rename = "lastSend")]
|
||||
pub last_send: i64,
|
||||
#[serde(rename = "lastReceive")]
|
||||
pub last_receive: i64,
|
||||
pub alive: bool,
|
||||
pub preferred: bool
|
||||
}
|
||||
|
||||
impl Path {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_from_capi(p: &ztcore::ZT_Path) -> Path {
|
||||
Path{
|
||||
endpoint: Endpoint::new_from_capi(&p.endpoint),
|
||||
last_send: p.lastSend,
|
||||
last_receive: p.lastReceive,
|
||||
alive: p.alive != 0,
|
||||
preferred: p.preferred != 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Peer {
|
||||
pub address: Address,
|
||||
pub identity: Identity,
|
||||
pub fingerprint: Fingerprint,
|
||||
#[serde(rename = "versionMajor")]
|
||||
pub version_major: i32,
|
||||
#[serde(rename = "versionMinor")]
|
||||
pub version_minor: i32,
|
||||
#[serde(rename = "versionRev")]
|
||||
pub version_rev: i32,
|
||||
#[serde(rename = "versionProto")]
|
||||
pub version_proto: i32,
|
||||
pub latency: i32,
|
||||
pub root: bool,
|
||||
pub networks: Vec<NetworkId>,
|
||||
pub paths: Vec<Path>,
|
||||
pub locator: Option<Locator>
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
pub(crate) fn new_from_capi(p: &ztcore::ZT_Peer) -> Peer {
|
||||
unsafe {
|
||||
let mut networks: Vec<NetworkId> = Vec::new();
|
||||
for i in 0..(p.networkCount as isize) {
|
||||
networks.push(NetworkId(*p.networks.offset(i)));
|
||||
}
|
||||
let mut paths: Vec<Path> = Vec::new();
|
||||
for i in 0..(p.pathCount as isize) {
|
||||
paths.push(Path::new_from_capi(&(*p.paths.offset(i))));
|
||||
}
|
||||
return Peer {
|
||||
address: Address(p.address),
|
||||
identity: Identity::new_from_capi(p.identity, false).clone(), // clone to get a copy independent of 'p'
|
||||
fingerprint: Fingerprint::new_from_capi(&(*p.fingerprint)),
|
||||
version_major: p.versionMajor as i32,
|
||||
version_minor: p.versionMinor as i32,
|
||||
version_rev: p.versionRev as i32,
|
||||
version_proto: p.versionProto as i32,
|
||||
latency: p.latency as i32,
|
||||
root: p.root != 0,
|
||||
networks,
|
||||
paths,
|
||||
locator: if p.locator.is_null() { None } else { Some(Locator::new_from_capi(p.locator, false).clone() )}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Peer {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, p: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(&p))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Peer {
|
||||
fn cmp(&self, p: &Self) -> Ordering {
|
||||
let c = self.address.cmp(&p.address);
|
||||
if c == Ordering::Equal {
|
||||
self.identity.cmp(&p.identity)
|
||||
} else {
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#[cfg(all(target_pointer_width = "32"))]
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(all(target_pointer_width = "64"))]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
// This implements a basic atomic i64 that uses a mutex on 32-bit systems,
|
||||
// since you can't atomically access something larger than word size in most
|
||||
// cases.
|
||||
|
||||
#[cfg(all(target_pointer_width = "32"))]
|
||||
pub struct PortableAtomicI64 {
|
||||
i: Mutex<i64>
|
||||
}
|
||||
|
||||
#[cfg(all(target_pointer_width = "32"))]
|
||||
impl PortableAtomicI64 {
|
||||
#[inline(always)]
|
||||
pub fn new(v: i64) -> PortableAtomicI64 {
|
||||
PortableAtomicI64{
|
||||
i: Mutex::new(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get(&self) -> i64 {
|
||||
*self.i.lock().unwrap()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set(&self, v: i64) {
|
||||
*self.i.lock().unwrap() = v;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fetch_add(&self, v: i64) -> i64 {
|
||||
let i = self.i.lock().unwrap();
|
||||
let j = *i;
|
||||
*i += v;
|
||||
j
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_pointer_width = "64"))]
|
||||
pub struct PortableAtomicI64 {
|
||||
i: AtomicI64
|
||||
}
|
||||
|
||||
#[cfg(all(target_pointer_width = "64"))]
|
||||
impl PortableAtomicI64 {
|
||||
#[inline(always)]
|
||||
pub fn new(v: i64) -> PortableAtomicI64 {
|
||||
PortableAtomicI64{
|
||||
i: AtomicI64::new(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get(&self) -> i64 {
|
||||
self.i.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set(&self, v: i64) {
|
||||
self.i.store(v, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fetch_add(&self, v: i64) -> i64 {
|
||||
self.i.fetch_add(v, Ordering::Relaxed)
|
||||
}
|
||||
}
|
|
@ -1,542 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use std::str;
|
||||
|
||||
use crate::{capi as ztcore, Dictionary, Endpoint, Fingerprint, IpScope, MAC, Address, CredentialType};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
#[derive(FromPrimitive, PartialEq, Eq)]
|
||||
pub enum TracePacketDropReason {
|
||||
Unspecified = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED as isize,
|
||||
PeerTooOld = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_PEER_TOO_OLD as isize,
|
||||
MalformedPacket = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET as isize,
|
||||
MacFailed = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED as isize,
|
||||
RateLimitExceeded = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED as isize,
|
||||
InvalidObject = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT as isize,
|
||||
InvalidCompressedData = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_COMPRESSED_DATA as isize,
|
||||
UnrecognizedVerb = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB as isize,
|
||||
ReplyNotExpected = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_REPLY_NOT_EXPECTED as isize,
|
||||
}
|
||||
|
||||
impl TracePacketDropReason {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
TracePacketDropReason::Unspecified => "Unspecified",
|
||||
TracePacketDropReason::PeerTooOld => "PeerTooOld",
|
||||
TracePacketDropReason::MalformedPacket => "MalformedPacket",
|
||||
TracePacketDropReason::MacFailed => "MacFailed",
|
||||
TracePacketDropReason::RateLimitExceeded => "RateLimitExceeded",
|
||||
TracePacketDropReason::InvalidObject => "InvalidObject",
|
||||
TracePacketDropReason::InvalidCompressedData => "InvalidCompressedData",
|
||||
TracePacketDropReason::UnrecognizedVerb => "UnrecognizedVerb",
|
||||
TracePacketDropReason::ReplyNotExpected => "ReplyNotExpected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, PartialEq, Eq)]
|
||||
pub enum TraceFrameDropReason {
|
||||
Unspecified = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_UNSPECIFIED as isize,
|
||||
BridgingNotAllowedRemote = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_REMOTE as isize,
|
||||
BridgingNotAllowedLocal = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL as isize,
|
||||
MulticastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED as isize,
|
||||
BroadcastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BROADCAST_DISABLED as isize,
|
||||
FilterBlocked = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED as isize,
|
||||
FilterBlockedAtBridgeReplication = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED_AT_BRIDGE_REPLICATION as isize,
|
||||
PermissionDenied = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED as isize,
|
||||
}
|
||||
|
||||
impl TraceFrameDropReason {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
TraceFrameDropReason::Unspecified => "Unspecified",
|
||||
TraceFrameDropReason::BridgingNotAllowedRemote => "BridgingNotAllowedRemote",
|
||||
TraceFrameDropReason::BridgingNotAllowedLocal => "BridgingNotAllowedLocal",
|
||||
TraceFrameDropReason::MulticastDisabled => "MulticastDisabled",
|
||||
TraceFrameDropReason::BroadcastDisabled => "BroadcastDisabled",
|
||||
TraceFrameDropReason::FilterBlocked => "FilterBlocked",
|
||||
TraceFrameDropReason::FilterBlockedAtBridgeReplication => "FilterBlockedAtBridgeReplication",
|
||||
TraceFrameDropReason::PermissionDenied => "PermissionDenied",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, PartialEq, Eq)]
|
||||
pub enum TraceCredentialRejectionReason {
|
||||
SignatureVerificationFailed = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED as isize,
|
||||
Revoked = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED as isize,
|
||||
OlderThanLatest = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST as isize,
|
||||
Invalid = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID as isize,
|
||||
}
|
||||
|
||||
impl TraceCredentialRejectionReason {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
TraceCredentialRejectionReason::SignatureVerificationFailed => "SignatureVerificationFailed",
|
||||
TraceCredentialRejectionReason::Revoked => "Revoked",
|
||||
TraceCredentialRejectionReason::OlderThanLatest => "OlderThanLatest",
|
||||
TraceCredentialRejectionReason::Invalid => "Invalid",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum TraceFilterResult {
|
||||
Reject,
|
||||
Accept,
|
||||
SuperAccept,
|
||||
}
|
||||
|
||||
impl TraceFilterResult {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
TraceFilterResult::Reject => "Reject",
|
||||
TraceFilterResult::Accept => "Accept",
|
||||
TraceFilterResult::SuperAccept => "SuperAccept",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum TraceEvent {
|
||||
UnexpectedError {
|
||||
code_location: u32,
|
||||
message: String,
|
||||
},
|
||||
ResetingPathsInScope {
|
||||
code_location: u32,
|
||||
reporter: Option<Fingerprint>,
|
||||
reporter_endpoint: Option<Endpoint>,
|
||||
my_old_external: Option<Endpoint>,
|
||||
my_new_external: Option<Endpoint>,
|
||||
scope: IpScope,
|
||||
},
|
||||
TryingNewPath {
|
||||
code_location: u32,
|
||||
trying_peer: Fingerprint,
|
||||
trying_endpoint: Endpoint,
|
||||
trigger_peer: Option<Fingerprint>,
|
||||
trigger_packet_from: Option<Endpoint>,
|
||||
trigger_packet_id: u64,
|
||||
trigger_packet_verb: i32,
|
||||
},
|
||||
LearnedNewPath {
|
||||
code_location: u32,
|
||||
learned_from_packet_id: u64,
|
||||
peer: Fingerprint,
|
||||
new_endpoint: Option<Endpoint>,
|
||||
old_endpoint: Option<Endpoint>,
|
||||
},
|
||||
IncomingPacketDropped {
|
||||
code_location: u32,
|
||||
packet_id: u64,
|
||||
network_id: u64,
|
||||
peer: Option<Fingerprint>,
|
||||
peer_endpoint: Option<Endpoint>,
|
||||
hops: i32,
|
||||
verb: i32,
|
||||
reason: TracePacketDropReason,
|
||||
},
|
||||
OutgoingFrameDropped {
|
||||
code_location: u32,
|
||||
network_id: u64,
|
||||
source_mac: MAC,
|
||||
dest_mac: MAC,
|
||||
ethertype: u16,
|
||||
frame_length: u32,
|
||||
frame_data: Vec<u8>,
|
||||
reason: TraceFrameDropReason,
|
||||
},
|
||||
IncomingFrameDropped {
|
||||
code_location: u32,
|
||||
network_id: u64,
|
||||
source_mac: MAC,
|
||||
dest_mac: MAC,
|
||||
ethertype: u16,
|
||||
peer: Fingerprint,
|
||||
peer_endpoint: Option<Endpoint>,
|
||||
hops: i32,
|
||||
verb: i32,
|
||||
frame_length: u32,
|
||||
frame_data: Vec<u8>,
|
||||
credential_request_sent: bool,
|
||||
reason: TraceFrameDropReason,
|
||||
},
|
||||
NetworkConfigRequested {
|
||||
code_location: u32,
|
||||
network_id: u64,
|
||||
},
|
||||
NetworkFilter {
|
||||
code_location: u32,
|
||||
network_id: u64,
|
||||
primary_rule_set_log: Vec<u8>,
|
||||
matching_capability_rule_set_log: Vec<u8>,
|
||||
matching_capability_id: u32,
|
||||
matching_capability_timestamp: i64,
|
||||
source_address: Address,
|
||||
dest_address: Address,
|
||||
source_mac: MAC,
|
||||
dest_mac: MAC,
|
||||
frame_length: u32,
|
||||
frame_data: Vec<u8>,
|
||||
ethertype: u16,
|
||||
vlan_id: u16,
|
||||
flag_notee: bool,
|
||||
inbound: bool,
|
||||
result: TraceFilterResult,
|
||||
},
|
||||
NetworkCredentialRejected {
|
||||
code_location: u32,
|
||||
network_id: u64,
|
||||
from_peer: Fingerprint,
|
||||
credential_id: u32,
|
||||
credential_timestamp: i64,
|
||||
credential_type: CredentialType,
|
||||
reason: TraceCredentialRejectionReason,
|
||||
},
|
||||
}
|
||||
|
||||
fn trace_to_string_optional<T: ToString>(x: &Option<T>) -> String {
|
||||
x.as_ref().map_or_else(|| { "<unknown>".to_string() }, |xx| { xx.to_string() })
|
||||
}
|
||||
|
||||
fn trace_peer_address_to_string_optional(p: &Option<Fingerprint>) -> String {
|
||||
p.as_ref().map_or_else(|| { "<unknown>".to_string() }, |pp| { pp.address.to_string() })
|
||||
}
|
||||
|
||||
impl ToString for TraceEvent {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
TraceEvent::UnexpectedError { code_location, message } => {
|
||||
format!("UnexpectedError: {} ({:0>8x})", message, code_location)
|
||||
}
|
||||
TraceEvent::ResetingPathsInScope { code_location, reporter, reporter_endpoint, my_old_external, my_new_external, scope } => {
|
||||
format!(
|
||||
"VL1 ResettingPathsInScope: resetting scope {} because {}@{} reported that my address changed from {} to {} ({:0>8x})",
|
||||
scope.to_str(),
|
||||
trace_peer_address_to_string_optional(reporter),
|
||||
trace_to_string_optional(reporter_endpoint),
|
||||
trace_to_string_optional(my_old_external),
|
||||
trace_to_string_optional(my_new_external),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::TryingNewPath { code_location, trying_peer, trying_endpoint, trigger_peer, trigger_packet_from, trigger_packet_id, .. } => {
|
||||
format!(
|
||||
"VL1 TryingNewPath: trying {}@{} triggered by packet {:0>16x} from {}@{} ({:0>8x})",
|
||||
trying_peer.address.to_string(),
|
||||
trying_endpoint.to_string(),
|
||||
trigger_packet_id,
|
||||
trace_peer_address_to_string_optional(trigger_peer),
|
||||
trace_to_string_optional(trigger_packet_from),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::LearnedNewPath { code_location, learned_from_packet_id, peer, old_endpoint, new_endpoint } => {
|
||||
format!(
|
||||
"VL1 LearnedNewPath: {} is now at {}, was at {}, learned from packet {:0>16x} ({:0>8x})",
|
||||
peer.address.to_string(),
|
||||
trace_to_string_optional(new_endpoint),
|
||||
trace_to_string_optional(old_endpoint),
|
||||
learned_from_packet_id,
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::IncomingPacketDropped { code_location, packet_id, peer, peer_endpoint, hops, verb, reason, .. } => {
|
||||
format!(
|
||||
"VL1 IncomingPacketDropped: packet {:0>16x} from {}@{} (hops: {}, verb: {}) dropped: {} ({:0>8x})",
|
||||
packet_id,
|
||||
trace_peer_address_to_string_optional(peer),
|
||||
trace_to_string_optional(peer_endpoint),
|
||||
hops,
|
||||
verb,
|
||||
reason.to_str(),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::OutgoingFrameDropped { code_location, network_id, source_mac, dest_mac, ethertype, frame_length, reason, .. } => {
|
||||
format!(
|
||||
"VL2 OutgoingFrameDropped: network {:0>16x} {} -> {} ethertype {:0>4x} length {} dropped: {} ({:0>8x})",
|
||||
network_id,
|
||||
source_mac.to_string(),
|
||||
dest_mac.to_string(),
|
||||
ethertype,
|
||||
frame_length,
|
||||
reason.to_str(),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::IncomingFrameDropped { code_location, network_id, source_mac, dest_mac, ethertype, peer, peer_endpoint, frame_length, reason, .. } => {
|
||||
format!(
|
||||
"VL2 IncomingFrameDropped: network {:0>16x} {} -> {} ethertype {:0>4x} length {} from {}@{} dropped: {} ({:0>8x})",
|
||||
network_id,
|
||||
source_mac.to_string(),
|
||||
dest_mac.to_string(),
|
||||
ethertype,
|
||||
frame_length,
|
||||
peer.address.to_string(),
|
||||
trace_to_string_optional(peer_endpoint),
|
||||
reason.to_str(),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::NetworkConfigRequested { code_location, network_id } => {
|
||||
format!(
|
||||
"VL2 NetworkConfigRequested: {:0>16x} ({:0>8x})", network_id, code_location)
|
||||
}
|
||||
TraceEvent::NetworkFilter { code_location, network_id, source_address, dest_address, source_mac, dest_mac, frame_length, ethertype, inbound, result, .. } => {
|
||||
format!(
|
||||
"VL2 NetworkFilter: network {:0>16x} {}: {} via {} -> {} via {} length {} ethertype {:0>4x} result {} ({:0>8x})",
|
||||
network_id,
|
||||
if *inbound { "IN" } else { "OUT" },
|
||||
source_mac.to_string(),
|
||||
source_address.to_string(),
|
||||
dest_mac.to_string(),
|
||||
dest_address.to_string(),
|
||||
frame_length,
|
||||
ethertype,
|
||||
result.to_str(),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
TraceEvent::NetworkCredentialRejected { code_location, network_id, from_peer, credential_id, credential_timestamp, credential_type, reason } => {
|
||||
format!(
|
||||
"VL2 NetworkCredentialRejected: network {:0>16x} from {} id {:0>8x} timestamp {} type {}: {} ({:0>8x})",
|
||||
network_id,
|
||||
from_peer.address.to_string(),
|
||||
credential_id,
|
||||
credential_timestamp,
|
||||
credential_type.to_str(),
|
||||
reason.to_str(),
|
||||
code_location,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trace_optional_endpoint(bytes: Option<&Vec<u8>>) -> Option<Endpoint> {
|
||||
bytes.map_or(None, |ep| {
|
||||
Endpoint::new_from_bytes(ep.as_slice()).map_or(None, |ep| {
|
||||
Some(ep)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn trace_optional_fingerprint(bytes: Option<&Vec<u8>>) -> Option<Fingerprint> {
|
||||
bytes.map_or(None, |fp| {
|
||||
Fingerprint::new_from_bytes(fp).map_or(None, |fp| {
|
||||
Some(fp)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// What layer or log level does this trace event belong to?
|
||||
pub enum TraceEventLayer {
|
||||
VL1,
|
||||
VL2,
|
||||
VL2Filter,
|
||||
VL2Multicast,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl TraceEvent {
|
||||
#[inline(always)]
|
||||
pub fn layer(&self) -> TraceEventLayer {
|
||||
match *self {
|
||||
TraceEvent::UnexpectedError {..} => TraceEventLayer::Other,
|
||||
|
||||
TraceEvent::ResetingPathsInScope {..} => TraceEventLayer::VL1,
|
||||
TraceEvent::TryingNewPath {..} => TraceEventLayer::VL1,
|
||||
TraceEvent::LearnedNewPath {..} => TraceEventLayer::VL1,
|
||||
TraceEvent::IncomingPacketDropped {..} => TraceEventLayer::VL1,
|
||||
|
||||
TraceEvent::OutgoingFrameDropped {..} => TraceEventLayer::VL2,
|
||||
TraceEvent::IncomingFrameDropped {..} => TraceEventLayer::VL2,
|
||||
TraceEvent::NetworkConfigRequested {..} => TraceEventLayer::VL2,
|
||||
TraceEvent::NetworkFilter {..} => TraceEventLayer::VL2Filter,
|
||||
TraceEvent::NetworkCredentialRejected {..} => TraceEventLayer::VL2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode a trace event packaged in a dictionary and return a TraceEvent if it is valid.
|
||||
pub fn parse_message(msg: &Dictionary) -> Option<TraceEvent> {
|
||||
msg.get_ui(ztcore::ZT_TRACE_FIELD_TYPE).map_or(None, |mt: u64| -> Option<TraceEvent> {
|
||||
let cl = msg.get_ui(ztcore::ZT_TRACE_FIELD_CODE_LOCATION).unwrap_or(0) as u32;
|
||||
match mt as ztcore::ZT_TraceEventType {
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE => {
|
||||
Some(TraceEvent::ResetingPathsInScope {
|
||||
code_location: cl,
|
||||
reporter: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT)),
|
||||
reporter_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT)),
|
||||
my_old_external: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_OLD_ENDPOINT)),
|
||||
my_new_external: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_NEW_ENDPOINT)),
|
||||
scope: IpScope::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE).unwrap_or(0) as i32).unwrap_or(IpScope::None),
|
||||
})
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL1_TRYING_NEW_PATH => {
|
||||
let tf = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT);
|
||||
let ep = msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT);
|
||||
if tf.is_some() && ep.is_some() {
|
||||
let tf = Fingerprint::new_from_bytes(tf.unwrap().as_slice()).ok();
|
||||
let ep = Endpoint::new_from_bytes(ep.unwrap().as_slice()).ok();
|
||||
if tf.is_some() && ep.is_some() {
|
||||
return Some(TraceEvent::TryingNewPath {
|
||||
code_location: cl,
|
||||
trying_peer: tf.unwrap(),
|
||||
trying_endpoint: ep.unwrap(),
|
||||
trigger_peer: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT)),
|
||||
trigger_packet_from: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT)),
|
||||
trigger_packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID).unwrap_or(0),
|
||||
trigger_packet_verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB).unwrap_or(0) as i32,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL1_LEARNED_NEW_PATH => {
|
||||
let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT);
|
||||
if fp.is_some() {
|
||||
let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok();
|
||||
if fp.is_some() {
|
||||
return Some(TraceEvent::LearnedNewPath {
|
||||
code_location: cl,
|
||||
learned_from_packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_ID).unwrap_or(0),
|
||||
peer: fp.unwrap(),
|
||||
new_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)),
|
||||
old_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_OLD_ENDPOINT)),
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL1_INCOMING_PACKET_DROPPED => {
|
||||
Some(TraceEvent::IncomingPacketDropped {
|
||||
code_location: cl,
|
||||
packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_ID).unwrap_or(0),
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
peer: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT)),
|
||||
peer_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)),
|
||||
hops: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_HOPS).unwrap_or(0) as i32,
|
||||
verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_VERB).unwrap_or(0) as i32,
|
||||
reason: TracePacketDropReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TracePacketDropReason::Unspecified),
|
||||
})
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED => {
|
||||
Some(TraceEvent::OutgoingFrameDropped {
|
||||
code_location: cl,
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)),
|
||||
dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)),
|
||||
ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16,
|
||||
frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32,
|
||||
frame_data: msg.get(ztcore::ZT_TRACE_FIELD_FRAME_DATA).map_or_else(|| -> Vec<u8> {
|
||||
Vec::new()
|
||||
}, |d: &Vec<u8>| -> Vec<u8> {
|
||||
d.clone()
|
||||
}),
|
||||
reason: TraceFrameDropReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceFrameDropReason::Unspecified),
|
||||
})
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL2_INCOMING_FRAME_DROPPED => {
|
||||
let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT);
|
||||
if fp.is_some() {
|
||||
let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok();
|
||||
if fp.is_some() {
|
||||
return Some(TraceEvent::IncomingFrameDropped {
|
||||
code_location: cl,
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)),
|
||||
dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)),
|
||||
ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16,
|
||||
peer: fp.unwrap(),
|
||||
peer_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)),
|
||||
hops: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_HOPS).unwrap_or(0) as i32,
|
||||
verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_VERB).unwrap_or(0) as i32,
|
||||
frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32,
|
||||
frame_data: msg.get_or_empty(ztcore::ZT_TRACE_FIELD_FRAME_DATA),
|
||||
credential_request_sent: msg.get_ui(ztcore::ZT_TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT).unwrap_or(0) != 0,
|
||||
reason: TraceFrameDropReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceFrameDropReason::Unspecified),
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED => {
|
||||
Some(TraceEvent::NetworkConfigRequested {
|
||||
code_location: cl,
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
})
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_FILTER => {
|
||||
let verdict_int = msg.get(ztcore::ZT_TRACE_FIELD_RULE_FLAG_ACCEPT).map_or_else(|| -> i32 { 0 as i32 }, |a| -> i32 { i32::from_str_radix(str::from_utf8(a).unwrap_or("0"), 16).unwrap_or(0) });
|
||||
let mut verdict = TraceFilterResult::Reject;
|
||||
if verdict_int == 1 {
|
||||
verdict = TraceFilterResult::Accept;
|
||||
} else if verdict_int > 1 {
|
||||
verdict = TraceFilterResult::SuperAccept;
|
||||
}
|
||||
Some(TraceEvent::NetworkFilter {
|
||||
code_location: cl,
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
primary_rule_set_log: msg.get_or_empty(ztcore::ZT_TRACE_FIELD_PRIMARY_RULE_SET_LOG),
|
||||
matching_capability_rule_set_log: msg.get_or_empty(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG),
|
||||
matching_capability_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID).unwrap_or(0) as u32,
|
||||
matching_capability_timestamp: msg.get_ui(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP).unwrap_or(0) as i64,
|
||||
source_address: Address(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_ZT_ADDRESS).unwrap_or(0)),
|
||||
dest_address: Address(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_ZT_ADDRESS).unwrap_or(0)),
|
||||
source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)),
|
||||
dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)),
|
||||
frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32,
|
||||
frame_data: msg.get_or_empty(ztcore::ZT_TRACE_FIELD_FRAME_DATA),
|
||||
ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16,
|
||||
vlan_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_VLAN_ID).unwrap_or(0) as u16,
|
||||
flag_notee: msg.get_ui(ztcore::ZT_TRACE_FIELD_RULE_FLAG_NOTEE).unwrap_or(0) != 0,
|
||||
inbound: msg.get_ui(ztcore::ZT_TRACE_FIELD_RULE_FLAG_INBOUND).unwrap_or(0) != 0,
|
||||
result: verdict,
|
||||
})
|
||||
},
|
||||
ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CREDENTIAL_REJECTED => {
|
||||
let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT);
|
||||
if fp.is_some() {
|
||||
let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok();
|
||||
if fp.is_some() {
|
||||
return Some(TraceEvent::NetworkCredentialRejected {
|
||||
code_location: cl,
|
||||
network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0),
|
||||
from_peer: fp.unwrap(),
|
||||
credential_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_ID).unwrap_or(0) as u32,
|
||||
credential_timestamp: msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TIMESTAMP).unwrap_or(0) as i64,
|
||||
credential_type: CredentialType::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TYPE).unwrap_or(0) as i32).unwrap_or(CredentialType::Null),
|
||||
reason: TraceCredentialRejectionReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceCredentialRejectionReason::Invalid),
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
_ => { // ztcore::ZT_TraceEventType_ZT_TRACE_UNEXPECTED_ERROR
|
||||
Some(TraceEvent::UnexpectedError {
|
||||
code_location: cl,
|
||||
message: msg.get_str(ztcore::ZT_TRACE_FIELD_MESSAGE).map_or_else(|| {
|
||||
format!("WARNING: unknown trace message type {}, this version may be too old!", mt)
|
||||
}, |m| {
|
||||
m.to_string()
|
||||
}),
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::*;
|
||||
use crate::capi as ztcore;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(FromPrimitive,ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum VirtualNetworkType {
|
||||
Private = ztcore::ZT_VirtualNetworkType_ZT_NETWORK_TYPE_PRIVATE as isize,
|
||||
Public = ztcore::ZT_VirtualNetworkType_ZT_NETWORK_TYPE_PUBLIC as isize
|
||||
}
|
||||
|
||||
impl VirtualNetworkType {
|
||||
pub fn to_str(&self) -> &str {
|
||||
match *self {
|
||||
//VirtualNetworkType::Private => "PRIVATE",
|
||||
VirtualNetworkType::Public => "PUBLIC",
|
||||
_ => "PRIVATE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for VirtualNetworkType {
|
||||
fn from(s: &str) -> VirtualNetworkType {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
//"requesting_configuration" | "requestingconfiguration" => VirtualNetworkStatus::RequestingConfiguration,
|
||||
"public" => VirtualNetworkType::Public,
|
||||
_ => VirtualNetworkType::Private
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for VirtualNetworkType {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
self.to_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for VirtualNetworkType {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
serializer.serialize_str(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct VirtualNetworkTypeVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for VirtualNetworkTypeVisitor {
|
||||
type Value = VirtualNetworkType;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("VirtualNetworkType value in string form")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
Ok(VirtualNetworkType::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for VirtualNetworkType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
deserializer.deserialize_str(VirtualNetworkTypeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(FromPrimitive,ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum VirtualNetworkRuleType {
|
||||
ActionDrop = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_DROP as isize,
|
||||
ActionAccept = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_ACCEPT as isize,
|
||||
ActionTee = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_TEE as isize,
|
||||
ActionWatch = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_WATCH as isize,
|
||||
ActionRedirect = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_REDIRECT as isize,
|
||||
ActionBreak = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_BREAK as isize,
|
||||
ActionPriority = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_ACTION_PRIORITY as isize,
|
||||
MatchSourceZeroTierAddress = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS as isize,
|
||||
MatchDestinationZeroTierAddress = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS as isize,
|
||||
MatchVlanId = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_VLAN_ID as isize,
|
||||
MatchVlanPcp = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_VLAN_PCP as isize,
|
||||
MatchVlanDei = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_VLAN_DEI as isize,
|
||||
MatchMacSource = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_MAC_SOURCE as isize,
|
||||
MatchMacDestination = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_MAC_DEST as isize,
|
||||
MatchIpv4Source = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IPV4_SOURCE as isize,
|
||||
MatchIpv4Destination = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IPV4_DEST as isize,
|
||||
MatchIpv6Source = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IPV6_SOURCE as isize,
|
||||
MatchIpv6Destination = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IPV6_DEST as isize,
|
||||
MatchIpTos = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IP_TOS as isize,
|
||||
MatchIpProtocol = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IP_PROTOCOL as isize,
|
||||
MatchEtherType = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_ETHERTYPE as isize,
|
||||
MatchIcmp = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_ICMP as isize,
|
||||
MatchIpSourcePortRange = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE as isize,
|
||||
MatchIpDestinationSourceRange = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE as isize,
|
||||
MatchCharacteristics = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_CHARACTERISTICS as isize,
|
||||
MatchFrameSizeRange = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE as isize,
|
||||
MatchRandom = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_RANDOM as isize,
|
||||
MatchTagsDifference = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE as isize,
|
||||
MatchTagsBitwiseAnd = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND as isize,
|
||||
MatchTagsBitwiseOr = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR as isize,
|
||||
MatchTagsBitwiseXor = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR as isize,
|
||||
MatchTagsEqual = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAGS_EQUAL as isize,
|
||||
MatchTagSender = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAG_SENDER as isize,
|
||||
MatchTagReceiver = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_TAG_RECEIVER as isize,
|
||||
MatchIntegerRange = ztcore::ZT_VirtualNetworkRuleType_ZT_NETWORK_RULE_MATCH_INTEGER_RANGE as isize
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(FromPrimitive,ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum VirtualNetworkConfigOperation {
|
||||
Up = ztcore::ZT_VirtualNetworkConfigOperation_ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP as isize,
|
||||
ConfigUpdate = ztcore::ZT_VirtualNetworkConfigOperation_ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE as isize,
|
||||
Down = ztcore::ZT_VirtualNetworkConfigOperation_ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN as isize,
|
||||
Destroy = ztcore::ZT_VirtualNetworkConfigOperation_ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY as isize
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(FromPrimitive,ToPrimitive, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum VirtualNetworkStatus {
|
||||
RequestingConfiguration = ztcore::ZT_VirtualNetworkStatus_ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION as isize,
|
||||
Ok = ztcore::ZT_VirtualNetworkStatus_ZT_NETWORK_STATUS_OK as isize,
|
||||
AccessDenied = ztcore::ZT_VirtualNetworkStatus_ZT_NETWORK_STATUS_ACCESS_DENIED as isize,
|
||||
NotFound = ztcore::ZT_VirtualNetworkStatus_ZT_NETWORK_STATUS_NOT_FOUND as isize
|
||||
}
|
||||
|
||||
impl VirtualNetworkStatus {
|
||||
pub fn to_str(&self) -> &str {
|
||||
match *self {
|
||||
VirtualNetworkStatus::RequestingConfiguration => "REQUESTING_CONFIGURATION",
|
||||
VirtualNetworkStatus::Ok => "OK",
|
||||
VirtualNetworkStatus::AccessDenied => "ACCESS_DENIED",
|
||||
VirtualNetworkStatus::NotFound => "NOT_FOUND"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for VirtualNetworkStatus {
|
||||
fn from(s: &str) -> VirtualNetworkStatus {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
//"requesting_configuration" | "requestingconfiguration" => VirtualNetworkStatus::RequestingConfiguration,
|
||||
"ok" => VirtualNetworkStatus::Ok,
|
||||
"access_denied" | "accessdenied" => VirtualNetworkStatus::AccessDenied,
|
||||
"not_found" | "notfound" => VirtualNetworkStatus::NotFound,
|
||||
_ => VirtualNetworkStatus::RequestingConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for VirtualNetworkStatus {
|
||||
#[inline(always)]
|
||||
fn to_string(&self) -> String {
|
||||
self.to_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for VirtualNetworkStatus {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
serializer.serialize_str(self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct VirtualNetworkStatusVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for VirtualNetworkStatusVisitor {
|
||||
type Value = VirtualNetworkStatus;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("VirtualNetworkStatus value in string form")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
|
||||
Ok(VirtualNetworkStatus::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for VirtualNetworkStatus {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
|
||||
deserializer.deserialize_str(VirtualNetworkStatusVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct VirtualNetworkRoute {
|
||||
pub target: Option<InetAddress>,
|
||||
pub via: Option<InetAddress>,
|
||||
pub flags: u16,
|
||||
pub metric: u16
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct VirtualNetworkConfig {
|
||||
pub nwid: NetworkId,
|
||||
pub mac: MAC,
|
||||
pub name: String,
|
||||
pub status: VirtualNetworkStatus,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: VirtualNetworkType,
|
||||
pub mtu: u32,
|
||||
pub bridge: bool,
|
||||
#[serde(rename = "broadcastEnabled")]
|
||||
pub broadcast_enabled: bool,
|
||||
#[serde(rename = "netconfRevision")]
|
||||
pub netconf_revision: u64,
|
||||
#[serde(rename = "assignedAddresses")]
|
||||
pub assigned_addresses: Vec<InetAddress>,
|
||||
pub routes: Vec<VirtualNetworkRoute>
|
||||
}
|
||||
|
||||
impl VirtualNetworkConfig {
|
||||
pub(crate) fn new_from_capi(vnc: &ztcore::ZT_VirtualNetworkConfig) -> VirtualNetworkConfig {
|
||||
let mut aa: Vec<InetAddress> = Vec::new();
|
||||
let saptr = vnc.assignedAddresses.as_ptr();
|
||||
for i in 0..vnc.assignedAddressCount as isize {
|
||||
let a = InetAddress::new_from_capi(unsafe { &*saptr.offset(i) });
|
||||
if a.is_some() {
|
||||
aa.push(a.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
let mut rts: Vec<VirtualNetworkRoute> = Vec::new();
|
||||
let rtptr = vnc.routes.as_ptr();
|
||||
for i in 0..vnc.routeCount as isize {
|
||||
let r = unsafe { *rtptr.offset(i) };
|
||||
rts.push(VirtualNetworkRoute{
|
||||
target: InetAddress::new_from_capi(&r.target),
|
||||
via: InetAddress::new_from_capi(&r.via),
|
||||
flags: r.flags,
|
||||
metric: r.metric
|
||||
})
|
||||
}
|
||||
|
||||
return VirtualNetworkConfig{
|
||||
nwid: NetworkId(vnc.nwid),
|
||||
mac: MAC(vnc.mac),
|
||||
name: unsafe { cstr_to_string(vnc.name.as_ptr(), vnc.name.len() as isize) },
|
||||
status: FromPrimitive::from_i32(vnc.status as i32).unwrap_or(VirtualNetworkStatus::RequestingConfiguration),
|
||||
type_: FromPrimitive::from_i32(vnc.type_ as i32).unwrap_or(VirtualNetworkType::Private),
|
||||
mtu: vnc.mtu as u32,
|
||||
bridge: vnc.bridge != 0,
|
||||
broadcast_enabled: vnc.broadcastEnabled != 0,
|
||||
netconf_revision: vnc.netconfRevision as u64,
|
||||
assigned_addresses: aa,
|
||||
routes: rts
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue