Merge branch 'capability' into 'main'

Capability support for FindNode

See merge request veilid/veilid!45
This commit is contained in:
John Smith 2023-07-04 16:44:19 +00:00
commit c682de9c27
53 changed files with 766 additions and 564 deletions

View File

@ -50,6 +50,7 @@ core:
reverse_connection_receipt_time_ms: 5000 reverse_connection_receipt_time_ms: 5000
hole_punch_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000
network_key_password: null network_key_password: null
disable_capabilites: []
routing_table: routing_table:
node_id: null node_id: null
node_id_secret: null node_id_secret: null

View File

@ -189,6 +189,7 @@ network:
reverse_connection_receipt_time_ms: 5000 reverse_connection_receipt_time_ms: 5000
hole_punch_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000
network_key_password: null network_key_password: null
disable_capabilites: []
node_id: null node_id: null
node_id_secret: null node_id_secret: null
bootstrap: ['bootstrap.veilid.net'] bootstrap: ['bootstrap.veilid.net']

View File

@ -34,6 +34,7 @@ using TunnelID = UInt64; # Id for tunnels
using CryptoKind = UInt32; # FOURCC code for cryptography type using CryptoKind = UInt32; # FOURCC code for cryptography type
using ValueSeqNum = UInt32; # sequence numbers for values using ValueSeqNum = UInt32; # sequence numbers for values
using Subkey = UInt32; # subkey index for dht using Subkey = UInt32; # subkey index for dht
using Capability = UInt32; # FOURCC code for capability
struct TypedKey @0xe2d567a9f1e61b29 { struct TypedKey @0xe2d567a9f1e61b29 {
kind @0 :CryptoKind; kind @0 :CryptoKind;
@ -189,24 +190,8 @@ struct DialInfoDetail @0x96423aa1d67b74d8 {
class @1 :DialInfoClass; class @1 :DialInfoClass;
} }
struct PublicInternetNodeStatus @0x9c9d7f1f12eb088f {
willRoute @0 :Bool;
willTunnel @1 :Bool;
willSignal @2 :Bool;
willRelay @3 :Bool;
willValidateDialInfo @4 :Bool;
}
struct LocalNetworkNodeStatus @0x957f5bfed2d0b5a5 {
willRelay @0 :Bool;
willValidateDialInfo @1 :Bool;
}
struct NodeStatus @0xd36b9e7a3bf3330d { struct NodeStatus @0xd36b9e7a3bf3330d {
union { # Reserved for non-nodeinfo status
publicInternet @0 :PublicInternetNodeStatus;
localNetwork @1 :LocalNetworkNodeStatus;
}
} }
struct ProtocolTypeSet @0x82f12f55a1b73326 { struct ProtocolTypeSet @0x82f12f55a1b73326 {
@ -231,7 +216,8 @@ struct NodeInfo @0xe125d847e3f9f419 {
addressTypes @2 :AddressTypeSet; # address types supported addressTypes @2 :AddressTypeSet; # address types supported
envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions
cryptoSupport @4 :List(CryptoKind); # cryptography systems supported cryptoSupport @4 :List(CryptoKind); # cryptography systems supported
dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node capabilities @5 :List(Capability); # capabilities supported by the node
dialInfoDetailList @6 :List(DialInfoDetail); # inbound dial info details for this node
} }
struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 { struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 {
@ -288,6 +274,7 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb {
struct OperationFindNodeQ @0xfdef788fe9623bcd { struct OperationFindNodeQ @0xfdef788fe9623bcd {
nodeId @0 :TypedKey; # node id to locate nodeId @0 :TypedKey; # node id to locate
capabilities @1 :List(Capability); # required capabilities returned peers must have
} }
struct OperationFindNodeA @0xa84cf2fb40c77089 { struct OperationFindNodeA @0xa84cf2fb40c77089 {

View File

@ -4,7 +4,7 @@ use data_encoding::BASE64URL_NOPAD;
use digest::Digest; use digest::Digest;
use rand::RngCore; use rand::RngCore;
const AEAD_OVERHEAD: usize = PUBLIC_KEY_LENGTH; const AEAD_OVERHEAD: usize = PUBLIC_KEY_LENGTH;
pub const CRYPTO_KIND_NONE: CryptoKind = FourCC([b'N', b'O', b'N', b'E']); pub const CRYPTO_KIND_NONE: CryptoKind = FourCC(*b"NONE");
pub fn none_generate_keypair() -> KeyPair { pub fn none_generate_keypair() -> KeyPair {
let mut csprng = VeilidRng {}; let mut csprng = VeilidRng {};

View File

@ -15,7 +15,7 @@ use ed25519_dalek as ed;
use x25519_dalek as xd; use x25519_dalek as xd;
const AEAD_OVERHEAD: usize = 16; const AEAD_OVERHEAD: usize = 16;
pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC([b'V', b'L', b'D', b'0']); pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC(*b"VLD0");
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> VeilidAPIResult<xd::PublicKey> { fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> VeilidAPIResult<xd::PublicKey> {
let bytes = key.to_bytes(); let bytes = key.to_bytes();

View File

@ -115,6 +115,7 @@ impl AddressFilter {
} }
} }
for key in dead_keys { for key in dead_keys {
log_net!(debug ">>> FORGIVING: {}", key);
inner.punishments_by_ip4.remove(&key); inner.punishments_by_ip4.remove(&key);
} }
} }
@ -130,6 +131,7 @@ impl AddressFilter {
} }
} }
for key in dead_keys { for key in dead_keys {
log_net!(debug ">>> FORGIVING: {}", key);
inner.punishments_by_ip6_prefix.remove(&key); inner.punishments_by_ip6_prefix.remove(&key);
} }
} }

View File

@ -5,31 +5,31 @@ mod native;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
mod wasm; mod wasm;
mod direct_boot;
mod send_data;
mod connection_handle;
mod address_filter; mod address_filter;
mod connection_handle;
mod connection_manager; mod connection_manager;
mod connection_table; mod connection_table;
mod direct_boot;
mod network_connection; mod network_connection;
mod send_data;
mod stats;
mod tasks; mod tasks;
mod types; mod types;
mod stats;
pub mod tests; pub mod tests;
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
pub use connection_manager::*; pub use connection_manager::*;
pub use network_connection::*;
pub use types::*;
pub use send_data::*;
pub use direct_boot::*; pub use direct_boot::*;
pub use network_connection::*;
pub use send_data::*;
pub use stats::*; pub use stats::*;
pub use types::*;
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
use connection_handle::*;
use address_filter::*; use address_filter::*;
use connection_handle::*;
use crypto::*; use crypto::*;
use futures_util::stream::FuturesUnordered; use futures_util::stream::FuturesUnordered;
use hashlink::LruCache; use hashlink::LruCache;
@ -47,13 +47,16 @@ use wasm::*;
pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE; pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE;
pub const IPADDR_TABLE_SIZE: usize = 1024; pub const IPADDR_TABLE_SIZE: usize = 1024;
pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration =
TimestampDuration::new(300_000_000u64); // 5 minutes
pub const NODE_CONTACT_METHOD_CACHE_SIZE: usize = 1024; pub const NODE_CONTACT_METHOD_CACHE_SIZE: usize = 1024;
pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3; pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3;
pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8; pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8;
pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60; pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60;
pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration =
pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration = TimestampDuration::new(3600_000_000u64); // 60 minutes TimestampDuration::new(300_000_000u64); // 5 minutes
pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration =
TimestampDuration::new(3600_000_000u64); // 60 minutes
pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60; pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60;
pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; pub const BOOT_MAGIC: &[u8; 4] = b"BOOT";
@ -75,7 +78,6 @@ struct NetworkComponents {
receipt_manager: ReceiptManager, receipt_manager: ReceiptManager,
} }
#[derive(Debug)] #[derive(Debug)]
struct ClientWhitelistEntry { struct ClientWhitelistEntry {
last_seen_ts: Timestamp, last_seen_ts: Timestamp,
@ -134,7 +136,7 @@ struct NetworkManagerUnlockedInner {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store: BlockStore, block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
address_filter: AddressFilter, address_filter: AddressFilter,
@ -171,8 +173,7 @@ impl NetworkManager {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")] block_store: BlockStore,
block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
network_key: Option<SharedSecret>, network_key: Option<SharedSecret>,
) -> NetworkManagerUnlockedInner { ) -> NetworkManagerUnlockedInner {
@ -181,7 +182,7 @@ impl NetworkManager {
storage_manager, storage_manager,
protected_store, protected_store,
table_store, table_store,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store, block_store,
crypto, crypto,
address_filter: AddressFilter::new(config), address_filter: AddressFilter::new(config),
@ -200,18 +201,20 @@ impl NetworkManager {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")] block_store: BlockStore,
block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
) -> Self { ) -> Self {
// Make the network key // Make the network key
let network_key = { let network_key = {
let c = config.get(); let c = config.get();
let network_key_password = if let Some(nkp) = c.network.network_key_password.clone() { let network_key_password = if let Some(nkp) = c.network.network_key_password.clone() {
Some(nkp) Some(nkp)
} else { } else {
if c.network.routing_table.bootstrap.contains(&"bootstrap.veilid.net".to_owned()) { if c.network
.routing_table
.bootstrap
.contains(&"bootstrap.veilid.net".to_owned())
{
None None
} else { } else {
Some(c.network.routing_table.bootstrap.join(",")) Some(c.network.routing_table.bootstrap.join(","))
@ -224,7 +227,13 @@ impl NetworkManager {
let bcs = crypto.best(); let bcs = crypto.best();
// Yes the use of the salt this way is generally bad, but this just needs to be hashed // Yes the use of the salt this way is generally bad, but this just needs to be hashed
Some(bcs.derive_shared_secret(network_key_password.as_bytes(), network_key_password.as_bytes()).expect("failed to derive network key")) Some(
bcs.derive_shared_secret(
network_key_password.as_bytes(),
network_key_password.as_bytes(),
)
.expect("failed to derive network key"),
)
} else { } else {
None None
} }
@ -242,7 +251,7 @@ impl NetworkManager {
storage_manager, storage_manager,
protected_store, protected_store,
table_store, table_store,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store, block_store,
crypto, crypto,
network_key, network_key,
@ -271,7 +280,7 @@ impl NetworkManager {
pub fn table_store(&self) -> TableStore { pub fn table_store(&self) -> TableStore {
self.unlocked_inner.table_store.clone() self.unlocked_inner.table_store.clone()
} }
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
pub fn block_store(&self) -> BlockStore { pub fn block_store(&self) -> BlockStore {
self.unlocked_inner.block_store.clone() self.unlocked_inner.block_store.clone()
} }
@ -443,7 +452,7 @@ impl NetworkManager {
pub fn update_client_whitelist(&self, client: TypedKey) { pub fn update_client_whitelist(&self, client: TypedKey) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client, |_k,_v| { match inner.client_whitelist.entry(client, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}) { }) {
hashlink::lru_cache::Entry::Occupied(mut entry) => { hashlink::lru_cache::Entry::Occupied(mut entry) => {
@ -461,7 +470,7 @@ impl NetworkManager {
pub fn check_client_whitelist(&self, client: TypedKey) -> bool { pub fn check_client_whitelist(&self, client: TypedKey) -> bool {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client, |_k,_v| { match inner.client_whitelist.entry(client, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}) { }) {
hashlink::lru_cache::Entry::Occupied(mut entry) => { hashlink::lru_cache::Entry::Occupied(mut entry) => {
@ -475,7 +484,8 @@ impl NetworkManager {
pub fn purge_client_whitelist(&self) { pub fn purge_client_whitelist(&self) {
let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms); let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms);
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
let cutoff_timestamp = get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64); let cutoff_timestamp =
get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64);
// Remove clients from the whitelist that haven't been since since our whitelist timeout // Remove clients from the whitelist that haven't been since since our whitelist timeout
while inner while inner
.client_whitelist .client_whitelist
@ -493,66 +503,8 @@ impl NetworkManager {
net.needs_restart() net.needs_restart()
} }
/// Get our node's capabilities in the PublicInternet routing domain pub fn generate_node_status(&self, _routing_domain: RoutingDomain) -> NodeStatus {
fn generate_public_internet_node_status(&self) -> PublicInternetNodeStatus { NodeStatus {}
let Some(own_peer_info) = self
.routing_table()
.get_own_peer_info(RoutingDomain::PublicInternet) else {
return PublicInternetNodeStatus {
will_route: false,
will_tunnel: false,
will_signal: false,
will_relay: false,
will_validate_dial_info: false,
};
};
let own_node_info = own_peer_info.signed_node_info().node_info();
let will_route = own_node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added
let will_tunnel = own_node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point
let will_signal = own_node_info.can_signal();
let will_relay = own_node_info.can_inbound_relay();
let will_validate_dial_info = own_node_info.can_validate_dial_info();
PublicInternetNodeStatus {
will_route,
will_tunnel,
will_signal,
will_relay,
will_validate_dial_info,
}
}
/// Get our node's capabilities in the LocalNetwork routing domain
fn generate_local_network_node_status(&self) -> LocalNetworkNodeStatus {
let Some(own_peer_info) = self
.routing_table()
.get_own_peer_info(RoutingDomain::LocalNetwork) else {
return LocalNetworkNodeStatus {
will_relay: false,
will_validate_dial_info: false,
};
};
let own_node_info = own_peer_info.signed_node_info().node_info();
let will_relay = own_node_info.can_inbound_relay();
let will_validate_dial_info = own_node_info.can_validate_dial_info();
LocalNetworkNodeStatus {
will_relay,
will_validate_dial_info,
}
}
pub fn generate_node_status(&self, routing_domain: RoutingDomain) -> NodeStatus {
match routing_domain {
RoutingDomain::PublicInternet => {
NodeStatus::PublicInternet(self.generate_public_internet_node_status())
}
RoutingDomain::LocalNetwork => {
NodeStatus::LocalNetwork(self.generate_local_network_node_status())
}
}
} }
/// Generates a multi-shot/normal receipt /// Generates a multi-shot/normal receipt
@ -574,7 +526,13 @@ impl NetworkManager {
let node_id = routing_table.node_id(vcrypto.kind()); let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let receipt = Receipt::try_new(
best_envelope_version(),
node_id.kind,
nonce,
node_id.value,
extra_data,
)?;
let out = receipt let out = receipt
.to_signed_data(self.crypto(), &node_id_secret) .to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?; .wrap_err("failed to generate signed receipt")?;
@ -603,7 +561,13 @@ impl NetworkManager {
let node_id = routing_table.node_id(vcrypto.kind()); let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let receipt = Receipt::try_new(
best_envelope_version(),
node_id.kind,
nonce,
node_id.value,
extra_data,
)?;
let out = receipt let out = receipt
.to_signed_data(self.crypto(), &node_id_secret) .to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?; .wrap_err("failed to generate signed receipt")?;
@ -715,9 +679,10 @@ impl NetworkManager {
) { ) {
Ok(nr) => nr, Ok(nr) => nr,
Err(e) => { Err(e) => {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(format!(
format!("unable to register reverse connect peerinfo: {}", e) "unable to register reverse connect peerinfo: {}",
)); e
)));
} }
}; };
@ -738,9 +703,10 @@ impl NetworkManager {
) { ) {
Ok(nr) => nr, Ok(nr) => nr,
Err(e) => { Err(e) => {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(format!(
format!("unable to register hole punch connect peerinfo: {}", e) "unable to register hole punch connect peerinfo: {}",
)); e
)));
} }
}; };
@ -784,7 +750,10 @@ impl NetworkManager {
} }
/// Builds an envelope for sending over the network /// Builds an envelope for sending over the network
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), err))] #[cfg_attr(
feature = "verbose-tracing",
instrument(level = "trace", skip(self, body), err)
)]
fn build_envelope<B: AsRef<[u8]>>( fn build_envelope<B: AsRef<[u8]>>(
&self, &self,
dest_node_id: TypedKey, dest_node_id: TypedKey,
@ -805,9 +774,21 @@ impl NetworkManager {
let nonce = vcrypto.random_nonce(); let nonce = vcrypto.random_nonce();
// Encode envelope // Encode envelope
let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.value, dest_node_id.value); let envelope = Envelope::new(
version,
node_id.kind,
ts,
nonce,
node_id.value,
dest_node_id.value,
);
envelope envelope
.to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret, &self.unlocked_inner.network_key) .to_encrypted_data(
self.crypto(),
body.as_ref(),
&node_id_secret,
&self.unlocked_inner.network_key,
)
.wrap_err("envelope failed to encode") .wrap_err("envelope failed to encode")
} }
@ -815,14 +796,16 @@ impl NetworkManager {
/// node_ref is the direct destination to which the envelope will be sent /// node_ref is the direct destination to which the envelope will be sent
/// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to /// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to
/// which will cause the envelope to be relayed /// which will cause the envelope to be relayed
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), ret, err))] #[cfg_attr(
feature = "verbose-tracing",
instrument(level = "trace", skip(self, body), ret, err)
)]
pub async fn send_envelope<B: AsRef<[u8]>>( pub async fn send_envelope<B: AsRef<[u8]>>(
&self, &self,
node_ref: NodeRef, node_ref: NodeRef,
destination_node_ref: Option<NodeRef>, destination_node_ref: Option<NodeRef>,
body: B, body: B,
) -> EyreResult<NetworkResult<SendDataKind>> { ) -> EyreResult<NetworkResult<SendDataKind>> {
let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone(); let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone();
if !node_ref.same_entry(&destination_node_ref) { if !node_ref.same_entry(&destination_node_ref) {
@ -886,7 +869,7 @@ impl NetworkManager {
data: &mut [u8], data: &mut [u8],
connection_descriptor: ConnectionDescriptor, connection_descriptor: ConnectionDescriptor,
) -> EyreResult<bool> { ) -> EyreResult<bool> {
#[cfg(feature="verbose-tracing")] #[cfg(feature = "verbose-tracing")]
let root = span!( let root = span!(
parent: None, parent: None,
Level::TRACE, Level::TRACE,
@ -894,7 +877,7 @@ impl NetworkManager {
"data.len" = data.len(), "data.len" = data.len(),
"descriptor" = ?connection_descriptor "descriptor" = ?connection_descriptor
); );
#[cfg(feature="verbose-tracing")] #[cfg(feature = "verbose-tracing")]
let _root_enter = root.enter(); let _root_enter = root.enter();
log_net!( log_net!(
@ -905,10 +888,7 @@ impl NetworkManager {
let remote_addr = connection_descriptor.remote_address().to_ip_addr(); let remote_addr = connection_descriptor.remote_address().to_ip_addr();
// Network accounting // Network accounting
self.stats_packet_rcvd( self.stats_packet_rcvd(remote_addr, ByteCount::new(data.len() as u64));
remote_addr,
ByteCount::new(data.len() as u64),
);
// If this is a zero length packet, just drop it, because these are used for hole punching // If this is a zero length packet, just drop it, because these are used for hole punching
// and possibly other low-level network connectivity tasks and will never require // and possibly other low-level network connectivity tasks and will never require
@ -949,7 +929,9 @@ impl NetworkManager {
} }
// Decode envelope header (may fail signature validation) // Decode envelope header (may fail signature validation)
let envelope = match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key) { let envelope =
match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key)
{
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
log_net!(debug "envelope failed to decode: {}", e); log_net!(debug "envelope failed to decode: {}", e);
@ -961,8 +943,16 @@ impl NetworkManager {
// Get timestamp range // Get timestamp range
let (tsbehind, tsahead) = self.with_config(|c| { let (tsbehind, tsahead) = self.with_config(|c| {
( (
c.network.rpc.max_timestamp_behind_ms.map(ms_to_us).map(TimestampDuration::new), c.network
c.network.rpc.max_timestamp_ahead_ms.map(ms_to_us).map(TimestampDuration::new), .rpc
.max_timestamp_behind_ms
.map(ms_to_us)
.map(TimestampDuration::new),
c.network
.rpc
.max_timestamp_ahead_ms
.map(ms_to_us)
.map(TimestampDuration::new),
) )
}); });
@ -1005,7 +995,10 @@ impl NetworkManager {
let some_relay_nr = if self.check_client_whitelist(sender_id) { let some_relay_nr = if self.check_client_whitelist(sender_id) {
// Full relay allowed, do a full resolve_node // Full relay allowed, do a full resolve_node
match rpc.resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default())).await { match rpc
.resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default()))
.await
{
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e); log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e);
@ -1064,8 +1057,12 @@ impl NetworkManager {
let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind()); let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind());
// Decrypt the envelope body // Decrypt the envelope body
let body = match envelope let body = match envelope.decrypt_body(
.decrypt_body(self.crypto(), data, &node_id_secret, &self.unlocked_inner.network_key) { self.crypto(),
data,
&node_id_secret,
&self.unlocked_inner.network_key,
) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
log_net!(debug "failed to decrypt envelope body: {}",e); log_net!(debug "failed to decrypt envelope body: {}",e);
@ -1166,7 +1163,7 @@ impl NetworkManager {
if pait.contains_key(&ipblock) { if pait.contains_key(&ipblock) {
return; return;
} }
pacc.insert(ipblock, socket_address, |_k,_v| { pacc.insert(ipblock, socket_address, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}); });
@ -1224,8 +1221,8 @@ impl NetworkManager {
.public_address_inconsistencies_table .public_address_inconsistencies_table
.entry(key) .entry(key)
.or_insert_with(|| HashMap::new()); .or_insert_with(|| HashMap::new());
let exp_ts = let exp_ts = get_aligned_timestamp()
get_aligned_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US;
for i in inconsistencies { for i in inconsistencies {
pait.insert(i, exp_ts); pait.insert(i, exp_ts);
} }
@ -1296,5 +1293,4 @@ impl NetworkManager {
} }
} }
} }
} }

View File

@ -686,30 +686,30 @@ impl Network {
let c = self.config.get(); let c = self.config.get();
let mut inbound = ProtocolTypeSet::new(); let mut inbound = ProtocolTypeSet::new();
if c.network.protocol.udp.enabled && c.capabilities.protocol_udp { if c.network.protocol.udp.enabled {
inbound.insert(ProtocolType::UDP); inbound.insert(ProtocolType::UDP);
} }
if c.network.protocol.tcp.listen && c.capabilities.protocol_accept_tcp { if c.network.protocol.tcp.listen {
inbound.insert(ProtocolType::TCP); inbound.insert(ProtocolType::TCP);
} }
if c.network.protocol.ws.listen && c.capabilities.protocol_accept_ws { if c.network.protocol.ws.listen {
inbound.insert(ProtocolType::WS); inbound.insert(ProtocolType::WS);
} }
if c.network.protocol.wss.listen && c.capabilities.protocol_accept_wss { if c.network.protocol.wss.listen {
inbound.insert(ProtocolType::WSS); inbound.insert(ProtocolType::WSS);
} }
let mut outbound = ProtocolTypeSet::new(); let mut outbound = ProtocolTypeSet::new();
if c.network.protocol.udp.enabled && c.capabilities.protocol_udp { if c.network.protocol.udp.enabled {
outbound.insert(ProtocolType::UDP); outbound.insert(ProtocolType::UDP);
} }
if c.network.protocol.tcp.connect && c.capabilities.protocol_connect_tcp { if c.network.protocol.tcp.connect {
outbound.insert(ProtocolType::TCP); outbound.insert(ProtocolType::TCP);
} }
if c.network.protocol.ws.connect && c.capabilities.protocol_connect_ws { if c.network.protocol.ws.connect {
outbound.insert(ProtocolType::WS); outbound.insert(ProtocolType::WS);
} }
if c.network.protocol.wss.connect && c.capabilities.protocol_connect_wss { if c.network.protocol.wss.connect {
outbound.insert(ProtocolType::WSS); outbound.insert(ProtocolType::WSS);
} }
@ -773,16 +773,34 @@ impl Network {
// set up the routing table's network config // set up the routing table's network config
// if we have static public dialinfo, upgrade our network class // if we have static public dialinfo, upgrade our network class
let public_internet_capabilities = {
let c = self.config.get();
PUBLIC_INTERNET_CAPABILITIES
.iter()
.copied()
.filter(|cap| !c.capabilities.disable.contains(cap))
.collect::<Vec<Capability>>()
};
let local_network_capabilities = {
let c = self.config.get();
LOCAL_NETWORK_CAPABILITIES
.iter()
.copied()
.filter(|cap| !c.capabilities.disable.contains(cap))
.collect::<Vec<Capability>>()
};
editor_public_internet.setup_network( editor_public_internet.setup_network(
protocol_config.outbound, protocol_config.outbound,
protocol_config.inbound, protocol_config.inbound,
protocol_config.family_global, protocol_config.family_global,
public_internet_capabilities,
); );
editor_local_network.setup_network( editor_local_network.setup_network(
protocol_config.outbound, protocol_config.outbound,
protocol_config.inbound, protocol_config.inbound,
protocol_config.family_local, protocol_config.family_local,
local_network_capabilities,
); );
let detect_address_changes = { let detect_address_changes = {
let c = self.config.get(); let c = self.config.get();

View File

@ -118,8 +118,8 @@ impl Network {
return; return;
} }
}; };
let address_filter = self.network_manager().address_filter();
// Check to see if it is punished // Check to see if it is punished
let address_filter = self.network_manager().address_filter();
if address_filter.is_punished(peer_addr.ip()) { if address_filter.is_punished(peer_addr.ip()) {
return; return;
} }

View File

@ -240,6 +240,7 @@ impl NetworkConnection {
); );
let network_manager = connection_manager.network_manager(); let network_manager = connection_manager.network_manager();
let address_filter = network_manager.address_filter();
let mut unord = FuturesUnordered::new(); let mut unord = FuturesUnordered::new();
let mut need_receiver = true; let mut need_receiver = true;
let mut need_sender = true; let mut need_sender = true;
@ -301,11 +302,20 @@ impl NetworkConnection {
.then(|res| async { .then(|res| async {
match res { match res {
Ok(v) => { Ok(v) => {
if v.is_no_connection() {
let peer_address = protocol_connection.descriptor().remote(); let peer_address = protocol_connection.descriptor().remote();
log_net!(debug "Connection closed from: {} ({})", peer_address.socket_address().to_socket_addr(), peer_address.protocol_type());
// Check to see if it is punished
if address_filter.is_punished(peer_address.to_socket_addr().ip()) {
return RecvLoopAction::Finish; return RecvLoopAction::Finish;
} }
// Check for connection close
if v.is_no_connection() {
log_net!(debug "Connection closed from: {} ({})", peer_address.to_socket_addr(), peer_address.protocol_type());
return RecvLoopAction::Finish;
}
// Log other network results
let mut message = network_result_value_or_log!(v => [ format!(": protocol_connection={:?}", protocol_connection) ] { let mut message = network_result_value_or_log!(v => [ format!(": protocol_connection={:?}", protocol_connection) ] {
return RecvLoopAction::Finish; return RecvLoopAction::Finish;
}); });

View File

@ -20,6 +20,7 @@ pub async fn test_signed_node_info() {
AddressTypeSet::all(), AddressTypeSet::all(),
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![DialInfoDetail { vec![DialInfoDetail {
class: DialInfoClass::Mapped, class: DialInfoClass::Mapped,
dial_info: DialInfo::udp(SocketAddress::default()), dial_info: DialInfo::udp(SocketAddress::default()),
@ -75,6 +76,7 @@ pub async fn test_signed_node_info() {
AddressTypeSet::all(), AddressTypeSet::all(),
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![DialInfoDetail { vec![DialInfoDetail {
class: DialInfoClass::Blocked, class: DialInfoClass::Blocked,
dial_info: DialInfo::udp(SocketAddress::default()), dial_info: DialInfo::udp(SocketAddress::default()),

View File

@ -51,7 +51,7 @@ pub struct BucketEntryPublicInternet {
/// The last node info timestamp of ours that this entry has seen /// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: Timestamp, last_seen_our_node_info_ts: Timestamp,
/// Last known node status /// Last known node status
node_status: Option<PublicInternetNodeStatus>, node_status: Option<NodeStatus>,
} }
/// Bucket entry information specific to the LocalNetwork RoutingDomain /// Bucket entry information specific to the LocalNetwork RoutingDomain
@ -63,7 +63,7 @@ pub struct BucketEntryLocalNetwork {
/// The last node info timestamp of ours that this entry has seen /// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: Timestamp, last_seen_our_node_info_ts: Timestamp,
/// Last known node status /// Last known node status
node_status: Option<LocalNetworkNodeStatus>, node_status: Option<NodeStatus>,
} }
/// The data associated with each bucket entry /// The data associated with each bucket entry
@ -170,6 +170,13 @@ impl BucketEntryInner {
common_crypto_kinds(&self.validated_node_ids.kinds(), other) common_crypto_kinds(&self.validated_node_ids.kinds(), other)
} }
/// Capability check
pub fn has_capabilities(&self, routing_domain: RoutingDomain, capabilities: &[Capability]) -> bool {
let Some(ni) = self.node_info(routing_domain) else {
return false;
};
ni.has_capabilities(capabilities)
}
// Less is faster // Less is faster
pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering { pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering {
@ -502,13 +509,13 @@ impl BucketEntryInner {
&self.peer_stats &self.peer_stats
} }
pub fn update_node_status(&mut self, status: NodeStatus) { pub fn update_node_status(&mut self, routing_domain: RoutingDomain, status: NodeStatus) {
match status { match routing_domain {
NodeStatus::LocalNetwork(ln) => { RoutingDomain::LocalNetwork => {
self.local_network.node_status = Some(ln); self.local_network.node_status = Some(status);
} }
NodeStatus::PublicInternet(pi) => { RoutingDomain::PublicInternet => {
self.public_internet.node_status = Some(pi); self.public_internet.node_status = Some(status);
} }
} }
} }
@ -518,12 +525,12 @@ impl BucketEntryInner {
.local_network .local_network
.node_status .node_status
.as_ref() .as_ref()
.map(|ln| NodeStatus::LocalNetwork(ln.clone())), .map(|ns| ns.clone()),
RoutingDomain::PublicInternet => self RoutingDomain::PublicInternet => self
.public_internet .public_internet
.node_status .node_status
.as_ref() .as_ref()
.map(|pi| NodeStatus::PublicInternet(pi.clone())), .map(|ns| ns.clone()),
} }
} }

View File

@ -2,7 +2,11 @@ use super::*;
impl RoutingTable { impl RoutingTable {
/// Utility to find all closest nodes to a particular key, including possibly our own node and nodes further away from the key than our own, returning their peer info /// Utility to find all closest nodes to a particular key, including possibly our own node and nodes further away from the key than our own, returning their peer info
pub fn find_all_closest_peers(&self, key: TypedKey) -> NetworkResult<Vec<PeerInfo>> { pub fn find_all_closest_peers(
&self,
key: TypedKey,
capabilities: &[Capability],
) -> NetworkResult<Vec<PeerInfo>> {
let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else { let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else {
// Our own node info is not yet available, drop this request. // Our own node info is not yet available, drop this request.
return NetworkResult::service_unavailable("Not finding closest peers because our peer info is not yet available"); return NetworkResult::service_unavailable("Not finding closest peers because our peer info is not yet available");
@ -12,11 +16,27 @@ impl RoutingTable {
let filter = Box::new( let filter = Box::new(
move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| { move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
// Ensure only things that are valid/signed in the PublicInternet domain are returned // Ensure only things that are valid/signed in the PublicInternet domain are returned
rti.filter_has_valid_signed_node_info( if !rti.filter_has_valid_signed_node_info(
RoutingDomain::PublicInternet, RoutingDomain::PublicInternet,
true, true,
opt_entry, opt_entry.clone(),
) ) {
return false;
}
// Ensure capabilities are met
match opt_entry {
Some(entry) => entry.with(rti, |_rti, e| {
e.has_capabilities(RoutingDomain::PublicInternet, capabilities)
}),
None => rti
.get_own_peer_info(RoutingDomain::PublicInternet)
.map(|pi| {
pi.signed_node_info()
.node_info()
.has_capabilities(capabilities)
})
.unwrap_or(false),
}
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;
let filters = VecDeque::from([filter]); let filters = VecDeque::from([filter]);
@ -40,7 +60,12 @@ impl RoutingTable {
} }
/// Utility to find nodes that are closer to a key than our own node, returning their peer info /// Utility to find nodes that are closer to a key than our own node, returning their peer info
pub fn find_peers_closer_to_key(&self, key: TypedKey) -> NetworkResult<Vec<PeerInfo>> { /// Can filter based on a particular set of capabiltiies
pub fn find_peers_closer_to_key(
&self,
key: TypedKey,
required_capabilities: Vec<Capability>,
) -> NetworkResult<Vec<PeerInfo>> {
// add node information for the requesting node to our routing table // add node information for the requesting node to our routing table
let crypto_kind = key.kind; let crypto_kind = key.kind;
let own_node_id = self.node_id(crypto_kind); let own_node_id = self.node_id(crypto_kind);
@ -59,6 +84,11 @@ impl RoutingTable {
let Some(entry) = opt_entry else { let Some(entry) = opt_entry else {
return false; return false;
}; };
// Ensure only things that have a minimum set of capabilities are returned
entry.with(rti, |rti, e| {
if !e.has_capabilities(RoutingDomain::PublicInternet, &required_capabilities) {
return false;
}
// Ensure only things that are valid/signed in the PublicInternet domain are returned // Ensure only things that are valid/signed in the PublicInternet domain are returned
if !rti.filter_has_valid_signed_node_info( if !rti.filter_has_valid_signed_node_info(
RoutingDomain::PublicInternet, RoutingDomain::PublicInternet,
@ -68,15 +98,15 @@ impl RoutingTable {
return false; return false;
} }
// Ensure things further from the key than our own node are not included // Ensure things further from the key than our own node are not included
let Some(entry_node_id) = entry.with(rti, |_rti, e| e.node_ids().get(crypto_kind)) else { let Some(entry_node_id) = e.node_ids().get(crypto_kind) else {
return false; return false;
}; };
let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value); let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value);
if entry_distance >= own_distance { if entry_distance >= own_distance {
return false; return false;
} }
true true
})
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;
let filters = VecDeque::from([filter]); let filters = VecDeque::from([filter]);

View File

@ -1072,7 +1072,7 @@ impl RoutingTable {
let res = network_result_try!( let res = network_result_try!(
rpc_processor rpc_processor
.clone() .clone()
.rpc_call_find_node(Destination::direct(node_ref), node_id) .rpc_call_find_node(Destination::direct(node_ref), node_id, vec![])
.await? .await?
); );

View File

@ -118,9 +118,9 @@ pub trait NodeRefBase: Sized {
fn set_updated_since_last_network_change(&self) { fn set_updated_since_last_network_change(&self) {
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true)); self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
} }
fn update_node_status(&self, node_status: NodeStatus) { fn update_node_status(&self, routing_domain: RoutingDomain, node_status: NodeStatus) {
self.operate_mut(|_rti, e| { self.operate_mut(|_rti, e| {
e.update_node_status(node_status); e.update_node_status(routing_domain, node_status);
}); });
} }
fn envelope_support(&self) -> Vec<u8> { fn envelope_support(&self) -> Vec<u8> {

View File

@ -257,20 +257,9 @@ impl RouteSpecStore {
// Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route
entry.with_inner(|e| { entry.with_inner(|e| {
let node_info_ok = e.signed_node_info(RoutingDomain::PublicInternet).map(|sni|
if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) { sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().has_capability(CAP_ROUTE)
sni.has_sequencing_matched_dial_info(sequencing) ).unwrap_or(false)
} else {
false
};
let node_status_ok =
if let Some(ns) = e.node_status(RoutingDomain::PublicInternet) {
ns.will_route()
} else {
false
};
node_info_ok && node_status_ok
}) })
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;

View File

@ -13,6 +13,7 @@ enum RoutingDomainChange {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
}, },
SetNetworkClass { SetNetworkClass {
network_class: Option<NetworkClass>, network_class: Option<NetworkClass>,
@ -79,11 +80,13 @@ impl RoutingDomainEditor {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
) { ) {
self.changes.push(RoutingDomainChange::SetupNetwork { self.changes.push(RoutingDomainChange::SetupNetwork {
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
}) })
} }
@ -142,27 +145,32 @@ impl RoutingDomainEditor {
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
} => { } => {
let old_outbound_protocols = detail.common().outbound_protocols(); let old_outbound_protocols = detail.common().outbound_protocols();
let old_inbound_protocols = detail.common().inbound_protocols(); let old_inbound_protocols = detail.common().inbound_protocols();
let old_address_types = detail.common().address_types(); let old_address_types = detail.common().address_types();
let old_capabilities = detail.common().capabilities();
let this_changed = old_outbound_protocols != outbound_protocols let this_changed = old_outbound_protocols != outbound_protocols
|| old_inbound_protocols != inbound_protocols || old_inbound_protocols != inbound_protocols
|| old_address_types != address_types; || old_address_types != address_types
|| old_capabilities != capabilities;
debug!( debug!(
"[{:?}] setup network: {:?} {:?} {:?}", "[{:?}] setup network: {:?} {:?} {:?} {:?}",
self.routing_domain, self.routing_domain,
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types address_types,
capabilities
); );
detail.common_mut().setup_network( detail.common_mut().setup_network(
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
); );
if this_changed { if this_changed {
changed = true; changed = true;

View File

@ -27,6 +27,7 @@ pub struct RoutingDomainDetailCommon {
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
relay_node: Option<NodeRef>, relay_node: Option<NodeRef>,
capabilities: Vec<Capability>,
dial_info_details: Vec<DialInfoDetail>, dial_info_details: Vec<DialInfoDetail>,
// caches // caches
cached_peer_info: Mutex<Option<PeerInfo>>, cached_peer_info: Mutex<Option<PeerInfo>>,
@ -41,6 +42,7 @@ impl RoutingDomainDetailCommon {
inbound_protocols: Default::default(), inbound_protocols: Default::default(),
address_types: Default::default(), address_types: Default::default(),
relay_node: Default::default(), relay_node: Default::default(),
capabilities: Default::default(),
dial_info_details: Default::default(), dial_info_details: Default::default(),
cached_peer_info: Mutex::new(Default::default()), cached_peer_info: Mutex::new(Default::default()),
} }
@ -52,10 +54,12 @@ impl RoutingDomainDetailCommon {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
) { ) {
self.outbound_protocols = outbound_protocols; self.outbound_protocols = outbound_protocols;
self.inbound_protocols = inbound_protocols; self.inbound_protocols = inbound_protocols;
self.address_types = address_types; self.address_types = address_types;
self.capabilities = capabilities;
self.clear_cache(); self.clear_cache();
} }
@ -75,6 +79,9 @@ impl RoutingDomainDetailCommon {
pub fn address_types(&self) -> AddressTypeSet { pub fn address_types(&self) -> AddressTypeSet {
self.address_types self.address_types
} }
pub fn capabilities(&self) -> Vec<Capability> {
self.capabilities.clone()
}
pub fn relay_node(&self) -> Option<NodeRef> { pub fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone() self.relay_node.clone()
} }
@ -108,6 +115,7 @@ impl RoutingDomainDetailCommon {
self.address_types, self.address_types,
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
self.capabilities.clone(),
self.dial_info_details.clone() self.dial_info_details.clone()
); );

View File

@ -344,6 +344,7 @@ impl RoutingTable {
AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
bsrec.envelope_support, // Envelope support is as specified in the bootstrap list bsrec.envelope_support, // Envelope support is as specified in the bootstrap list
crypto_support, // Crypto support is derived from list of node ids crypto_support, // Crypto support is derived from list of node ids
vec![], // Bootstrap needs no capabilities
bsrec.dial_info_details, // Dial info is as specified in the bootstrap list bsrec.dial_info_details, // Dial info is as specified in the bootstrap list
))); )));

View File

@ -7,6 +7,14 @@ use stop_token::future::FutureExt as StopFutureExt;
const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2; const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2;
impl RoutingTable { impl RoutingTable {
fn get_background_safety_route_count(&self) -> usize {
let c = self.config.get();
if c.capabilities.disable.contains(&CAP_ROUTE) {
0
} else {
BACKGROUND_SAFETY_ROUTE_COUNT
}
}
/// Fastest routes sort /// Fastest routes sort
fn route_sort_latency_fn(a: &(RouteId, u64), b: &(RouteId, u64)) -> cmp::Ordering { fn route_sort_latency_fn(a: &(RouteId, u64), b: &(RouteId, u64)) -> cmp::Ordering {
let mut al = a.1; let mut al = a.1;
@ -69,13 +77,14 @@ impl RoutingTable {
unpublished_routes.sort_by(Self::route_sort_latency_fn); unpublished_routes.sort_by(Self::route_sort_latency_fn);
// Save up to N unpublished routes and test them // Save up to N unpublished routes and test them
for x in 0..(usize::min(BACKGROUND_SAFETY_ROUTE_COUNT, unpublished_routes.len())) { let background_safety_route_count = self.get_background_safety_route_count();
for x in 0..(usize::min(background_safety_route_count, unpublished_routes.len())) {
must_test_routes.push(unpublished_routes[x].0); must_test_routes.push(unpublished_routes[x].0);
} }
// Kill off all but N unpublished routes rather than testing them // Kill off all but N unpublished routes rather than testing them
if unpublished_routes.len() > BACKGROUND_SAFETY_ROUTE_COUNT { if unpublished_routes.len() > background_safety_route_count {
for x in &unpublished_routes[BACKGROUND_SAFETY_ROUTE_COUNT..] { for x in &unpublished_routes[background_safety_route_count..] {
expired_routes.push(x.0); expired_routes.push(x.0);
} }
} }
@ -192,8 +201,11 @@ impl RoutingTable {
} }
Option::<()>::None Option::<()>::None
}); });
if local_unpublished_route_count < BACKGROUND_SAFETY_ROUTE_COUNT {
let routes_to_allocate = BACKGROUND_SAFETY_ROUTE_COUNT - local_unpublished_route_count; let background_safety_route_count = self.get_background_safety_route_count();
if local_unpublished_route_count < background_safety_route_count {
let routes_to_allocate = background_safety_route_count - local_unpublished_route_count;
// Newly allocated routes // Newly allocated routes
let mut newly_allocated_routes = Vec::new(); let mut newly_allocated_routes = Vec::new();

View File

@ -100,6 +100,11 @@ impl RoutingTable {
let can_serve_as_relay = e let can_serve_as_relay = e
.node_info(RoutingDomain::PublicInternet) .node_info(RoutingDomain::PublicInternet)
.map(|n| { .map(|n| {
if !(n.has_capability(CAP_RELAY) && n.is_signal_capable()) {
// Needs to be able to signal and relay
return false;
}
let dids = n.all_filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| { let dids = n.all_filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| {
did.matches_filter(&outbound_dif) did.matches_filter(&outbound_dif)
}); });
@ -145,10 +150,8 @@ impl RoutingTable {
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| { inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| {
let entry2 = entry.clone(); let entry2 = entry.clone();
entry.with(rti, |rti, e| { entry.with(rti, |rti, e| {
// Ensure we have the node's status // Filter this node
if let Some(node_status) = e.node_status(routing_domain) { if relay_node_filter(e) {
// Ensure the node will relay
if node_status.will_relay() {
// Compare against previous candidate // Compare against previous candidate
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
// Less is faster // Less is faster
@ -158,15 +161,14 @@ impl RoutingTable {
== std::cmp::Ordering::Less == std::cmp::Ordering::Less
}); });
// Now apply filter function and see if this node should be included // Now apply filter function and see if this node should be included
if better && relay_node_filter(e) { if better {
*best_inbound_relay = entry2; *best_inbound_relay = entry2;
} }
} else if relay_node_filter(e) { } else {
// Always store the first candidate // Always store the first candidate
best_inbound_relay = Some(entry2); best_inbound_relay = Some(entry2);
} }
} }
}
}); });
// Don't end early, iterate through all entries // Don't end early, iterate through all entries
Option::<()>::None Option::<()>::None

View File

@ -100,6 +100,7 @@ pub async fn test_round_trip_peerinfo() {
AddressTypeSet::new(), AddressTypeSet::new(),
vec![0], vec![0],
vec![CRYPTO_KIND_VLD0], vec![CRYPTO_KIND_VLD0],
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![], vec![],
), ),
Timestamp::new(0), Timestamp::new(0),

View File

@ -1,5 +1,54 @@
use super::*; use super::*;
pub type Capability = FourCC;
pub const CAP_ROUTE: Capability = FourCC(*b"ROUT");
#[cfg(feature = "unstable-tunnels")]
pub const CAP_TUNNEL: Capability = FourCC(*b"TUNL");
pub const CAP_SIGNAL: Capability = FourCC(*b"SGNL");
pub const CAP_RELAY: Capability = FourCC(*b"RLAY");
pub const CAP_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL");
pub const CAP_DHT: Capability = FourCC(*b"DHTV");
pub const CAP_APPMESSAGE: Capability = FourCC(*b"APPM");
#[cfg(feature = "unstable-blockstore")]
pub const CAP_BLOCKSTORE: Capability = FourCC(*b"BLOC");
cfg_if! {
if #[cfg(all(feature = "unstable-blockstore", feature="unstable-tunnels"))] {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 8;
} else if #[cfg(any(feature = "unstable-blockstore", feature="unstable-tunnels"))] {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 7;
} else {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 6;
}
}
pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIES_LEN] = [
CAP_ROUTE,
#[cfg(feature = "unstable-tunnels")]
CAP_TUNNEL,
CAP_SIGNAL,
CAP_RELAY,
CAP_VALIDATE_DIAL_INFO,
CAP_DHT,
CAP_APPMESSAGE,
#[cfg(feature = "unstable-blockstore")]
CAP_BLOCKSTORE,
];
#[cfg(feature = "unstable-blockstore")]
const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 4;
#[cfg(not(feature = "unstable-blockstore"))]
const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3;
pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [
CAP_RELAY,
CAP_DHT,
CAP_APPMESSAGE,
#[cfg(feature = "unstable-blockstore")]
CAP_BLOCKSTORE,
];
pub const MAX_CAPABILITIES: usize = 64;
#[derive( #[derive(
Clone, Clone,
Default, Default,
@ -21,6 +70,7 @@ pub struct NodeInfo {
address_types: AddressTypeSet, address_types: AddressTypeSet,
envelope_support: Vec<u8>, envelope_support: Vec<u8>,
crypto_support: Vec<CryptoKind>, crypto_support: Vec<CryptoKind>,
capabilities: Vec<Capability>,
dial_info_detail_list: Vec<DialInfoDetail>, dial_info_detail_list: Vec<DialInfoDetail>,
} }
@ -31,6 +81,7 @@ impl NodeInfo {
address_types: AddressTypeSet, address_types: AddressTypeSet,
envelope_support: Vec<u8>, envelope_support: Vec<u8>,
crypto_support: Vec<CryptoKind>, crypto_support: Vec<CryptoKind>,
capabilities: Vec<Capability>,
dial_info_detail_list: Vec<DialInfoDetail>, dial_info_detail_list: Vec<DialInfoDetail>,
) -> Self { ) -> Self {
Self { Self {
@ -39,6 +90,7 @@ impl NodeInfo {
address_types, address_types,
envelope_support, envelope_support,
crypto_support, crypto_support,
capabilities,
dial_info_detail_list, dial_info_detail_list,
} }
} }
@ -58,6 +110,9 @@ impl NodeInfo {
pub fn crypto_support(&self) -> &[CryptoKind] { pub fn crypto_support(&self) -> &[CryptoKind] {
&self.crypto_support &self.crypto_support
} }
pub fn capabilities(&self) -> &[Capability] {
&self.capabilities
}
pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] { pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] {
&self.dial_info_detail_list &self.dial_info_detail_list
} }
@ -144,8 +199,27 @@ impl NodeInfo {
false false
} }
pub fn has_capability(&self, cap: Capability) -> bool {
self.capabilities.contains(&cap)
}
pub fn has_capabilities(&self, capabilities: &[Capability]) -> bool {
for cap in capabilities {
if !self.has_capability(*cap) {
return false;
}
}
true
}
/// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
pub fn can_signal(&self) -> bool { /// Also used to determine if nodes are capable of validation of dial info, as that operation
/// has the same requirements, inbound capability and a dial info that requires no assistance
pub fn is_signal_capable(&self) -> bool {
// Has capability?
if !self.has_capability(CAP_SIGNAL) {
return false;
}
// Must be inbound capable // Must be inbound capable
if !matches!(self.network_class, NetworkClass::InboundCapable) { if !matches!(self.network_class, NetworkClass::InboundCapable) {
return false; return false;
@ -158,16 +232,4 @@ impl NodeInfo {
} }
true true
} }
/// Can this node relay be an inbound relay?
pub fn can_inbound_relay(&self) -> bool {
// For now this is the same
self.can_signal()
}
/// Is this node capable of validating dial info
pub fn can_validate_dial_info(&self) -> bool {
// For now this is the same
self.can_signal()
}
} }

View File

@ -1,66 +1,9 @@
use super::*; use super::*;
/// RoutingDomain-specific status for each node /// Non-nodeinfo status for each node is returned by the StatusA call
/// is returned by the StatusA call
/// PublicInternet RoutingDomain Status
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct PublicInternetNodeStatus {
pub will_route: bool,
pub will_tunnel: bool,
pub will_signal: bool,
pub will_relay: bool,
pub will_validate_dial_info: bool,
}
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct LocalNetworkNodeStatus {
pub will_relay: bool,
pub will_validate_dial_info: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] #[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(u8), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes))]
pub enum NodeStatus { pub struct NodeStatus {
PublicInternet(PublicInternetNodeStatus), // Reserved for expansion
LocalNetwork(LocalNetworkNodeStatus),
}
impl NodeStatus {
pub fn will_route(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_route,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_tunnel(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_tunnel,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_signal(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_signal,
NodeStatus::LocalNetwork(_) => false,
}
}
pub fn will_relay(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_relay,
NodeStatus::LocalNetwork(ln) => ln.will_relay,
}
}
pub fn will_validate_dial_info(&self) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info,
NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info,
}
}
} }

View File

@ -31,6 +31,18 @@ pub fn encode_node_info(
s.clone_from_slice(&csvec); s.clone_from_slice(&csvec);
} }
let mut cap_builder = builder
.reborrow()
.init_capabilities(node_info.capabilities().len() as u32);
if let Some(s) = cap_builder.as_slice() {
let capvec: Vec<u32> = node_info
.capabilities()
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&capvec);
}
let mut didl_builder = builder.reborrow().init_dial_info_detail_list( let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
node_info node_info
.dial_info_detail_list() .dial_info_detail_list()
@ -71,13 +83,11 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
.map_err(RPCError::protocol)?, .map_err(RPCError::protocol)?,
)?; )?;
let envelope_support = reader let es_reader = reader
.reborrow() .reborrow()
.get_envelope_support() .get_envelope_support()
.map_err(RPCError::protocol)? .map_err(RPCError::protocol)?;
.as_slice() let envelope_support = es_reader.as_slice().map(|s| s.to_vec()).unwrap_or_default();
.map(|s| s.to_vec())
.unwrap_or_default();
// Ensure envelope versions are not duplicated // Ensure envelope versions are not duplicated
// Unsorted is okay, some nodes may have a different envelope order preference // Unsorted is okay, some nodes may have a different envelope order preference
@ -94,10 +104,16 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
return Err(RPCError::protocol("no envelope versions")); return Err(RPCError::protocol("no envelope versions"));
} }
let crypto_support: Vec<CryptoKind> = reader let cs_reader = reader
.reborrow() .reborrow()
.get_crypto_support() .get_crypto_support()
.map_err(RPCError::protocol)? .map_err(RPCError::protocol)?;
if cs_reader.len() as usize > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many crypto kinds"));
}
let crypto_support: Vec<CryptoKind> = cs_reader
.as_slice() .as_slice()
.map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect())
.unwrap_or_default(); .unwrap_or_default();
@ -117,6 +133,18 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
return Err(RPCError::protocol("no crypto kinds")); return Err(RPCError::protocol("no crypto kinds"));
} }
let cap_reader = reader
.reborrow()
.get_capabilities()
.map_err(RPCError::protocol)?;
if cap_reader.len() as usize > MAX_CAPABILITIES {
return Err(RPCError::protocol("too many capabilities"));
}
let capabilities = cap_reader
.as_slice()
.map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect())
.unwrap_or_default();
let didl_reader = reader let didl_reader = reader
.reborrow() .reborrow()
.get_dial_info_detail_list() .get_dial_info_detail_list()
@ -137,6 +165,7 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
address_types, address_types,
envelope_support, envelope_support,
crypto_support, crypto_support,
capabilities,
dial_info_detail_list, dial_info_detail_list,
)) ))
} }

View File

@ -1,83 +1,14 @@
use super::*; use super::*;
pub fn encode_public_internet_node_status(
public_internet_node_status: &PublicInternetNodeStatus,
builder: &mut veilid_capnp::public_internet_node_status::Builder,
) -> Result<(), RPCError> {
builder.set_will_route(public_internet_node_status.will_route);
builder.set_will_tunnel(public_internet_node_status.will_tunnel);
builder.set_will_signal(public_internet_node_status.will_signal);
builder.set_will_relay(public_internet_node_status.will_relay);
builder.set_will_validate_dial_info(public_internet_node_status.will_validate_dial_info);
Ok(())
}
pub fn decode_public_internet_node_status(
reader: &veilid_capnp::public_internet_node_status::Reader,
) -> Result<PublicInternetNodeStatus, RPCError> {
Ok(PublicInternetNodeStatus {
will_route: reader.reborrow().get_will_route(),
will_tunnel: reader.reborrow().get_will_tunnel(),
will_signal: reader.reborrow().get_will_signal(),
will_relay: reader.reborrow().get_will_relay(),
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
})
}
pub fn encode_local_network_node_status(
local_network_node_status: &LocalNetworkNodeStatus,
builder: &mut veilid_capnp::local_network_node_status::Builder,
) -> Result<(), RPCError> {
builder.set_will_relay(local_network_node_status.will_relay);
builder.set_will_validate_dial_info(local_network_node_status.will_validate_dial_info);
Ok(())
}
pub fn decode_local_network_node_status(
reader: &veilid_capnp::local_network_node_status::Reader,
) -> Result<LocalNetworkNodeStatus, RPCError> {
Ok(LocalNetworkNodeStatus {
will_relay: reader.reborrow().get_will_relay(),
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
})
}
pub fn encode_node_status( pub fn encode_node_status(
node_status: &NodeStatus, _node_status: &NodeStatus,
builder: &mut veilid_capnp::node_status::Builder, _builder: &mut veilid_capnp::node_status::Builder,
) -> Result<(), RPCError> { ) -> Result<(), RPCError> {
match node_status { Ok(())
NodeStatus::PublicInternet(ns) => {
let mut pi_builder = builder.reborrow().init_public_internet();
encode_public_internet_node_status(&ns, &mut pi_builder)
}
NodeStatus::LocalNetwork(ns) => {
let mut ln_builder = builder.reborrow().init_local_network();
encode_local_network_node_status(&ns, &mut ln_builder)
}
}
} }
pub fn decode_node_status( pub fn decode_node_status(
reader: &veilid_capnp::node_status::Reader, _reader: &veilid_capnp::node_status::Reader,
) -> Result<NodeStatus, RPCError> { ) -> Result<NodeStatus, RPCError> {
Ok( Ok(NodeStatus {})
match reader
.which()
.map_err(RPCError::map_internal("invalid node status"))?
{
veilid_capnp::node_status::PublicInternet(pi) => {
let r = pi.map_err(RPCError::protocol)?;
let pins = decode_public_internet_node_status(&r)?;
NodeStatus::PublicInternet(pins)
}
veilid_capnp::node_status::LocalNetwork(ln) => {
let r = ln.map_err(RPCError::protocol)?;
let lnns = decode_local_network_node_status(&r)?;
NodeStatus::LocalNetwork(lnns)
}
},
)
} }

View File

@ -5,11 +5,15 @@ const MAX_FIND_NODE_A_PEERS_LEN: usize = 20;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RPCOperationFindNodeQ { pub struct RPCOperationFindNodeQ {
node_id: TypedKey, node_id: TypedKey,
capabilities: Vec<Capability>,
} }
impl RPCOperationFindNodeQ { impl RPCOperationFindNodeQ {
pub fn new(node_id: TypedKey) -> Self { pub fn new(node_id: TypedKey, capabilities: Vec<Capability>) -> Self {
Self { node_id } Self {
node_id,
capabilities,
}
} }
pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> { pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> {
Ok(()) Ok(())
@ -18,15 +22,33 @@ impl RPCOperationFindNodeQ {
// pub fn node_id(&self) -> &TypedKey { // pub fn node_id(&self) -> &TypedKey {
// &self.node_id // &self.node_id
// } // }
// pub fn capabilities(&self) -> &[Capability] {
// &self.capabilities
// }
pub fn destructure(self) -> TypedKey { pub fn destructure(self) -> (TypedKey, Vec<Capability>) {
self.node_id (self.node_id, self.capabilities)
} }
pub fn decode(reader: &veilid_capnp::operation_find_node_q::Reader) -> Result<Self, RPCError> { pub fn decode(reader: &veilid_capnp::operation_find_node_q::Reader) -> Result<Self, RPCError> {
let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?; let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?;
let node_id = decode_typed_key(&ni_reader)?; let node_id = decode_typed_key(&ni_reader)?;
Ok(Self { node_id }) let cap_reader = reader
.reborrow()
.get_capabilities()
.map_err(RPCError::protocol)?;
if cap_reader.len() as usize > MAX_CAPABILITIES {
return Err(RPCError::protocol("too many capabilities"));
}
let capabilities = cap_reader
.as_slice()
.map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect())
.unwrap_or_default();
Ok(Self {
node_id,
capabilities,
})
} }
pub fn encode( pub fn encode(
&self, &self,
@ -34,6 +56,19 @@ impl RPCOperationFindNodeQ {
) -> Result<(), RPCError> { ) -> Result<(), RPCError> {
let mut ni_builder = builder.reborrow().init_node_id(); let mut ni_builder = builder.reborrow().init_node_id();
encode_typed_key(&self.node_id, &mut ni_builder); encode_typed_key(&self.node_id, &mut ni_builder);
let mut cap_builder = builder
.reborrow()
.init_capabilities(self.capabilities.len() as u32);
if let Some(s) = cap_builder.as_slice() {
let capvec: Vec<u32> = self
.capabilities
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&capvec);
}
Ok(()) Ok(())
} }
} }

View File

@ -117,6 +117,13 @@ impl RPCMessageHeader {
// RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.peer_noderef.clone(), // RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.peer_noderef.clone(),
// } // }
// } // }
pub fn routing_domain(&self) -> RoutingDomain {
match &self.detail {
RPCMessageHeaderDetail::Direct(d) => d.routing_domain,
RPCMessageHeaderDetail::SafetyRouted(s) => s.direct.routing_domain,
RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.routing_domain,
}
}
pub fn direct_sender_node_id(&self) -> TypedKey { pub fn direct_sender_node_id(&self) -> TypedKey {
match &self.detail { match &self.detail {
RPCMessageHeaderDetail::Direct(d) => { RPCMessageHeaderDetail::Direct(d) => {
@ -435,9 +442,11 @@ impl RPCProcessor {
&self, &self,
routing_domain: RoutingDomain, routing_domain: RoutingDomain,
signed_node_info: &SignedNodeInfo, signed_node_info: &SignedNodeInfo,
capabilities: &[Capability],
) -> bool { ) -> bool {
let routing_table = self.routing_table(); let routing_table = self.routing_table();
routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info) routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info)
&& signed_node_info.node_info().has_capabilities(capabilities)
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -463,6 +472,7 @@ impl RPCProcessor {
.rpc_call_find_node( .rpc_call_find_node(
Destination::direct(next_node).with_safety(safety_selection), Destination::direct(next_node).with_safety(safety_selection),
node_id, node_id,
vec![],
) )
.await .await
{ {
@ -1467,7 +1477,11 @@ impl RPCProcessor {
// Ensure the sender peer info is for the actual sender specified in the envelope // Ensure the sender peer info is for the actual sender specified in the envelope
// Sender PeerInfo was specified, update our routing table with it // Sender PeerInfo was specified, update our routing table with it
if !self.verify_node_info(routing_domain, sender_peer_info.signed_node_info()) { if !self.verify_node_info(
routing_domain,
sender_peer_info.signed_node_info(),
&[],
) {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"sender peerinfo has invalid peer scope", "sender peerinfo has invalid peer scope",
)); ));

View File

@ -53,6 +53,22 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi
.signed_node_info()
.node_info()
.has_capability(CAP_APPMESSAGE)
{
return Ok(NetworkResult::service_unavailable(
"app call is not available",
));
}
}
}
// Get the question // Get the question
let (op_id, _, _, kind) = msg.operation.clone().destructure(); let (op_id, _, _, kind) = msg.operation.clone().destructure();
let app_call_q = match kind { let app_call_q = match kind {

View File

@ -24,6 +24,22 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi
.signed_node_info()
.node_info()
.has_capability(CAP_APPMESSAGE)
{
return Ok(NetworkResult::service_unavailable(
"app message is not available",
));
}
}
}
// Get the statement // Get the statement
let (_, _, _, kind) = msg.operation.destructure(); let (_, _, _, kind) = msg.operation.destructure();
let app_message = match kind { let app_message = match kind {

View File

@ -6,6 +6,25 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi
.signed_node_info()
.node_info()
.has_capability(CAP_TUNNEL)
{
return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
}
}
Err(RPCError::unimplemented("process_cancel_tunnel_q")) Err(RPCError::unimplemented("process_cancel_tunnel_q"))
} }
} }

View File

@ -6,6 +6,24 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi
.signed_node_info()
.node_info()
.has_capability(CAP_TUNNEL)
{
return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
}
}
Err(RPCError::unimplemented("process_complete_tunnel_q")) Err(RPCError::unimplemented("process_complete_tunnel_q"))
} }
} }

View File

@ -6,6 +6,20 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
#[cfg(feature = "unstable-blockstore")]
{
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) {
if !opi.signed_node_info().node_info().can_blockstore() {
return Ok(NetworkResult::service_unavailable(
"block store is not available",
));
}
}
}
}
Err(RPCError::unimplemented("process_find_block_q")) Err(RPCError::unimplemented("process_find_block_q"))
} }
} }

View File

@ -15,6 +15,7 @@ impl RPCProcessor {
self, self,
dest: Destination, dest: Destination,
node_id: TypedKey, node_id: TypedKey,
capabilities: Vec<Capability>,
) -> Result<NetworkResult<Answer<Vec<PeerInfo>>>, RPCError> { ) -> Result<NetworkResult<Answer<Vec<PeerInfo>>>, RPCError> {
// Ensure destination never has a private route // Ensure destination never has a private route
if matches!( if matches!(
@ -29,7 +30,8 @@ impl RPCProcessor {
)); ));
} }
let find_node_q_detail = RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id)); let find_node_q_detail =
RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id, capabilities.clone()));
let find_node_q = RPCQuestion::new( let find_node_q = RPCQuestion::new(
network_result_try!(self.get_destination_respond_to(&dest)?), network_result_try!(self.get_destination_respond_to(&dest)?),
find_node_q_detail, find_node_q_detail,
@ -60,9 +62,13 @@ impl RPCProcessor {
let peers = find_node_a.destructure(); let peers = find_node_a.destructure();
for peer_info in &peers { for peer_info in &peers {
if !self.verify_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) { if !self.verify_node_info(
RoutingDomain::PublicInternet,
peer_info.signed_node_info(),
&capabilities,
) {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"find_node response has invalid peer scope", "find_node response does not meet peer criteria",
)); ));
} }
} }
@ -94,11 +100,12 @@ impl RPCProcessor {
}, },
_ => panic!("not a question"), _ => panic!("not a question"),
}; };
let node_id = find_node_q.destructure(); let (node_id, capabilities) = find_node_q.destructure();
// Get a chunk of the routing table near the requested node id // Get a chunk of the routing table near the requested node id
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let closest_nodes = network_result_try!(routing_table.find_all_closest_peers(node_id)); let closest_nodes =
network_result_try!(routing_table.find_all_closest_peers(node_id, &capabilities));
// Make FindNode answer // Make FindNode answer
let find_node_a = RPCOperationFindNodeA::new(closest_nodes)?; let find_node_a = RPCOperationFindNodeA::new(closest_nodes)?;

View File

@ -163,6 +163,7 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ensure this never came over a private route, safety route is okay though // Ensure this never came over a private route, safety route is okay though
match &msg.header.detail { match &msg.header.detail {
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {}
@ -172,7 +173,17 @@ impl RPCProcessor {
)) ))
} }
} }
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().has_capability(CAP_DHT) {
return Ok(NetworkResult::service_unavailable(
"dht is not available",
));
}
}
}
// Get the question // Get the question
let kind = msg.operation.kind().clone(); let kind = msg.operation.kind().clone();
let get_value_q = match kind { let get_value_q = match kind {
@ -188,7 +199,7 @@ impl RPCProcessor {
// Get the nodes that we know about that are closer to the the key than our own node // Get the nodes that we know about that are closer to the the key than our own node
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT]));
let debug_string = format!( let debug_string = format!(
"IN <=== GetValueQ({} #{}{}) <== {}", "IN <=== GetValueQ({} #{}{}) <== {}",

View File

@ -365,6 +365,18 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().has_capability(CAP_ROUTE) {
return Ok(NetworkResult::service_unavailable(
"route is not available",
));
}
}
}
// Get header detail, must be direct and not inside a route itself // Get header detail, must be direct and not inside a route itself
let detail = match msg.header.detail { let detail = match msg.header.detail {
RPCMessageHeaderDetail::Direct(detail) => detail, RPCMessageHeaderDetail::Direct(detail) => detail,

View File

@ -175,6 +175,17 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().has_capability(CAP_DHT) {
return Ok(NetworkResult::service_unavailable(
"dht is not available",
));
}
}
}
// Ensure this never came over a private route, safety route is okay though // Ensure this never came over a private route, safety route is okay though
match &msg.header.detail { match &msg.header.detail {
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {}
@ -200,7 +211,7 @@ impl RPCProcessor {
// Get the nodes that we know about that are closer to the the key than our own node // Get the nodes that we know about that are closer to the the key than our own node
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT]));
let debug_string = format!( let debug_string = format!(
"IN <=== SetValueQ({} #{} len={} seq={} writer={}{}) <== {}", "IN <=== SetValueQ({} #{} len={} seq={} writer={}{}) <== {}",

View File

@ -37,6 +37,18 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().is_signal_capable() {
return Ok(NetworkResult::service_unavailable(
"signal is not available",
));
}
}
}
// Can't allow anything other than direct packets here, as handling reverse connections // Can't allow anything other than direct packets here, as handling reverse connections
// or anything like via signals over private routes would deanonymize the route // or anything like via signals over private routes would deanonymize the route
match &msg.header.detail { match &msg.header.detail {

View File

@ -6,6 +6,24 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi
.signed_node_info()
.node_info()
.has_capability(CAP_TUNNEL)
{
return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
}
}
Err(RPCError::unimplemented("process_start_tunnel_q")) Err(RPCError::unimplemented("process_start_tunnel_q"))
} }
} }

View File

@ -133,25 +133,8 @@ impl RPCProcessor {
// Ensure the returned node status is the kind for the routing domain we asked for // Ensure the returned node status is the kind for the routing domain we asked for
if let Some(target_nr) = opt_target_nr { if let Some(target_nr) = opt_target_nr {
if let Some(a_node_status) = a_node_status { if let Some(a_node_status) = a_node_status {
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(a_node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(a_node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// Update latest node status in routing table // Update latest node status in routing table
target_nr.update_node_status(a_node_status.clone()); target_nr.update_node_status(routing_domain, a_node_status.clone());
} }
} }
@ -236,27 +219,10 @@ impl RPCProcessor {
// Ensure the node status from the question is the kind for the routing domain we received the request in // Ensure the node status from the question is the kind for the routing domain we received the request in
if let Some(q_node_status) = q_node_status { if let Some(q_node_status) = q_node_status {
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(q_node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(q_node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// update node status for the requesting node to our routing table // update node status for the requesting node to our routing table
if let Some(sender_nr) = msg.opt_sender_nr.clone() { if let Some(sender_nr) = msg.opt_sender_nr.clone() {
// Update latest node status in routing table for the statusq sender // Update latest node status in routing table for the statusq sender
sender_nr.update_node_status(q_node_status.clone()); sender_nr.update_node_status(routing_domain, q_node_status.clone());
} }
} }

View File

@ -6,6 +6,20 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
#[cfg(feature = "unstable-blockstore")]
{
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) {
if !opi.signed_node_info().node_info().can_blockstore() {
return Ok(NetworkResult::service_unavailable(
"block store is not available",
));
}
}
}
}
Err(RPCError::unimplemented("process_supply_block_q")) Err(RPCError::unimplemented("process_supply_block_q"))
} }
} }

View File

@ -67,6 +67,19 @@ impl RPCProcessor {
} }
}; };
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) {
let ni = opi.signed_node_info().node_info();
if !ni.has_capability(CAP_VALIDATE_DIAL_INFO) || !ni.is_signal_capable() {
return Ok(NetworkResult::service_unavailable(
"validate dial info is not available",
));
}
}
}
// Get the statement // Get the statement
let (_, _, _, kind) = msg.operation.destructure(); let (_, _, _, kind) = msg.operation.destructure();
let (dial_info, receipt, redirect) = match kind { let (dial_info, receipt, redirect) = match kind {
@ -83,7 +96,6 @@ impl RPCProcessor {
// We filter on the -outgoing- protocol capability status not the node's dial info // We filter on the -outgoing- protocol capability status not the node's dial info
// Use the address type though, to ensure we reach an ipv6 capable node if this is // Use the address type though, to ensure we reach an ipv6 capable node if this is
// an ipv6 address // an ipv6 address
let routing_table = self.routing_table();
let sender_node_id = TypedKey::new( let sender_node_id = TypedKey::new(
detail.envelope.get_crypto_kind(), detail.envelope.get_crypto_kind(),
detail.envelope.get_sender_id(), detail.envelope.get_sender_id(),
@ -104,11 +116,11 @@ impl RPCProcessor {
move |rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| { move |rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap(); let entry = v.unwrap();
entry.with(rti, move |_rti, e| { entry.with(rti, move |_rti, e| {
if let Some(status) = &e.node_status(routing_domain) { e.node_info(routing_domain)
status.will_validate_dial_info() .map(|ni| {
} else { ni.has_capability(CAP_VALIDATE_DIAL_INFO) && ni.is_signal_capable()
true })
} .unwrap_or(false)
}) })
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;

View File

@ -6,6 +6,15 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().has_capability(CAP_DHT) {
return Ok(NetworkResult::service_unavailable("dht is not available"));
}
}
}
Err(RPCError::unimplemented("process_value_changed")) Err(RPCError::unimplemented("process_value_changed"))
} }
} }

View File

@ -6,6 +6,15 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
let routing_table = self.routing_table();
{
if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if !opi.signed_node_info().node_info().has_capability(CAP_DHT) {
return Ok(NetworkResult::service_unavailable("dht is not available"));
}
}
}
Err(RPCError::unimplemented("process_watch_value_q")) Err(RPCError::unimplemented("process_watch_value_q"))
} }
} }

View File

@ -168,13 +168,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
match key.as_str() { match key.as_str() {
"program_name" => Ok(Box::new(String::from("VeilidCoreTests"))), "program_name" => Ok(Box::new(String::from("VeilidCoreTests"))),
"namespace" => Ok(Box::new(String::from(""))), "namespace" => Ok(Box::new(String::from(""))),
"capabilities.protocol_udp" => Ok(Box::new(true)), "capabilities.disable" => Ok(Box::new(Vec::<FourCC>::new())),
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)),
"capabilities.protocol_connect_ws" => Ok(Box::new(true)),
"capabilities.protocol_accept_ws" => Ok(Box::new(true)),
"capabilities.protocol_connect_wss" => Ok(Box::new(true)),
"capabilities.protocol_accept_wss" => Ok(Box::new(true)),
"table_store.directory" => Ok(Box::new(get_table_store_path())), "table_store.directory" => Ok(Box::new(get_table_store_path())),
"table_store.delete" => Ok(Box::new(true)), "table_store.delete" => Ok(Box::new(true)),
"block_store.directory" => Ok(Box::new(get_block_store_path())), "block_store.directory" => Ok(Box::new(get_block_store_path())),
@ -299,13 +293,7 @@ pub async fn test_config() {
let inner = vc.get(); let inner = vc.get();
assert_eq!(inner.program_name, String::from("VeilidCoreTests")); assert_eq!(inner.program_name, String::from("VeilidCoreTests"));
assert_eq!(inner.namespace, String::from("")); assert_eq!(inner.namespace, String::from(""));
assert_eq!(inner.capabilities.protocol_udp, true); assert_eq!(inner.capabilities.disable, Vec::<FourCC>::new());
assert_eq!(inner.capabilities.protocol_connect_tcp, true);
assert_eq!(inner.capabilities.protocol_accept_tcp, true);
assert_eq!(inner.capabilities.protocol_connect_ws, true);
assert_eq!(inner.capabilities.protocol_accept_ws, true);
assert_eq!(inner.capabilities.protocol_connect_wss, true);
assert_eq!(inner.capabilities.protocol_accept_wss, true);
assert_eq!(inner.table_store.directory, get_table_store_path()); assert_eq!(inner.table_store.directory, get_table_store_path());
assert_eq!(inner.table_store.delete, true); assert_eq!(inner.table_store.delete, true);
assert_eq!(inner.block_store.directory, get_block_store_path()); assert_eq!(inner.block_store.directory, get_block_store_path());

View File

@ -76,13 +76,7 @@ pub fn fix_veilidconfiginner() -> VeilidConfigInner {
program_name: "Bob".to_string(), program_name: "Bob".to_string(),
namespace: "Internets".to_string(), namespace: "Internets".to_string(),
capabilities: VeilidConfigCapabilities { capabilities: VeilidConfigCapabilities {
protocol_udp: false, disable: Vec::new(),
protocol_connect_tcp: true,
protocol_accept_tcp: false,
protocol_connect_ws: true,
protocol_accept_ws: false,
protocol_connect_wss: true,
protocol_accept_wss: false,
}, },
protected_store: VeilidConfigProtectedStore { protected_store: VeilidConfigProtectedStore {
allow_insecure_fallback: true, allow_insecure_fallback: true,

View File

@ -468,13 +468,7 @@ pub struct VeilidConfigProtectedStore {
JsonSchema, JsonSchema,
)] )]
pub struct VeilidConfigCapabilities { pub struct VeilidConfigCapabilities {
pub protocol_udp: bool, pub disable: Vec<FourCC>,
pub protocol_connect_tcp: bool,
pub protocol_accept_tcp: bool,
pub protocol_connect_ws: bool,
pub protocol_accept_ws: bool,
pub protocol_connect_wss: bool,
pub protocol_accept_wss: bool,
} }
#[derive( #[derive(
@ -670,13 +664,7 @@ impl VeilidConfig {
get_config!(inner.program_name); get_config!(inner.program_name);
get_config!(inner.namespace); get_config!(inner.namespace);
get_config!(inner.capabilities.protocol_udp); get_config!(inner.capabilities.disable);
get_config!(inner.capabilities.protocol_connect_tcp);
get_config!(inner.capabilities.protocol_accept_tcp);
get_config!(inner.capabilities.protocol_connect_ws);
get_config!(inner.capabilities.protocol_accept_ws);
get_config!(inner.capabilities.protocol_connect_wss);
get_config!(inner.capabilities.protocol_accept_wss);
get_config!(inner.table_store.directory); get_config!(inner.table_store.directory);
get_config!(inner.table_store.delete); get_config!(inner.table_store.delete);
get_config!(inner.block_store.directory); get_config!(inner.block_store.directory);

View File

@ -52,15 +52,7 @@ Future<VeilidConfig> getDefaultVeilidConfig(String programName) async {
return VeilidConfig( return VeilidConfig(
programName: programName, programName: programName,
namespace: "", namespace: "",
capabilities: VeilidConfigCapabilities( capabilities: VeilidConfigCapabilities(disable: []),
protocolUDP: !kIsWeb,
protocolConnectTCP: !kIsWeb,
protocolAcceptTCP: !kIsWeb,
protocolConnectWS: true,
protocolAcceptWS: !kIsWeb,
protocolConnectWSS: true,
protocolAcceptWSS: false,
),
protectedStore: VeilidConfigProtectedStore( protectedStore: VeilidConfigProtectedStore(
allowInsecureFallback: false, allowInsecureFallback: false,
alwaysUseInsecureStorage: false, alwaysUseInsecureStorage: false,

View File

@ -867,44 +867,19 @@ class VeilidConfigProtectedStore {
//////////// ////////////
class VeilidConfigCapabilities { class VeilidConfigCapabilities {
bool protocolUDP; List<String> disable;
bool protocolConnectTCP;
bool protocolAcceptTCP;
bool protocolConnectWS;
bool protocolAcceptWS;
bool protocolConnectWSS;
bool protocolAcceptWSS;
VeilidConfigCapabilities({ VeilidConfigCapabilities({
required this.protocolUDP, required this.disable,
required this.protocolConnectTCP,
required this.protocolAcceptTCP,
required this.protocolConnectWS,
required this.protocolAcceptWS,
required this.protocolConnectWSS,
required this.protocolAcceptWSS,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'protocol_udp': protocolUDP, 'disable': disable,
'protocol_connect_tcp': protocolConnectTCP,
'protocol_accept_tcp': protocolAcceptTCP,
'protocol_connect_ws': protocolConnectWS,
'protocol_accept_ws': protocolAcceptWS,
'protocol_connect_wss': protocolConnectWSS,
'protocol_accept_wss': protocolAcceptWSS,
}; };
} }
VeilidConfigCapabilities.fromJson(dynamic json) VeilidConfigCapabilities.fromJson(dynamic json) : disable = json['disable'];
: protocolUDP = json['protocol_udp'],
protocolConnectTCP = json['protocol_connect_tcp'],
protocolAcceptTCP = json['protocol_accept_tcp'],
protocolConnectWS = json['protocol_connect_ws'],
protocolAcceptWS = json['protocol_accept_ws'],
protocolConnectWSS = json['protocol_connect_wss'],
protocolAcceptWSS = json['protocol_accept_wss'];
} }
//////////// ////////////

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass, fields
from enum import StrEnum from enum import StrEnum
from typing import Optional, Self from typing import Optional, Self
from .types import TypedKey, TypedSecret from .types import TypedKey, TypedSecret, Capability
class VeilidConfigLogLevel(StrEnum): class VeilidConfigLogLevel(StrEnum):
@ -41,13 +41,7 @@ class ConfigBase:
@dataclass @dataclass
class VeilidConfigCapabilities(ConfigBase): class VeilidConfigCapabilities(ConfigBase):
protocol_udp: bool disable: list[Capability]
protocol_connect_tcp: bool
protocol_accept_tcp: bool
protocol_connect_ws: bool
protocol_accept_ws: bool
protocol_connect_wss: bool
protocol_accept_wss: bool
@dataclass @dataclass

View File

@ -52,6 +52,17 @@ class CryptoKind(StrEnum):
CRYPTO_KIND_VLD0 = "VLD0" CRYPTO_KIND_VLD0 = "VLD0"
class Capability(StrEnum):
CAP_WILL_ROUTE = "ROUT"
CAP_TUNNEL = "TUNL"
CAP_WILL_SIGNAL = "SGNL"
CAP_WILL_RELAY = "RLAY"
CAP_WILL_VALIDATE_DIAL_INFO = "DIAL"
CAP_WILL_DHT = "DHTV"
CAP_WILL_APPMESSAGE = "APPM"
CAP_BLOCKSTORE = "BLOC"
class Stability(StrEnum): class Stability(StrEnum):
LOW_LATENCY = "LowLatency" LOW_LATENCY = "LowLatency"
RELIABLE = "Reliable" RELIABLE = "Reliable"
@ -67,6 +78,7 @@ class DHTSchemaKind(StrEnum):
DFLT = "DFLT" DFLT = "DFLT"
SMPL = "SMPL" SMPL = "SMPL"
class SafetySelectionKind(StrEnum): class SafetySelectionKind(StrEnum):
UNSAFE = "Unsafe" UNSAFE = "Unsafe"
SAFE = "Safe" SAFE = "Safe"
@ -239,7 +251,6 @@ class VeilidVersion:
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, VeilidVersion) and self.data == other.data and self.seq == other.seq and self.writer == other.writer return isinstance(other, VeilidVersion) and self.data == other.data and self.seq == other.seq and self.writer == other.writer
@property @property
def major(self): def major(self):
return self._major return self._major
@ -308,7 +319,8 @@ class DHTSchema:
if DHTSchemaKind(j["kind"]) == DHTSchemaKind.SMPL: if DHTSchemaKind(j["kind"]) == DHTSchemaKind.SMPL:
return cls.smpl( return cls.smpl(
j["o_cnt"], j["o_cnt"],
[DHTSchemaSMPLMember.from_json(member) for member in j["members"]], [DHTSchemaSMPLMember.from_json(member)
for member in j["members"]],
) )
raise Exception("Unknown DHTSchema kind", j["kind"]) raise Exception("Unknown DHTSchema kind", j["kind"])
@ -339,7 +351,8 @@ class DHTRecordDescriptor:
return cls( return cls(
TypedKey(j["key"]), TypedKey(j["key"]),
PublicKey(j["owner"]), PublicKey(j["owner"]),
None if j["owner_secret"] is None else SecretKey(j["owner_secret"]), None if j["owner_secret"] is None else SecretKey(
j["owner_secret"]),
DHTSchema.from_json(j["schema"]), DHTSchema.from_json(j["schema"]),
) )
@ -412,6 +425,7 @@ class SafetySpec:
def to_json(self) -> dict: def to_json(self) -> dict:
return self.__dict__ return self.__dict__
class SafetySelection: class SafetySelection:
kind: SafetySelectionKind kind: SafetySelectionKind
@ -438,9 +452,8 @@ class SafetySelection:
def to_json(self) -> dict: def to_json(self) -> dict:
if self.kind == SafetySelectionKind.UNSAFE: if self.kind == SafetySelectionKind.UNSAFE:
return {"Unsafe": self.sequencing } return {"Unsafe": self.sequencing}
elif self.kind == SafetySelectionKind.SAFE: elif self.kind == SafetySelectionKind.SAFE:
return {"Safe": self.safety_spec.to_json() } return {"Safe": self.safety_spec.to_json()}
else: else:
raise Exception("Invalid SafetySelection") raise Exception("Invalid SafetySelection")

View File

@ -46,6 +46,8 @@ logging:
testing: testing:
subnode_index: 0 subnode_index: 0
core: core:
capabilities:
disable: []
protected_store: protected_store:
allow_insecure_fallback: true allow_insecure_fallback: true
always_use_insecure_storage: true always_use_insecure_storage: true
@ -70,6 +72,7 @@ core:
reverse_connection_receipt_time_ms: 5000 reverse_connection_receipt_time_ms: 5000
hole_punch_receipt_time_ms: 5000 hole_punch_receipt_time_ms: 5000
network_key_password: null network_key_password: null
disable_capabilites: []
routing_table: routing_table:
node_id: null node_id: null
node_id_secret: null node_id_secret: null
@ -622,8 +625,14 @@ pub struct ProtectedStore {
pub new_device_encryption_key_password: Option<String>, pub new_device_encryption_key_password: Option<String>,
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct Capabilities {
pub disable: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Core { pub struct Core {
pub capabilities: Capabilities,
pub protected_store: ProtectedStore, pub protected_store: ProtectedStore,
pub table_store: TableStore, pub table_store: TableStore,
pub block_store: BlockStore, pub block_store: BlockStore,
@ -962,6 +971,7 @@ impl Settings {
set_config_value!(inner.logging.otlp.grpc_endpoint, value); set_config_value!(inner.logging.otlp.grpc_endpoint, value);
set_config_value!(inner.logging.console.enabled, value); set_config_value!(inner.logging.console.enabled, value);
set_config_value!(inner.testing.subnode_index, value); set_config_value!(inner.testing.subnode_index, value);
set_config_value!(inner.core.capabilities.disable, value);
set_config_value!(inner.core.protected_store.allow_insecure_fallback, value); set_config_value!(inner.core.protected_store.allow_insecure_fallback, value);
set_config_value!( set_config_value!(
inner.core.protected_store.always_use_insecure_storage, inner.core.protected_store.always_use_insecure_storage,
@ -1093,13 +1103,14 @@ impl Settings {
} else { } else {
format!("subnode{}", inner.testing.subnode_index) format!("subnode{}", inner.testing.subnode_index)
})), })),
"capabilities.protocol_udp" => Ok(Box::new(true)), "capabilities.disable" => {
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)), let mut caps = Vec::<FourCC>::new();
"capabilities.protocol_accept_tcp" => Ok(Box::new(true)), for c in &inner.core.capabilities.disable {
"capabilities.protocol_connect_ws" => Ok(Box::new(true)), let cap = FourCC::from_str(c.as_str()).map_err(VeilidAPIError::generic)?;
"capabilities.protocol_accept_ws" => Ok(Box::new(true)), caps.push(cap);
"capabilities.protocol_connect_wss" => Ok(Box::new(true)), }
"capabilities.protocol_accept_wss" => Ok(Box::new(true)), Ok(Box::new(caps))
}
"protected_store.allow_insecure_fallback" => { "protected_store.allow_insecure_fallback" => {
Ok(Box::new(inner.core.protected_store.allow_insecure_fallback)) Ok(Box::new(inner.core.protected_store.allow_insecure_fallback))
} }

View File

@ -33,13 +33,7 @@ fn init_callbacks() {
window.configCallback = (configKey) => { window.configCallback = (configKey) => {
switch(configKey) { switch(configKey) {
case "namespace": return ""; case "namespace": return "";
case "capabilities.protocol_udp": return false; case "capabilities.disable": return [];
case "capabilities.protocol_connect_tcp": return false;
case "capabilities.protocol_accept_tcp": return false;
case "capabilities.protocol_connect_ws": return true;
case "capabilities.protocol_accept_ws": return false;
case "capabilities.protocol_connect_wss": return true;
case "capabilities.protocol_accept_wss": return false;
case "tablestore.directory": return ""; case "tablestore.directory": return "";
case "network.routing_table.node_id": return []; case "network.routing_table.node_id": return [];
case "network.routing_table.node_id_secret": return []; case "network.routing_table.node_id_secret": return [];