mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 20:13:44 +02:00
Can make a CSR now.
This commit is contained in:
parent
ce77372e01
commit
8aee8cb78b
6 changed files with 307 additions and 39 deletions
|
@ -320,28 +320,45 @@ implement_to_from_json!(CertificateName);
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct CertificateNetwork {
|
pub struct CertificateNetwork {
|
||||||
pub id: NetworkId,
|
pub id: NetworkId,
|
||||||
pub controller: Fingerprint,
|
pub controller: Option<Fingerprint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CertificateNetwork {
|
impl CertificateNetwork {
|
||||||
pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Network) -> CertificateNetwork {
|
pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Network) -> CertificateNetwork {
|
||||||
CertificateNetwork {
|
if is_all_zeroes(cn.controller.hash) {
|
||||||
id: NetworkId(cn.id),
|
CertificateNetwork {
|
||||||
controller: Fingerprint {
|
id: NetworkId(cn.id),
|
||||||
address: Address(cn.controller.address),
|
controller: None,
|
||||||
hash: cn.controller.hash,
|
}
|
||||||
},
|
} 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 {
|
pub(crate) fn to_capi(&self) -> ztcore::ZT_Certificate_Network {
|
||||||
ztcore::ZT_Certificate_Network {
|
self.controller.as_ref().map_or_else(|| {
|
||||||
id: self.id.0,
|
ztcore::ZT_Certificate_Network {
|
||||||
controller: ztcore::ZT_Fingerprint {
|
id: self.id.0,
|
||||||
address: self.controller.address.0,
|
controller: ztcore::ZT_Fingerprint {
|
||||||
hash: self.controller.hash,
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,10 +550,10 @@ impl CertificateSubject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_csr(&self, uid: Option<&CertificateSubjectUniqueIdSecret>) -> Result<Box<[u8]>, ResultCode> {
|
pub fn new_csr(&self, uid: Option<&CertificateSubjectUniqueIdSecret>) -> Result<Vec<u8>, ResultCode> {
|
||||||
let mut csr: Vec<u8> = Vec::new();
|
let mut csr: Vec<u8> = Vec::new();
|
||||||
csr.resize(16384, 0);
|
csr.resize(65536, 0);
|
||||||
let mut csr_size: c_int = 16384;
|
let mut csr_size: c_int = 65536;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let capi = self.to_capi();
|
let capi = self.to_capi();
|
||||||
|
@ -551,8 +568,9 @@ impl CertificateSubject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
csr.resize(csr_size as usize, 0);
|
||||||
|
|
||||||
return Ok(csr.into_boxed_slice());
|
return Ok(csr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,17 @@ pub fn random() -> u64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test whether this byte array or slice is all zeroes.
|
||||||
|
pub fn is_all_zeroes<B: AsRef<[u8]>>(b: B) -> bool {
|
||||||
|
let bb = b.as_ref();
|
||||||
|
for c in bb.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.
|
/// 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
|
/// 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 string is not zero-terminated. A size limit can be passed in if available to reduce this risk, or
|
||||||
|
|
|
@ -93,7 +93,7 @@ Advanced Operations:
|
||||||
· list List certificates at local node
|
· list List certificates at local node
|
||||||
· show <serial> Show certificate details
|
· show <serial> Show certificate details
|
||||||
newsid [sid secret out] Create a new subject unique ID
|
newsid [sid secret out] Create a new subject unique ID
|
||||||
newcsr [csr out] Create a subject CSR
|
newcsr <csr output path> Create a subject CSR (interactive)
|
||||||
sign <csr> <identity> [cert out] Sign a CSR to create a certificate
|
sign <csr> <identity> [cert out] Sign a CSR to create a certificate
|
||||||
verify <cert> Verify certificate (not chain)
|
verify <cert> Verify certificate (not chain)
|
||||||
dump <cert> Verify and print certificate
|
dump <cert> Verify and print certificate
|
||||||
|
@ -233,7 +233,7 @@ pub(crate) fn parse_cli_args() -> ArgMatches<'static> {
|
||||||
.subcommand(App::new("newsid")
|
.subcommand(App::new("newsid")
|
||||||
.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")
|
||||||
.arg(Arg::with_name("path").index(2).required(false)))
|
.arg(Arg::with_name("path").index(1).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("identity").index(2).required(true))
|
||||||
|
|
|
@ -11,22 +11,27 @@
|
||||||
*/
|
*/
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
use clap::ArgMatches;
|
|
||||||
use crate::store::Store;
|
|
||||||
use zerotier_core::{CertificateSubjectUniqueIdSecret, CertificateUniqueIdType};
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use dialoguer::Input;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use zerotier_core::*;
|
||||||
|
|
||||||
|
use crate::store::Store;
|
||||||
|
use crate::utils::read_identity;
|
||||||
|
use futures::SinkExt;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn list(store: &Store, auth_token: &Option<String>) -> i32 {
|
fn list(store: &Store, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn show<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn newsid<'a>(store: &Store, cli_args: Option<&ArgMatches<'a>>, auth_token: &Option<String>) -> i32 {
|
fn newsid<'a>(store: &Store, cli_args: Option<&ArgMatches<'a>>, auth_token: &Option<String>) -> i32 {
|
||||||
let sid = CertificateSubjectUniqueIdSecret::new(CertificateUniqueIdType::NistP384); // right now there's only one type
|
let sid = CertificateSubjectUniqueIdSecret::new(CertificateUniqueIdType::NistP384); // right now there's only one type
|
||||||
let sid = sid.to_json();
|
let sid = sid.to_json();
|
||||||
|
@ -44,42 +49,225 @@ fn newsid<'a>(store: &Store, cli_args: Option<&ArgMatches<'a>>, auth_token: &Opt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn newcsr<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
fn newcsr<'a>(store: &Store, cli_args: Option<&ArgMatches<'a>>, auth_token: &Option<String>) -> i32 {
|
let theme = &dialoguer::theme::SimpleTheme;
|
||||||
0
|
|
||||||
|
let subject_unique_id: String = Input::with_theme(theme)
|
||||||
|
.with_prompt("Path to subject unique ID secret key (recommended)")
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let subject_unique_id: Option<CertificateSubjectUniqueIdSecret> = if subject_unique_id.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let b = crate::utils::read_limit(subject_unique_id, 16384);
|
||||||
|
if b.is_err() {
|
||||||
|
println!("ERROR: unable to read subject unique ID secret file: {}", b.err().unwrap().to_string());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let json = String::from_utf8(b.unwrap());
|
||||||
|
if json.is_err() {
|
||||||
|
println!("ERROR: invalid subject unique ID secret: {}", json.err().unwrap().to_string());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let sid = CertificateSubjectUniqueIdSecret::new_from_json(json.unwrap().as_str());
|
||||||
|
if sid.is_err() {
|
||||||
|
println!("ERROR: invalid subject unique ID secret: {}", sid.err().unwrap());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Some(sid.unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let timestamp: i64 = Input::with_theme(theme)
|
||||||
|
.with_prompt("Subject timestamp (seconds since epoch)")
|
||||||
|
.with_initial_text((crate::utils::ms_since_epoch() / 1000).to_string())
|
||||||
|
.allow_empty(false)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or(0);
|
||||||
|
if timestamp < 0 {
|
||||||
|
println!("ERROR: invalid timestamp");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Identities to include in subject");
|
||||||
|
let mut identities: Vec<CertificateIdentity> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let identity: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] Identity or path to identity (empty to end)", identities.len() + 1))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
if identity.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let identity = read_identity(identity.as_str(), true);
|
||||||
|
if identity.is_err() {
|
||||||
|
println!("ERROR: identity invalid or unable to read from file.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let identity = identity.unwrap();
|
||||||
|
if identity.has_private() {
|
||||||
|
println!("ERROR: identity contains private key, use public only for CSR!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let locator: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] Locator for {} (optional)", identities.len() + 1, identity.address.to_string()))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let locator = if locator.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let l = Locator::new_from_string(locator.as_str());
|
||||||
|
if l.is_err() {
|
||||||
|
println!("ERROR: locator invalid: {}", l.err().unwrap().to_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
l.ok()
|
||||||
|
};
|
||||||
|
|
||||||
|
identities.push(CertificateIdentity {
|
||||||
|
identity,
|
||||||
|
locator,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Networks to include in subject (empty to end)");
|
||||||
|
let mut networks: Vec<CertificateNetwork> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let nwid: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] Network ID (empty to end)", networks.len() + 1))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
if nwid.len() != 16 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let nwid = NetworkId::new_from_string(nwid.as_str());
|
||||||
|
|
||||||
|
let fingerprint: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] Fingerprint of primary controller (optional)", networks.len() + 1))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let fingerprint = if fingerprint.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let f = Fingerprint::new_from_string(fingerprint.as_str());
|
||||||
|
if f.is_err() {
|
||||||
|
println!("ERROR: fingerprint invalid: {}", f.err().unwrap().to_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
f.ok()
|
||||||
|
};
|
||||||
|
|
||||||
|
networks.push(CertificateNetwork {
|
||||||
|
id: nwid,
|
||||||
|
controller: fingerprint,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Certificates to reference in subject (empty to end)");
|
||||||
|
let mut certificates: Vec<CertificateSerialNo> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let sn: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] Certificate serial number (empty to end)", certificates.len() + 1))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
if sn.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let sn = CertificateSerialNo::new_from_string(sn.as_str());
|
||||||
|
if sn.is_err() {
|
||||||
|
println!("ERROR: invalid certificate serial number: {}", sn.err().unwrap().to_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
certificates.push(sn.ok().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("URLs to check for updated certificates for this subject");
|
||||||
|
let mut update_urls: Vec<String> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let url: String = Input::with_theme(theme)
|
||||||
|
.with_prompt(format!(" [{}] URL (empty to end)", update_urls.len() + 1))
|
||||||
|
.allow_empty(true)
|
||||||
|
.interact_text()
|
||||||
|
.unwrap_or_default();
|
||||||
|
if url.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let url_parsed = hyper::Uri::from_str(url.as_str());
|
||||||
|
if url_parsed.is_err() {
|
||||||
|
println!("ERROR: invalid URL: {}", url_parsed.err().unwrap().to_string());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
update_urls.push(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Certificate \"name\" (same as X509 certificates, all fields optional)");
|
||||||
|
let name = CertificateName {
|
||||||
|
serial_no: Input::with_theme(theme).with_prompt(" Serial").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
common_name: Input::with_theme(theme).with_prompt(" Common Name").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
organization: Input::with_theme(theme).with_prompt(" Organization").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
unit: Input::with_theme(theme).with_prompt(" Organizational Unit").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
country: Input::with_theme(theme).with_prompt(" Country").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
province: Input::with_theme(theme).with_prompt(" State/Province").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
locality: Input::with_theme(theme).with_prompt(" Locality").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
street_address: Input::with_theme(theme).with_prompt(" Street Address").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
postal_code: Input::with_theme(theme).with_prompt(" Postal Code").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
email: Input::with_theme(theme).with_prompt(" E-Mail").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
url: Input::with_theme(theme).with_prompt(" URL (informational)").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
host: Input::with_theme(theme).with_prompt(" Host").allow_empty(true).interact_text().unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let subject = CertificateSubject {
|
||||||
|
timestamp,
|
||||||
|
identities,
|
||||||
|
networks,
|
||||||
|
certificates,
|
||||||
|
update_urls,
|
||||||
|
name,
|
||||||
|
unique_id: Vec::new(),
|
||||||
|
unique_id_proof_signature: Vec::new(),
|
||||||
|
};
|
||||||
|
subject.new_csr(subject_unique_id.as_ref()).map_or(1, |csr| {
|
||||||
|
let p = cli_args.value_of("path").unwrap();
|
||||||
|
std::fs::write(p, csr).map_or_else(|e| {
|
||||||
|
println!("ERROR: unable to write CSR: {}", e.to_string());
|
||||||
|
1
|
||||||
|
}, |_| {
|
||||||
|
println!("CSR written to {}", p);
|
||||||
|
0
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn sign<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn sign<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn verify<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn dump<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn dump<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn import<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn import<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn restore(store: &Store, auth_token: &Option<String>) -> i32 {
|
fn restore(store: &Store, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn export<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn export<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn delete<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
fn delete<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Option<String>) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
@ -89,7 +277,7 @@ pub(crate) fn run<'a>(store: &Store, cli_args: &ArgMatches<'a>, auth_token: &Opt
|
||||||
("list", None) => list(store, auth_token),
|
("list", None) => list(store, auth_token),
|
||||||
("show", Some(sub_cli_args)) => show(store, sub_cli_args, auth_token),
|
("show", Some(sub_cli_args)) => show(store, sub_cli_args, auth_token),
|
||||||
("newsid", sub_cli_args) => newsid(store, sub_cli_args, auth_token),
|
("newsid", sub_cli_args) => newsid(store, sub_cli_args, auth_token),
|
||||||
("newcsr", sub_cli_args) => newcsr(store, sub_cli_args, auth_token),
|
("newcsr", Some(sub_cli_args)) => newcsr(store, sub_cli_args, auth_token),
|
||||||
("sign", Some(sub_cli_args)) => sign(store, sub_cli_args, auth_token),
|
("sign", Some(sub_cli_args)) => sign(store, sub_cli_args, auth_token),
|
||||||
("verify", Some(sub_cli_args)) => verify(store, sub_cli_args, auth_token),
|
("verify", Some(sub_cli_args)) => verify(store, sub_cli_args, auth_token),
|
||||||
("dump", Some(sub_cli_args)) => dump(store, sub_cli_args, auth_token),
|
("dump", Some(sub_cli_args)) => dump(store, sub_cli_args, auth_token),
|
||||||
|
|
|
@ -11,8 +11,14 @@
|
||||||
*/
|
*/
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::os::raw::c_uint;
|
use std::os::raw::c_uint;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use zerotier_core::Identity;
|
||||||
|
|
||||||
use crate::osdep;
|
use crate::osdep;
|
||||||
|
|
||||||
|
@ -41,3 +47,48 @@ pub(crate) fn ms_since_epoch() -> i64 {
|
||||||
// This is easy to do in the Rust stdlib, but the version in OSUtils is probably faster.
|
// This is easy to do in the Rust stdlib, but the version in OSUtils is probably faster.
|
||||||
unsafe { osdep::msSinceEpoch() }
|
unsafe { osdep::msSinceEpoch() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function to read up to limit bytes from a file.
|
||||||
|
/// If the file is larger than limit, the excess is not read.
|
||||||
|
pub(crate) fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||||
|
let mut v: Vec<u8> = Vec::new();
|
||||||
|
let _ = File::open(path)?.take(limit as u64).read_to_end(&mut v)?;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read an identity as either a literal or from a file.
|
||||||
|
/// This is used in parsing command lines, allowing either a literal or a path
|
||||||
|
/// to be specified and automagically disambiguating.
|
||||||
|
pub(crate) fn read_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||||
|
let id = Identity::new_from_string(input);
|
||||||
|
if id.is_err() {
|
||||||
|
let input = Path::new(input);
|
||||||
|
if !input.exists() || !input.is_file() {
|
||||||
|
return Err(format!("invalid identity: {}", id.err().unwrap().to_str()));
|
||||||
|
}
|
||||||
|
read_limit(input, 16384).map_or_else(|e| {
|
||||||
|
Err(e.to_string())
|
||||||
|
}, |v| {
|
||||||
|
String::from_utf8(v).map_or_else(|e| {
|
||||||
|
Err(e.to_string())
|
||||||
|
}, |s| {
|
||||||
|
Identity::new_from_string(s.as_str()).map_or_else(|_| {
|
||||||
|
Err(format!("Invalid identity in file {}", input.to_str().unwrap_or("")))
|
||||||
|
}, |id| {
|
||||||
|
if validate && !id.validate() {
|
||||||
|
Err(String::from("invalid identity: local validation failed"))
|
||||||
|
} else {
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let id = id.ok().unwrap();
|
||||||
|
if validate && !id.validate() {
|
||||||
|
Err(String::from("invalid identity: local validation failed"))
|
||||||
|
} else {
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -57,8 +57,8 @@ use crate::vnic::VNIC;
|
||||||
use crate::osdep::getifmaddrs;
|
use crate::osdep::getifmaddrs;
|
||||||
|
|
||||||
const BPF_BUFFER_SIZE: usize = 131072;
|
const BPF_BUFFER_SIZE: usize = 131072;
|
||||||
const IFCONFIG: &str = "/sbin/ifconfig";
|
const IFCONFIG: &'static str = "/sbin/ifconfig";
|
||||||
const SYSCTL: &str = "/usr/sbin/sysctl";
|
const SYSCTL: &'static str = "/usr/sbin/sysctl";
|
||||||
|
|
||||||
// Holds names of feth devices and destroys them on Drop.
|
// Holds names of feth devices and destroys them on Drop.
|
||||||
struct MacFethDevice {
|
struct MacFethDevice {
|
||||||
|
|
Loading…
Add table
Reference in a new issue