This commit is contained in:
John Smith 2022-11-04 19:29:44 -04:00
parent 60c4648530
commit ed0049dc22
9 changed files with 497 additions and 142 deletions

View File

@ -2,6 +2,7 @@ use crate::xx::*;
use crate::*; use crate::*;
use data_encoding::BASE64URL_NOPAD; use data_encoding::BASE64URL_NOPAD;
use keyring_manager::*; use keyring_manager::*;
use serde::{Deserialize, Serialize};
use std::path::Path; use std::path::Path;
pub struct ProtectedStoreInner { pub struct ProtectedStoreInner {
@ -31,15 +32,18 @@ impl ProtectedStore {
#[instrument(level = "trace", skip(self), err)] #[instrument(level = "trace", skip(self), err)]
pub async fn delete_all(&self) -> EyreResult<()> { pub async fn delete_all(&self) -> EyreResult<()> {
// Delete all known keys // Delete all known keys
if self.remove_user_secret_string("node_id").await? { if self.remove_user_secret("node_id").await? {
debug!("deleted protected_store key 'node_id'"); debug!("deleted protected_store key 'node_id'");
} }
if self.remove_user_secret_string("node_id_secret").await? { if self.remove_user_secret("node_id_secret").await? {
debug!("deleted protected_store key 'node_id_secret'"); debug!("deleted protected_store key 'node_id_secret'");
} }
if self.remove_user_secret_string("_test_key").await? { if self.remove_user_secret("_test_key").await? {
debug!("deleted protected_store key '_test_key'"); debug!("deleted protected_store key '_test_key'");
} }
if self.remove_user_secret("RouteSpecStore").await? {
debug!("deleted protected_store key 'RouteSpecStore'");
}
Ok(()) Ok(())
} }
@ -139,19 +143,30 @@ impl ProtectedStore {
} }
} }
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn remove_user_secret_string(&self, key: &str) -> EyreResult<bool> { pub async fn save_user_secret_cbor<T>(&self, key: &str, value: &T) -> EyreResult<bool>
let inner = self.inner.lock(); where
match inner T: Serialize,
.keyring_manager {
.as_ref() let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?;
.ok_or_else(|| eyre!("Protected store not initialized"))? self.save_user_secret(&key, &v).await
.with_keyring(&self.service_name(), key, |kr| kr.delete_value()) }
{
Ok(_) => Ok(true), #[instrument(level = "trace", skip(self), err)]
Err(KeyringError::NoPasswordFound) => Ok(false), pub async fn load_user_secret_cbor<T>(&self, key: &str) -> EyreResult<Option<T>>
Err(e) => Err(eyre!("Failed to remove user secret: {}", e)), where
} T: for<'de> Deserialize<'de>,
{
let out = self.load_user_secret(key).await?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
};
let obj = serde_cbor::from_slice::<T>(&b).wrap_err("failed to deserialize")?;
Ok(Some(obj))
} }
#[instrument(level = "trace", skip(self, value), ret, err)] #[instrument(level = "trace", skip(self, value), ret, err)]
@ -195,6 +210,16 @@ impl ProtectedStore {
#[instrument(level = "trace", skip(self), ret, err)] #[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> { pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
self.remove_user_secret_string(key).await let inner = self.inner.lock();
match inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| kr.delete_value())
{
Ok(_) => Ok(true),
Err(KeyringError::NoPasswordFound) => Ok(false),
Err(e) => Err(eyre!("Failed to remove user secret: {}", e)),
}
} }
} }

View File

@ -17,38 +17,40 @@ extern "C" {
fn keytar_deletePassword(service: &str, account: &str) -> Result<Promise, JsValue>; fn keytar_deletePassword(service: &str, account: &str) -> Result<Promise, JsValue>;
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ProtectedStore { pub struct ProtectedStore {
config: VeilidConfig, config: VeilidConfig,
} }
impl ProtectedStore { impl ProtectedStore {
pub fn new(config: VeilidConfig) -> Self { pub fn new(config: VeilidConfig) -> Self {
Self { Self { config }
config,
}
} }
#[instrument(level = "trace", skip(self), err)]
pub async fn delete_all(&self) -> EyreResult<()> { pub async fn delete_all(&self) -> EyreResult<()> {
// Delete all known keys // Delete all known keys
if self.remove_user_secret_string("node_id").await? { if self.remove_user_secret("node_id").await? {
debug!("deleted protected_store key 'node_id'"); debug!("deleted protected_store key 'node_id'");
} }
if self.remove_user_secret_string("node_id_secret").await? { if self.remove_user_secret("node_id_secret").await? {
debug!("deleted protected_store key 'node_id_secret'"); debug!("deleted protected_store key 'node_id_secret'");
} }
if self.remove_user_secret_string("_test_key").await? { if self.remove_user_secret("_test_key").await? {
debug!("deleted protected_store key '_test_key'"); debug!("deleted protected_store key '_test_key'");
} }
if self.remove_user_secret("RouteSpecStore").await? {
debug!("deleted protected_store key 'RouteSpecStore'");
}
Ok(()) Ok(())
} }
#[instrument(level = "debug", skip(self), err)]
pub async fn init(&self) -> EyreResult<()> { pub async fn init(&self) -> EyreResult<()> {
Ok(()) Ok(())
} }
#[instrument(level = "debug", skip(self))]
pub async fn terminate(&self) {} pub async fn terminate(&self) {}
fn keyring_name(&self) -> String { fn keyring_name(&self) -> String {
@ -69,12 +71,13 @@ impl ProtectedStore {
} }
} }
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> EyreResult<bool> { pub async fn save_user_secret_string(&self, key: &str, value: &str) -> EyreResult<bool> {
if is_nodejs() { if is_nodejs() {
let prev = match JsFuture::from( let prev = match JsFuture::from(
keytar_getPassword(self.keyring_name().as_str(), key) keytar_getPassword(self.keyring_name().as_str(), key)
.map_err(map_jsvalue_error) .map_err(map_jsvalue_error)
.wrap_err("exception thrown")?, .wrap_err("exception thrown")?,
) )
.await .await
{ {
@ -84,8 +87,8 @@ impl ProtectedStore {
match JsFuture::from( match JsFuture::from(
keytar_setPassword(self.keyring_name().as_str(), key, value) keytar_setPassword(self.keyring_name().as_str(), key, value)
.map_err(map_jsvalue_error) .map_err(map_jsvalue_error)
.wrap_err("exception thrown")?, .wrap_err("exception thrown")?,
) )
.await .await
{ {
@ -134,6 +137,7 @@ impl ProtectedStore {
} }
} }
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret_string(&self, key: &str) -> EyreResult<Option<String>> { pub async fn load_user_secret_string(&self, key: &str) -> EyreResult<Option<String>> {
if is_nodejs() { if is_nodejs() {
let prev = match JsFuture::from( let prev = match JsFuture::from(
@ -181,7 +185,73 @@ impl ProtectedStore {
} }
} }
pub async fn remove_user_secret_string(&self, key: &str) -> EyreResult<bool> { #[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret_cbor<T>(&self, key: &str, value: &T) -> EyreResult<bool>
where
T: Serialize,
{
let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?;
self.save_user_secret(&key, &v).await
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret_cbor<T>(&self, key: &str) -> EyreResult<Option<T>>
where
T: for<'de> Deserialize<'de>,
{
let out = self.load_user_secret(key).await?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
};
let obj = serde_cbor::from_slice::<T>(&b).wrap_err("failed to deserialize")?;
Ok(Some(obj))
}
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
self.save_user_secret_string(key, s.as_str()).await
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
return Ok(None);
}
};
if s.pop() != Some('!') {
bail!("User secret is not a buffer");
}
let mut bytes = Vec::<u8>::new();
let res = BASE64URL_NOPAD.decode_len(s.len());
match res {
Ok(l) => {
bytes.resize(l, 0u8);
}
Err(_) => {
bail!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Some(bytes)),
Err(_) => bail!("Failed to decode"),
}
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
if is_nodejs() { if is_nodejs() {
match JsFuture::from( match JsFuture::from(
keytar_deletePassword(self.keyring_name().as_str(), key) keytar_deletePassword(self.keyring_name().as_str(), key)
@ -231,45 +301,4 @@ impl ProtectedStore {
unimplemented!(); unimplemented!();
} }
} }
}
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
self.save_user_secret_string(key, s.as_str()).await
}
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
return Ok(None);
}
};
if s.pop() != Some('!') {
bail!("User secret is not a buffer");
}
let mut bytes = Vec::<u8>::new();
let res = BASE64URL_NOPAD.decode_len(s.len());
match res {
Ok(l) => {
bytes.resize(l, 0u8);
}
Err(_) => {
bail!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Some(bytes)),
Err(_) => bail!("Failed to decode"),
}
}
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
self.remove_user_secret_string(key).await
}
}

View File

@ -1747,7 +1747,7 @@ impl NetworkManager {
// Ignore these reports if we are currently detecting public dial info // Ignore these reports if we are currently detecting public dial info
let net = self.net(); let net = self.net();
if net.doing_public_dial_info_check() { if net.needs_public_dial_info_check() {
return; return;
} }

View File

@ -63,8 +63,6 @@ struct NetworkInner {
enable_ipv6_local: bool, enable_ipv6_local: bool,
/// set if we need to calculate our public dial info again /// set if we need to calculate our public dial info again
needs_public_dial_info_check: bool, needs_public_dial_info_check: bool,
/// set during the actual execution of the public dial info check to ensure we don't do it more than once
doing_public_dial_info_check: bool,
/// the punishment closure to enax /// the punishment closure to enax
public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>, public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
/// udp socket record for bound-first sockets, which are used to guarantee a port is available before /// udp socket record for bound-first sockets, which are used to guarantee a port is available before
@ -116,7 +114,6 @@ impl Network {
network_started: false, network_started: false,
network_needs_restart: false, network_needs_restart: false,
needs_public_dial_info_check: false, needs_public_dial_info_check: false,
doing_public_dial_info_check: false,
public_dial_info_check_punishment: None, public_dial_info_check_punishment: None,
protocol_config: Default::default(), protocol_config: Default::default(),
static_public_dialinfo: ProtocolTypeSet::empty(), static_public_dialinfo: ProtocolTypeSet::empty(),
@ -871,16 +868,11 @@ impl Network {
inner.public_dial_info_check_punishment = punishment; inner.public_dial_info_check_punishment = punishment;
} }
fn needs_public_dial_info_check(&self) -> bool { pub fn needs_public_dial_info_check(&self) -> bool {
let inner = self.inner.lock(); let inner = self.inner.lock();
inner.needs_public_dial_info_check inner.needs_public_dial_info_check
} }
pub fn doing_public_dial_info_check(&self) -> bool {
let inner = self.inner.lock();
inner.doing_public_dial_info_check
}
////////////////////////////////////////// //////////////////////////////////////////
#[instrument(level = "trace", skip(self), err)] #[instrument(level = "trace", skip(self), err)]

View File

@ -883,15 +883,11 @@ impl Network {
l: u64, l: u64,
t: u64, t: u64,
) -> EyreResult<()> { ) -> EyreResult<()> {
// Note that we are doing the public dial info check
// We don't have to check this for concurrency, since this routine is run in a TickTask/SingleFuture
self.inner.lock().doing_public_dial_info_check = true;
// Do the public dial info check // Do the public dial info check
let out = self.do_public_dial_info_check(stop_token, l, t).await; let out = self.do_public_dial_info_check(stop_token, l, t).await;
// Done with public dial info check // Done with public dial info check
self.inner.lock().doing_public_dial_info_check = false; self.inner.lock().needs_public_dial_info_check = false;
out out
} }

View File

@ -46,7 +46,7 @@ impl Network {
NetworkUnlockedInner { NetworkUnlockedInner {
network_manager, network_manager,
routing_table, routing_table,
connection_manager connection_manager,
} }
} }
@ -58,7 +58,11 @@ impl Network {
Self { Self {
config: network_manager.config(), config: network_manager.config(),
inner: Arc::new(Mutex::new(Self::new_inner())), inner: Arc::new(Mutex::new(Self::new_inner())),
unlocked_inner: Arc::new(Self::new_unlocked_inner(network_manager, routing_table, connection_manager)) unlocked_inner: Arc::new(Self::new_unlocked_inner(
network_manager,
routing_table,
connection_manager,
)),
} }
} }
@ -319,12 +323,15 @@ impl Network {
} }
////////////////////////////////////////// //////////////////////////////////////////
pub fn set_needs_public_dial_info_check(&self, _punishment: Option<Box<dyn FnOnce() + Send + 'static>>) { pub fn set_needs_public_dial_info_check(
&self,
_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
) {
// //
} }
pub fn doing_public_dial_info_check(&self) -> bool { pub fn needs_public_dial_info_check(&self) -> bool {
false false
} }

View File

@ -155,7 +155,7 @@ fn with_route_permutations(
// initial permutation // initial permutation
let mut permutation: Vec<usize> = Vec::with_capacity(hop_count); let mut permutation: Vec<usize> = Vec::with_capacity(hop_count);
for n in 0..hop_count { for n in 0..hop_count {
permutation[n] = start + n; permutation.push(start + n);
} }
// if we have one hop or two, then there's only one permutation // if we have one hop or two, then there's only one permutation
if hop_count == 1 || hop_count == 2 { if hop_count == 1 || hop_count == 2 {
@ -229,22 +229,17 @@ impl RouteSpecStore {
// Load secrets from pstore // Load secrets from pstore
let pstore = routing_table.network_manager().protected_store(); let pstore = routing_table.network_manager().protected_store();
let out: Vec<(DHTKey, DHTKeySecret)> = pstore
.load_user_secret_cbor("RouteSpecStore")
.await?
.unwrap_or_default();
let mut dead_keys = Vec::new(); let mut dead_keys = Vec::new();
for (k, v) in &mut content.details { for (k, v) in out {
if let Some(secret_key) = pstore if let Some(rsd) = content.details.get_mut(&k) {
.load_user_secret(&format!("RouteSpecStore_{}", k.encode())) rsd.secret_key = v;
.await?
{
match secret_key.try_into() {
Ok(s) => {
v.secret_key = DHTKeySecret::new(s);
}
Err(_) => {
dead_keys.push(*k);
}
}
} else { } else {
dead_keys.push(*k); dead_keys.push(k);
} }
} }
for k in dead_keys { for k in dead_keys {
@ -293,18 +288,14 @@ impl RouteSpecStore {
.routing_table .routing_table
.network_manager() .network_manager()
.protected_store(); .protected_store();
let mut out: Vec<(DHTKey, DHTKeySecret)> = Vec::with_capacity(content.details.len());
for (k, v) in &content.details { for (k, v) in &content.details {
if pstore out.push((*k, v.secret_key));
.save_user_secret(
&format!("RouteSpecStore_{}", k.encode()),
&v.secret_key.bytes,
)
.await?
{
panic!("route spec should not already have secret key saved");
}
} }
let _ = pstore.save_user_secret_cbor("RouteSpecStore", &out).await?; // ignore if this previously existed or not
Ok(()) Ok(())
} }
@ -346,6 +337,14 @@ impl RouteSpecStore {
inner.content.details.get_mut(public_key) inner.content.details.get_mut(public_key)
} }
/// Purge the route spec store
pub async fn purge(&self) -> EyreResult<()> {
let inner = &mut *self.inner.lock();
inner.content = Default::default();
inner.cache = Default::default();
self.save().await
}
/// Create a new route /// Create a new route
/// Prefers nodes that are not currently in use by another route /// Prefers nodes that are not currently in use by another route
/// The route is not yet tested for its reachability /// The route is not yet tested for its reachability
@ -675,7 +674,7 @@ impl RouteSpecStore {
))) )))
} }
pub fn release_route(&self, public_key: DHTKey) { pub fn release_route(&self, public_key: DHTKey) -> EyreResult<()> {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if let Some(detail) = inner.content.details.remove(&public_key) { if let Some(detail) = inner.content.details.remove(&public_key) {
// Remove from hop cache // Remove from hop cache
@ -710,8 +709,9 @@ impl RouteSpecStore {
} }
} }
} else { } else {
panic!("can't release route that was never allocated"); bail!("can't release route that was never allocated");
} }
Ok(())
} }
/// Find first matching unpublished route that fits into the selection criteria /// Find first matching unpublished route that fits into the selection criteria
@ -739,6 +739,22 @@ impl RouteSpecStore {
None None
} }
/// List all routes
pub fn list_routes(&self) -> Vec<DHTKey> {
let inner = self.inner.lock();
let mut out = Vec::with_capacity(inner.content.details.len());
for detail in &inner.content.details {
out.push(*detail.0);
}
out
}
/// Get the debug description of a route
pub fn debug_route(&self, key: &DHTKey) -> Option<String> {
let inner = &*self.inner.lock();
Self::detail(inner, key).map(|rsd| format!("{:#?}", rsd))
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
/// Compiles a safety route to the private route, with caching /// Compiles a safety route to the private route, with caching
@ -973,17 +989,18 @@ impl RouteSpecStore {
/// Assemble private route for publication /// Assemble private route for publication
pub fn assemble_private_route( pub fn assemble_private_route(
&self, &self,
rti: &RoutingTableInner,
routing_table: RoutingTable,
key: &DHTKey, key: &DHTKey,
optimize: Option<bool>,
) -> EyreResult<PrivateRoute> { ) -> EyreResult<PrivateRoute> {
let inner = &*self.inner.lock(); let inner = &*self.inner.lock();
let routing_table = self.unlocked_inner.routing_table.clone();
let rti = &*routing_table.inner.read();
let rsd = Self::detail(inner, &key).ok_or_else(|| eyre!("route does not exist"))?; let rsd = Self::detail(inner, key).ok_or_else(|| eyre!("route does not exist"))?;
// See if we can optimize this compilation yet // See if we can optimize this compilation yet
// We don't want to include full nodeinfo if we don't have to // We don't want to include full nodeinfo if we don't have to
let optimize = rsd.reachable; let optimize = optimize.unwrap_or(rsd.reachable);
// Make innermost route hop to our own node // Make innermost route hop to our own node
let mut route_hop = RouteHop { let mut route_hop = RouteHop {
@ -1053,79 +1070,79 @@ impl RouteSpecStore {
/// Mark route as published /// Mark route as published
/// When first deserialized, routes must be re-published in order to ensure they remain /// When first deserialized, routes must be re-published in order to ensure they remain
/// in the RouteSpecStore. /// in the RouteSpecStore.
pub fn mark_route_published(&mut self, key: &DHTKey) -> EyreResult<()> { pub fn mark_route_published(&self, key: &DHTKey, published: bool) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
Self::detail_mut(inner, &key) Self::detail_mut(inner, key)
.ok_or_else(|| eyre!("route does not exist"))? .ok_or_else(|| eyre!("route does not exist"))?
.published = true; .published = published;
Ok(()) Ok(())
} }
/// Mark route as reachable /// Mark route as reachable
/// When first deserialized, routes must be re-tested for reachability /// When first deserialized, routes must be re-tested for reachability
/// This can be used to determine if routes need to be sent with full peerinfo or can just use a node id /// This can be used to determine if routes need to be sent with full peerinfo or can just use a node id
pub fn mark_route_reachable(&mut self, key: &DHTKey) -> EyreResult<()> { pub fn mark_route_reachable(&self, key: &DHTKey, reachable: bool) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
Self::detail_mut(inner, &key) Self::detail_mut(inner, key)
.ok_or_else(|| eyre!("route does not exist"))? .ok_or_else(|| eyre!("route does not exist"))?
.published = true; .reachable = reachable;
Ok(()) Ok(())
} }
/// Mark route as checked /// Mark route as checked
pub fn touch_route_checked(&mut self, key: &DHTKey, cur_ts: u64) -> EyreResult<()> { pub fn touch_route_checked(&self, key: &DHTKey, cur_ts: u64) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
Self::detail_mut(inner, &key) Self::detail_mut(inner, key)
.ok_or_else(|| eyre!("route does not exist"))? .ok_or_else(|| eyre!("route does not exist"))?
.last_checked_ts = Some(cur_ts); .last_checked_ts = Some(cur_ts);
Ok(()) Ok(())
} }
/// Mark route as used /// Mark route as used
pub fn touch_route_used(&mut self, key: &DHTKey, cur_ts: u64) -> EyreResult<()> { pub fn touch_route_used(&self, key: &DHTKey, cur_ts: u64) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
Self::detail_mut(inner, &key) Self::detail_mut(inner, key)
.ok_or_else(|| eyre!("route does not exist"))? .ok_or_else(|| eyre!("route does not exist"))?
.last_used_ts = Some(cur_ts); .last_used_ts = Some(cur_ts);
Ok(()) Ok(())
} }
/// Record latency on the route /// Record latency on the route
pub fn record_latency(&mut self, key: &DHTKey, latency: u64) -> EyreResult<()> { pub fn record_latency(&self, key: &DHTKey, latency: u64) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let rsd = Self::detail_mut(inner, &key).ok_or_else(|| eyre!("route does not exist"))?; let rsd = Self::detail_mut(inner, key).ok_or_else(|| eyre!("route does not exist"))?;
rsd.latency_stats = rsd.latency_stats_accounting.record_latency(latency); rsd.latency_stats = rsd.latency_stats_accounting.record_latency(latency);
Ok(()) Ok(())
} }
/// Get the calculated latency stats /// Get the calculated latency stats
pub fn latency_stats(&mut self, key: &DHTKey) -> EyreResult<LatencyStats> { pub fn latency_stats(&self, key: &DHTKey) -> EyreResult<LatencyStats> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
Ok(Self::detail_mut(inner, &key) Ok(Self::detail_mut(inner, key)
.ok_or_else(|| eyre!("route does not exist"))? .ok_or_else(|| eyre!("route does not exist"))?
.latency_stats .latency_stats
.clone()) .clone())
} }
/// Add download transfers to route /// Add download transfers to route
pub fn add_down(&mut self, key: &DHTKey, bytes: u64) -> EyreResult<()> { pub fn add_down(&self, key: &DHTKey, bytes: u64) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let rsd = Self::detail_mut(inner, &key).ok_or_else(|| eyre!("route does not exist"))?; let rsd = Self::detail_mut(inner, key).ok_or_else(|| eyre!("route does not exist"))?;
rsd.transfer_stats_accounting.add_down(bytes); rsd.transfer_stats_accounting.add_down(bytes);
Ok(()) Ok(())
} }
/// Add upload transfers to route /// Add upload transfers to route
pub fn add_up(&mut self, key: &DHTKey, bytes: u64) -> EyreResult<()> { pub fn add_up(&self, key: &DHTKey, bytes: u64) -> EyreResult<()> {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
let rsd = Self::detail_mut(inner, &key).ok_or_else(|| eyre!("route does not exist"))?; let rsd = Self::detail_mut(inner, key).ok_or_else(|| eyre!("route does not exist"))?;
rsd.transfer_stats_accounting.add_up(bytes); rsd.transfer_stats_accounting.add_up(bytes);
Ok(()) Ok(())
} }
/// Process transfer statistics to get averages /// Process transfer statistics to get averages
pub fn roll_transfers(&mut self, last_ts: u64, cur_ts: u64) { pub fn roll_transfers(&self, last_ts: u64, cur_ts: u64) {
let inner = &mut *self.inner.lock(); let inner = &mut *self.inner.lock();
for rsd in inner.content.details.values_mut() { for rsd in inner.content.details.values_mut() {
rsd.transfer_stats_accounting.roll_transfers( rsd.transfer_stats_accounting.roll_transfers(
@ -1135,4 +1152,22 @@ impl RouteSpecStore {
); );
} }
} }
/// Convert private route to binary blob
pub fn private_route_to_blob(private_route: &PrivateRoute) -> EyreResult<Vec<u8>> {
let mut pr_message = ::capnp::message::Builder::new_default();
let mut pr_builder = pr_message.init_root::<veilid_capnp::private_route::Builder>();
encode_private_route(&private_route, &mut pr_builder)
.wrap_err("failed to encode private route")?;
builder_to_vec(pr_message).wrap_err("failed to convert builder to vec")
}
/// Convert binary blob to private route
pub fn blob_to_private_route(blob: Vec<u8>) -> EyreResult<PrivateRoute> {
let reader = ::capnp::message::Reader::new(RPCMessageData::new(blob), Default::default());
let pr_reader = reader
.get_root::<veilid_capnp::private_route::Reader>()
.wrap_err("failed to make reader for private_route")?;
decode_private_route(&pr_reader).wrap_err("failed to decode private route")
}
} }

View File

@ -90,10 +90,16 @@ struct RPCMessageHeader {
impl RPCMessageHeader {} impl RPCMessageHeader {}
#[derive(Debug)] #[derive(Debug)]
struct RPCMessageData { pub struct RPCMessageData {
contents: Vec<u8>, // rpc messages must be a canonicalized single segment contents: Vec<u8>, // rpc messages must be a canonicalized single segment
} }
impl RPCMessageData {
pub fn new(contents: Vec<u8>) -> Self {
Self { contents }
}
}
impl ReaderSegments for RPCMessageData { impl ReaderSegments for RPCMessageData {
fn get_segment(&self, idx: u32) -> Option<&[u8]> { fn get_segment(&self, idx: u32) -> Option<&[u8]> {
if idx > 0 { if idx > 0 {

View File

@ -16,12 +16,41 @@ fn get_bucket_entry_state(text: &str) -> Option<BucketEntryState> {
None None
} }
} }
fn get_string(text: &str) -> Option<String> {
Some(text.to_owned())
}
fn get_route_id(rss: RouteSpecStore) -> impl Fn(&str) -> Option<DHTKey> {
return move |text: &str| {
match DHTKey::try_decode(text).ok() {
Some(key) => {
let routes = rss.list_routes();
if routes.contains(&key) {
return Some(key);
}
}
None => {
let routes = rss.list_routes();
for r in routes {
let rkey = r.encode();
if rkey.starts_with(text) {
return Some(r);
}
}
}
}
None
};
}
fn get_number(text: &str) -> Option<usize> { fn get_number(text: &str) -> Option<usize> {
usize::from_str(text).ok() usize::from_str(text).ok()
} }
fn get_dht_key(text: &str) -> Option<DHTKey> { fn get_dht_key(text: &str) -> Option<DHTKey> {
DHTKey::try_decode(text).ok() DHTKey::try_decode(text).ok()
} }
fn get_protocol_type(text: &str) -> Option<ProtocolType> { fn get_protocol_type(text: &str) -> Option<ProtocolType> {
let lctext = text.to_ascii_lowercase(); let lctext = text.to_ascii_lowercase();
if lctext == "udp" { if lctext == "udp" {
@ -36,6 +65,41 @@ fn get_protocol_type(text: &str) -> Option<ProtocolType> {
None None
} }
} }
fn get_sequencing(text: &str) -> Option<Sequencing> {
let seqtext = text.to_ascii_lowercase();
if seqtext == "np" {
Some(Sequencing::NoPreference)
} else if seqtext == "ord" {
Some(Sequencing::PreferOrdered)
} else if seqtext == "*ord" {
Some(Sequencing::EnsureOrdered)
} else {
None
}
}
fn get_stability(text: &str) -> Option<Stability> {
let sttext = text.to_ascii_lowercase();
if sttext == "ll" {
Some(Stability::LowLatency)
} else if sttext == "rel" {
Some(Stability::Reliable)
} else {
None
}
}
fn get_direction_set(text: &str) -> Option<DirectionSet> {
let dstext = text.to_ascii_lowercase();
if dstext == "in" {
Some(Direction::Inbound.into())
} else if dstext == "out" {
Some(Direction::Outbound.into())
} else if dstext == "inout" {
Some(DirectionSet::all())
} else {
None
}
}
fn get_address_type(text: &str) -> Option<AddressType> { fn get_address_type(text: &str) -> Option<AddressType> {
let lctext = text.to_ascii_lowercase(); let lctext = text.to_ascii_lowercase();
if lctext == "ipv4" { if lctext == "ipv4" {
@ -251,6 +315,13 @@ impl VeilidAPI {
.purge_last_connections(); .purge_last_connections();
Ok("Connections purged".to_owned()) Ok("Connections purged".to_owned())
} else if args[0] == "routes" {
// Purge route spec store
let rss = self.network_manager()?.routing_table().route_spec_store();
match rss.purge().await {
Ok(_) => Ok("Routes purged".to_owned()),
Err(e) => Ok(format!("Routes purged but failed to save: {}", e)),
}
} else { } else {
Err(VeilidAPIError::InvalidArgument { Err(VeilidAPIError::InvalidArgument {
context: "debug_purge".to_owned(), context: "debug_purge".to_owned(),
@ -420,6 +491,191 @@ impl VeilidAPI {
Ok(format!("{:#?}", out)) Ok(format!("{:#?}", out))
} }
async fn debug_route_allocate(&self, args: Vec<String>) -> Result<String, VeilidAPIError> {
// [ord|*ord] [rel] [<count>] [in|out]
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let config = self.config().unwrap();
let default_route_hop_count = {
let c = config.get();
c.network.rpc.default_route_hop_count as usize
};
let mut ai = 1;
let mut sequencing = Sequencing::NoPreference;
let mut stability = Stability::LowLatency;
let mut hop_count = default_route_hop_count;
let mut directions = DirectionSet::all();
while ai < args.len() {
if let Ok(seq) =
get_debug_argument_at(&args, ai, "debug_route", "sequencing", get_sequencing)
{
sequencing = seq;
} else if let Ok(sta) =
get_debug_argument_at(&args, ai, "debug_route", "stability", get_stability)
{
stability = sta;
} else if let Ok(hc) =
get_debug_argument_at(&args, ai, "debug_route", "hop_count", get_number)
{
hop_count = hc;
} else if let Ok(ds) =
get_debug_argument_at(&args, ai, "debug_route", "direction_set", get_direction_set)
{
directions = ds;
} else {
return Ok(format!("Invalid argument specified: {}", args[ai]));
}
ai += 1;
}
// Allocate route
let out = match rss.allocate_route(stability, sequencing, hop_count, directions) {
Ok(Some(v)) => format!("{}", v.encode()),
Ok(None) => format!("<unavailable>"),
Err(e) => {
format!("Route allocation failed: {}", e)
}
};
Ok(out)
}
async fn debug_route_release(&self, args: Vec<String>) -> Result<String, VeilidAPIError> {
// <route id>
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_dht_key)?;
// Release route
let out = match rss.release_route(route_id) {
Ok(()) => format!("Released"),
Err(e) => {
format!("Route release failed: {}", e)
}
};
Ok(out)
}
async fn debug_route_publish(&self, args: Vec<String>) -> Result<String, VeilidAPIError> {
// <route id> [full]
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_dht_key)?;
let full = {
if args.len() > 2 {
let full_val = get_debug_argument_at(&args, 2, "debug_route", "full", get_string)?
.to_ascii_lowercase();
if full_val == "full" {
true
} else {
return Err(VeilidAPIError::invalid_argument(
"debug_route",
"full",
full_val,
));
}
} else {
false
}
};
// Publish route
let out = match rss.assemble_private_route(&route_id, Some(!full)) {
Ok(private_route) => {
if let Err(e) = rss.mark_route_published(&route_id, true) {
return Ok(format!("Couldn't mark route published: {}", e));
}
// Convert to blob
let blob_data = RouteSpecStore::private_route_to_blob(&private_route)
.map_err(VeilidAPIError::internal)?;
data_encoding::BASE64URL_NOPAD.encode(&blob_data)
}
Err(e) => {
format!("Couldn't assemble private route: {}", e)
}
};
Ok(out)
}
async fn debug_route_unpublish(&self, args: Vec<String>) -> Result<String, VeilidAPIError> {
// <route id>
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_dht_key)?;
// Unpublish route
let out = if let Err(e) = rss.mark_route_published(&route_id, false) {
return Ok(format!("Couldn't mark route unpublished: {}", e));
} else {
"Route unpublished".to_owned()
};
Ok(out)
}
async fn debug_route_print(&self, args: Vec<String>) -> Result<String, VeilidAPIError> {
// <route id>
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_dht_key)?;
match rss.debug_route(&route_id) {
Some(s) => Ok(s),
None => Ok("Route does not exist".to_owned()),
}
}
async fn debug_route_list(&self, _args: Vec<String>) -> Result<String, VeilidAPIError> {
//
let netman = self.network_manager()?;
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let routes = rss.list_routes();
let mut out = format!("Routes: (count = {}):\n", routes.len());
for r in routes {
out.push_str(&format!("{}\n", r.encode()));
}
Ok(out)
}
async fn debug_route_import(&self, _args: Vec<String>) -> Result<String, VeilidAPIError> {
// <blob>
let out = format!("");
return Ok(out);
}
async fn debug_route(&self, args: String) -> Result<String, VeilidAPIError> {
let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect();
let command = get_debug_argument_at(&args, 0, "debug_route", "command", get_string)?;
if command == "allocate" {
self.debug_route_allocate(args).await
} else if command == "release" {
self.debug_route_release(args).await
} else if command == "publish" {
self.debug_route_publish(args).await
} else if command == "unpublish" {
self.debug_route_unpublish(args).await
} else if command == "print" {
self.debug_route_print(args).await
} else if command == "list" {
self.debug_route_list(args).await
} else if command == "import" {
self.debug_route_import(args).await
} else {
Ok(">>> Unknown command\n".to_owned())
}
}
pub async fn debug_help(&self, _args: String) -> Result<String, VeilidAPIError> { pub async fn debug_help(&self, _args: String) -> Result<String, VeilidAPIError> {
Ok(r#">>> Debug commands: Ok(r#">>> Debug commands:
help help
@ -435,6 +691,13 @@ impl VeilidAPI {
restart network restart network
ping <node_id> [protocol_type][address_type][routing_domain] ping <node_id> [protocol_type][address_type][routing_domain]
contact <node_id> [protocol_type [address_type]] contact <node_id> [protocol_type [address_type]]
route allocate [ord|*ord] [rel] [<count>] [in|out]
route release <route id>
route publish <route id> [full]
route unpublish <route id>
route print <route id>
route list
route import <blob>
"# "#
.to_owned()) .to_owned())
} }
@ -476,6 +739,8 @@ impl VeilidAPI {
self.debug_config(rest).await self.debug_config(rest).await
} else if arg == "restart" { } else if arg == "restart" {
self.debug_restart(rest).await self.debug_restart(rest).await
} else if arg == "route" {
self.debug_route(rest).await
} else { } else {
Ok(">>> Unknown command\n".to_owned()) Ok(">>> Unknown command\n".to_owned())
} }