Implement usage flags in certificate, make root sets require a usage flag and a more general local trust flag, and clean up Identity return in Rust Node.

This commit is contained in:
Adam Ierymenko 2021-04-23 18:16:49 -04:00
parent 24ab618123
commit 251cc082f2
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
18 changed files with 512 additions and 169 deletions

View file

@ -40,7 +40,7 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
m_clear(); m_clear();
Utils::copy< sizeof(this->serialNo) >(this->serialNo, cert.serialNo); Utils::copy< sizeof(this->serialNo) >(this->serialNo, cert.serialNo);
this->flags = cert.flags; this->usageFlags = cert.usageFlags;
this->timestamp = cert.timestamp; this->timestamp = cert.timestamp;
this->validity[0] = cert.validity[0]; this->validity[0] = cert.validity[0];
this->validity[1] = cert.validity[1]; this->validity[1] = cert.validity[1];
@ -180,8 +180,8 @@ Vector< uint8_t > Certificate::encode(const bool omitSignature) const
* purposes the keys must be always be in order. * purposes the keys must be always be in order.
*/ */
if (this->flags != 0) if (this->usageFlags != 0)
d.add("f", this->flags); d.add("f", this->usageFlags);
if (this->timestamp > 0) if (this->timestamp > 0)
d.add("t", (uint64_t)this->timestamp); d.add("t", (uint64_t)this->timestamp);
if (this->validity[0] > 0) if (this->validity[0] > 0)
@ -223,7 +223,7 @@ bool Certificate::decode(const void *const data, const unsigned int len)
m_clear(); m_clear();
this->flags = d.getUI("f"); this->usageFlags = d.getUI("f");
this->timestamp = (int64_t)d.getUI("t"); this->timestamp = (int64_t)d.getUI("t");
this->validity[0] = (int64_t)d.getUI("v#0"); this->validity[0] = (int64_t)d.getUI("v#0");
this->validity[1] = (int64_t)d.getUI("v#1"); this->validity[1] = (int64_t)d.getUI("v#1");

View file

@ -300,7 +300,7 @@ static bool ZTT_deepCompareCertificateName(const ZT_Certificate_Name &a, const Z
static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate &b) static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate &b)
{ {
if ( if (
(a.flags != b.flags) || (a.usageFlags != b.usageFlags) ||
(a.timestamp != b.timestamp) || (a.timestamp != b.timestamp) ||
(a.validity[0] != b.validity[0]) || (a.validity[0] != b.validity[0]) ||
(a.validity[1] != b.validity[1]) || (a.validity[1] != b.validity[1]) ||

View file

@ -39,19 +39,22 @@ Map< Identity, SharedPtr< const Locator > > TrustStore::roots()
for (Map< Fingerprint, Vector< SharedPtr< Entry > > >::const_iterator cv(m_bySubjectIdentity.begin()); cv != m_bySubjectIdentity.end(); ++cv) { for (Map< Fingerprint, Vector< SharedPtr< Entry > > >::const_iterator cv(m_bySubjectIdentity.begin()); cv != m_bySubjectIdentity.end(); ++cv) {
for (Vector< SharedPtr< Entry > >::const_iterator c(cv->second.begin()); c != cv->second.end(); ++c) { for (Vector< SharedPtr< Entry > >::const_iterator c(cv->second.begin()); c != cv->second.end(); ++c) {
if (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) != 0) { // A root set cert must be marked for this use and authorized to influence this node's config.
if ((((*c)->m_certificate.usageFlags & ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET) != 0) && (((*c)->m_localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG) != 0)) {
// Add all identities to the root set, and for each entry in the set make sure we have the latest locator if there's more than one cert with one.
for (unsigned int j = 0; j < (*c)->certificate().subject.identityCount; ++j) { for (unsigned int j = 0; j < (*c)->certificate().subject.identityCount; ++j) {
const Identity *const id = reinterpret_cast<const Identity *>((*c)->certificate().subject.identities[j].identity); const Identity *const id = reinterpret_cast<const Identity *>((*c)->certificate().subject.identities[j].identity);
if ((id) && (*id)) { // sanity check if ((id) && (*id)) { // sanity check
SharedPtr< const Locator > &existingLoc = r[*id]; SharedPtr< const Locator > &existingLoc = r[*id];
const Locator *const loc = reinterpret_cast<const Locator *>((*c)->certificate().subject.identities[j].locator); const Locator *const loc = reinterpret_cast<const Locator *>((*c)->certificate().subject.identities[j].locator);
if (loc) { if (loc) {
// If more than one certificate names a root, this ensures that the newest locator is used.
if ((!existingLoc) || (existingLoc->revision() < loc->revision())) if ((!existingLoc) || (existingLoc->revision() < loc->revision()))
existingLoc.set(new Locator(*loc)); existingLoc.set(new Locator(*loc));
} }
} }
} }
} }
} }
@ -198,10 +201,13 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
m_bySubjectUniqueId.clear(); m_bySubjectUniqueId.clear();
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) { if (c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) {
const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize; const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize;
if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) { if ((uniqueIdSize > 0) && (uniqueIdSize <= ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE)) {
SharedPtr< Entry > &entry = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)]; SharedPtr< Entry > &entry = m_bySubjectUniqueId[Blob< ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE >(c->second->m_certificate.subject.uniqueId, uniqueIdSize)];
if (entry) { if (entry) {
// If there's already an entry, see if there's a newer certificate for this subject.
if (c->second->m_certificate.subject.timestamp > entry->m_certificate.subject.timestamp) { if (c->second->m_certificate.subject.timestamp > entry->m_certificate.subject.timestamp) {
entry->m_subjectDeprecated = true; entry->m_subjectDeprecated = true;
entry = c->second; entry = c->second;
@ -216,10 +222,12 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
c->second->m_subjectDeprecated = true; c->second->m_subjectDeprecated = true;
} }
} }
} else { } else {
entry = c->second; entry = c->second;
} }
} }
} }
} }
@ -228,17 +236,17 @@ bool TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const
m_bySubjectIdentity.clear(); m_bySubjectIdentity.clear();
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
if ((c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) && (!c->second->m_subjectDeprecated)) { if ((c->second->m_error == ZT_CERTIFICATE_ERROR_NONE) && (!c->second->m_subjectDeprecated)) {
for (unsigned int i = 0; i < c->second->m_certificate.subject.identityCount; ++i) { for (unsigned int i = 0; i < c->second->m_certificate.subject.identityCount; ++i) {
const Identity *const id = reinterpret_cast<const Identity *>(c->second->m_certificate.subject.identities[i].identity); const Identity *const id = reinterpret_cast<const Identity *>(c->second->m_certificate.subject.identities[i].identity);
if ((id) && (*id)) // sanity check if ((id) && (*id)) // sanity check
m_bySubjectIdentity[id->fingerprint()].push_back(c->second); m_bySubjectIdentity[id->fingerprint()].push_back(c->second);
} }
} }
} }
// Purge error certificates and return them if 'purge' is non-NULL. This purges error certificates // If purge is set, erase and return error and deprecated certs (that are not on a trust path).
// and deprecated certificates not on a trust path. Deprecated certificates on a trust path remain
// as they are still technically valid and other possibly wanted certificates depend on them.
if (purge) { if (purge) {
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) {
if ( (c->second->error() != ZT_CERTIFICATE_ERROR_NONE) || ((c->second->m_subjectDeprecated) && (!c->second->m_onTrustPath)) ) { if ( (c->second->error() != ZT_CERTIFICATE_ERROR_NONE) || ((c->second->m_subjectDeprecated) && (!c->second->m_onTrustPath)) ) {
@ -259,15 +267,15 @@ Vector< uint8_t > TrustStore::save() const
int compSize; int compSize;
{ {
Vector< uint8_t > b;
b.reserve(65536);
RWMutex::RLock l(m_lock); RWMutex::RLock l(m_lock);
Vector< uint8_t > b;
b.reserve(4096);
// A version byte. // A version byte.
b.push_back(0); b.push_back(0);
// <size> <certificate> <trust> tuples terminated by a 0 size. // <size[2]> <certificate[...]> <trust[2]> tuples terminated by a 0 size.
for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) {
const Vector< uint8_t > cdata(c->second->certificate().encode()); const Vector< uint8_t > cdata(c->second->certificate().encode());
const unsigned long size = (uint32_t)cdata.size(); const unsigned long size = (uint32_t)cdata.size();
@ -276,8 +284,6 @@ Vector< uint8_t > TrustStore::save() const
b.push_back((uint8_t)size); b.push_back((uint8_t)size);
b.insert(b.end(), cdata.begin(), cdata.end()); b.insert(b.end(), cdata.begin(), cdata.end());
const uint32_t localTrust = (uint32_t)c->second->localTrust(); const uint32_t localTrust = (uint32_t)c->second->localTrust();
b.push_back((uint8_t)(localTrust >> 24U));
b.push_back((uint8_t)(localTrust >> 16U));
b.push_back((uint8_t)(localTrust >> 8U)); b.push_back((uint8_t)(localTrust >> 8U));
b.push_back((uint8_t)localTrust); b.push_back((uint8_t)localTrust);
} }
@ -291,15 +297,8 @@ Vector< uint8_t > TrustStore::save() const
return Vector< uint8_t >(); return Vector< uint8_t >();
const uint32_t uncompSize = (uint32_t)b.size(); const uint32_t uncompSize = (uint32_t)b.size();
const uint32_t cksum = Utils::fnv1a32(b.data(), (unsigned int)uncompSize); Utils::storeBigEndian(comp.data(), uncompSize);
comp[0] = (uint8_t)(uncompSize >> 24); Utils::storeBigEndian(comp.data() + 4, Utils::fnv1a32(b.data(), (unsigned int)uncompSize));
comp[1] = (uint8_t)(uncompSize >> 16);
comp[2] = (uint8_t)(uncompSize >> 8);
comp[3] = (uint8_t)uncompSize;
comp[4] = (uint8_t)(cksum >> 24);
comp[5] = (uint8_t)(cksum >> 16);
comp[6] = (uint8_t)(cksum >> 8);
comp[7] = (uint8_t)cksum;
compSize += 8; compSize += 8;
} }
@ -314,8 +313,8 @@ int TrustStore::load(const Vector< uint8_t > &data)
if (data.size() < 8) if (data.size() < 8)
return -1; return -1;
const unsigned long uncompSize = Utils::loadBigEndian< uint32_t >(data.data()); const unsigned int uncompSize = Utils::loadBigEndian< uint32_t >(data.data());
if ((uncompSize == 0) || (uncompSize > (data.size() * 128))) if ((uncompSize == 0) || (uncompSize > (unsigned int)(data.size() * 128)))
return -1; return -1;
Vector< uint8_t > uncomp; Vector< uint8_t > uncomp;
@ -336,20 +335,20 @@ int TrustStore::load(const Vector< uint8_t > &data)
for (;;) { for (;;) {
if ((b + 2) > eof) if ((b + 2) > eof)
break; break;
const uint32_t cdataSize = Utils::loadBigEndian< uint16_t >(b); const uint32_t certDataSize = Utils::loadBigEndian< uint16_t >(b);
b += 2; b += 2;
if (cdataSize == 0) if (certDataSize == 0)
break; break;
if ((b + cdataSize + 4) > eof) if ((b + certDataSize + 2) > eof) // certificate length + 2 bytes for trust flags
break; break;
Certificate c; Certificate c;
if (c.decode(b, (unsigned int)cdataSize)) { if (c.decode(b, (unsigned int)certDataSize)) {
b += cdataSize; b += certDataSize;
const uint32_t localTrust = Utils::loadBigEndian< uint32_t >(b); this->add(c, Utils::loadBigEndian< uint16_t >(b));
b += 4; b += 2;
this->add(c, (unsigned int)localTrust);
++readCount; ++readCount;
} }
} }

View file

@ -101,6 +101,7 @@ public:
Entry &operator=(const Entry &) { return *this; } Entry &operator=(const Entry &) { return *this; }
ZT_INLINE Entry(RWMutex &l, const Certificate &cert, const unsigned int lt) noexcept: ZT_INLINE Entry(RWMutex &l, const Certificate &cert, const unsigned int lt) noexcept:
__refCount(0),
m_lock(l), m_lock(l),
m_certificate(cert), m_certificate(cert),
m_localTrust(lt), m_localTrust(lt),
@ -109,11 +110,12 @@ public:
m_onTrustPath(false) m_onTrustPath(false)
{} {}
std::atomic< int > __refCount;
RWMutex &m_lock; RWMutex &m_lock;
const Certificate m_certificate; const Certificate m_certificate;
unsigned int m_localTrust; unsigned int m_localTrust;
ZT_CertificateError m_error; ZT_CertificateError m_error;
std::atomic< int > __refCount;
bool m_subjectDeprecated; bool m_subjectDeprecated;
bool m_onTrustPath; bool m_onTrustPath;
}; };

View file

@ -328,9 +328,81 @@ typedef struct
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA 0x0001U #define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA 0x0001U
/** /**
* Certificate's subject describes a set of roots (local trust flag) * Certificate can affect configuration of the node.
*
* An example is a ZeroTier root set certificate which adds root servers.
*/ */
#define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET 0x0002U #define ZT_CERTIFICATE_LOCAL_TRUST_FLAG_CONFIG 0x0002U
/*
* Certificate usage flags
*
* Not all of these are used in ZeroTier. Some are simply reserved because
* same named usage attributes exist in X509 and we want to be future proof
* against future desires to translate over X509 functionality.
*/
/**
* Certificate can sign things (general).
*/
#define ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE 0x00000001U
/**
* Certificate can verify signatures to verify actions.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_NON_REPUDIATION 0x00000002U
/**
* Certificate's key can encipher other keys.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_KEY_ENCIPHERMENT 0x00000004U
/**
* Certificate's key can encipher data.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_DATA_ENCIPHERMENT 0x00000008U
/**
* Certificate's key can be used for Diffie-Hellman style key agreemtn.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_KEY_AGREEMENT 0x00000010U
/**
* Certificate can sign other certificates.
*/
#define ZT_CERTIFICATE_USAGE_CERTIFICATE_SIGNING 0x00000020U
/**
* Certificate can revoke signatures.
*/
#define ZT_CERTIFICATE_USAGE_CRL_SIGNING 0x00000040U
/**
* Certificate can sign executable code.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE 0x00000080U
/**
* Certificate's public key can be used for a timestamp service.
*
* (not used in ZeroTier)
*/
#define ZT_CERTIFICATE_USAGE_TIMESTAMPING 0x00000100U
/**
* Certificate can enumerate a set of ZeroTier root nodes.
*/
#define ZT_CERTIFICATE_USAGE_ZEROTIER_ROOT_SET 0x00000200U
/** /**
* Errors returned by functions that verify or handle certificates. * Errors returned by functions that verify or handle certificates.
@ -572,9 +644,9 @@ typedef struct
uint8_t serialNo[ZT_CERTIFICATE_HASH_SIZE]; uint8_t serialNo[ZT_CERTIFICATE_HASH_SIZE];
/** /**
* Flags indicating certificate usage and any other attributes. * Certificate usage flags.
*/ */
uint64_t flags; uint64_t usageFlags;
/** /**
* Certificate timestamp in milliseconds since epoch. * Certificate timestamp in milliseconds since epoch.

View file

@ -11,6 +11,8 @@
*/ */
/****/ /****/
use std::hash::{Hash, Hasher};
#[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)] #[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
pub struct Address(pub u64); pub struct Address(pub u64);
@ -28,14 +30,10 @@ impl Address {
} }
} }
impl From<&[u8]> for Address { impl Hash for Address {
#[inline(always)] #[inline(always)]
fn from(bytes: &[u8]) -> Self { fn hash<H: Hasher>(&self, state: &mut H) {
if bytes.len() >= 5 { self.0.hash(state);
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)
}
} }
} }
@ -48,14 +46,25 @@ impl ToString for Address {
impl From<u64> for Address { impl From<u64> for Address {
#[inline(always)] #[inline(always)]
fn from(i: u64) -> Self { fn from(i: u64) -> Self {
Address(i) Address(i & 0xffffffffff)
} }
} }
impl From<&str> for Address { impl From<&str> for Address {
#[inline(always)] #[inline(always)]
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
Address(u64::from_str_radix(s, 16).unwrap_or(0)) 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)
}
} }
} }
@ -77,6 +86,7 @@ impl<'de> serde::de::Visitor<'de> for AddressVisitor {
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") } fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("ZeroTier Address") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(s)) } 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)) } fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: serde::de::Error { Ok(Address::from(v)) }
} }
impl<'de> serde::Deserialize<'de> for Address { impl<'de> serde::Deserialize<'de> for Address {

View file

@ -13,10 +13,10 @@
use std::ffi::CString; use std::ffi::CString;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem::zeroed; use std::mem::{zeroed, MaybeUninit};
use std::os::raw::{c_char, c_uint, c_void}; use std::os::raw::{c_char, c_uint, c_void};
use std::pin::Pin; use std::pin::Pin;
use std::ptr::{copy_nonoverlapping, null, null_mut}; use std::ptr::{copy_nonoverlapping, null, null_mut, read_unaligned, write_bytes};
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -32,13 +32,39 @@ pub const CERTIFICATE_MAX_STRING_LENGTH: isize = ztcore::ZT_CERTIFICATE_MAX_STRI
/// Certificate local trust bit field flag: this certificate self-signs a root CA. /// 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_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
/// Certificate local trust bit field flag: this certificate specifies a set of ZeroTier roots. pub const CERTIFICATE_USAGE_DIGITAL_SIGNATURE: u64 = ztcore::ZT_CERTIFICATE_USAGE_DIGITAL_SIGNATURE as u64;
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET; 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;
/// All certificate usage flags and their corresponding canonical abbreviations.
pub const ALL_CERTIFICATE_USAGE_FLAGS: [(u64, &'static str); 9] = [
(CERTIFICATE_USAGE_DIGITAL_SIGNATURE, "ds"),
(CERTIFICATE_USAGE_NON_REPUDIATION, "nr"),
(CERTIFICATE_USAGE_KEY_ENCIPHERMENT, "ke"),
(CERTIFICATE_USAGE_DATA_ENCIPHERMENT, "de"),
(CERTIFICATE_USAGE_KEY_AGREEMENT, "ka"),
(CERTIFICATE_USAGE_CERTIFICATE_SIGNING, "cs"),
(CERTIFICATE_USAGE_CRL_SIGNING, "crl"),
(CERTIFICATE_USAGE_EXECUTABLE_SIGNATURE, "es"),
(CERTIFICATE_USAGE_TIMESTAMPING, "ts"),
];
#[inline(always)]
fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] { fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] {
let mut a = [0_u8; L]; unsafe {
unsafe { copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr(), v.len().min(L)) }; let mut a: MaybeUninit<[u8; L]> = MaybeUninit::uninit();
a copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr().cast::<u8>(), v.len().min(L));
if v.len() < L {
write_bytes(a.as_mut_ptr().cast::<u8>(), 0, L - v.len());
}
a.assume_init()
}
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -62,25 +88,44 @@ impl From<i32> for CertificatePublicKeyAlgorithm {
pub struct CertificateSerialNo(pub [u8; 48]); pub struct CertificateSerialNo(pub [u8; 48]);
impl CertificateSerialNo { impl CertificateSerialNo {
#[inline(always)] pub const SIZE: usize = 48;
pub fn new() -> CertificateSerialNo { CertificateSerialNo([0; 48]) } pub const SIZE_HEX: usize = 96;
pub fn new_from_string(s: &str) -> Result<CertificateSerialNo, ResultCode> { hex::decode(s).map_or_else(|_| { Err(ResultCode::ErrorBadParameter) }, |b| { Ok(CertificateSerialNo::from(b)) }) }
}
impl<A: AsRef<[u8]>> From<A> for CertificateSerialNo { #[inline(always)]
fn from(a: A) -> CertificateSerialNo { pub fn new() -> CertificateSerialNo { CertificateSerialNo([0_u8; 48]) }
let mut sn = CertificateSerialNo::new();
let aa = a.as_ref(); /// Create a new certificate serial from a hex string, returning None if invalid.
for i in 0..aa.len() { /// The from() alternative for converting from a string returns an a nil (all zero) serial on error.
sn.0[i] = aa[i]; pub fn new_from_string(s: &str) -> Option<CertificateSerialNo> {
} hex::decode(s).map_or(None, |b| {
sn 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)
} }
} }
impl Hash for CertificateSerialNo { impl Hash for CertificateSerialNo {
#[inline(always)] #[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state); } 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 { impl ToString for CertificateSerialNo {
@ -95,7 +140,7 @@ struct CertificateSerialNoVisitor;
impl<'de> serde::de::Visitor<'de> for CertificateSerialNoVisitor { impl<'de> serde::de::Visitor<'de> for CertificateSerialNoVisitor {
type Value = CertificateSerialNo; type Value = CertificateSerialNo;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("object") } 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)) },|id| { Ok(id as Self::Value) }) } 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 { 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) } fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateSerialNoVisitor) }
@ -117,22 +162,27 @@ pub enum CertificateError {
Revoked = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_REVOKED 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 { impl ToString for CertificateError {
#[inline(always)]
fn to_string(&self) -> String { fn to_string(&self) -> String {
String::from( String::from(self.to_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",
}
)
} }
} }
@ -338,10 +388,10 @@ pub struct CertificateSubject {
#[serde(rename = "updateURLs")] #[serde(rename = "updateURLs")]
pub update_urls: Vec<String>, pub update_urls: Vec<String>,
pub name: CertificateName, pub name: CertificateName,
#[serde(with = "Base64Standard")] #[serde(with = "Base64URLSafeNoPad")]
#[serde(rename = "uniqueId")] #[serde(rename = "uniqueId")]
pub unique_id: Vec<u8>, pub unique_id: Vec<u8>,
#[serde(with = "Base64Standard")] #[serde(with = "Base64URLSafeNoPad")]
#[serde(rename = "uniqueIdSignature")] #[serde(rename = "uniqueIdSignature")]
pub unique_id_signature: Vec<u8>, pub unique_id_signature: Vec<u8>,
} }
@ -509,7 +559,8 @@ impl CertificateSubject {
pub struct Certificate { pub struct Certificate {
#[serde(rename = "serialNo")] #[serde(rename = "serialNo")]
pub serial_no: CertificateSerialNo, pub serial_no: CertificateSerialNo,
pub flags: u64, #[serde(rename = "usageFlags")]
pub usage_flags: u64,
pub timestamp: i64, pub timestamp: i64,
pub validity: [i64; 2], pub validity: [i64; 2],
pub subject: CertificateSubject, pub subject: CertificateSubject,
@ -520,7 +571,7 @@ pub struct Certificate {
pub public_key: Vec<u8>, pub public_key: Vec<u8>,
#[serde(rename = "extendedAttributes")] #[serde(rename = "extendedAttributes")]
pub extended_attributes: Vec<u8>, pub extended_attributes: Vec<u8>,
#[serde(with = "Base64Standard")] #[serde(with = "Base64URLSafeNoPad")]
pub signature: Vec<u8>, pub signature: Vec<u8>,
#[serde(rename = "maxPathLength")] #[serde(rename = "maxPathLength")]
pub max_path_length: u32, pub max_path_length: u32,
@ -557,7 +608,7 @@ impl Certificate {
pub fn new() -> Certificate { pub fn new() -> Certificate {
Certificate { Certificate {
serial_no: CertificateSerialNo::new(), serial_no: CertificateSerialNo::new(),
flags: 0, usage_flags: 0,
timestamp: 0, timestamp: 0,
validity: [0, i64::MAX], validity: [0, i64::MAX],
subject: CertificateSubject::new(), subject: CertificateSubject::new(),
@ -573,7 +624,7 @@ impl Certificate {
pub(crate) unsafe fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate { pub(crate) unsafe fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate {
return Certificate { return Certificate {
serial_no: CertificateSerialNo(c.serialNo), serial_no: CertificateSerialNo(c.serialNo),
flags: c.flags, usage_flags: c.usageFlags,
timestamp: c.timestamp, timestamp: c.timestamp,
validity: c.validity, validity: c.validity,
subject: CertificateSubject::new_from_capi(&c.subject), subject: CertificateSubject::new_from_capi(&c.subject),
@ -591,7 +642,7 @@ impl Certificate {
CertificateCAPIContainer { CertificateCAPIContainer {
certificate: ztcore::ZT_Certificate { certificate: ztcore::ZT_Certificate {
serialNo: self.serial_no.0, serialNo: self.serial_no.0,
flags: self.flags, usageFlags: self.usage_flags,
timestamp: self.timestamp, timestamp: self.timestamp,
validity: self.validity, validity: self.validity,
subject: subject.subject, subject: subject.subject,
@ -689,7 +740,7 @@ mod tests {
let mut cert = Certificate{ let mut cert = Certificate{
serial_no: CertificateSerialNo::new(), serial_no: CertificateSerialNo::new(),
flags: 1, usage_flags: 1,
timestamp: 2, timestamp: 2,
validity: [ 1,10 ], validity: [ 1,10 ],
subject: CertificateSubject::new(), subject: CertificateSubject::new(),

View file

@ -11,15 +11,17 @@
*/ */
/****/ /****/
use std::cmp::Ordering;
use std::ffi::CString; use std::ffi::CString;
use std::mem::MaybeUninit; use std::mem::{MaybeUninit, transmute};
use std::os::raw::*; use std::os::raw::*;
use std::hash::{Hash, Hasher};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
use crate::*; use crate::*;
use crate::capi as ztcore; use crate::capi as ztcore;
use std::cmp::Ordering;
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)] #[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
pub enum IdentityType { pub enum IdentityType {
@ -27,9 +29,15 @@ pub enum IdentityType {
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 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 { impl ToString for IdentityType {
fn to_string(&self) -> String { fn to_string(&self) -> String {
String::from(if *self == IdentityType::Curve25519 { "c25519" } else { "p384" }) String::from(self.to_str())
} }
} }
@ -43,17 +51,25 @@ pub struct Identity {
impl Identity { impl Identity {
pub(crate) fn new_from_capi(id: *const ztcore::ZT_Identity, requires_delete: bool) -> Identity { pub(crate) fn new_from_capi(id: *const ztcore::ZT_Identity, requires_delete: bool) -> Identity {
unsafe { unsafe {
let idt = ztcore::ZT_Identity_type(id); Identity {
let a = ztcore::ZT_Identity_address(id); type_: FromPrimitive::from_i32(ztcore::ZT_Identity_type(id) as i32).unwrap(),
return Identity { address: Address(ztcore::ZT_Identity_address(id)),
type_: FromPrimitive::from_i32(idt as i32).unwrap(),
address: Address(a),
capi: id, capi: id,
requires_delete, 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. /// Generate a new identity.
/// This is time consuming due to one time proof of work. It can take several seconds. /// 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> { pub fn new_generate(id_type: IdentityType) -> Result<Identity, ResultCode> {
@ -143,6 +159,13 @@ impl Identity {
} }
} }
impl Hash for Identity {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
(self.address.0 | (self.type_.to_u64().unwrap_or(0) << 40)).hash(state);
}
}
impl PartialEq for Identity { impl PartialEq for Identity {
#[inline(always)] #[inline(always)]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {

View file

@ -58,7 +58,7 @@ pub use virtualnetworkconfig::*;
pub use multicastgroup::MulticastGroup; pub use multicastgroup::MulticastGroup;
pub use dictionary::*; pub use dictionary::*;
base64_serde_type!(Base64Standard, base64::URL_SAFE_NO_PAD); base64_serde_type!(Base64URLSafeNoPad, base64::URL_SAFE_NO_PAD);
/// Recommended minimum thread stack size for background threads. /// Recommended minimum thread stack size for background threads.
pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 524288; pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 524288;
@ -84,6 +84,16 @@ pub const DEFAULT_UDP_MTU: u32 = ztcore::ZT_DEFAULT_UDP_MTU;
/// Maximum UDP MTU (we never actually get this high). /// Maximum UDP MTU (we never actually get this high).
pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU; pub const MAX_UDP_MTU: u32 = ztcore::ZT_MAX_UDP_MTU;
/// Base64 encode using the URL-safe with no padding configuration.
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 with no padding configuration.
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)] #[allow(non_snake_case,non_upper_case_globals)]
pub mod RulePacketCharacteristicFlags { pub mod RulePacketCharacteristicFlags {
pub const Inbound: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_INBOUND as u64; pub const Inbound: u64 = crate::capi::ZT_RULE_PACKET_CHARACTERISTICS_INBOUND as u64;

View file

@ -11,6 +11,8 @@
*/ */
/****/ /****/
use std::hash::{Hash, Hasher};
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] #[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct NetworkId(pub u64); pub struct NetworkId(pub u64);
@ -21,6 +23,13 @@ impl Default for NetworkId {
} }
} }
impl Hash for NetworkId {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl ToString for NetworkId { impl ToString for NetworkId {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!("{:0>16x}", self.0) format!("{:0>16x}", self.0)

View file

@ -12,19 +12,20 @@
/****/ /****/
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
use std::intrinsics::copy_nonoverlapping; use std::marker::PhantomData;
use std::mem::{MaybeUninit, transmute}; use std::mem::{MaybeUninit, transmute};
use std::os::raw::{c_int, c_uint, c_ulong, c_void}; use std::os::raw::{c_int, c_uint, c_ulong, c_void};
use std::pin::Pin; use std::pin::Pin;
use std::ptr::{null_mut, slice_from_raw_parts}; use std::ptr::{null_mut, slice_from_raw_parts, copy_nonoverlapping};
use std::sync::Mutex; use std::sync::Mutex;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::*; use crate::*;
use crate::capi as ztcore; use crate::capi as ztcore;
use std::marker::PhantomData;
pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: i64 = 200; pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: i64 = 200;
@ -51,7 +52,8 @@ pub enum StateObjectType {
} }
impl StateObjectType { impl StateObjectType {
/// True if this state object should be protected. /// 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)] #[inline(always)]
pub fn is_secret(&self) -> bool { pub fn is_secret(&self) -> bool {
*self == StateObjectType::IdentitySecret || *self == StateObjectType::TrustStore *self == StateObjectType::IdentitySecret || *self == StateObjectType::TrustStore
@ -71,8 +73,8 @@ pub struct NodeStatus {
} }
/// An event handler that receives events, frames, and packets from the core. /// An event handler that receives events, frames, and packets from the core.
/// Note that these handlers can be called concurrently from any thread and /// Note that if multiple threads are calling into Node these may be called by any of these threads
/// must be thread safe. /// at any time and must be thread safe.
pub trait NodeEventHandler<N: Sync + Send + 'static> { pub trait NodeEventHandler<N: Sync + Send + 'static> {
/// Called when a configuration change or update should be applied to a network. /// 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>); fn virtual_network_config(&self, network_id: NetworkId, network_obj: &N, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>);
@ -99,7 +101,7 @@ pub trait NodeEventHandler<N: Sync + Send + 'static> {
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>; fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress>;
} }
pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> { pub(crate) struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
event_handler: T, event_handler: T,
capi: *mut ztcore::ZT_Node, capi: *mut ztcore::ZT_Node,
networks_by_id: Mutex<HashMap<u64, Pin<Box<N>>>>, networks_by_id: Mutex<HashMap<u64, Pin<Box<N>>>>,
@ -120,6 +122,7 @@ pub struct NodeIntl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send
/// multithreaded or async. /// multithreaded or async.
pub struct Node<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> { pub struct Node<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: NodeEventHandler<N>> {
intl: Pin<Box<NodeIntl<T, N, H>>>, intl: Pin<Box<NodeIntl<T, N, H>>>,
identity_wrapper: Option<Identity>,
event_handler_placeholder: PhantomData<H>, event_handler_placeholder: PhantomData<H>,
} }
@ -323,6 +326,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
recent_clock: PortableAtomicI64::new(clock), recent_clock: PortableAtomicI64::new(clock),
recent_ticks: PortableAtomicI64::new(ticks), recent_ticks: PortableAtomicI64::new(ticks),
}), }),
identity_wrapper: None,
event_handler_placeholder: PhantomData::default(), event_handler_placeholder: PhantomData::default(),
}; };
@ -342,6 +346,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
if rc == 0 { if rc == 0 {
assert!(!n.intl.capi.is_null()); 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) Ok(n)
} else { } else {
Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal)) Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal))
@ -352,6 +357,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds /// 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 /// since the node was created, and after this runs it returns the amount of time the caller
/// should wait before calling it again. /// should wait before calling it again.
#[inline(always)]
pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 { pub fn process_background_tasks(&self, clock: i64, ticks: i64) -> i64 {
self.intl.recent_clock.set(clock); self.intl.recent_clock.set(clock);
self.intl.recent_ticks.set(ticks); self.intl.recent_ticks.set(ticks);
@ -392,7 +398,9 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
self.intl.networks_by_id.lock().unwrap().remove(&nwid.0).map_or_else(|| { self.intl.networks_by_id.lock().unwrap().remove(&nwid.0).map_or_else(|| {
ResultCode::ErrorNetworkNotFound 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) } 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)
}
}) })
} }
@ -410,11 +418,14 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
}) })
} }
/// Get the address of this node.
#[inline(always)] #[inline(always)]
pub fn address(&self) -> Address { pub fn address(&self) -> Address {
unsafe { Address(ztcore::ZT_Node_address(self.intl.capi) as u64) } 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)] #[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 { 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 intl = &*self.intl;
@ -423,6 +434,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
rc rc
} }
/// Handle a packet sent via a virtual network interface such as a tun/tap device.
/// This is virtual -> physical.
#[inline(always)] #[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 { 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 intl = &*self.intl;
@ -431,30 +444,27 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
rc rc
} }
/// Subscribe to a multicast group on a given network.
#[inline(always)] #[inline(always)]
pub fn multicast_subscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode { 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) } 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)] #[inline(always)]
pub fn multicast_unsubscribe(&self, clock: i64, ticks: i64, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode { 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) } 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 a copy of this node's identity. /// Get this node's identity.
#[inline(always)] #[inline(always)]
pub fn identity(&self) -> Identity { pub fn identity(&self) -> &Identity {
unsafe { self.identity_fast().clone() } let id = self.identity_wrapper.as_ref().unwrap();
} unsafe { id.sync_type_and_address_with_capi() };
id
/// Get an identity that simply holds a pointer to the underlying node's identity.
/// This is unsafe because the identity object becomes invalid if the node ceases
/// to exist the Identity becomes invalid. Use clone() on it to get a copy.
#[inline(always)]
pub(crate) unsafe fn identity_fast(&self) -> Identity {
Identity::new_from_capi(ztcore::ZT_Node_identity(self.intl.capi), false)
} }
/// Get status information for this node.
pub fn status(&self, clock: i64, ticks: i64) -> NodeStatus { pub fn status(&self, clock: i64, ticks: i64) -> NodeStatus {
let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed(); let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed();
unsafe { unsafe {
@ -473,6 +483,8 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
} }
} }
/// 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> { pub fn peers(&self, clock: i64, ticks: i64) -> Vec<Peer> {
let mut p: Vec<Peer> = Vec::new(); let mut p: Vec<Peer> = Vec::new();
unsafe { unsafe {
@ -489,6 +501,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
p p
} }
/// Get the networks this node has joined.
pub fn networks(&self) -> Vec<VirtualNetworkConfig> { pub fn networks(&self) -> Vec<VirtualNetworkConfig> {
let mut n: Vec<VirtualNetworkConfig> = Vec::new(); let mut n: Vec<VirtualNetworkConfig> = Vec::new();
unsafe { unsafe {
@ -505,6 +518,7 @@ impl<T: AsRef<H> + Sync + Send + Clone + 'static, N: Sync + Send + 'static, H: N
n 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)> { pub fn certificates(&self, clock: i64, ticks: i64) -> Vec<(Certificate, u32)> {
let mut c: Vec<(Certificate, u32)> = Vec::new(); let mut c: Vec<(Certificate, u32)> = Vec::new();
unsafe { unsafe {

View file

@ -12,6 +12,7 @@
/****/ /****/
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::Endpoint; use crate::Endpoint;
use crate::capi as ztcore; use crate::capi as ztcore;

View file

@ -19,13 +19,163 @@ use dialoguer::Input;
use zerotier_core::*; use zerotier_core::*;
use crate::store::Store; use crate::store::Store;
use crate::utils::{read_limit, ms_since_epoch, to_json_pretty};
use crate::GlobalFlags;
/// Dump a certificate in human-readable format to stdout.
fn dump_cert(certificate: &Certificate) {
let mut subject_identities = String::new();
let mut subject_networks = String::new();
let mut subject_update_urls = String::new();
let mut usage_flags = String::new();
fn string_or_dash(s: &str) -> &str {
if s.is_empty() {
"-"
} else {
s
}
}
if certificate.subject.identities.is_empty() {
subject_identities.push_str(": (none)");
} else {
for x in certificate.subject.identities.iter() {
subject_identities.push_str("\n ");
subject_identities.push_str(x.identity.to_string().as_str());
if x.locator.is_some() {
subject_identities.push_str(" ");
subject_identities.push_str(x.locator.as_ref().unwrap().to_string().as_str());
}
}
}
if certificate.subject.networks.is_empty() {
subject_networks.push_str(": (none)");
} else {
for x in certificate.subject.networks.iter() {
subject_networks.push_str("\n ");
subject_networks.push_str(x.id.to_string().as_str());
if x.controller.is_some() {
subject_networks.push_str(" at ");
subject_networks.push_str(x.controller.as_ref().unwrap().to_string().as_str());
}
}
}
if certificate.subject.update_urls.is_empty() {
subject_update_urls.push_str(": (none)");
} else {
for x in certificate.subject.update_urls.iter() {
subject_update_urls.push_str("\n ");
subject_update_urls.push_str(x.as_ref());
}
}
if certificate.usage_flags != 0 {
usage_flags.push_str(" (");
for f in ALL_CERTIFICATE_USAGE_FLAGS.iter() {
if (certificate.usage_flags & (*f).0) != 0 {
if !usage_flags.is_empty() {
usage_flags.push(',');
}
usage_flags.push_str((*f).1);
}
}
usage_flags.push(')');
}
println!(r###"Serial Number (SHA384): {}
Usage Flags: 0x{:0>8x}{}
Timestamp: {}
Validity: {} to {}
Subject
Timestamp: {}
Identities{}
Networks{}
Update URLs{}
Name
Serial: {}
Common Name: {}
Country: {}
Organization: {}
Unit: {}
Locality: {}
State/Province: {}
Street Address: {}
Postal Code: {}
E-Mail: {}
URL: {}
Host: {}
Unique ID: {}
Unique ID Signature: {}
Issuer: {}
Issuer Public Key: {}
Public Key: {}
Extended Attributes: {} bytes
Signature: {}
Maximum Path Length: {}{}"###,
certificate.serial_no.to_string(),
certificate.usage_flags, usage_flags,
certificate.timestamp,
certificate.validity[0], certificate.validity[1],
certificate.subject.timestamp,
subject_identities,
subject_networks,
subject_update_urls,
string_or_dash(certificate.subject.name.serial_no.as_str()),
string_or_dash(certificate.subject.name.common_name.as_str()),
string_or_dash(certificate.subject.name.country.as_str()),
string_or_dash(certificate.subject.name.organization.as_str()),
string_or_dash(certificate.subject.name.unit.as_str()),
string_or_dash(certificate.subject.name.locality.as_str()),
string_or_dash(certificate.subject.name.province.as_str()),
string_or_dash(certificate.subject.name.street_address.as_str()),
string_or_dash(certificate.subject.name.postal_code.as_str()),
string_or_dash(certificate.subject.name.email.as_str()),
string_or_dash(certificate.subject.name.url.as_str()),
string_or_dash(certificate.subject.name.host.as_str()),
string_or_dash(base64_encode(&certificate.subject.unique_id).as_str()),
string_or_dash(base64_encode(&certificate.subject.unique_id_signature).as_str()),
certificate.issuer.to_string(),
string_or_dash(base64_encode(&certificate.issuer_public_key).as_str()),
string_or_dash(base64_encode(&certificate.public_key).as_str()),
certificate.extended_attributes.len(),
string_or_dash(base64_encode(&certificate.signature).as_str()),
certificate.max_path_length, if certificate.max_path_length == 0 { " (leaf)" } else { " (CA or sub-CA)" });
}
fn list(store: &Arc<Store>) -> i32 { fn list(store: &Arc<Store>) -> i32 {
0 0
} }
fn show<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 { fn show<'a>(store: &Arc<Store>, global_flags: &GlobalFlags, cli_args: &ArgMatches<'a>) -> i32 {
0 let serial_or_path = cli_args.value_of("serialorpath").unwrap().trim();
CertificateSerialNo::new_from_string(serial_or_path).map_or_else(|| {
read_limit(serial_or_path, 65536).map_or_else(|e| {
println!("ERROR: unable to read certificate from '{}': {}", serial_or_path, e.to_string());
1
}, |cert_json| {
serde_json::from_slice::<Certificate>(cert_json.as_ref()).map_or_else(|e| {
println!("ERROR: unable to decode certificate from '{}': {}", serial_or_path, e.to_string());
1
}, |certificate| {
if global_flags.json_output {
println!("{}", to_json_pretty(&certificate));
} else {
dump_cert(&certificate);
}
let cv = certificate.verify(ms_since_epoch());
if cv != CertificateError::None {
println!("\nWARNING: certificate validity check failed: {}", cv.to_str());
}
0
})
})
}, |serial| {
// TODO: query node
0
})
} }
fn newsuid(cli_args: Option<&ArgMatches>) -> i32 { fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
@ -35,13 +185,13 @@ fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
1 1
} else { } else {
let (_, privk) = key_pair.ok().unwrap(); let (_, privk) = key_pair.ok().unwrap();
let privk_hex = hex::encode(privk); let privk_base64 = base64_encode(&privk);
let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") }); let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") });
if path.is_empty() { if path.is_empty() {
println!("{}", privk_hex); println!("{}", privk_base64);
0 0
} else { } else {
std::fs::write(path, privk_hex.as_bytes()).map_or_else(|e| { std::fs::write(path, privk_base64.as_bytes()).map_or_else(|e| {
eprintln!("FATAL: error writing '{}': {}", path, e.to_string()); eprintln!("FATAL: error writing '{}': {}", path, e.to_string());
e.raw_os_error().unwrap_or(1) e.raw_os_error().unwrap_or(1)
}, |_| { }, |_| {
@ -251,10 +401,6 @@ fn verify<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0 0
} }
fn dump<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
fn import<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 { fn import<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0 0
} }
@ -271,19 +417,18 @@ fn delete<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0 0
} }
pub(crate) fn run(store: Arc<Store>, cli_args: &ArgMatches) -> i32 { pub(crate) fn run(store: Arc<Store>, global_flags: GlobalFlags, cli_args: &ArgMatches) -> i32 {
match cli_args.subcommand() { match cli_args.subcommand() {
("list", None) => list(&store), ("list", None) => list(&store),
("show", Some(sub_cli_args)) => show(&store, sub_cli_args), ("show", Some(sub_cli_args)) => show(&store, &global_flags, sub_cli_args),
("newsuid", sub_cli_args) => newsuid(sub_cli_args), ("newsuid", sub_cli_args) => newsuid(sub_cli_args),
("newcsr", Some(sub_cli_args)) => newcsr(sub_cli_args), ("newcsr", Some(sub_cli_args)) => newcsr(sub_cli_args),
("sign", Some(sub_cli_args)) => sign(&store, sub_cli_args), ("sign", Some(sub_cli_args)) => sign(&store, sub_cli_args),
("verify", Some(sub_cli_args)) => verify(&store, sub_cli_args), ("verify", Some(sub_cli_args)) => verify(&store, sub_cli_args),
("dump", Some(sub_cli_args)) => dump(&store, sub_cli_args),
("import", Some(sub_cli_args)) => import(&store, sub_cli_args), ("import", Some(sub_cli_args)) => import(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store),
("export", Some(sub_cli_args)) => export(&store, sub_cli_args), ("export", Some(sub_cli_args)) => export(&store, sub_cli_args),
("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args), ("delete", Some(sub_cli_args)) => delete(&store, sub_cli_args),
("factoryreset", None) => factoryreset(&store),
_ => { _ => {
crate::print_help(); crate::print_help();
1 1

View file

@ -96,7 +96,7 @@ mod tests {
fn test_getifaddrs() { fn test_getifaddrs() {
println!("starting getifaddrs..."); println!("starting getifaddrs...");
crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| { crate::getifaddrs::for_each_address(|a: &InetAddress, dev: &str| {
println!(" device: {} ip: {}", dev, a.to_string()) println!(" {} {}", dev, a.to_string())
}); });
println!("done.") println!("done.")
} }

View file

@ -85,12 +85,12 @@ Common Operations:
· defaultroute <boolean> Can default route be overridden? · defaultroute <boolean> Can default route be overridden?
· join [-...] <network> Join a virtual network · join [-...] <network> Join a virtual network
-c <identity | fingerprint> Controller identity / fingerprint -c <?identity | fingerprint> Controller identity / fingerprint
· leave <network> Leave a virtual network · leave <network> Leave a virtual network
Advanced Operations: Advanced Operations:
service Start this node service Start node
(usually not invoked directly) (usually not invoked directly)
controller <command> [option] controller <command> [option]
@ -103,39 +103,35 @@ Advanced Operations:
identity <command> [args] identity <command> [args]
new [c25519 | p384] Create identity (default: c25519) new [c25519 | p384] Create identity (default: c25519)
getpublic <identity> Extract public part of identity getpublic <?identity> Extract public part of identity
fingerprint <identity> Get an identity's fingerprint fingerprint <?identity> Get an identity's fingerprint
validate <identity> Locally validate an identity validate <?identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key sign <?identity> <@file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature verify <?identity> <@file> <sig> Verify a signature
locator <command> [args] locator <command> [args]
new [-...] <identity> <endpoint> [...] Create new signed locator new [-...] <?identity> <endpoint,...> Create new signed locator
-r <revision> Revision number (default: time) -r <revision> Revision number (default: time)
verify <identity> <locator> Verify locator signature verify <?identity> <?locator> Verify locator signature
show <locator> Show contents of a locator show <?locator> Show contents of a locator
cert <command> [args] cert <command> [args]
· list List certificates at local node · list List certificates at local node
· show <serial> Show certificate details show <@cert|·serial> Show certificate details
newsuid [suid secret out] Create a subject unique ID secret newsuid [@secret] Create a subject unique ID secret
newcsr <csr out> <secret out> Create a CSR (interactive) newcsr <@csr> <@secret> Create a CSR (interactive)
sign <csr> <identity> [cert out] Sign a CSR to create a certificate sign <@csr> <@secret> <@cert> Sign a CSR to create a certificate
verify <cert> Verify certificate (not chain) verify <@cert> Internally verify certificate
dump <cert> Verify and print certificate · import <@cert> [trust,trust,...] Import certificate into this node
· import <cert> [trust,trust,...] Import certificate into this node trust flag: rootca Root (or self-signed) CA
trust flag: rootca Certificate is a root CA trust flag: config Can influence node configuration
trust flag: ztrootset ZeroTier root node set · export <serial> [@cert] Export a certificate from this node
· factoryreset Re-import compiled-in default certs
· export <serial> [path] Export a certificate from this node
· delete <serial|ALL> Delete certificate from this node · delete <serial|ALL> Delete certificate from this node
· factoryreset Re-import compiled-in default certs
· Command requires a running node and access to a local API token. · Command (or command with argument type) requires a running node.
@ Argument is a path to an object, not the object itself.
An <address> may be specified as a 10-digit short ZeroTier address, a ? Argument can be either an inline value or a path to it.
fingerprint containing both an address and a SHA384 hash, or an identity.
Identities and locators can be specified as either paths to files on the
filesystem or verbatim objects in string format. This is auto-detected.
"###, ver.0, ver.1, ver.2) "###, ver.0, ver.1, ver.2)
} }
@ -178,10 +174,11 @@ fn make_store(cli_args: &ArgMatches) -> Arc<Store> {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct GlobalFlags { pub(crate) struct GlobalFlags {
pub json_output: bool, pub json_output: bool,
} }
#[inline(always)]
fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags { fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags {
GlobalFlags { GlobalFlags {
json_output: cli_args.is_present("json") json_output: cli_args.is_present("json")
@ -276,7 +273,7 @@ fn main() {
.subcommand(App::new("cert") .subcommand(App::new("cert")
.subcommand(App::new("list")) .subcommand(App::new("list"))
.subcommand(App::new("show") .subcommand(App::new("show")
.arg(Arg::with_name("serial").index(1).required(true))) .arg(Arg::with_name("serialorpath").index(1).required(true)))
.subcommand(App::new("newsuid") .subcommand(App::new("newsuid")
.arg(Arg::with_name("path").index(1).required(false))) .arg(Arg::with_name("path").index(1).required(false)))
.subcommand(App::new("newcsr") .subcommand(App::new("newcsr")
@ -284,7 +281,7 @@ fn main() {
.arg(Arg::with_name("secretpath").index(2).required(true))) .arg(Arg::with_name("secretpath").index(2).required(true)))
.subcommand(App::new("sign") .subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true)) .arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("identity").index(2).required(true)) .arg(Arg::with_name("secretpath").index(2).required(true))
.arg(Arg::with_name("output").index(3).required(false))) .arg(Arg::with_name("output").index(3).required(false)))
.subcommand(App::new("verify") .subcommand(App::new("verify")
.arg(Arg::with_name("cert").index(1).required(true))) .arg(Arg::with_name("cert").index(1).required(true)))
@ -340,7 +337,7 @@ fn main() {
("controller", Some(sub_cli_args)) => { 0 } ("controller", Some(sub_cli_args)) => { 0 }
("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args), ("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args),
("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args), ("locator", Some(sub_cli_args)) => crate::commands::locator::run(sub_cli_args),
("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), sub_cli_args), ("cert", Some(sub_cli_args)) => crate::commands::cert::run(make_store(&cli_args), get_global_flags(&cli_args), sub_cli_args),
_ => { _ => {
print_help(); print_help();
1 1

View file

@ -219,7 +219,7 @@ impl Service {
uptime: ms_monotonic() - self.startup_time_monotonic, uptime: ms_monotonic() - self.startup_time_monotonic,
config: (*self.local_config()).clone(), config: (*self.local_config()).clone(),
online: self.online(), online: self.online(),
public_identity: node.identity(), public_identity: node.identity().clone(),
version: format!("{}.{}.{}", ver.0, ver.1, ver.2), version: format!("{}.{}.{}", ver.0, ver.1, ver.2),
version_major: ver.0, version_major: ver.0,
version_minor: ver.1, version_minor: ver.1,
@ -237,7 +237,7 @@ unsafe impl Send for Service {}
unsafe impl Sync for Service {} unsafe impl Sync for Service {}
async fn run_async(store: Arc<Store>, local_config: Arc<LocalConfig>) -> i32 { async fn run_async(store: Arc<Store>, local_config: Arc<LocalConfig>) -> i32 {
let mut process_exit_value: i32 = 0; let process_exit_value: i32 = 0;
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new(); let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
let mut http_listeners: BTreeMap<InetAddress, HttpListener> = BTreeMap::new(); let mut http_listeners: BTreeMap<InetAddress, HttpListener> = BTreeMap::new();

View file

@ -123,6 +123,16 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 {
} }
} }
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub(crate) fn to_json<O: serde::Serialize>(o: &O) -> String {
serde_json::to_string(o).unwrap_or("null".into())
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub(crate) fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
serde_json::to_string_pretty(o).unwrap_or("null".into())
}
/// Recursively patch a JSON object. /// Recursively patch a JSON object.
/// This is slightly different from a usual JSON merge. For objects in the target their fields /// This is slightly different from a usual JSON merge. For objects in the target their fields
/// are updated by recursively calling json_patch if the same field is present in the source. /// are updated by recursively calling json_patch if the same field is present in the source.

View file

@ -32,7 +32,7 @@ pub(crate) fn get_l2_multicast_subscriptions(dev: &str) -> BTreeSet<MulticastGro
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family as i32 == osdep::AF_LINK as i32 { if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family as i32 == osdep::AF_LINK as i32 {
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast()); let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast()); let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 { if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len().as_() && crate::osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen.as_()) == 0 {
let mi = la.sdl_nlen as usize; let mi = la.sdl_nlen as usize;
groups.insert(MulticastGroup{ groups.insert(MulticastGroup{
mac: MAC((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi+1] as u64) << 32 | (la.sdl_data[mi+2] as u64) << 24 | (la.sdl_data[mi+3] as u64) << 16 | (la.sdl_data[mi+4] as u64) << 8 | la.sdl_data[mi+5] as u64), mac: MAC((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi+1] as u64) << 32 | (la.sdl_data[mi+2] as u64) << 24 | (la.sdl_data[mi+3] as u64) << 16 | (la.sdl_data[mi+4] as u64) << 8 | la.sdl_data[mi+5] as u64),