Merge branch 'dev' into 'main'

Latest from dev: Private Routing

See merge request veilid/veilid!12
This commit is contained in:
John Smith 2022-11-22 23:49:02 +00:00
commit 2f3485e9b7
164 changed files with 13798 additions and 5835 deletions

View File

@ -1,3 +1,6 @@
[build]
rustflags = ["--cfg", "tokio_unstable"]
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"

1544
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ FROM --platform amd64 ubuntu:16.04
# Install build prerequisites
deps-base:
RUN apt-get -y update
RUN apt-get install -y iproute2 curl build-essential cmake libssl-dev openssl file git pkg-config libdbus-1-dev libdbus-glib-1-dev libgirepository1.0-dev libcairo2-dev
RUN apt-get install -y iproute2 curl build-essential cmake libssl-dev openssl file git pkg-config libdbus-1-dev libdbus-glib-1-dev libgirepository1.0-dev libcairo2-dev checkinstall unzip
# Install Cap'n Proto
deps-capnp:
@ -15,9 +15,15 @@ deps-capnp:
COPY scripts/earthly/install_capnproto.sh /
RUN /bin/bash /install_capnproto.sh; rm /install_capnproto.sh
# Install protoc
deps-protoc:
FROM +deps-capnp
COPY scripts/earthly/install_protoc.sh /
RUN /bin/bash /install_protoc.sh; rm /install_protoc.sh
# Install Rust
deps-rust:
FROM +deps-capnp
FROM +deps-protoc
ENV RUSTUP_HOME=/usr/local/rustup
ENV CARGO_HOME=/usr/local/cargo
ENV PATH=/usr/local/cargo/bin:$PATH
@ -49,35 +55,43 @@ deps-android:
RUN curl -o /Android/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip
RUN cd /Android; unzip /Android/cmdline-tools.zip
RUN yes | /Android/cmdline-tools/bin/sdkmanager --sdk_root=/Android/Sdk build-tools\;30.0.3 ndk\;22.0.7026061 cmake\;3.18.1 platform-tools platforms\;android-30
RUN apt-get clean
# Clean up the apt cache to save space
deps:
FROM +deps-android
# Just linux build not android
deps-linux:
FROM +deps-cross
RUN apt-get clean
code:
FROM +deps
# Code + Linux deps
code-linux:
FROM +deps-linux
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
WORKDIR /veilid
# Code + Linux + Android deps
code-android:
FROM +deps-android
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
WORKDIR /veilid
# Clippy only
clippy:
FROM +code
FROM +code-linux
RUN cargo clippy
# Build
build-linux-amd64:
FROM +code
FROM +code-linux
RUN cargo build --target x86_64-unknown-linux-gnu --release
SAVE ARTIFACT ./target/x86_64-unknown-linux-gnu AS LOCAL ./target/artifacts/x86_64-unknown-linux-gnu
build-linux-arm64:
FROM +code
FROM +code-linux
RUN cargo build --target aarch64-unknown-linux-gnu --release
SAVE ARTIFACT ./target/aarch64-unknown-linux-gnu AS LOCAL ./target/artifacts/aarch64-unknown-linux-gnu
build-android:
FROM +code
FROM +code-android
WORKDIR /veilid/veilid-core
ENV PATH=$PATH:/Android/Sdk/ndk/22.0.7026061/toolchains/llvm/prebuilt/linux-x86_64/bin/
RUN cargo build --target aarch64-linux-android --release
@ -92,11 +106,11 @@ build-android:
# Unit tests
unit-tests-linux-amd64:
FROM +code
FROM +code-linux
RUN cargo test --target x86_64-unknown-linux-gnu --release
unit-tests-linux-arm64:
FROM +code
FROM +code-linux
RUN cargo test --target aarch64-unknown-linux-gnu --release
# Package

View File

@ -63,7 +63,9 @@ core:
max_timestamp_behind_ms: 10000
max_timestamp_ahead_ms: 10000
timeout_ms: 10000
max_route_hop_count: 7
max_route_hop_count: 4
default_route_hop_count: 1
dht:
resolve_node_timeout:
resolve_node_count: 20

View File

@ -228,7 +228,8 @@ rpc:
max_timestamp_behind_ms: 10000
max_timestamp_ahead_ms: 10000
timeout_ms: 10000
max_route_hop_count: 7
max_route_hop_count: 4
default_route_hop_count: 1
```
#### core:network:dht

2
external/cursive vendored

@ -1 +1 @@
Subproject commit fea04c2f9bb8c4c9551ca6eb4f2cb1268551120f
Subproject commit f1504cf37a7021454020cda5cfba815755399794

@ -1 +1 @@
Subproject commit fd560c499be0f34305e0d48aca7f1bc3d015a17f
Subproject commit effa60cea24e99f294865ed325ffc57612d72785

2
external/hashlink vendored

@ -1 +1 @@
Subproject commit c8da3a58485c850f4029a58de99b1af83112ba8a
Subproject commit a089b448071ef36633947693b90023c67dc8485f

View File

@ -1,11 +1,28 @@
#!/bin/bash
mkdir /tmp/capnproto-install
cd /tmp/capnproto-install
curl -O https://capnproto.org/capnproto-c++-0.9.1.tar.gz
tar zxf capnproto-c++-0.9.1.tar.gz
cd capnproto-c++-0.9.1
./configure
pushd /tmp/capnproto-install
curl -O https://capnproto.org/capnproto-c++-0.10.2.tar.gz
tar zxf capnproto-c++-0.10.2.tar.gz
cd capnproto-c++-0.10.2
./configure --without-openssl
make -j6 check
make install
cd /
rm -rf /tmp/capnproto-install
if [ "$EUID" -ne 0 ]; then
if command -v checkinstall &> /dev/null; then
sudo checkinstall -y
cp *.deb ~
else
sudo make install
fi
popd
sudo rm -rf /tmp/capnproto-install
else
if command -v checkinstall &> /dev/null; then
checkinstall -y
cp *.deb ~
else
make install
fi
popd
rm -rf /tmp/capnproto-install
fi

View File

@ -0,0 +1,26 @@
#!/bin/bash
VERSION=21.9
mkdir /tmp/protoc-install
pushd /tmp/protoc-install
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip
unzip protoc-$VERSION-linux-x86_64.zip
if [ "$EUID" -ne 0 ]; then
if command -v checkinstall &> /dev/null; then
sudo checkinstall --pkgversion=$VERSION -y cp -r bin include /usr/local/
cp *.deb ~
else
sudo make install
fi
popd
sudo rm -rf /tmp/protoc-install
else
if command -v checkinstall &> /dev/null; then
checkinstall --pkgversion=$VERSION -y cp -r bin include /usr/local/
cp *.deb ~
else
make install
fi
popd
rm -rf /tmp/protoc-install
fi

View File

@ -74,7 +74,12 @@ fi
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android wasm32-unknown-unknown
# install cargo packages
cargo install wasm-bindgen-cli
cargo install wasm-bindgen-cli wasm-pack
# Ensure packages are installed
sudo apt-get install libc6-dev-i386 libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 openjdk-11-jdk llvm wabt capnproto
sudo apt-get install libc6-dev-i386 libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 openjdk-11-jdk llvm wabt checkinstall
# Install capnproto using the same mechanism as our earthly build
$SCRIPTDIR/scripts/earthly/install_capnproto.sh
# Install protoc using the same mechanism as our earthly build
$SCRIPTDIR/scripts/earthly/install_protoc.sh

View File

@ -90,13 +90,7 @@ fi
rustup target add aarch64-apple-darwin aarch64-apple-ios x86_64-apple-darwin x86_64-apple-ios wasm32-unknown-unknown aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
# install cargo packages
cargo install wasm-bindgen-cli
# install bitcode compatible ios toolchain
# echo Manual Step:
# echo install +ios-arm64-1.57.0 toolchain for bitcode from https://github.com/getditto/rust-bitcode/releases/latest and unzip
# echo xattr -d -r com.apple.quarantine .
# echo ./install.sh
cargo install wasm-bindgen-cli wasm-pack
# ensure we have command line tools
xcode-select --install
@ -114,5 +108,5 @@ if [ "$BREW_USER" == "" ]; then
BREW_USER=`whoami`
fi
fi
sudo -H -u $BREW_USER brew install capnp cmake wabt llvm
sudo -H -u $BREW_USER brew install capnp cmake wabt llvm protobuf

View File

@ -42,7 +42,9 @@ bugsalot = "^0"
flexi_logger = { version = "^0", features = ["use_chrono_for_offset"] }
thiserror = "^1"
crossbeam-channel = "^0"
veilid-core = { path = "../veilid-core", default_features = false}
hex = "^0"
veilid-core = { path = "../veilid-core", default_features = false }
json = "^0"
[dev-dependencies]
serial_test = "^0"

View File

@ -3,6 +3,7 @@ use crate::tools::*;
use crate::veilid_client_capnp::*;
use capnp::capability::Promise;
use capnp_rpc::{pry, rpc_twoparty_capnp, twoparty, Disconnector, RpcSystem};
use futures::future::FutureExt;
use serde::de::DeserializeOwned;
use std::cell::RefCell;
use std::net::SocketAddr;
@ -76,12 +77,21 @@ impl veilid_client::Server for VeilidClientImpl {
VeilidUpdate::Log(log) => {
self.comproc.update_log(log);
}
VeilidUpdate::AppMessage(msg) => {
self.comproc.update_app_message(msg);
}
VeilidUpdate::AppCall(call) => {
self.comproc.update_app_call(call);
}
VeilidUpdate::Attachment(attachment) => {
self.comproc.update_attachment(attachment);
}
VeilidUpdate::Network(network) => {
self.comproc.update_network_status(network);
}
VeilidUpdate::Config(config) => {
self.comproc.update_config(config);
}
VeilidUpdate::Shutdown => self.comproc.update_shutdown(),
}
@ -94,7 +104,9 @@ struct ClientApiConnectionInner {
connect_addr: Option<SocketAddr>,
disconnector: Option<Disconnector<rpc_twoparty_capnp::Side>>,
server: Option<Rc<RefCell<veilid_server::Client>>>,
server_settings: Option<String>,
disconnect_requested: bool,
cancel_eventual: Eventual,
}
type Handle<T> = Rc<RefCell<T>>;
@ -112,10 +124,21 @@ impl ClientApiConnection {
connect_addr: None,
disconnector: None,
server: None,
server_settings: None,
disconnect_requested: false,
cancel_eventual: Eventual::new(),
})),
}
}
pub fn cancel(&self) {
let eventual = {
let inner = self.inner.borrow();
inner.cancel_eventual.clone()
};
eventual.resolve(); // don't need to await this
}
async fn process_veilid_state<'a>(
&'a mut self,
veilid_state: VeilidState,
@ -123,7 +146,7 @@ impl ClientApiConnection {
let mut inner = self.inner.borrow_mut();
inner.comproc.update_attachment(veilid_state.attachment);
inner.comproc.update_network_status(veilid_state.network);
inner.comproc.update_config(veilid_state.config);
Ok(())
}
@ -191,19 +214,27 @@ impl ClientApiConnection {
.map_err(|e| format!("failed to get deserialize veilid state: {}", e))?;
self.process_veilid_state(veilid_state).await?;
// Save server settings
let server_settings = response
.get_settings()
.map_err(|e| format!("failed to get initial veilid server settings: {}", e))?
.to_owned();
self.inner.borrow_mut().server_settings = Some(server_settings.clone());
// Don't drop the registration, doing so will remove the client
// object mapping from the server which we need for the update backchannel
// Wait until rpc system completion or disconnect was requested
let res = rpc_jh.await;
#[cfg(feature="rt-tokio")]
#[cfg(feature = "rt-tokio")]
let res = res.map_err(|e| format!("join error: {}", e))?;
res.map_err(|e| format!("client RPC system error: {}", e))
res.map_err(|e| format!("client RPC system error: {}", e))
}
async fn handle_connection(&mut self) -> Result<(), String> {
async fn handle_connection(&mut self, connect_addr: SocketAddr) -> Result<(), String> {
trace!("ClientApiConnection::handle_connection");
let connect_addr = self.inner.borrow().connect_addr.unwrap();
self.inner.borrow_mut().connect_addr = Some(connect_addr);
// Connect the TCP socket
let stream = TcpStream::connect(connect_addr)
.await
@ -245,9 +276,11 @@ impl ClientApiConnection {
// Drop the server and disconnector too (if we still have it)
let mut inner = self.inner.borrow_mut();
let disconnect_requested = inner.disconnect_requested;
inner.server_settings = None;
inner.server = None;
inner.disconnector = None;
inner.disconnect_requested = false;
inner.connect_addr = None;
if !disconnect_requested {
// Connection lost
@ -258,6 +291,34 @@ impl ClientApiConnection {
}
}
pub fn cancellable<T>(&mut self, p: Promise<T, capnp::Error>) -> Promise<T, capnp::Error>
where
T: 'static,
{
let (mut cancel_instance, cancel_eventual) = {
let inner = self.inner.borrow();
(
inner.cancel_eventual.instance_empty().fuse(),
inner.cancel_eventual.clone(),
)
};
let mut p = p.fuse();
Promise::from_future(async move {
let out = select! {
a = p => {
a
},
_ = cancel_instance => {
Err(capnp::Error::failed("cancelled".into()))
}
};
drop(cancel_instance);
cancel_eventual.reset();
out
})
}
pub async fn server_attach(&mut self) -> Result<(), String> {
trace!("ClientApiConnection::server_attach");
let server = {
@ -269,7 +330,10 @@ impl ClientApiConnection {
.clone()
};
let request = server.borrow().attach_request();
let response = request.send().promise.await.map_err(map_to_string)?;
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
@ -290,7 +354,10 @@ impl ClientApiConnection {
.clone()
};
let request = server.borrow().detach_request();
let response = request.send().promise.await.map_err(map_to_string)?;
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
@ -311,7 +378,10 @@ impl ClientApiConnection {
.clone()
};
let request = server.borrow().shutdown_request();
let response = request.send().promise.await.map_err(map_to_string)?;
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
response.get().map(drop).map_err(map_to_string)
}
@ -327,7 +397,10 @@ impl ClientApiConnection {
};
let mut request = server.borrow().debug_request();
request.get().set_command(&what);
let response = request.send().promise.await.map_err(map_to_string)?;
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
@ -355,7 +428,36 @@ impl ClientApiConnection {
request.get().set_layer(&layer);
let log_level_json = veilid_core::serialize_json(&log_level);
request.get().set_log_level(&log_level_json);
let response = request.send().promise.await.map_err(map_to_string)?;
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
.get_result()
.map_err(map_to_string)?;
let res: Result<(), VeilidAPIError> = decode_api_result(&reader);
res.map_err(map_to_string)
}
pub async fn server_appcall_reply(&mut self, id: u64, msg: Vec<u8>) -> Result<(), String> {
trace!("ClientApiConnection::appcall_reply");
let server = {
let inner = self.inner.borrow();
inner
.server
.as_ref()
.ok_or_else(|| "Not connected, ignoring change_log_level request".to_owned())?
.clone()
};
let mut request = server.borrow().app_call_reply_request();
request.get().set_id(id);
request.get().set_message(&msg);
let response = self
.cancellable(request.send().promise)
.await
.map_err(map_to_string)?;
let reader = response
.get()
.map_err(map_to_string)?
@ -369,9 +471,7 @@ impl ClientApiConnection {
pub async fn connect(&mut self, connect_addr: SocketAddr) -> Result<(), String> {
trace!("ClientApiConnection::connect");
// Save the address to connect to
self.inner.borrow_mut().connect_addr = Some(connect_addr);
self.handle_connection().await
self.handle_connection(connect_addr).await
}
// End Client API connection
@ -382,7 +482,6 @@ impl ClientApiConnection {
Some(d) => {
self.inner.borrow_mut().disconnect_requested = true;
d.await.unwrap();
self.inner.borrow_mut().connect_addr = None;
}
None => {
debug!("disconnector doesn't exist");

View File

@ -49,6 +49,7 @@ struct CommandProcessorInner {
autoreconnect: bool,
server_addr: Option<SocketAddr>,
connection_waker: Eventual,
last_call_id: Option<u64>,
}
type Handle<T> = Rc<RefCell<T>>;
@ -70,6 +71,7 @@ impl CommandProcessor {
autoreconnect: settings.autoreconnect,
server_addr: None,
connection_waker: Eventual::new(),
last_call_id: None,
})),
}
}
@ -100,6 +102,12 @@ impl CommandProcessor {
}
}
pub fn cancel_command(&self) {
trace!("CommandProcessor::cancel_command");
let capi = self.capi();
capi.cancel();
}
pub fn cmd_help(&self, _rest: Option<String>, callback: UICallback) -> Result<(), String> {
trace!("CommandProcessor::cmd_help");
self.ui().add_node_event(
@ -111,6 +119,7 @@ attach - attach the server to the Veilid network
detach - detach the server from the Veilid network
debug - send a debugging command to the Veilid server
change_log_level - change the log level for a tracing layer
reply - reply to an AppCall not handled directly by the server
"#
.to_owned(),
);
@ -225,6 +234,66 @@ change_log_level - change the log level for a tracing layer
Ok(())
}
pub fn cmd_reply(&self, rest: Option<String>, callback: UICallback) -> Result<(), String> {
trace!("CommandProcessor::cmd_reply");
let mut capi = self.capi();
let ui = self.ui();
let some_last_id = self.inner_mut().last_call_id.take();
spawn_detached_local(async move {
let (first, second) = Self::word_split(&rest.clone().unwrap_or_default());
let (id, msg) = if let Some(second) = second {
let id = match u64::from_str(&first) {
Err(e) => {
ui.add_node_event(format!("invalid appcall id: {}", e));
ui.send_callback(callback);
return;
}
Ok(v) => v,
};
(id, second)
} else {
let id = match some_last_id {
None => {
ui.add_node_event("must specify last call id".to_owned());
ui.send_callback(callback);
return;
}
Some(v) => v,
};
(id, rest.unwrap_or_default())
};
let msg = if msg[0..1] == "#".to_owned() {
match hex::decode(msg[1..].as_bytes().to_vec()) {
Err(e) => {
ui.add_node_event(format!("invalid hex message: {}", e));
ui.send_callback(callback);
return;
}
Ok(v) => v,
}
} else {
msg[1..].as_bytes().to_vec()
};
let msglen = msg.len();
match capi.server_appcall_reply(id, msg).await {
Ok(()) => {
ui.add_node_event(format!("reply sent to {} : {} bytes", id, msglen));
ui.send_callback(callback);
return;
}
Err(e) => {
ui.display_string_dialog(
"Server command 'appcall_reply' failed",
e.to_string(),
callback,
);
}
}
});
Ok(())
}
pub fn run_command(&self, command_line: &str, callback: UICallback) -> Result<(), String> {
//
let (cmd, rest) = Self::word_split(command_line);
@ -238,6 +307,7 @@ change_log_level - change the log level for a tracing layer
"detach" => self.cmd_detach(callback),
"debug" => self.cmd_debug(rest, callback),
"change_log_level" => self.cmd_change_log_level(rest, callback),
"reply" => self.cmd_reply(rest, callback),
_ => {
let ui = self.ui();
ui.send_callback(callback);
@ -318,6 +388,7 @@ change_log_level - change the log level for a tracing layer
// called by client_api_connection
// calls into ui
////////////////////////////////////////////
pub fn update_attachment(&mut self, attachment: veilid_core::VeilidStateAttachment) {
self.inner_mut().ui.set_attachment_state(attachment.state);
}
@ -330,8 +401,11 @@ change_log_level - change the log level for a tracing layer
network.peers,
);
}
pub fn update_config(&mut self, config: veilid_core::VeilidStateConfig) {
self.inner_mut().ui.set_config(config.config)
}
pub fn update_log(&mut self, log: veilid_core::VeilidStateLog) {
pub fn update_log(&mut self, log: veilid_core::VeilidLog) {
self.inner().ui.add_node_event(format!(
"{}: {}{}",
log.log_level,
@ -344,6 +418,49 @@ change_log_level - change the log level for a tracing layer
));
}
pub fn update_app_message(&mut self, msg: veilid_core::VeilidAppMessage) {
// check is message body is ascii printable
let mut printable = true;
for c in &msg.message {
if *c < 32 || *c > 126 {
printable = false;
}
}
let strmsg = if printable {
String::from_utf8_lossy(&msg.message).to_string()
} else {
hex::encode(&msg.message)
};
self.inner()
.ui
.add_node_event(format!("AppMessage ({:?}): {}", msg.sender, strmsg));
}
pub fn update_app_call(&mut self, call: veilid_core::VeilidAppCall) {
// check is message body is ascii printable
let mut printable = true;
for c in &call.message {
if *c < 32 || *c > 126 {
printable = false;
}
}
let strmsg = if printable {
String::from_utf8_lossy(&call.message).to_string()
} else {
format!("#{}", hex::encode(&call.message))
};
self.inner().ui.add_node_event(format!(
"AppCall ({:?}) id = {:016x} : {}",
call.sender, call.id, strmsg
));
self.inner_mut().last_call_id = Some(call.id);
}
pub fn update_shutdown(&mut self) {
// Do nothing with this, we'll process shutdown when rpc connection closes
}
@ -381,7 +498,6 @@ change_log_level - change the log level for a tracing layer
// calls into client_api_connection
////////////////////////////////////////////
pub fn attach(&mut self) {
trace!("CommandProcessor::attach");
let mut capi = self.capi();
spawn_detached_local(async move {
@ -392,7 +508,6 @@ change_log_level - change the log level for a tracing layer
}
pub fn detach(&mut self) {
trace!("CommandProcessor::detach");
let mut capi = self.capi();
spawn_detached_local(async move {

View File

@ -55,6 +55,7 @@ struct UIState {
network_down_up: Dirty<(f32, f32)>,
connection_state: Dirty<ConnectionState>,
peers_state: Dirty<Vec<PeerTableData>>,
node_id: Dirty<String>,
}
impl UIState {
@ -65,6 +66,7 @@ impl UIState {
network_down_up: Dirty::new((0.0, 0.0)),
connection_state: Dirty::new(ConnectionState::Disconnected),
peers_state: Dirty::new(Vec::new()),
node_id: Dirty::new("".to_owned()),
}
}
}
@ -213,7 +215,13 @@ impl UI {
UI::setup_quit_handler(s);
});
}
fn clear_handler(siv: &mut Cursive) {
cursive_flexi_logger_view::clear_log();
UI::update_cb(siv);
}
fn node_events_panel(s: &mut Cursive) -> ViewRef<Panel<ScrollView<FlexiLoggerView>>> {
s.find_name("node-events-panel").unwrap()
}
fn command_line(s: &mut Cursive) -> ViewRef<EditView> {
s.find_name("command-line").unwrap()
}
@ -306,11 +314,18 @@ impl UI {
fn run_command(s: &mut Cursive, text: &str) -> Result<(), String> {
// disable ui
Self::enable_command_ui(s, false);
// run command
s.set_global_callback(cursive::event::Event::Key(Key::Esc), |s| {
let cmdproc = Self::command_processor(s);
cmdproc.cancel_command();
});
let cmdproc = Self::command_processor(s);
cmdproc.run_command(
text,
Box::new(|s| {
s.set_global_callback(cursive::event::Event::Key(Key::Esc), UI::quit_handler);
Self::enable_command_ui(s, true);
}),
)
@ -565,6 +580,12 @@ impl UI {
}
}
fn refresh_main_titlebar(s: &mut Cursive) {
let mut main_window = UI::node_events_panel(s);
let inner = Self::inner_mut(s);
main_window.set_title(format!("Node: {}", inner.ui_state.node_id.get()));
}
fn refresh_statusbar(s: &mut Cursive) {
let mut statusbar = UI::status_bar(s);
@ -627,6 +648,7 @@ impl UI {
let mut refresh_button_attach = false;
let mut refresh_connection_dialog = false;
let mut refresh_peers = false;
let mut refresh_main_titlebar = false;
if inner.ui_state.attachment_state.take_dirty() {
refresh_statusbar = true;
refresh_button_attach = true;
@ -647,6 +669,9 @@ impl UI {
if inner.ui_state.peers_state.take_dirty() {
refresh_peers = true;
}
if inner.ui_state.node_id.take_dirty() {
refresh_main_titlebar = true;
}
drop(inner);
@ -662,6 +687,9 @@ impl UI {
if refresh_peers {
Self::refresh_peers(s);
}
if refresh_main_titlebar {
Self::refresh_main_titlebar(s);
}
}
////////////////////////////////////////////////////////////////////////////
@ -709,13 +737,11 @@ impl UI {
// Create layouts
let node_events_view = Panel::new(
FlexiLoggerView::new_scrollable()
.with_name("node-events")
.full_screen(),
)
.title_position(HAlign::Left)
.title("Node Events");
let node_events_view = Panel::new(FlexiLoggerView::new_scrollable())
.title_position(HAlign::Left)
.title("Node Events")
.with_name("node-events-panel")
.full_screen();
let peers_table_view = PeersTableView::new()
.column(PeerTableColumn::NodeId, "Node Id", |c| c.width(43))
@ -794,6 +820,7 @@ impl UI {
UI::setup_colors(&mut siv, &mut inner, settings);
UI::setup_quit_handler(&mut siv);
siv.set_global_callback(cursive::event::Event::CtrlChar('k'), UI::clear_handler);
drop(inner);
drop(siv);
@ -832,6 +859,16 @@ impl UI {
inner.ui_state.peers_state.set(peers);
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
}
pub fn set_config(&mut self, config: VeilidConfigInner) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.node_id.set(
config
.network
.node_id
.map(|x| x.encode())
.unwrap_or("<unknown>".to_owned()),
);
}
pub fn set_connection_state(&mut self, state: ConnectionState) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.connection_state.set(state);

View File

@ -58,7 +58,11 @@ digest = "0.9.0"
rtnetlink = { version = "^0", default-features = false, optional = true }
async-std-resolver = { version = "^0", optional = true }
trust-dns-resolver = { version = "^0", optional = true }
keyvaluedb = { path = "../external/keyvaluedb/keyvaluedb" }
serde_bytes = { version = "^0" }
#rkyv = { version = "^0", default_features = false, features = ["std", "alloc", "strict", "size_64", "validation"] }
rkyv = { git = "https://github.com/crioux/rkyv.git", branch = "issue_326", default_features = false, features = ["std", "alloc", "strict", "size_64", "validation"] }
bytecheck = "^0"
# Dependencies for native builds only
# Linux, Windows, Mac, iOS, Android
@ -72,7 +76,6 @@ async-tungstenite = { version = "^0", features = ["async-tls"] }
maplit = "^1"
config = { version = "^0", features = ["yaml"] }
keyring-manager = { path = "../external/keyring-manager" }
lru = "^0"
async-tls = "^0.11"
igd = { path = "../external/rust-igd" }
webpki = "^0"
@ -83,7 +86,6 @@ futures-util = { version = "^0", default-features = false, features = ["async-aw
keyvaluedb-sqlite = { path = "../external/keyvaluedb/keyvaluedb-sqlite" }
data-encoding = { version = "^2" }
serde = { version = "^1", features = ["derive" ] }
serde_cbor = { version = "^0" }
serde_json = { version = "^1" }
socket2 = "^0"
bugsalot = "^0"
@ -96,13 +98,10 @@ nix = "^0"
wasm-bindgen = "^0"
js-sys = "^0"
wasm-bindgen-futures = "^0"
hashbrown = "^0"
lru = {version = "^0", features = ["hashbrown"] }
no-std-net = { path = "../external/no-std-net", features = ["serde"] }
keyvaluedb-web = { path = "../external/keyvaluedb/keyvaluedb-web" }
data-encoding = { version = "^2", default_features = false, features = ["alloc"] }
serde = { version = "^1", default-features = false, features = ["derive", "alloc"] }
serde_cbor = { version = "^0", default-features = false, features = ["alloc"] }
serde_json = { version = "^1", default-features = false, features = ["alloc"] }
getrandom = { version = "^0", features = ["js"] }
ws_stream_wasm = "^0"

View File

@ -3,14 +3,14 @@
# IDs And Hashes
##############################
struct Curve25519PublicKey {
struct Key256 @0xdde44e3286f6a90d {
u0 @0 :UInt64;
u1 @1 :UInt64;
u2 @2 :UInt64;
u3 @3 :UInt64;
}
struct Ed25519Signature {
struct Signature512 @0x806749043a129c12 {
u0 @0 :UInt64;
u1 @1 :UInt64;
u2 @2 :UInt64;
@ -21,79 +21,72 @@ struct Ed25519Signature {
u7 @7 :UInt64;
}
struct XChaCha20Poly1305Nonce {
struct Nonce24 @0xb6260db25d8d7dfc {
u0 @0 :UInt64;
u1 @1 :UInt64;
u2 @2 :UInt64;
}
struct BLAKE3Hash {
u0 @0 :UInt64;
u1 @1 :UInt64;
u2 @2 :UInt64;
u3 @3 :UInt64;
}
using NodeID = Curve25519PublicKey;
using RoutePublicKey = Curve25519PublicKey;
using ValueID = Curve25519PublicKey;
using Nonce = XChaCha20Poly1305Nonce;
using Signature = Ed25519Signature;
using BlockID = BLAKE3Hash;
using NodeID = Key256;
using RoutePublicKey = Key256;
using ValueID = Key256;
using Nonce = Nonce24;
using Signature = Signature512;
using BlockID = Key256;
using TunnelID = UInt64;
# Node Dial Info
################################################################
struct AddressIPV4 {
struct AddressIPV4 @0xdb8769881266a6a0 {
addr @0 :UInt32; # Address in big endian format
}
struct AddressIPV6 {
struct AddressIPV6 @0xb35d6e6011dc5c20 {
addr0 @0 :UInt32; # \
addr1 @1 :UInt32; # \ Address in big
addr2 @2 :UInt32; # / endian format
addr3 @3 :UInt32; # /
}
struct Address {
struct Address @0x812706e9e57d108b {
union {
ipv4 @0 :AddressIPV4;
ipv6 @1 :AddressIPV6;
}
}
struct SocketAddress {
struct SocketAddress @0x82df4272f4dd3a62 {
address @0 :Address;
port @1 :UInt16;
}
enum ProtocolKind {
enum ProtocolKind @0xde0bf5787c067d5a {
udp @0;
ws @1;
wss @2;
tcp @3;
}
struct DialInfoUDP {
struct DialInfoUDP @0xbb38a8b8b7024a7c {
socketAddress @0 :SocketAddress;
}
struct DialInfoTCP {
struct DialInfoTCP @0x9e0a9371b9a9f7fc {
socketAddress @0 :SocketAddress;
}
struct DialInfoWS {
struct DialInfoWS @0xd7795f7a92ab15b0 {
socketAddress @0 :SocketAddress;
request @1 :Text;
}
struct DialInfoWSS {
struct DialInfoWSS @0xe639faa41b7d7b04 {
socketAddress @0 :SocketAddress;
request @1 :Text;
}
struct DialInfo {
struct DialInfo @0xe1cd1c39fc2defdf {
union {
udp @0 :DialInfoUDP;
tcp @1 :DialInfoTCP;
@ -102,20 +95,15 @@ struct DialInfo {
}
}
struct NodeDialInfo {
nodeId @0 :NodeID; # node id
dialInfo @1 :DialInfo; # how to get to the node
}
# Signals
##############################
struct SignalInfoHolePunch {
struct SignalInfoHolePunch @0xeeb9ab6861890c9a {
receipt @0 :Data; # receipt to return with hole punch
peerInfo @1 :PeerInfo; # peer info of the signal sender for hole punch attempt
}
struct SignalInfoReverseConnect {
struct SignalInfoReverseConnect @0xd9ebd3bd0d46e013 {
receipt @0 :Data; # receipt to return with reverse connect
peerInfo @1 :PeerInfo; # peer info of the signal sender for reverse connect attempt
}
@ -123,29 +111,38 @@ struct SignalInfoReverseConnect {
# Private Routes
##############################
struct RouteHopData {
struct RouteHopData @0x8ce231f9d1b7adf2 {
nonce @0 :Nonce; # nonce for encrypted blob
blob @1 :Data; # encrypted blob with ENC(nonce,DH(PK,SK))
# can be one of:
# if more hops remain in this route: RouteHop (0 byte appended as key)
# if end of safety route and starting private route: PrivateRoute (1 byte appended as key)
# if this is a safety route RouteHopData, there is a single byte tag appended to the end of the encrypted blob
# it can be one of:
# if more hops remain in this route: RouteHop (0 byte appended as tag)
# if end of safety route and starting private route: PrivateRoute (1 byte appended as tag)
# if this is a private route RouteHopData, only can decode to RouteHop, no tag is appended
}
struct RouteHop {
dialInfo @0 :NodeDialInfo; # dial info for this hop
nextHop @1 :RouteHopData; # Optional: next hop in encrypted blob
# Null means no next hop, at destination (only used in private route, safety routes must enclose a stub private route)
struct RouteHop @0xf8f672d75cce0c3b {
node :union {
nodeId @0 :NodeID; # node id only for established routes
peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route
}
nextHop @2 :RouteHopData; # optional: If this the end of a private route, this field will not exist
# if this is a safety route routehop, this field is not optional and must exist
}
struct PrivateRoute {
struct PrivateRoute @0x8a83fccb0851e776 {
publicKey @0 :RoutePublicKey; # private route public key (unique per private route)
hopCount @1 :UInt8; # Count of hops left in the private route
firstHop @2 :RouteHop; # Optional: first hop in the private route
}
hopCount @1 :UInt8; # Count of hops left in the private route (for timeout calculation purposes only)
hops :union {
firstHop @2 :RouteHop; # first hop of a private route is unencrypted (hopcount > 0)
data @3 :RouteHopData; # private route has more hops (hopcount > 0 && hopcount < total_hopcount)
empty @4 :Void; # private route has ended (hopcount = 0)
}
}
struct SafetyRoute {
struct SafetyRoute @0xf554734d07cb5d59 {
publicKey @0 :RoutePublicKey; # safety route public key (unique per safety route)
hopCount @1 :UInt8; # Count of hops left in the safety route
hopCount @1 :UInt8; # Count of hops left in the safety route (for timeout calculation purposes only)
hops :union {
data @2 :RouteHopData; # safety route has more hops
private @3 :PrivateRoute; # safety route has ended and private route follows
@ -157,7 +154,7 @@ struct SafetyRoute {
using ValueSeqNum = UInt32; # sequence numbers for values
struct ValueKey {
struct ValueKey @0xe64b0992c21a0736 {
publicKey @0 :ValueID; # the location of the value
subkey @1 :Text; # the name of the subkey (or empty if the whole key)
}
@ -167,21 +164,22 @@ struct ValueKey {
# seq @1 :ValueSeqNum; # the sequence number of the value subkey
# }
struct ValueData {
data @0 :Data; # value or subvalue contents in CBOR format
struct ValueData @0xb4b7416f169f2a3d {
data @0 :Data; # value or subvalue contents
seq @1 :ValueSeqNum; # sequence number of value
}
# Operations
##############################
enum NetworkClass {
inboundCapable @0; # I = Inbound capable without relay, may require signal
outboundOnly @1; # O = Outbound only, inbound relay required except with reverse connect signal
webApp @2; # W = PWA, outbound relay is required in most cases
enum NetworkClass @0x8cebfc2a6230717f {
invalid @0; # X = Invalid network class, network is not yet set up
inboundCapable @1; # I = Inbound capable without relay, may require signal
outboundOnly @2; # O = Outbound only, inbound relay required except with reverse connect signal
webApp @3; # W = PWA, outbound relay is required in most cases
}
enum DialInfoClass {
enum DialInfoClass @0x880005edfdd38b1e {
direct @0; # D = Directly reachable with public IP and no firewall, with statically configured port
mapped @1; # M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port
fullConeNAT @2; # F = Directly reachable device without portmap behind full-cone NAT
@ -190,12 +188,12 @@ enum DialInfoClass {
portRestrictedNAT @5; # P = Device without portmap behind address-and-port restricted NAT
}
struct DialInfoDetail {
struct DialInfoDetail @0x96423aa1d67b74d8 {
dialInfo @0 :DialInfo;
class @1 :DialInfoClass;
}
struct PublicInternetNodeStatus {
struct PublicInternetNodeStatus @0x9c9d7f1f12eb088f {
willRoute @0 :Bool;
willTunnel @1 :Bool;
willSignal @2 :Bool;
@ -203,225 +201,253 @@ struct PublicInternetNodeStatus {
willValidateDialInfo @4 :Bool;
}
struct LocalNetworkNodeStatus {
struct LocalNetworkNodeStatus @0x957f5bfed2d0b5a5 {
willRelay @0 :Bool;
willValidateDialInfo @1 :Bool;
}
struct NodeStatus {
struct NodeStatus @0xd36b9e7a3bf3330d {
union {
publicInternet @0 :PublicInternetNodeStatus;
localNetwork @1 :LocalNetworkNodeStatus;
}
}
struct ProtocolTypeSet {
struct ProtocolTypeSet @0x82f12f55a1b73326 {
udp @0 :Bool;
tcp @1 :Bool;
ws @2 :Bool;
wss @3 :Bool;
}
struct AddressTypeSet {
struct AddressTypeSet @0x9f52d5430d349e6b {
ipv4 @0 :Bool;
ipv6 @1 :Bool;
}
struct NodeInfo {
struct SenderInfo @0x8a4464fab4b1d101 {
socketAddress @0 :SocketAddress; # socket address that for the sending peer
}
struct NodeInfo @0xe125d847e3f9f419 {
networkClass @0 :NetworkClass; # network class of this node
outboundProtocols @1 :ProtocolTypeSet; # protocols that can go outbound
addressTypes @2 :AddressTypeSet; # address types supported
minVersion @3 :UInt8; # minimum protocol version for rpc
maxVersion @4 :UInt8; # maximum protocol version for rpc
dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node
relayPeerInfo @6 :PeerInfo; # (optional) relay peer info for this node
}
struct SignedNodeInfo {
struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 {
nodeInfo @0 :NodeInfo; # node info
signature @1 :Signature; # signature
timestamp @2 :UInt64; # when signed node info was generated
timestamp @1 :UInt64; # when signed node info was generated
signature @2 :Signature; # signature
}
struct SenderInfo {
socketAddress @0 :SocketAddress; # socket address was available for peer
struct SignedRelayedNodeInfo @0xb39e8428ccd87cbb {
nodeInfo @0 :NodeInfo; # node info
relayId @1 :NodeID; # node id for relay
relayInfo @2 :SignedDirectNodeInfo; # signed node info for relay
timestamp @3 :UInt64; # when signed node info was generated
signature @4 :Signature; # signature
}
struct PeerInfo {
struct SignedNodeInfo @0xd2478ce5f593406a {
union {
direct @0 :SignedDirectNodeInfo; # node info for nodes reachable without a relay
relayed @1 :SignedRelayedNodeInfo; # node info for nodes requiring a relay
}
}
struct PeerInfo @0xfe2d722d5d3c4bcb {
nodeId @0 :NodeID; # node id for 'closer peer'
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
}
struct RoutedOperation {
signatures @0 :List(Signature); # signatures from nodes that have handled the private route
nonce @1 :Nonce; # nonce Xmsg
data @2 :Data; # Operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
struct RoutedOperation @0xcbcb8535b839e9dd {
version @0 :UInt8; # crypto version in use for the data
signatures @1 :List(Signature); # signatures from nodes that have handled the private route
nonce @2 :Nonce; # nonce Xmsg
data @3 :Data; # operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
}
struct OperationStatusQ {
nodeStatus @0 :NodeStatus; # node status update about the statusq sender
struct OperationStatusQ @0x865d80cea70d884a {
nodeStatus @0 :NodeStatus; # Optional: node status update about the statusq sender
}
struct OperationStatusA {
nodeStatus @0 :NodeStatus; # returned node status
senderInfo @1 :SenderInfo; # info about StatusQ sender from the perspective of the replier
struct OperationStatusA @0xb306f407fa812a55 {
nodeStatus @0 :NodeStatus; # Optional: returned node status
senderInfo @1 :SenderInfo; # Optional: info about StatusQ sender from the perspective of the replier
}
struct OperationValidateDialInfo {
struct OperationValidateDialInfo @0xbc716ad7d5d060c8 {
dialInfo @0 :DialInfo; # dial info to use for the receipt
receipt @1 :Data; # receipt to return to dial info to prove it is reachable
redirect @2 :Bool; # request a different node do the validate
}
struct OperationReturnReceipt {
struct OperationReturnReceipt @0xeb0fb5b5a9160eeb {
receipt @0 :Data; # receipt being returned to its origin
}
struct OperationFindNodeQ {
struct OperationFindNodeQ @0xfdef788fe9623bcd {
nodeId @0 :NodeID; # node id to locate
}
struct OperationFindNodeA {
struct OperationFindNodeA @0xa84cf2fb40c77089 {
peers @0 :List(PeerInfo); # returned 'closer peer' information
}
struct OperationRoute {
struct OperationRoute @0x96741859ce6ac7dd {
safetyRoute @0 :SafetyRoute; # Where this should go
operation @1 :RoutedOperation; # The operation to be routed
}
struct OperationNodeInfoUpdate {
struct OperationNodeInfoUpdate @0xc9647b32a48b66ce {
signedNodeInfo @0 :SignedNodeInfo; # Our signed node info
}
struct OperationGetValueQ {
struct OperationAppCallQ @0xade67b9f09784507 {
message @0 :Data; # Opaque request to application
}
struct OperationAppCallA @0xf7c797ac85f214b8 {
message @0 :Data; # Opaque response from application
}
struct OperationAppMessage @0x9baf542d81b411f5 {
message @0 :Data; # Opaque message to application
}
struct OperationGetValueQ @0xf88a5b6da5eda5d0 {
key @0 :ValueKey; # key for value to get
}
struct OperationGetValueA {
struct OperationGetValueA @0xd896bb46f2e0249f {
union {
data @0 :ValueData; # the value if successful
peers @1 :List(PeerInfo); # returned 'closer peer' information if not successful
}
}
struct OperationSetValueQ {
struct OperationSetValueQ @0xbac06191ff8bdbc5 {
key @0 :ValueKey; # key for value to update
value @1 :ValueData; # value or subvalue contents in CBOR format (older or equal seq number gets dropped)
value @1 :ValueData; # value or subvalue contents (older or equal seq number gets dropped)
}
struct OperationSetValueA {
struct OperationSetValueA @0x9378d0732dc95be2 {
union {
data @0 :ValueData; # the new value if successful, may be a different value than what was set if the seq number was lower or equal
peers @1 :List(PeerInfo); # returned 'closer peer' information if not successful
}
}
struct OperationWatchValueQ {
struct OperationWatchValueQ @0xf9a5a6c547b9b228 {
key @0 :ValueKey; # key for value to watch
}
struct OperationWatchValueA {
struct OperationWatchValueA @0xa726cab7064ba893 {
expiration @0 :UInt64; # timestamp when this watch will expire in usec since epoch (0 if watch failed)
peers @1 :List(PeerInfo); # returned list of other nodes to ask that could propagate watches
}
struct OperationValueChanged {
struct OperationValueChanged @0xd1c59ebdd8cc1bf6 {
key @0 :ValueKey; # key for value that changed
value @1 :ValueData; # value or subvalue contents in CBOR format with sequence number
value @1 :ValueData; # value or subvalue contents with sequence number
}
struct OperationSupplyBlockQ {
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
blockId @0 :BlockID; # hash of the block we can supply
}
struct OperationSupplyBlockA {
struct OperationSupplyBlockA @0xf003822e83b5c0d7 {
union {
expiration @0 :UInt64; # when the block supplier entry will need to be refreshed
peers @1 :List(PeerInfo); # returned 'closer peer' information if not successful
}
}
struct OperationFindBlockQ {
struct OperationFindBlockQ @0xaf4353ff004c7156 {
blockId @0 :BlockID; # hash of the block to locate
}
struct OperationFindBlockA {
struct OperationFindBlockA @0xc51455bc4915465d {
data @0 :Data; # Optional: the actual block data if we have that block ourselves
# null if we don't have a block to return
suppliers @1 :List(PeerInfo); # returned list of suppliers if we have them
peers @2 :List(PeerInfo); # returned 'closer peer' information
}
struct OperationSignal {
struct OperationSignal @0xd4f94f2a5d207e49 {
union {
holePunch @0 :SignalInfoHolePunch;
reverseConnect @1 :SignalInfoReverseConnect;
}
}
enum TunnelEndpointMode {
enum TunnelEndpointMode @0xef06f4c29beb7458 {
raw @0; # raw tunnel
turn @1; # turn tunnel
}
enum TunnelError {
enum TunnelError @0xb82c6bfb1ec38c7c {
badId @0; # Tunnel ID was rejected
noEndpoint @1; # Endpoint was unreachable
rejectedMode @2; # Endpoint couldn't provide mode
noCapacity @3; # Endpoint is full
}
struct TunnelEndpoint {
struct TunnelEndpoint @0xc2602aa983cc337d {
mode @0 :TunnelEndpointMode; # what kind of endpoint this is
description @1 :Text; # endpoint description (TODO)
}
struct FullTunnel {
struct FullTunnel @0x9821c3dc75373f63 {
id @0 :TunnelID; # tunnel id to use everywhere
timeout @1 :UInt64; # duration from last data when this expires if no data is sent or received
local @2 :TunnelEndpoint; # local endpoint
remote @3 :TunnelEndpoint; # remote endpoint
}
struct PartialTunnel {
struct PartialTunnel @0x827a7ebc02be2fc8 {
id @0 :TunnelID; # tunnel id to use everywhere
timeout @1 :UInt64; # timestamp when this expires if not completed
local @2 :TunnelEndpoint; # local endpoint
}
struct OperationStartTunnelQ {
struct OperationStartTunnelQ @0xa9c49afce44187af {
id @0 :TunnelID; # tunnel id to use everywhere
localMode @1 :TunnelEndpointMode; # what kind of local endpoint mode is being requested
depth @2 :UInt8; # the number of nodes in the tunnel
}
struct OperationStartTunnelA {
struct OperationStartTunnelA @0x818162e4cc61bf1e {
union {
partial @0 :PartialTunnel; # the first half of the tunnel
error @1 :TunnelError; # if we didn't start the tunnel, why not
}
}
struct OperationCompleteTunnelQ {
struct OperationCompleteTunnelQ @0xe978594588eb950b {
id @0 :TunnelID; # tunnel id to use everywhere
localMode @1 :TunnelEndpointMode; # what kind of local endpoint mode is being requested
depth @2 :UInt8; # the number of nodes in the tunnel
endpoint @3 :TunnelEndpoint; # the remote endpoint to complete
}
struct OperationCompleteTunnelA {
struct OperationCompleteTunnelA @0x84090791bb765f2a {
union {
tunnel @0 :FullTunnel; # the tunnel description
error @1 :TunnelError; # if we didn't complete the tunnel, why not
}
}
struct OperationCancelTunnelQ {
struct OperationCancelTunnelQ @0xae2811ae0a003738 {
id @0 :TunnelID; # the tunnel id to cancel
}
struct OperationCancelTunnelA {
struct OperationCancelTunnelA @0xbba23c992eff97bc {
union {
tunnel @0 :TunnelID; # the tunnel id that was cancelled
error @1 :TunnelError; # if we couldn't cancel, why not
@ -429,7 +455,7 @@ struct OperationCancelTunnelA {
}
# Things that want an answer
struct Question {
struct Question @0xd8510bc33492ef70 {
respondTo :union {
sender @0 :Void; # sender
privateRoute @1 :PrivateRoute; # embedded private route to be used for reply
@ -445,16 +471,17 @@ struct Question {
watchValueQ @6 :OperationWatchValueQ;
supplyBlockQ @7 :OperationSupplyBlockQ;
findBlockQ @8 :OperationFindBlockQ;
appCallQ @9 :OperationAppCallQ;
# Tunnel operations
startTunnelQ @9 :OperationStartTunnelQ;
completeTunnelQ @10 :OperationCompleteTunnelQ;
cancelTunnelQ @11 :OperationCancelTunnelQ;
startTunnelQ @10 :OperationStartTunnelQ;
completeTunnelQ @11 :OperationCompleteTunnelQ;
cancelTunnelQ @12 :OperationCancelTunnelQ;
}
}
# Things that don't want an answer
struct Statement {
struct Statement @0x990e20828f404ae1 {
detail :union {
# Direct operations
validateDialInfo @0 :OperationValidateDialInfo;
@ -465,11 +492,12 @@ struct Statement {
valueChanged @3 :OperationValueChanged;
signal @4 :OperationSignal;
returnReceipt @5 :OperationReturnReceipt;
appMessage @6 :OperationAppMessage;
}
}
# Things that are answers
struct Answer {
struct Answer @0xacacb8b6988c1058 {
detail :union {
# Direct operations
statusA @0 :OperationStatusA;
@ -480,16 +508,17 @@ struct Answer {
setValueA @3 :OperationSetValueA;
watchValueA @4 :OperationWatchValueA;
supplyBlockA @5 :OperationSupplyBlockA;
findBlockA @6 :OperationFindBlockA;
findBlockA @6 :OperationFindBlockA;
appCallA @7 :OperationAppCallA;
# Tunnel operations
startTunnelA @7 :OperationStartTunnelA;
completeTunnelA @8 :OperationCompleteTunnelA;
cancelTunnelA @9 :OperationCancelTunnelA;
startTunnelA @8 :OperationStartTunnelA;
completeTunnelA @9 :OperationCompleteTunnelA;
cancelTunnelA @10 :OperationCancelTunnelA;
}
}
struct Operation {
struct Operation @0xbf2811c435403c3b {
opId @0 :UInt64; # Random RPC ID. Must be random to foil reply forgery attacks.
senderNodeInfo @1 :SignedNodeInfo; # (optional) SignedNodeInfo for the sender to be cached by the receiver.
kind :union {

View File

@ -103,7 +103,7 @@ impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLa
None
};
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
(inner.update_callback)(VeilidUpdate::Log(VeilidLog {
log_level,
message,
backtrace,

View File

@ -1,15 +1,16 @@
use crate::callback_state_machine::*;
use crate::dht::Crypto;
use crate::crypto::Crypto;
use crate::network_manager::*;
use crate::routing_table::*;
use crate::xx::*;
use crate::*;
use core::convert::TryFrom;
use core::fmt;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
use serde::*;
state_machine! {
derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)
derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,)
pub Attachment(Detached)
//---
Detached(AttachRequested) => Attaching [StartAttachment],
@ -102,48 +103,77 @@ impl TryFrom<String> for AttachmentState {
}
pub struct AttachmentManagerInner {
config: VeilidConfig,
attachment_machine: CallbackStateMachine<Attachment>,
network_manager: NetworkManager,
maintain_peers: bool,
attach_timestamp: Option<u64>,
update_callback: Option<UpdateCallback>,
attachment_maintainer_jh: Option<MustJoinHandle<()>>,
}
pub struct AttachmentManagerUnlockedInner {
config: VeilidConfig,
network_manager: NetworkManager,
}
#[derive(Clone)]
pub struct AttachmentManager {
inner: Arc<Mutex<AttachmentManagerInner>>,
unlocked_inner: Arc<AttachmentManagerUnlockedInner>,
}
impl AttachmentManager {
fn new_inner(
fn new_unlocked_inner(
config: VeilidConfig,
protected_store: ProtectedStore,
table_store: TableStore,
block_store: BlockStore,
crypto: Crypto,
) -> AttachmentManagerInner {
AttachmentManagerInner {
) -> AttachmentManagerUnlockedInner {
AttachmentManagerUnlockedInner {
config: config.clone(),
network_manager: NetworkManager::new(
config,
protected_store,
table_store,
block_store,
crypto,
),
}
}
fn new_inner() -> AttachmentManagerInner {
AttachmentManagerInner {
attachment_machine: CallbackStateMachine::new(),
network_manager: NetworkManager::new(config, table_store, crypto),
maintain_peers: false,
attach_timestamp: None,
update_callback: None,
attachment_maintainer_jh: None,
}
}
pub fn new(config: VeilidConfig, table_store: TableStore, crypto: Crypto) -> Self {
pub fn new(
config: VeilidConfig,
protected_store: ProtectedStore,
table_store: TableStore,
block_store: BlockStore,
crypto: Crypto,
) -> Self {
Self {
inner: Arc::new(Mutex::new(Self::new_inner(config, table_store, crypto))),
inner: Arc::new(Mutex::new(Self::new_inner())),
unlocked_inner: Arc::new(Self::new_unlocked_inner(
config,
protected_store,
table_store,
block_store,
crypto,
)),
}
}
pub fn config(&self) -> VeilidConfig {
self.inner.lock().config.clone()
self.unlocked_inner.config.clone()
}
pub fn network_manager(&self) -> NetworkManager {
self.inner.lock().network_manager.clone()
self.unlocked_inner.network_manager.clone()
}
pub fn is_attached(&self) -> bool {
@ -202,9 +232,10 @@ impl AttachmentManager {
AttachmentManager::translate_attachment_state(&inner.attachment_machine.state());
// get reliable peer count from routing table
let routing_table = inner.network_manager.routing_table();
let routing_table = self.network_manager().routing_table();
let health = routing_table.get_routing_table_health();
let routing_table_config = &inner.config.get().network.routing_table;
let config = self.config();
let routing_table_config = &config.get().network.routing_table;
let new_peer_state_input =
AttachmentManager::translate_routing_table_health(health, routing_table_config);
@ -223,11 +254,8 @@ impl AttachmentManager {
#[instrument(level = "debug", skip(self))]
async fn attachment_maintainer(self) {
debug!("attachment starting");
let netman = {
let mut inner = self.inner.lock();
inner.attach_timestamp = Some(intf::get_timestamp());
inner.network_manager.clone()
};
self.inner.lock().attach_timestamp = Some(intf::get_timestamp());
let netman = self.network_manager();
let mut restart;
loop {
@ -286,7 +314,7 @@ impl AttachmentManager {
#[instrument(level = "debug", skip_all, err)]
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
trace!("init");
let network_manager = {
{
let mut inner = self.inner.lock();
inner.update_callback = Some(update_callback.clone());
let update_callback2 = update_callback.clone();
@ -297,10 +325,9 @@ impl AttachmentManager {
}))
},
));
inner.network_manager.clone()
};
network_manager.init(update_callback).await?;
self.network_manager().init(update_callback).await?;
Ok(())
}
@ -309,30 +336,33 @@ impl AttachmentManager {
pub async fn terminate(&self) {
// Ensure we detached
self.detach().await;
let network_manager = {
let inner = self.inner.lock();
inner.network_manager.clone()
};
network_manager.terminate().await;
let mut inner = self.inner.lock();
inner.update_callback = None;
self.network_manager().terminate().await;
self.inner.lock().update_callback = None;
}
#[instrument(level = "trace", skip(self))]
fn attach(&self) {
// Create long-running connection maintenance routine
let this = self.clone();
self.inner.lock().maintain_peers = true;
self.inner.lock().attachment_maintainer_jh =
Some(intf::spawn(this.attachment_maintainer()));
let mut inner = self.inner.lock();
if inner.attachment_maintainer_jh.is_some() {
return;
}
inner.maintain_peers = true;
inner.attachment_maintainer_jh = Some(intf::spawn(self.clone().attachment_maintainer()));
}
#[instrument(level = "trace", skip(self))]
async fn detach(&self) {
let attachment_maintainer_jh = self.inner.lock().attachment_maintainer_jh.take();
let attachment_maintainer_jh = {
let mut inner = self.inner.lock();
let attachment_maintainer_jh = inner.attachment_maintainer_jh.take();
if attachment_maintainer_jh.is_some() {
// Terminate long-running connection maintenance routine
inner.maintain_peers = false;
}
attachment_maintainer_jh
};
if let Some(jh) = attachment_maintainer_jh {
// Terminate long-running connection maintenance routine
self.inner.lock().maintain_peers = false;
jh.await;
}
}

View File

@ -1,6 +1,6 @@
use crate::api_tracing_layer::*;
use crate::attachment_manager::*;
use crate::dht::Crypto;
use crate::crypto::Crypto;
use crate::veilid_api::*;
use crate::veilid_config::*;
use crate::xx::*;
@ -103,7 +103,13 @@ impl ServicesContext {
// Set up attachment manager
trace!("init attachment manager");
let update_callback = self.update_callback.clone();
let attachment_manager = AttachmentManager::new(self.config.clone(), table_store, crypto);
let attachment_manager = AttachmentManager::new(
self.config.clone(),
protected_store,
table_store,
block_store,
crypto,
);
if let Err(e) = attachment_manager.init(update_callback).await {
self.shutdown().await;
return Err(e);
@ -171,7 +177,7 @@ impl VeilidCoreContext {
// Set up config from callback
trace!("setup config with callback");
let mut config = VeilidConfig::new();
config.setup(config_callback)?;
config.setup(config_callback, update_callback.clone())?;
Self::new_common(update_callback, config).await
}
@ -184,7 +190,7 @@ impl VeilidCoreContext {
// Set up config from callback
trace!("setup config with json");
let mut config = VeilidConfig::new();
config.setup_from_json(config_json)?;
config.setup_from_json(config_json, update_callback.clone())?;
Self::new_common(update_callback, config).await
}

View File

@ -1,7 +1,7 @@
#![allow(dead_code)]
#![allow(clippy::absurd_extreme_comparisons)]
use super::crypto::*;
use super::key::*;
use super::*;
use crate::routing_table::VersionRange;
use crate::xx::*;
use crate::*;
use core::convert::TryInto;
@ -38,8 +38,6 @@ use core::convert::TryInto;
pub const MAX_ENVELOPE_SIZE: usize = 65507;
pub const MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + Signature
pub const ENVELOPE_MAGIC: &[u8; 4] = b"VLID";
pub const MIN_VERSION: u8 = 0u8;
pub const MAX_VERSION: u8 = 0u8;
pub type EnvelopeNonce = [u8; 24];
#[derive(Debug, Clone, PartialEq, Eq, Default)]
@ -61,15 +59,12 @@ impl Envelope {
sender_id: DHTKey,
recipient_id: DHTKey,
) -> Self {
assert!(sender_id.valid);
assert!(recipient_id.valid);
assert!(version >= MIN_VERSION);
assert!(version <= MAX_VERSION);
assert!(version >= MIN_CRYPTO_VERSION);
assert!(version <= MAX_CRYPTO_VERSION);
Self {
version,
min_version: MIN_VERSION,
max_version: MAX_VERSION,
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
timestamp,
nonce,
sender_id,
@ -94,9 +89,9 @@ impl Envelope {
// Check version
let version = data[0x04];
if version > MAX_VERSION || version < MIN_VERSION {
if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION {
return Err(VeilidAPIError::parse_error(
"unsupported protocol version",
"unsupported cryptography version",
version,
));
}
@ -208,15 +203,6 @@ impl Envelope {
body: &[u8],
node_id_secret: &DHTKeySecret,
) -> Result<Vec<u8>, VeilidAPIError> {
// Ensure sender node id is valid
if !self.sender_id.valid {
return Err(VeilidAPIError::generic("sender id is invalid"));
}
// Ensure recipient node id is valid
if !self.recipient_id.valid {
return Err(VeilidAPIError::generic("recipient id is invalid"));
}
// Ensure body isn't too long
let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE;
if envelope_size > MAX_ENVELOPE_SIZE {
@ -274,8 +260,11 @@ impl Envelope {
self.version
}
pub fn get_min_max_version(&self) -> (u8, u8) {
(self.min_version, self.max_version)
pub fn get_min_max_version(&self) -> VersionRange {
VersionRange {
min: self.min_version,
max: self.max_version,
}
}
pub fn get_timestamp(&self) -> u64 {

View File

@ -2,29 +2,35 @@ use crate::veilid_rng::*;
use crate::xx::*;
use crate::*;
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::hash::{Hash, Hasher};
use core::hash::Hash;
use data_encoding::BASE64URL_NOPAD;
use digest::generic_array::typenum::U64;
use digest::{Digest, Output};
use ed25519_dalek::{Keypair, PublicKey, Signature};
use generic_array::GenericArray;
use serde::{Deserialize, Serialize};
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
//////////////////////////////////////////////////////////////////////
/// Length of a DHT key in bytes
#[allow(dead_code)]
pub const DHT_KEY_LENGTH: usize = 32;
/// Length of a DHT key in bytes after encoding to base64url
#[allow(dead_code)]
pub const DHT_KEY_LENGTH_ENCODED: usize = 43;
/// Length of a DHT secret in bytes
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH: usize = 32;
/// Length of a DHT secret in bytes after encoding to base64url
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH_ENCODED: usize = 43;
/// Length of a DHT signature in bytes
#[allow(dead_code)]
/// Length of a DHT signature in bytes after encoding to base64url
pub const DHT_SIGNATURE_LENGTH: usize = 64;
#[allow(dead_code)]
pub const DHT_SIGNATURE_LENGTH_ENCODED: usize = 86;
@ -33,33 +39,47 @@ pub const DHT_SIGNATURE_LENGTH_ENCODED: usize = 86;
macro_rules! byte_array_type {
($name:ident, $size:expr) => {
#[derive(Clone, Copy)]
#[derive(
Clone,
Copy,
Hash,
Eq,
PartialEq,
PartialOrd,
Ord,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, Eq, PartialEq, PartialOrd, Ord))]
pub struct $name {
pub bytes: [u8; $size],
pub valid: bool,
}
impl Serialize for $name {
impl Default for $name {
fn default() -> Self {
Self {
bytes: [0u8; $size],
}
}
}
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s: String;
if self.valid {
s = self.encode();
} else {
s = "".to_owned();
}
s.serialize(serializer)
let s = self.encode();
serde::Serialize::serialize(&s, serializer)
}
}
impl<'de> Deserialize<'de> for $name {
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
if s == "" {
return Ok($name::default());
}
@ -69,28 +89,19 @@ macro_rules! byte_array_type {
impl $name {
pub fn new(bytes: [u8; $size]) -> Self {
Self { bytes, valid: true }
Self { bytes }
}
pub fn try_from_vec(v: Vec<u8>) -> Result<Self, VeilidAPIError> {
let mut this = Self {
bytes: [0u8; $size],
valid: true,
};
if v.len() != $size {
apibail_generic!(format!(
"Expected a Vec of length {} but it was {}",
$size,
v.len()
));
}
for n in 0..v.len() {
this.bytes[n] = v[n];
}
Ok(this)
let vl = v.len();
Ok(Self {
bytes: v.try_into().map_err(|_| {
VeilidAPIError::generic(format!(
"Expected a Vec of length {} but it was {}",
$size, vl
))
})?,
})
}
pub fn bit(&self, index: usize) -> bool {
@ -136,14 +147,13 @@ macro_rules! byte_array_type {
}
pub fn encode(&self) -> String {
assert!(self.valid);
BASE64URL_NOPAD.encode(&self.bytes)
}
pub fn try_decode(input: &str) -> Result<Self, VeilidAPIError> {
pub fn try_decode<S: AsRef<str>>(input: S) -> Result<Self, VeilidAPIError> {
let mut bytes = [0u8; $size];
let res = BASE64URL_NOPAD.decode_len(input.len());
let res = BASE64URL_NOPAD.decode_len(input.as_ref().len());
match res {
Ok(v) => {
if v != $size {
@ -155,103 +165,38 @@ macro_rules! byte_array_type {
}
}
let res = BASE64URL_NOPAD.decode_mut(input.as_bytes(), &mut bytes);
let res = BASE64URL_NOPAD.decode_mut(input.as_ref().as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Self::new(bytes)),
Err(_) => apibail_generic!("Failed to decode"),
}
}
}
impl PartialOrd for $name {
fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $name {
fn cmp(&self, other: &$name) -> Ordering {
if !self.valid && !other.valid {
return Ordering::Equal;
}
if !self.valid && other.valid {
return Ordering::Less;
}
if self.valid && !other.valid {
return Ordering::Greater;
}
for n in 0..$size {
if self.bytes[n] < other.bytes[n] {
return Ordering::Less;
}
if self.bytes[n] > other.bytes[n] {
return Ordering::Greater;
}
}
Ordering::Equal
}
}
impl PartialEq<$name> for $name {
fn eq(&self, other: &$name) -> bool {
if self.valid != other.valid {
return false;
}
for n in 0..$size {
if self.bytes[n] != other.bytes[n] {
return false;
}
}
true
}
}
impl Eq for $name {}
impl Hash for $name {
fn hash<H: Hasher>(&self, state: &mut H) {
self.valid.hash(state);
if self.valid {
self.bytes.hash(state);
}
}
}
impl Default for $name {
fn default() -> Self {
let mut this = $name::new([0u8; $size]);
this.valid = false;
this
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from(self))
//write!(f, "{}", String::from(self))
write!(f, "{}", self.encode())
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(
f,
"{}",
if self.valid {
self.encode()
} else {
"".to_owned()
}
)?;
write!(f, "{}", self.encode())?;
write!(f, ")")
}
}
impl From<&$name> for String {
fn from(value: &$name) -> Self {
if !value.valid {
return "".to_owned();
}
let mut s = String::new();
for n in 0..($size / 8) {
let b: [u8; 8] = value.bytes[n * 8..(n + 1) * 8].try_into().unwrap();
s.push_str(hex::encode(b).as_str());
}
s
// let mut s = String::new();
// for n in 0..($size / 8) {
// let b: [u8; 8] = value.bytes[n * 8..(n + 1) * 8].try_into().unwrap();
// s.push_str(hex::encode(b).as_str());
// }
// s
value.encode()
}
}
@ -265,20 +210,18 @@ macro_rules! byte_array_type {
impl TryFrom<&str> for $name {
type Error = VeilidAPIError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut out = $name::default();
if value == "" {
return Ok(out);
}
if value.len() != ($size * 2) {
apibail_generic!(concat!(stringify!($name), " is incorrect length"));
}
match hex::decode_to_slice(value, &mut out.bytes) {
Ok(_) => {
out.valid = true;
Ok(out)
}
Err(err) => Err(VeilidAPIError::generic(err)),
}
// let mut out = $name::default();
// if value == "" {
// return Ok(out);
// }
// if value.len() != ($size * 2) {
// apibail_generic!(concat!(stringify!($name), " is incorrect length"));
// }
// match hex::decode_to_slice(value, &mut out.bytes) {
// Ok(_) => Ok(out),
// Err(err) => Err(VeilidAPIError::generic(err)),
// }
Self::try_decode(value)
}
}
};
@ -374,9 +317,6 @@ pub fn sign(
dht_key_secret: &DHTKeySecret,
data: &[u8],
) -> Result<DHTSignature, VeilidAPIError> {
assert!(dht_key.valid);
assert!(dht_key_secret.valid);
let mut kpb: [u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH] =
[0u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH];
@ -401,8 +341,6 @@ pub fn verify(
data: &[u8],
signature: &DHTSignature,
) -> Result<(), VeilidAPIError> {
assert!(dht_key.valid);
assert!(signature.valid);
let pk = PublicKey::from_bytes(&dht_key.bytes)
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
let sig = Signature::from_bytes(&signature.bytes)
@ -421,7 +359,6 @@ pub fn generate_hash(data: &[u8]) -> DHTKey {
}
pub fn validate_hash(data: &[u8], dht_key: &DHTKey) -> bool {
assert!(dht_key.valid);
let bytes = *blake3::hash(data).as_bytes();
bytes == dht_key.bytes
@ -439,8 +376,6 @@ pub fn validate_key(dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool {
}
pub fn distance(key1: &DHTKey, key2: &DHTKey) -> DHTKeyDistance {
assert!(key1.valid);
assert!(key2.valid);
let mut bytes = [0u8; DHT_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {

View File

@ -1,4 +1,18 @@
use super::key::*;
mod envelope;
mod key;
mod receipt;
mod value;
pub mod tests;
pub use envelope::*;
pub use key::*;
pub use receipt::*;
pub use value::*;
pub const MIN_CRYPTO_VERSION: u8 = 0u8;
pub const MAX_CRYPTO_VERSION: u8 = 0u8;
use crate::xx::*;
use crate::*;
use chacha20::cipher::{KeyIvInit, StreamCipher};
@ -98,19 +112,19 @@ impl Crypto {
let (table_store, node_id) = {
let mut inner = self.inner.lock();
let c = self.config.get();
inner.node_id = c.network.node_id;
inner.node_id_secret = c.network.node_id_secret;
inner.node_id = c.network.node_id.unwrap();
inner.node_id_secret = c.network.node_id_secret.unwrap();
(inner.table_store.clone(), c.network.node_id)
};
// load caches if they are valid for this node id
let mut db = table_store.open("crypto_caches", 1).await?;
let caches_valid = match db.load(0, b"node_id").await? {
Some(v) => v.as_slice() == node_id.bytes,
let caches_valid = match db.load(0, b"node_id")? {
Some(v) => v.as_slice() == node_id.unwrap().bytes,
None => false,
};
if caches_valid {
if let Some(b) = db.load(0, b"dh_cache").await? {
if let Some(b) = db.load(0, b"dh_cache")? {
let mut inner = self.inner.lock();
bytes_to_cache(&b, &mut inner.dh_cache);
}
@ -118,7 +132,7 @@ impl Crypto {
drop(db);
table_store.delete("crypto_caches").await?;
db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"node_id", &node_id.bytes).await?;
db.store(0, b"node_id", &node_id.unwrap().bytes)?;
}
// Schedule flushing
@ -145,7 +159,7 @@ impl Crypto {
};
let db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"dh_cache", &cache_bytes).await?;
db.store(0, b"dh_cache", &cache_bytes)?;
Ok(())
}
@ -206,8 +220,6 @@ impl Crypto {
// These are safe to use regardless of initialization status
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, VeilidAPIError> {
assert!(key.valid);
assert!(secret.valid);
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;

View File

@ -1,7 +1,6 @@
#![allow(dead_code)]
#![allow(clippy::absurd_extreme_comparisons)]
use super::envelope::{MAX_VERSION, MIN_VERSION};
use super::key::*;
use super::*;
use crate::xx::*;
use crate::*;
use core::convert::TryInto;
@ -59,7 +58,6 @@ impl Receipt {
sender_id: DHTKey,
extra_data: D,
) -> Result<Self, VeilidAPIError> {
assert!(sender_id.valid);
if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE {
return Err(VeilidAPIError::parse_error(
"extra data too large for receipt",
@ -90,9 +88,9 @@ impl Receipt {
// Check version
let version = data[0x04];
if version > MAX_VERSION || version < MIN_VERSION {
if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION {
return Err(VeilidAPIError::parse_error(
"unsupported protocol version",
"unsupported cryptography version",
version,
));
}
@ -152,11 +150,6 @@ impl Receipt {
}
pub fn to_signed_data(&self, secret: &DHTKeySecret) -> Result<Vec<u8>, VeilidAPIError> {
// Ensure sender node id is valid
if !self.sender_id.valid {
return Err(VeilidAPIError::internal("sender id is invalid"));
}
// Ensure extra data isn't too long
let receipt_size: usize = self.extra_data.len() + MIN_RECEIPT_SIZE;
if receipt_size > MAX_RECEIPT_SIZE {

View File

@ -88,9 +88,7 @@ pub async fn test_key_conversions() {
// Test default key
let (dht_key, dht_key_secret) = (key::DHTKey::default(), key::DHTKeySecret::default());
assert_eq!(dht_key.bytes, EMPTY_KEY);
assert!(!dht_key.valid);
assert_eq!(dht_key_secret.bytes, EMPTY_KEY_SECRET);
assert!(!dht_key_secret.valid);
let dht_key_string = String::from(&dht_key);
trace!("dht_key_string: {:?}", dht_key_string);
let dht_key_string2 = String::from(&dht_key);
@ -140,11 +138,11 @@ pub async fn test_key_conversions() {
// Assert string roundtrip
assert_eq!(String::from(&dht_key2_back), dht_key2_string);
assert!(key::DHTKey::try_from("") == Ok(key::DHTKey::default()));
assert!(key::DHTKeySecret::try_from("") == Ok(key::DHTKeySecret::default()));
// These conversions should fail
assert!(key::DHTKey::try_from("whatever").is_err());
assert!(key::DHTKeySecret::try_from("whatever").is_err());
assert!(key::DHTKey::try_from("").is_err());
assert!(key::DHTKeySecret::try_from("").is_err());
assert!(key::DHTKey::try_from(" ").is_err());
assert!(key::DHTKeySecret::try_from(" ").is_err());
assert!(key::DHTKey::try_from(

View File

@ -1,13 +0,0 @@
mod crypto;
mod envelope;
mod key;
mod receipt;
mod value;
pub mod tests;
pub use crypto::*;
pub use envelope::*;
pub use key::*;
pub use receipt::*;
pub use value::*;

View File

@ -2,6 +2,7 @@ use crate::xx::*;
use crate::*;
use data_encoding::BASE64URL_NOPAD;
use keyring_manager::*;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
use std::path::Path;
pub struct ProtectedStoreInner {
@ -31,15 +32,18 @@ impl ProtectedStore {
#[instrument(level = "trace", skip(self), err)]
pub async fn delete_all(&self) -> EyreResult<()> {
// Delete all known keys
if self.remove_user_secret_string("node_id").await? {
if self.remove_user_secret("node_id").await? {
debug!("deleted protected_store key 'node_id'");
}
if self.remove_user_secret_string("node_id_secret").await? {
if self.remove_user_secret("node_id_secret").await? {
debug!("deleted protected_store key 'node_id_secret'");
}
if self.remove_user_secret_string("_test_key").await? {
if self.remove_user_secret("_test_key").await? {
debug!("deleted protected_store key '_test_key'");
}
if self.remove_user_secret("RouteSpecStore").await? {
debug!("deleted protected_store key 'RouteSpecStore'");
}
Ok(())
}
@ -139,19 +143,60 @@ impl ProtectedStore {
}
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret_string(&self, key: &str) -> EyreResult<bool> {
let inner = self.inner.lock();
match inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| kr.delete_value())
{
Ok(_) => Ok(true),
Err(KeyringError::NoPasswordFound) => Ok(false),
Err(e) => Err(eyre!("Failed to remove user secret: {}", e)),
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_rkyv<T>(&self, key: &str, value: &T) -> EyreResult<bool>
where
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = to_rkyv(value)?;
self.save_user_secret(&key, &v).await
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_json<T>(&self, key: &str, value: &T) -> EyreResult<bool>
where
T: serde::Serialize,
{
let v = serde_json::to_vec(value)?;
self.save_user_secret(&key, &v).await
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_rkyv<T>(&self, key: &str) -> EyreResult<Option<T>>
where
T: RkyvArchive,
<T as RkyvArchive>::Archived:
for<'t> bytecheck::CheckBytes<rkyv::validation::validators::DefaultValidator<'t>>,
<T as RkyvArchive>::Archived:
RkyvDeserialize<T, rkyv::de::deserializers::SharedDeserializeMap>,
{
let out = self.load_user_secret(key).await?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
};
let obj = from_rkyv(b)?;
Ok(Some(obj))
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_json<T>(&self, key: &str) -> EyreResult<Option<T>>
where
T: for<'de> serde::de::Deserialize<'de>,
{
let out = self.load_user_secret(key).await?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
};
let obj = serde_json::from_slice(&b)?;
Ok(Some(obj))
}
#[instrument(level = "trace", skip(self, value), ret, err)]
@ -195,6 +240,16 @@ impl ProtectedStore {
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
self.remove_user_secret_string(key).await
let inner = self.inner.lock();
match inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| kr.delete_value())
{
Ok(_) => Ok(true),
Err(KeyringError::NoPasswordFound) => Ok(false),
Err(e) => Err(eyre!("Failed to remove user secret: {}", e)),
}
}
}

View File

@ -8,6 +8,8 @@ struct TableStoreInner {
opened: BTreeMap<String, Weak<Mutex<TableDBInner>>>,
}
/// Veilid Table Storage
/// Database for storing key value pairs persistently across runs
#[derive(Clone)]
pub struct TableStore {
config: VeilidConfig,
@ -20,31 +22,38 @@ impl TableStore {
opened: BTreeMap::new(),
}
}
pub fn new(config: VeilidConfig) -> Self {
pub(crate) fn new(config: VeilidConfig) -> Self {
Self {
config,
inner: Arc::new(Mutex::new(Self::new_inner())),
}
}
pub async fn delete_all(&self) -> EyreResult<()> {
// Delete all known keys
self.delete("crypto_caches").await?;
/// Delete all known tables
pub async fn delete_all(&self) {
if let Err(e) = self.delete("crypto_caches").await {
error!("failed to delete 'crypto_caches': {}", e);
}
if let Err(e) = self.delete("RouteSpecStore").await {
error!("failed to delete 'RouteSpecStore': {}", e);
}
if let Err(e) = self.delete("routing_table").await {
error!("failed to delete 'routing_table': {}", e);
}
}
pub(crate) async fn init(&self) -> EyreResult<()> {
Ok(())
}
pub async fn init(&self) -> EyreResult<()> {
Ok(())
}
pub async fn terminate(&self) {
pub(crate) async fn terminate(&self) {
assert!(
self.inner.lock().opened.is_empty(),
"all open databases should have been closed"
);
}
pub fn on_table_db_drop(&self, table: String) {
pub(crate) fn on_table_db_drop(&self, table: String) {
let mut inner = self.inner.lock();
if inner.opened.remove(&table).is_none() {
unreachable!("should have removed an item");
@ -82,6 +91,8 @@ impl TableStore {
})
}
/// Get or create a TableDB database table. If the column count is greater than an
/// existing TableDB's column count, the database will be upgraded to add the missing columns
pub async fn open(&self, name: &str, column_count: u32) -> EyreResult<TableDB> {
let table_name = self.get_table_name(name)?;
@ -121,6 +132,7 @@ impl TableStore {
Ok(table_db)
}
/// Delete a TableDB table by name
pub async fn delete(&self, name: &str) -> EyreResult<bool> {
let table_name = self.get_table_name(name)?;

View File

@ -1,12 +1,14 @@
use crate::xx::*;
use crate::*;
use serde::{Deserialize, Serialize};
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use keyvaluedb_web::*;
use keyvaluedb::*;
} else {
use keyvaluedb_sqlite::*;
use keyvaluedb::*;
}
}
@ -28,7 +30,7 @@ pub struct TableDB {
}
impl TableDB {
pub fn new(table: String, table_store: TableStore, database: Database) -> Self {
pub(super) fn new(table: String, table_store: TableStore, database: Database) -> Self {
Self {
inner: Arc::new(Mutex::new(TableDBInner {
table,
@ -38,22 +40,24 @@ impl TableDB {
}
}
pub fn try_new_from_weak_inner(weak_inner: Weak<Mutex<TableDBInner>>) -> Option<Self> {
pub(super) fn try_new_from_weak_inner(weak_inner: Weak<Mutex<TableDBInner>>) -> Option<Self> {
weak_inner.upgrade().map(|table_db_inner| Self {
inner: table_db_inner,
})
}
pub fn weak_inner(&self) -> Weak<Mutex<TableDBInner>> {
pub(super) fn weak_inner(&self) -> Weak<Mutex<TableDBInner>> {
Arc::downgrade(&self.inner)
}
pub async fn get_column_count(&self) -> EyreResult<u32> {
/// Get the total number of columns in the TableDB
pub fn get_column_count(&self) -> EyreResult<u32> {
let db = &self.inner.lock().database;
db.num_columns().wrap_err("failed to get column count: {}")
}
pub async fn get_keys(&self, col: u32) -> EyreResult<Vec<Box<[u8]>>> {
/// Get the list of keys in a column of the TableDB
pub fn get_keys(&self, col: u32) -> EyreResult<Vec<Box<[u8]>>> {
let db = &self.inner.lock().database;
let mut out: Vec<Box<[u8]>> = Vec::new();
db.iter(col, None, &mut |kv| {
@ -64,18 +68,29 @@ impl TableDB {
Ok(out)
}
pub async fn store(&self, col: u32, key: &[u8], value: &[u8]) -> EyreResult<()> {
/// Start a TableDB write transaction. The transaction object must be committed or rolled back before dropping.
pub fn transact<'a>(&'a self) -> TableDBTransaction<'a> {
let dbt = {
let db = &self.inner.lock().database;
db.transaction()
};
TableDBTransaction::new(self, dbt)
}
/// Store a key with a value in a column in the TableDB. Performs a single transaction immediately.
pub fn store(&self, col: u32, key: &[u8], value: &[u8]) -> EyreResult<()> {
let db = &self.inner.lock().database;
let mut dbt = db.transaction();
dbt.put(col, key, value);
db.write(dbt).wrap_err("failed to store key")
}
pub async fn store_cbor<T>(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()>
/// Store a key in rkyv format with a value in a column in the TableDB. Performs a single transaction immediately.
pub fn store_rkyv<T>(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()>
where
T: Serialize,
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = serde_cbor::to_vec(value).wrap_err("couldn't store as CBOR")?;
let v = to_rkyv(value)?;
let db = &self.inner.lock().database;
let mut dbt = db.transaction();
@ -83,14 +98,33 @@ impl TableDB {
db.write(dbt).wrap_err("failed to store key")
}
pub async fn load(&self, col: u32, key: &[u8]) -> EyreResult<Option<Vec<u8>>> {
/// Store a key in json format with a value in a column in the TableDB. Performs a single transaction immediately.
pub fn store_json<T>(&self, col: u32, key: &[u8], value: &T) -> EyreResult<()>
where
T: serde::Serialize,
{
let v = serde_json::to_vec(value)?;
let db = &self.inner.lock().database;
let mut dbt = db.transaction();
dbt.put(col, key, v.as_slice());
db.write(dbt).wrap_err("failed to store key")
}
/// Read a key from a column in the TableDB immediately.
pub fn load(&self, col: u32, key: &[u8]) -> EyreResult<Option<Vec<u8>>> {
let db = &self.inner.lock().database;
db.get(col, key).wrap_err("failed to get key")
}
pub async fn load_cbor<T>(&self, col: u32, key: &[u8]) -> EyreResult<Option<T>>
/// Read an rkyv key from a column in the TableDB immediately
pub fn load_rkyv<T>(&self, col: u32, key: &[u8]) -> EyreResult<Option<T>>
where
T: for<'de> Deserialize<'de>,
T: RkyvArchive,
<T as RkyvArchive>::Archived:
for<'t> bytecheck::CheckBytes<rkyv::validation::validators::DefaultValidator<'t>>,
<T as RkyvArchive>::Archived:
RkyvDeserialize<T, rkyv::de::deserializers::SharedDeserializeMap>,
{
let db = &self.inner.lock().database;
let out = db.get(col, key).wrap_err("failed to get key")?;
@ -100,11 +134,29 @@ impl TableDB {
return Ok(None);
}
};
let obj = serde_cbor::from_slice::<T>(&b).wrap_err("failed to deserialize")?;
let obj = from_rkyv(b)?;
Ok(Some(obj))
}
pub async fn delete(&self, col: u32, key: &[u8]) -> EyreResult<bool> {
/// Read an serde-json key from a column in the TableDB immediately
pub fn load_json<T>(&self, col: u32, key: &[u8]) -> EyreResult<Option<T>>
where
T: for<'de> serde::Deserialize<'de>,
{
let db = &self.inner.lock().database;
let out = db.get(col, key).wrap_err("failed to get key")?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
};
let obj = serde_json::from_slice(&b)?;
Ok(Some(obj))
}
/// Delete key with from a column in the TableDB
pub fn delete(&self, col: u32, key: &[u8]) -> EyreResult<bool> {
let db = &self.inner.lock().database;
let found = db.get(col, key).wrap_err("failed to get key")?;
match found {
@ -118,3 +170,76 @@ impl TableDB {
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// A TableDB transaction
/// Atomically commits a group of writes or deletes to the TableDB
pub struct TableDBTransaction<'a> {
db: &'a TableDB,
dbt: Option<DBTransaction>,
_phantom: core::marker::PhantomData<&'a ()>,
}
impl<'a> TableDBTransaction<'a> {
fn new(db: &'a TableDB, dbt: DBTransaction) -> Self {
Self {
db,
dbt: Some(dbt),
_phantom: Default::default(),
}
}
/// Commit the transaction. Performs all actions atomically.
pub fn commit(mut self) -> EyreResult<()> {
self.db
.inner
.lock()
.database
.write(self.dbt.take().unwrap())
.wrap_err("commit failed")
}
/// Rollback the transaction. Does nothing to the TableDB.
pub fn rollback(mut self) {
self.dbt = None;
}
/// Store a key with a value in a column in the TableDB
pub fn store(&mut self, col: u32, key: &[u8], value: &[u8]) {
self.dbt.as_mut().unwrap().put(col, key, value);
}
/// Store a key in rkyv format with a value in a column in the TableDB
pub fn store_rkyv<T>(&mut self, col: u32, key: &[u8], value: &T) -> EyreResult<()>
where
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = to_rkyv(value)?;
self.dbt.as_mut().unwrap().put(col, key, v.as_slice());
Ok(())
}
/// Store a key in rkyv format with a value in a column in the TableDB
pub fn store_json<T>(&mut self, col: u32, key: &[u8], value: &T) -> EyreResult<()>
where
T: serde::Serialize,
{
let v = serde_json::to_vec(value)?;
self.dbt.as_mut().unwrap().put(col, key, v.as_slice());
Ok(())
}
/// Delete key with from a column in the TableDB
pub fn delete(&mut self, col: u32, key: &[u8]) {
self.dbt.as_mut().unwrap().delete(col, key);
}
}
impl<'a> Drop for TableDBTransaction<'a> {
fn drop(&mut self) {
if self.dbt.is_some() {
warn!("Dropped transaction without commit or rollback");
}
}
}

View File

@ -3,52 +3,45 @@ use crate::xx::*;
use crate::*;
use data_encoding::BASE64URL_NOPAD;
use js_sys::*;
use send_wrapper::*;
use serde::{Deserialize, Serialize};
use wasm_bindgen_futures::*;
use web_sys::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(catch, js_name = setPassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_setPassword(service: &str, account: &str, password: &str)
-> Result<Promise, JsValue>;
#[wasm_bindgen(catch, js_name = getPassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_getPassword(service: &str, account: &str) -> Result<Promise, JsValue>;
#[wasm_bindgen(catch, js_name = deletePassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_deletePassword(service: &str, account: &str) -> Result<Promise, JsValue>;
}
#[derive(Clone)]
pub struct ProtectedStore {
config: VeilidConfig,
}
impl ProtectedStore {
pub fn new(config: VeilidConfig) -> Self {
Self {
config,
}
Self { config }
}
#[instrument(level = "trace", skip(self), err)]
pub async fn delete_all(&self) -> EyreResult<()> {
// Delete all known keys
if self.remove_user_secret_string("node_id").await? {
if self.remove_user_secret("node_id").await? {
debug!("deleted protected_store key 'node_id'");
}
if self.remove_user_secret_string("node_id_secret").await? {
if self.remove_user_secret("node_id_secret").await? {
debug!("deleted protected_store key 'node_id_secret'");
}
if self.remove_user_secret_string("_test_key").await? {
if self.remove_user_secret("_test_key").await? {
debug!("deleted protected_store key '_test_key'");
}
if self.remove_user_secret("RouteSpecStore").await? {
debug!("deleted protected_store key 'RouteSpecStore'");
}
Ok(())
}
#[instrument(level = "debug", skip(self), err)]
pub async fn init(&self) -> EyreResult<()> {
Ok(())
}
#[instrument(level = "debug", skip(self))]
pub async fn terminate(&self) {}
fn keyring_name(&self) -> String {
@ -69,32 +62,9 @@ impl ProtectedStore {
}
}
//#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> EyreResult<bool> {
if is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(self.keyring_name().as_str(), key)
.map_err(map_jsvalue_error)
.wrap_err("exception thrown")?,
)
.await
{
Ok(v) => v.is_truthy(),
Err(_) => false,
};
match JsFuture::from(
keytar_setPassword(self.keyring_name().as_str(), key, value)
.map_err(map_jsvalue_error)
.wrap_err("exception thrown")?,
)
.await
{
Ok(_) => {}
Err(_) => bail!("Failed to set password"),
}
Ok(prev)
} else if is_browser() {
if is_browser() {
let win = match window() {
Some(w) => w,
None => {
@ -134,25 +104,9 @@ impl ProtectedStore {
}
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret_string(&self, key: &str) -> EyreResult<Option<String>> {
if is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(self.keyring_name().as_str(), key)
.map_err(map_jsvalue_error)
.wrap_err("exception thrown")?,
)
.await
{
Ok(p) => p,
Err(_) => JsValue::UNDEFINED,
};
if prev.is_undefined() || prev.is_null() {
return Ok(None);
}
Ok(prev.as_string())
} else if is_browser() {
if is_browser() {
let win = match window() {
Some(w) => w,
None => {
@ -181,19 +135,78 @@ impl ProtectedStore {
}
}
pub async fn remove_user_secret_string(&self, key: &str) -> EyreResult<bool> {
if is_nodejs() {
match JsFuture::from(
keytar_deletePassword(self.keyring_name().as_str(), key)
.map_err(map_jsvalue_error)
.wrap_err("exception thrown")?,
)
.await
{
Ok(v) => Ok(v.is_truthy()),
Err(_) => bail!("Failed to delete"),
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_frozen<T>(&self, key: &str, value: &T) -> EyreResult<bool>
where
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = to_frozen(value)?;
self.save_user_secret(&key, &v).await
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_frozen<T>(&self, key: &str) -> EyreResult<Option<T>>
where
T: RkyvArchive,
<T as RkyvArchive>::Archived:
for<'t> bytecheck::CheckBytes<rkyv::validation::validators::DefaultValidator<'t>>,
<T as RkyvArchive>::Archived:
rkyv::Deserialize<T, rkyv::de::deserializers::SharedDeserializeMap>,
{
let out = self.load_user_secret(key).await?;
let b = match out {
Some(v) => v,
None => {
return Ok(None);
}
} else if is_browser() {
};
let obj = from_frozen(&b)?;
Ok(Some(obj))
}
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
self.save_user_secret_string(key, s.as_str()).await
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
return Ok(None);
}
};
if s.pop() != Some('!') {
bail!("User secret is not a buffer");
}
let mut bytes = Vec::<u8>::new();
let res = BASE64URL_NOPAD.decode_len(s.len());
match res {
Ok(l) => {
bytes.resize(l, 0u8);
}
Err(_) => {
bail!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Some(bytes)),
Err(_) => bail!("Failed to decode"),
}
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
if is_browser() {
let win = match window() {
Some(w) => w,
None => {
@ -231,45 +244,4 @@ impl ProtectedStore {
unimplemented!();
}
}
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
self.save_user_secret_string(key, s.as_str()).await
}
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
return Ok(None);
}
};
if s.pop() != Some('!') {
bail!("User secret is not a buffer");
}
let mut bytes = Vec::<u8>::new();
let res = BASE64URL_NOPAD.decode_len(s.len());
match res {
Ok(l) => {
bytes.resize(l, 0u8);
}
Err(_) => {
bail!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Some(bytes)),
Err(_) => bail!("Failed to decode"),
}
}
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
self.remove_user_secret_string(key).await
}
}
}

View File

@ -19,10 +19,8 @@ extern "C" {
pub fn get_timestamp() -> u64 {
if utils::is_browser() {
return (Date::now() * 1000.0f64) as u64;
} else if utils::is_nodejs() {
return (Date::now() * 1000.0f64) as u64;
} else {
panic!("WASM requires browser or nodejs environment");
panic!("WASM requires browser environment");
}
}
@ -85,18 +83,22 @@ pub fn spawn<Out>(future: impl Future<Output = Out> + Send + 'static) -> MustJoi
where
Out: Send + 'static,
{
MustJoinHandle::new(Bindgen
.spawn_handle(future)
.expect("wasm-bindgen-futures spawn should never error out"))
MustJoinHandle::new(
Bindgen
.spawn_handle(future)
.expect("wasm-bindgen-futures spawn should never error out"),
)
}
pub fn spawn_local<Out>(future: impl Future<Output = Out> + 'static) -> MustJoinHandle<Out>
where
Out: 'static,
{
MustJoinHandle::new(Bindgen
.spawn_handle_local(future)
.expect("wasm-bindgen-futures spawn_local should never error out"))
MustJoinHandle::new(
Bindgen
.spawn_handle_local(future)
.expect("wasm-bindgen-futures spawn_local should never error out"),
)
}
// pub fn spawn_with_local_set<Out>(
@ -114,10 +116,10 @@ where
{
Bindgen
.spawn_handle_local(future)
.expect("wasm-bindgen-futures spawn_local should never error out").detach()
.expect("wasm-bindgen-futures spawn_local should never error out")
.detach()
}
pub fn interval<F, FUT>(freq_ms: u32, callback: F) -> SendPinBoxFuture<()>
where
F: Fn() -> FUT + Send + Sync + 'static,
@ -160,12 +162,12 @@ pub async fn get_outbound_relay_peer() -> Option<crate::veilid_api::PeerInfo> {
// pub async fn get_pwa_web_server_config() -> {
// if utils::is_browser() {
// let win = window().unwrap();
// let doc = win.document().unwrap();
// let html_document = document.dyn_into::<web_sys::HtmlDocument>().unwrap();
// let cookie = html_document.cookie().unwrap();
// // let wait_millis = if millis > u32::MAX {
// // i32::MAX
// // } else {
@ -177,22 +179,14 @@ pub async fn get_outbound_relay_peer() -> Option<crate::veilid_api::PeerInfo> {
// // .unwrap();
// // });
// // JsFuture::from(promise).await.unwrap();
// } else if utils::is_nodejs() {
// // let promise = Promise::new(&mut |yes, _| {
// // nodejs_global_set_timeout_with_callback_and_timeout_and_arguments_0(&yes, millis)
// // .unwrap();
// // });
// // JsFuture::from(promise).await.unwrap();
// } else {
// panic!("WASM requires browser or nodejs environment");
// }
// panic!("WASM requires browser environment");
// }
// }
pub async fn txt_lookup<S: AsRef<str>>(_host: S) -> EyreResult<Vec<String>> {
bail!("wasm does not support txt lookup")
bail!("wasm does not support txt lookup")
}
pub async fn ptr_lookup(_ip_addr: IpAddr) -> EyreResult<String> {

View File

@ -22,7 +22,7 @@ impl TableStore {
opened: BTreeMap::new(),
}
}
pub fn new(config: VeilidConfig) -> Self {
pub(crate) fn new(config: VeilidConfig) -> Self {
Self {
config,
inner: Arc::new(Mutex::new(Self::new_inner())),
@ -30,12 +30,25 @@ impl TableStore {
}
}
pub async fn init(&self) -> EyreResult<()> {
/// Delete all known tables
pub async fn delete_all(&self) {
if let Err(e) = self.delete("crypto_caches").await {
error!("failed to delete 'crypto_caches': {}", e);
}
if let Err(e) = self.delete("RouteSpecStore").await {
error!("failed to delete 'RouteSpecStore': {}", e);
}
if let Err(e) = self.delete("routing_table").await {
error!("failed to delete 'routing_table': {}", e);
}
}
pub(crate) async fn init(&self) -> EyreResult<()> {
let _async_guard = self.async_lock.lock().await;
Ok(())
}
pub async fn terminate(&self) {
pub(crate) async fn terminate(&self) {
let _async_guard = self.async_lock.lock().await;
assert!(
self.inner.lock().opened.len() == 0,
@ -43,7 +56,7 @@ impl TableStore {
);
}
pub fn on_table_db_drop(&self, table: String) {
pub(crate) fn on_table_db_drop(&self, table: String) {
let mut inner = self.inner.lock();
match inner.opened.remove(&table) {
Some(_) => (),
@ -69,12 +82,14 @@ impl TableStore {
})
}
/// Get or create a TableDB database table. If the column count is greater than an
/// existing TableDB's column count, the database will be upgraded to add the missing columns
pub async fn open(&self, name: &str, column_count: u32) -> EyreResult<TableDB> {
let _async_guard = self.async_lock.lock().await;
let table_name = self.get_table_name(name)?;
{
let mut inner = self.inner.lock();
let mut inner = self.inner.lock();
if let Some(table_db_weak_inner) = inner.opened.get(&table_name) {
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) {
Some(tdb) => {
@ -89,7 +104,10 @@ impl TableStore {
let db = Database::open(table_name.clone(), column_count)
.await
.wrap_err("failed to open tabledb")?;
info!("opened table store '{}' with table name '{:?}' with {} columns", name, table_name, column_count);
info!(
"opened table store '{}' with table name '{:?}' with {} columns",
name, table_name, column_count
);
let table_db = TableDB::new(table_name.clone(), self.clone(), db);
@ -101,11 +119,12 @@ impl TableStore {
Ok(table_db)
}
/// Delete a TableDB table by name
pub async fn delete(&self, name: &str) -> EyreResult<bool> {
let _async_guard = self.async_lock.lock().await;
trace!("TableStore::delete {}", name);
let table_name = self.get_table_name(name)?;
{
let inner = self.inner.lock();
if inner.opened.contains_key(&table_name) {
@ -117,9 +136,7 @@ impl TableStore {
}
}
if utils::is_nodejs() {
unimplemented!();
} else if utils::is_browser() {
if utils::is_browser() {
let out = match Database::delete(table_name.clone()).await {
Ok(_) => true,
Err(_) => false,

View File

@ -15,21 +15,6 @@ extern "C" {
pub fn alert(s: &str);
}
pub fn is_nodejs() -> bool {
static CACHE: AtomicI8 = AtomicI8::new(-1);
let cache = CACHE.load(Ordering::Relaxed);
if cache != -1 {
return cache != 0;
}
let res = js_sys::eval("process.release.name === 'node'")
.map(|res| res.is_truthy())
.unwrap_or_default();
CACHE.store(res as i8, Ordering::Relaxed);
res
}
pub fn is_browser() -> bool {
static CACHE: AtomicI8 = AtomicI8::new(-1);
let cache = CACHE.load(Ordering::Relaxed);
@ -60,24 +45,6 @@ pub fn is_browser() -> bool {
// res
// }
// pub fn node_require(module: &str) -> JsValue {
// if !is_nodejs() {
// return JsValue::UNDEFINED;
// }
// let mut home = env!("CARGO_MANIFEST_DIR");
// if home.len() == 0 {
// home = ".";
// }
// match js_sys::eval(format!("require(\"{}/{}\")", home, module).as_str()) {
// Ok(v) => v,
// Err(e) => {
// panic!("node_require failed: {:?}", e);
// }
// }
// }
#[derive(ThisError, Debug, Clone, Eq, PartialEq)]
#[error("JsValue error")]
pub struct JsValueError(String);

View File

@ -22,7 +22,7 @@ mod api_tracing_layer;
mod attachment_manager;
mod callback_state_machine;
mod core_context;
mod dht;
mod crypto;
mod intf;
mod network_manager;
mod receipt_manager;
@ -64,12 +64,14 @@ pub fn veilid_version() -> (u32, u32, u32) {
#[cfg(target_os = "android")]
pub use intf::utils::android::{veilid_core_setup_android, veilid_core_setup_android_no_log};
pub static DEFAULT_LOG_IGNORE_LIST: [&str; 19] = [
pub static DEFAULT_LOG_IGNORE_LIST: [&str; 21] = [
"mio",
"h2",
"hyper",
"tower",
"tonic",
"tokio",
"runtime",
"tokio_util",
"want",
"serial_test",

View File

@ -4,7 +4,7 @@ use super::*;
pub struct ConnectionHandle {
id: u64,
descriptor: ConnectionDescriptor,
channel: flume::Sender<Vec<u8>>,
channel: flume::Sender<(Option<Id>, Vec<u8>)>,
}
#[derive(Debug)]
@ -17,7 +17,7 @@ impl ConnectionHandle {
pub(super) fn new(
id: u64,
descriptor: ConnectionDescriptor,
channel: flume::Sender<Vec<u8>>,
channel: flume::Sender<(Option<Id>, Vec<u8>)>,
) -> Self {
Self {
id,
@ -34,16 +34,22 @@ impl ConnectionHandle {
self.descriptor.clone()
}
#[instrument(level="trace", skip(self, message), fields(message.len = message.len()))]
pub fn send(&self, message: Vec<u8>) -> ConnectionHandleSendResult {
match self.channel.send(message) {
match self.channel.send((Span::current().id(), message)) {
Ok(()) => ConnectionHandleSendResult::Sent,
Err(e) => ConnectionHandleSendResult::NotSent(e.0),
Err(e) => ConnectionHandleSendResult::NotSent(e.0 .1),
}
}
#[instrument(level="trace", skip(self, message), fields(message.len = message.len()))]
pub async fn send_async(&self, message: Vec<u8>) -> ConnectionHandleSendResult {
match self.channel.send_async(message).await {
match self
.channel
.send_async((Span::current().id(), message))
.await
{
Ok(()) => ConnectionHandleSendResult::Sent,
Err(e) => ConnectionHandleSendResult::NotSent(e.0),
Err(e) => ConnectionHandleSendResult::NotSent(e.0 .1),
}
}
}

View File

@ -26,6 +26,7 @@ struct ConnectionManagerArc {
connection_initial_timeout_ms: u32,
connection_inactivity_timeout_ms: u32,
connection_table: ConnectionTable,
address_lock_table: AsyncTagLockTable<SocketAddr>,
inner: Mutex<Option<ConnectionManagerInner>>,
}
impl core::fmt::Debug for ConnectionManagerArc {
@ -69,6 +70,7 @@ impl ConnectionManager {
connection_initial_timeout_ms,
connection_inactivity_timeout_ms,
connection_table: ConnectionTable::new(config),
address_lock_table: AsyncTagLockTable::new(),
inner: Mutex::new(None),
}
}
@ -140,6 +142,7 @@ impl ConnectionManager {
// Internal routine to register new connection atomically.
// Registers connection in the connection table for later access
// and spawns a message processing loop for the connection
#[instrument(level = "trace", skip(self, inner), ret, err)]
fn on_new_protocol_network_connection(
&self,
inner: &mut ConnectionManagerInner,
@ -195,6 +198,7 @@ impl ConnectionManager {
}
// Returns a network connection if one already is established
//#[instrument(level = "trace", skip(self), ret)]
pub fn get_connection(&self, descriptor: ConnectionDescriptor) -> Option<ConnectionHandle> {
self.arc
.connection_table
@ -228,21 +232,29 @@ impl ConnectionManager {
});
// Wait for the killed connections to end their recv loops
let did_kill = !killed.is_empty();
for k in killed {
for mut k in killed {
k.close();
k.await;
}
did_kill
}
// Called when we want to create a new connection or get the current one that already exists
// This will kill off any connections that are in conflict with the new connection to be made
// in order to make room for the new connection in the system's connection table
// This routine needs to be atomic, or connections may exist in the table that are not established
/// Called when we want to create a new connection or get the current one that already exists
/// This will kill off any connections that are in conflict with the new connection to be made
/// in order to make room for the new connection in the system's connection table
/// This routine needs to be atomic, or connections may exist in the table that are not established
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn get_or_create_connection(
&self,
local_addr: Option<SocketAddr>,
dial_info: DialInfo,
) -> EyreResult<NetworkResult<ConnectionHandle>> {
// Async lock on the remote address for atomicity per remote
let peer_address = dial_info.to_peer_address();
let remote_addr = peer_address.to_socket_addr();
let _lock_guard = self.arc.address_lock_table.lock_tag(remote_addr).await;
log_net!(
"== get_or_create_connection local_addr={:?} dial_info={:?}",
local_addr.green(),
@ -253,21 +265,12 @@ impl ConnectionManager {
let did_kill = self.kill_off_colliding_connections(&dial_info).await;
let mut retry_count = if did_kill { 2 } else { 0 };
// Make a connection descriptor for this dialinfo
let peer_address = dial_info.to_peer_address();
let descriptor = match local_addr {
Some(la) => {
ConnectionDescriptor::new(peer_address, SocketAddress::from_socket_addr(la))
}
None => ConnectionDescriptor::new_no_local(peer_address),
};
// If any connection to this remote exists that has the same protocol, return it
// Any connection will do, we don't have to match the local address
if let Some(conn) = self
.arc
.connection_table
.get_last_connection_by_remote(descriptor.remote())
.get_last_connection_by_remote(peer_address)
{
log_net!(
"== Returning existing connection local_addr={:?} peer_address={:?}",
@ -288,6 +291,23 @@ impl ConnectionManager {
.await;
match result_net_res {
Ok(net_res) => {
// If the connection 'already exists', then try one last time to return a connection from the table, in case
// an 'accept' happened at literally the same time as our connect
if net_res.is_already_exists() {
if let Some(conn) = self
.arc
.connection_table
.get_last_connection_by_remote(peer_address)
{
log_net!(
"== Returning existing connection in race local_addr={:?} peer_address={:?}",
local_addr.green(),
peer_address.green()
);
return Ok(NetworkResult::Value(conn));
}
}
if net_res.is_value() || retry_count == 0 {
break net_res;
}
@ -351,7 +371,7 @@ impl ConnectionManager {
// Called by low-level network when any connection-oriented protocol connection appears
// either from incoming connections.
#[cfg_attr(target_os = "wasm32", allow(dead_code))]
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub(super) async fn on_accepted_protocol_network_connection(
&self,
protocol_connection: ProtocolNetworkConnection,
@ -378,6 +398,7 @@ impl ConnectionManager {
// Callback from network connection receive loop when it exits
// cleans up the entry in the connection table
#[instrument(level = "trace", skip(self))]
pub(super) async fn report_connection_finished(&self, connection_id: u64) {
// Get channel sender
let sender = {

View File

@ -72,6 +72,7 @@ impl ConnectionTable {
}
}
#[instrument(level = "trace", skip(self))]
pub async fn join(&self) {
let mut unord = {
let mut inner = self.inner.lock();
@ -90,6 +91,7 @@ impl ConnectionTable {
while unord.next().await.is_some() {}
}
#[instrument(level = "trace", skip(self), ret, err)]
pub fn add_connection(
&self,
network_connection: NetworkConnection,
@ -142,7 +144,7 @@ impl ConnectionTable {
let mut out_conn = None;
if inner.conn_by_id[protocol_index].len() > inner.max_connections[protocol_index] {
if let Some((lruk, lru_conn)) = inner.conn_by_id[protocol_index].remove_lru() {
debug!("connection lru out: {:?}", lru_conn);
log_net!(debug "connection lru out: {:?}", lru_conn);
out_conn = Some(lru_conn);
Self::remove_connection_records(&mut *inner, lruk);
}
@ -156,6 +158,8 @@ impl ConnectionTable {
Ok(out_conn)
}
//#[instrument(level = "trace", skip(self), ret)]
#[allow(dead_code)]
pub fn get_connection_by_id(&self, id: NetworkConnectionId) -> Option<ConnectionHandle> {
let mut inner = self.inner.lock();
let protocol_index = *inner.protocol_index_by_id.get(&id)?;
@ -163,6 +167,7 @@ impl ConnectionTable {
Some(out.get_handle())
}
//#[instrument(level = "trace", skip(self), ret)]
pub fn get_connection_by_descriptor(
&self,
descriptor: ConnectionDescriptor,
@ -175,6 +180,7 @@ impl ConnectionTable {
Some(out.get_handle())
}
//#[instrument(level = "trace", skip(self), ret)]
pub fn get_last_connection_by_remote(&self, remote: PeerAddress) -> Option<ConnectionHandle> {
let mut inner = self.inner.lock();
@ -184,7 +190,9 @@ impl ConnectionTable {
Some(out.get_handle())
}
pub fn _get_connection_ids_by_remote(&self, remote: PeerAddress) -> Vec<NetworkConnectionId> {
//#[instrument(level = "trace", skip(self), ret)]
#[allow(dead_code)]
pub fn get_connection_ids_by_remote(&self, remote: PeerAddress) -> Vec<NetworkConnectionId> {
let inner = self.inner.lock();
inner
.ids_by_remote
@ -219,6 +227,7 @@ impl ConnectionTable {
inner.conn_by_id.iter().fold(0, |acc, c| acc + c.len())
}
#[instrument(level = "trace", skip(inner), ret)]
fn remove_connection_records(
inner: &mut ConnectionTableInner,
id: NetworkConnectionId,
@ -251,6 +260,7 @@ impl ConnectionTable {
conn
}
#[instrument(level = "trace", skip(self), ret)]
pub fn remove_connection_by_id(&self, id: NetworkConnectionId) -> Option<NetworkConnection> {
let mut inner = self.inner.lock();

File diff suppressed because it is too large Load Diff

View File

@ -40,11 +40,9 @@ struct NetworkInner {
/// such as dhcp release or change of address or interfaces being added or removed
network_needs_restart: bool,
/// the calculated protocol configuration for inbound/outbound protocols
protocol_config: Option<ProtocolConfig>,
protocol_config: ProtocolConfig,
/// set of statically configured protocols with public dialinfo
static_public_dialinfo: ProtocolTypeSet,
/// network class per routing domain
network_class: [Option<NetworkClass>; RoutingDomain::count()],
/// join handles for all the low level network background tasks
join_handles: Vec<MustJoinHandle<()>>,
/// stop source for shutting down the low level network background tasks
@ -65,8 +63,6 @@ struct NetworkInner {
enable_ipv6_local: bool,
/// set if we need to calculate our public dial info again
needs_public_dial_info_check: bool,
/// set during the actual execution of the public dial info check to ensure we don't do it more than once
doing_public_dial_info_check: bool,
/// the punishment closure to enax
public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
/// udp socket record for bound-first sockets, which are used to guarantee a port is available before
@ -118,11 +114,9 @@ impl Network {
network_started: false,
network_needs_restart: false,
needs_public_dial_info_check: false,
doing_public_dial_info_check: false,
public_dial_info_check_punishment: None,
protocol_config: None,
protocol_config: Default::default(),
static_public_dialinfo: ProtocolTypeSet::empty(),
network_class: [None, None],
join_handles: Vec::new(),
stop_source: None,
udp_port: 0u16,
@ -462,11 +456,13 @@ impl Network {
// receive single response
let mut out = vec![0u8; MAX_MESSAGE_SIZE];
let (recv_len, recv_addr) =
network_result_try!(timeout(timeout_ms, h.recv_message(&mut out))
.await
.into_network_result())
.wrap_err("recv_message failure")?;
let (recv_len, recv_addr) = network_result_try!(timeout(
timeout_ms,
h.recv_message(&mut out).instrument(Span::current())
)
.await
.into_network_result())
.wrap_err("recv_message failure")?;
let recv_socket_addr = recv_addr.remote_address().to_socket_addr();
self.network_manager()
@ -618,7 +614,7 @@ impl Network {
/////////////////////////////////////////////////////////////////
pub fn get_protocol_config(&self) -> Option<ProtocolConfig> {
pub fn get_protocol_config(&self) -> ProtocolConfig {
self.inner.lock().protocol_config
}
@ -732,7 +728,8 @@ impl Network {
family_local,
}
};
inner.protocol_config = Some(protocol_config);
inner.protocol_config = protocol_config;
protocol_config
};
@ -769,27 +766,37 @@ impl Network {
// that we have ports available to us
self.free_bound_first_ports();
// If we have static public dialinfo, upgrade our network class
// set up the routing table's network config
// if we have static public dialinfo, upgrade our network class
editor_public_internet.setup_network(
protocol_config.inbound,
protocol_config.outbound,
protocol_config.family_global,
);
editor_local_network.setup_network(
protocol_config.inbound,
protocol_config.outbound,
protocol_config.family_local,
);
let detect_address_changes = {
let c = self.config.get();
c.network.detect_address_changes
};
if !detect_address_changes {
let mut inner = self.inner.lock();
let inner = self.inner.lock();
if !inner.static_public_dialinfo.is_empty() {
inner.network_class[RoutingDomain::PublicInternet as usize] =
Some(NetworkClass::InboundCapable);
editor_public_internet.set_network_class(Some(NetworkClass::InboundCapable));
}
}
info!("network started");
self.inner.lock().network_started = true;
// commit routing table edits
editor_public_internet.commit().await;
editor_local_network.commit().await;
info!("network started");
self.inner.lock().network_started = true;
Ok(())
}
@ -861,21 +868,11 @@ impl Network {
inner.public_dial_info_check_punishment = punishment;
}
fn needs_public_dial_info_check(&self) -> bool {
pub fn needs_public_dial_info_check(&self) -> bool {
let inner = self.inner.lock();
inner.needs_public_dial_info_check
}
pub fn doing_public_dial_info_check(&self) -> bool {
let inner = self.inner.lock();
inner.doing_public_dial_info_check
}
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
let inner = self.inner.lock();
inner.network_class[routing_domain as usize]
}
//////////////////////////////////////////
#[instrument(level = "trace", skip(self), err)]
@ -937,6 +934,7 @@ impl Network {
// If we need to figure out our network class, tick the task for it
if detect_address_changes {
let public_internet_network_class = self
.routing_table()
.get_network_class(RoutingDomain::PublicInternet)
.unwrap_or(NetworkClass::Invalid);
let needs_public_dial_info_check = self.needs_public_dial_info_check();

View File

@ -3,6 +3,10 @@ use futures_util::stream::FuturesUnordered;
use futures_util::FutureExt;
use stop_token::future::FutureExt as StopTokenFutureExt;
const PORT_MAP_VALIDATE_TRY_COUNT: usize = 3;
const PORT_MAP_VALIDATE_DELAY_MS: u32 = 500;
const PORT_MAP_TRY_COUNT: usize = 3;
struct DetectedPublicDialInfo {
dial_info: DialInfo,
class: DialInfoClass,
@ -79,7 +83,7 @@ impl DiscoveryContext {
async fn request_public_address(&self, node_ref: NodeRef) -> Option<SocketAddress> {
let rpc = self.routing_table.rpc_processor();
let res = network_result_value_or_log!(debug match rpc.rpc_call_status(node_ref.clone()).await {
let res = network_result_value_or_log!(debug match rpc.rpc_call_status(Destination::direct(node_ref.clone())).await {
Ok(v) => v,
Err(e) => {
log_net!(error
@ -98,7 +102,7 @@ impl DiscoveryContext {
node_ref,
res.answer
);
res.answer.socket_address
res.answer.map(|si| si.socket_address)
}
// find fast peers with a particular address type, and ask them to tell us what our external address is
@ -125,22 +129,24 @@ impl DiscoveryContext {
RoutingDomain::PublicInternet,
dial_info_filter.clone(),
);
let disallow_relays_filter = move |e: &BucketEntryInner| {
if let Some(n) = e.node_info(RoutingDomain::PublicInternet) {
n.relay_peer_info.is_none()
} else {
false
}
};
let filter = RoutingTable::combine_entry_filters(
inbound_dial_info_entry_filter,
disallow_relays_filter,
);
let disallow_relays_filter = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
let v = v.unwrap();
v.with(rti, |_rti, e| {
if let Some(n) = e.signed_node_info(RoutingDomain::PublicInternet) {
n.relay_id().is_none()
} else {
false
}
})
},
) as RoutingTableEntryFilter;
let filters = VecDeque::from([inbound_dial_info_entry_filter, disallow_relays_filter]);
// Find public nodes matching this filter
let peers = self
.routing_table
.find_fast_public_nodes_filtered(node_count, filter);
.find_fast_public_nodes_filtered(node_count, filters);
if peers.is_empty() {
log_net!(
"no external address detection peers of type {:?}:{:?}",
@ -218,6 +224,84 @@ impl DiscoveryContext {
out
}
#[instrument(level = "trace", skip(self), ret)]
async fn try_upnp_port_mapping(&self) -> Option<DialInfo> {
let (pt, llpt, at, external_address_1, node_1, local_port) = {
let inner = self.inner.lock();
let pt = inner.protocol_type.unwrap();
let llpt = pt.low_level_protocol_type();
let at = inner.address_type.unwrap();
let external_address_1 = inner.external_1_address.unwrap();
let node_1 = inner.node_1.as_ref().unwrap().clone();
let local_port = self.net.get_local_port(pt);
(pt, llpt, at, external_address_1, node_1, local_port)
};
let mut tries = 0;
loop {
tries += 1;
// Attempt a port mapping. If this doesn't succeed, it's not going to
let Some(mapped_external_address) = self
.net
.unlocked_inner
.igd_manager
.map_any_port(llpt, at, local_port, Some(external_address_1.to_ip_addr()))
.await else
{
return None;
};
// Make dial info from the port mapping
let external_mapped_dial_info =
self.make_dial_info(SocketAddress::from_socket_addr(mapped_external_address), pt);
// Attempt to validate the port mapping
let mut validate_tries = 0;
loop {
validate_tries += 1;
// Ensure people can reach us. If we're firewalled off, this is useless
if self
.validate_dial_info(node_1.clone(), external_mapped_dial_info.clone(), false)
.await
{
return Some(external_mapped_dial_info);
}
if validate_tries == PORT_MAP_VALIDATE_TRY_COUNT {
log_net!(debug "UPNP port mapping succeeded but port {}/{} is still unreachable.\nretrying\n",
local_port, match llpt {
LowLevelProtocolType::UDP => "udp",
LowLevelProtocolType::TCP => "tcp",
});
intf::sleep(PORT_MAP_VALIDATE_DELAY_MS).await
} else {
break;
}
}
// Release the mapping if we're still unreachable
let _ = self
.net
.unlocked_inner
.igd_manager
.unmap_port(llpt, at, external_address_1.port())
.await;
if tries == PORT_MAP_TRY_COUNT {
warn!("UPNP port mapping succeeded but port {}/{} is still unreachable.\nYou may need to add a local firewall allowed port on this machine.\n",
local_port, match llpt {
LowLevelProtocolType::UDP => "udp",
LowLevelProtocolType::TCP => "tcp",
}
);
break;
}
}
None
}
#[instrument(level = "trace", skip(self), ret)]
async fn try_port_mapping(&self) -> Option<DialInfo> {
let (enable_upnp, _enable_natpmp) = {
@ -226,44 +310,7 @@ impl DiscoveryContext {
};
if enable_upnp {
let (pt, llpt, at, external_address_1, node_1, local_port) = {
let inner = self.inner.lock();
let pt = inner.protocol_type.unwrap();
let llpt = pt.low_level_protocol_type();
let at = inner.address_type.unwrap();
let external_address_1 = inner.external_1_address.unwrap();
let node_1 = inner.node_1.as_ref().unwrap().clone();
let local_port = self.net.get_local_port(pt);
(pt, llpt, at, external_address_1, node_1, local_port)
};
if let Some(mapped_external_address) = self
.net
.unlocked_inner
.igd_manager
.map_any_port(llpt, at, local_port, Some(external_address_1.to_ip_addr()))
.await
{
// make dial info from the port mapping
let external_mapped_dial_info = self
.make_dial_info(SocketAddress::from_socket_addr(mapped_external_address), pt);
// ensure people can reach us. if we're firewalled off, this is useless
if self
.validate_dial_info(node_1.clone(), external_mapped_dial_info.clone(), false)
.await
{
return Some(external_mapped_dial_info);
} else {
// release the mapping if we're still unreachable
let _ = self
.net
.unlocked_inner
.igd_manager
.unmap_port(llpt, at, external_address_1.port())
.await;
}
}
return self.try_upnp_port_mapping().await;
}
None
@ -375,17 +422,6 @@ impl DiscoveryContext {
// If we know we are behind NAT check what kind
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn protocol_process_nat(&self) -> EyreResult<bool> {
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
let inner = self.inner.lock();
(
inner.node_1.as_ref().unwrap().clone(),
inner.external_1_dial_info.as_ref().unwrap().clone(),
inner.external_1_address.unwrap(),
inner.protocol_type.unwrap(),
inner.address_type.unwrap(),
)
};
// Attempt a port mapping via all available and enabled mechanisms
// Try this before the direct mapping in the event that we are restarting
// and may not have recorded a mapping created the last time
@ -397,8 +433,30 @@ impl DiscoveryContext {
// No more retries
return Ok(true);
}
// XXX: is this necessary?
// Redo our external_1 dial info detection because a failed port mapping attempt
// may cause it to become invalid
// Get our external address from some fast node, call it node 1
// if !self.protocol_get_external_address_1().await {
// // If we couldn't get an external address, then we should just try the whole network class detection again later
// return Ok(false);
// }
// Get the external dial info for our use here
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
let inner = self.inner.lock();
(
inner.node_1.as_ref().unwrap().clone(),
inner.external_1_dial_info.as_ref().unwrap().clone(),
inner.external_1_address.unwrap(),
inner.protocol_type.unwrap(),
inner.address_type.unwrap(),
)
};
// Do a validate_dial_info on the external address from a redirected node
else if self
if self
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
.await
{
@ -592,12 +650,14 @@ impl Network {
_l: u64,
_t: u64,
) -> EyreResult<()> {
let routing_table = self.routing_table();
// Figure out if we can optimize TCP/WS checking since they are often on the same port
let (protocol_config, existing_network_class, tcp_same_port) = {
let inner = self.inner.lock();
let protocol_config = inner.protocol_config.unwrap_or_default();
let protocol_config = inner.protocol_config;
let existing_network_class =
inner.network_class[RoutingDomain::PublicInternet as usize];
routing_table.get_network_class(RoutingDomain::PublicInternet);
let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP)
&& protocol_config.inbound.contains(ProtocolType::WS)
{
@ -607,7 +667,6 @@ impl Network {
};
(protocol_config, existing_network_class, tcp_same_port)
};
let routing_table = self.routing_table();
// Process all protocol and address combinations
let mut futures = FuturesUnordered::new();
@ -628,6 +687,7 @@ impl Network {
}
Some(vec![udpv4_context])
}
.instrument(trace_span!("do_public_dial_info_check UDPv4"))
.boxed(),
);
}
@ -647,6 +707,7 @@ impl Network {
}
Some(vec![udpv6_context])
}
.instrument(trace_span!("do_public_dial_info_check UDPv6"))
.boxed(),
);
}
@ -669,6 +730,7 @@ impl Network {
}
Some(vec![tcpv4_context])
}
.instrument(trace_span!("do_public_dial_info_check TCPv4"))
.boxed(),
);
}
@ -688,6 +750,7 @@ impl Network {
}
Some(vec![wsv4_context])
}
.instrument(trace_span!("do_public_dial_info_check WSv4"))
.boxed(),
);
}
@ -710,6 +773,7 @@ impl Network {
}
Some(vec![tcpv6_context])
}
.instrument(trace_span!("do_public_dial_info_check TCPv6"))
.boxed(),
);
}
@ -729,6 +793,7 @@ impl Network {
}
Some(vec![wsv6_context])
}
.instrument(trace_span!("do_public_dial_info_check WSv6"))
.boxed(),
);
}
@ -825,17 +890,16 @@ impl Network {
// Is the network class different?
if existing_network_class != new_network_class {
self.inner.lock().network_class[RoutingDomain::PublicInternet as usize] =
new_network_class;
editor.set_network_class(new_network_class);
changed = true;
log_net!(debug "PublicInternet network class changed to {:?}", new_network_class);
}
} else if existing_network_class.is_some() {
// Network class could not be determined
editor.clear_dial_info_details();
self.inner.lock().network_class[RoutingDomain::PublicInternet as usize] = None;
editor.set_network_class(None);
changed = true;
log_net!(debug "network class cleared");
log_net!(debug "PublicInternet network class cleared");
}
// Punish nodes that told us our public address had changed when it didn't
@ -857,15 +921,11 @@ impl Network {
l: u64,
t: u64,
) -> EyreResult<()> {
// Note that we are doing the public dial info check
// We don't have to check this for concurrency, since this routine is run in a TickTask/SingleFuture
self.inner.lock().doing_public_dial_info_check = true;
// Do the public dial info check
let out = self.do_public_dial_info_check(stop_token, l, t).await;
// Done with public dial info check
self.inner.lock().doing_public_dial_info_check = false;
self.inner.lock().needs_public_dial_info_check = false;
out
}

View File

@ -42,7 +42,8 @@ impl Network {
&self,
tls_acceptor: &TlsAcceptor,
stream: AsyncPeekStream,
addr: SocketAddr,
peer_addr: SocketAddr,
local_addr: SocketAddr,
protocol_handlers: &[Box<dyn ProtocolAcceptHandler>],
tls_connection_initial_timeout_ms: u32,
) -> EyreResult<Option<ProtocolNetworkConnection>> {
@ -65,18 +66,20 @@ impl Network {
.wrap_err("tls initial timeout")?
.wrap_err("failed to peek tls stream")?;
self.try_handlers(ps, addr, protocol_handlers).await
self.try_handlers(ps, peer_addr, local_addr, protocol_handlers)
.await
}
async fn try_handlers(
&self,
stream: AsyncPeekStream,
addr: SocketAddr,
peer_addr: SocketAddr,
local_addr: SocketAddr,
protocol_accept_handlers: &[Box<dyn ProtocolAcceptHandler>],
) -> EyreResult<Option<ProtocolNetworkConnection>> {
for ah in protocol_accept_handlers.iter() {
if let Some(nc) = ah
.on_accept(stream.clone(), addr)
.on_accept(stream.clone(), peer_addr, local_addr)
.await
.wrap_err("io error")?
{
@ -105,21 +108,35 @@ impl Network {
}
};
// XXX
// warn!(
// "DEBUGACCEPT: local={} remote={}",
// tcp_stream.local_addr().unwrap(),
// tcp_stream.peer_addr().unwrap(),
// );
let listener_state = listener_state.clone();
let connection_manager = connection_manager.clone();
// Limit the number of connections from the same IP address
// and the number of total connections
let addr = match tcp_stream.peer_addr() {
let peer_addr = match tcp_stream.peer_addr() {
Ok(addr) => addr,
Err(e) => {
log_net!(debug "failed to get peer address: {}", e);
return;
}
};
let local_addr = match tcp_stream.local_addr() {
Ok(addr) => addr,
Err(e) => {
log_net!(debug "failed to get local address: {}", e);
return;
}
};
// XXX limiting here instead for connection table? may be faster and avoids tls negotiation
log_net!("TCP connection from: {}", addr);
log_net!("TCP connection from: {}", peer_addr);
// Create a stream we can peek on
#[cfg(feature = "rt-tokio")]
@ -139,7 +156,7 @@ impl Network {
{
// If we fail to get a packet within the connection initial timeout
// then we punt this connection
log_net!("connection initial timeout from: {:?}", addr);
log_net!("connection initial timeout from: {:?}", peer_addr);
return;
}
@ -152,29 +169,30 @@ impl Network {
self.try_tls_handlers(
ls.tls_acceptor.as_ref().unwrap(),
ps,
addr,
peer_addr,
local_addr,
&ls.tls_protocol_handlers,
tls_connection_initial_timeout_ms,
)
.await
} else {
self.try_handlers(ps, addr, &ls.protocol_accept_handlers)
self.try_handlers(ps, peer_addr, local_addr, &ls.protocol_accept_handlers)
.await
};
let conn = match conn {
Ok(Some(c)) => {
log_net!("protocol handler found for {:?}: {:?}", addr, c);
log_net!("protocol handler found for {:?}: {:?}", peer_addr, c);
c
}
Ok(None) => {
// No protocol handlers matched? drop it.
log_net!(debug "no protocol handler for connection from {:?}", addr);
log_net!(debug "no protocol handler for connection from {:?}", peer_addr);
return;
}
Err(e) => {
// Failed to negotiate connection? drop it.
log_net!(debug "failed to negotiate connection from {:?}: {}", addr, e);
log_net!(debug "failed to negotiate connection from {:?}: {}", peer_addr, e);
return;
}
};
@ -311,7 +329,6 @@ impl Network {
.push(new_protocol_accept_handler(
self.network_manager().config(),
true,
addr,
));
} else {
ls.write()
@ -319,7 +336,6 @@ impl Network {
.push(new_protocol_accept_handler(
self.network_manager().config(),
false,
addr,
));
}

View File

@ -34,6 +34,7 @@ cfg_if! {
}
}
#[instrument(level = "trace", ret, err)]
pub fn new_unbound_shared_udp_socket(domain: Domain) -> io::Result<Socket> {
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
if domain == Domain::IPV6 {
@ -49,6 +50,7 @@ pub fn new_unbound_shared_udp_socket(domain: Domain) -> io::Result<Socket> {
Ok(socket)
}
#[instrument(level = "trace", ret, err)]
pub fn new_bound_shared_udp_socket(local_address: SocketAddr) -> io::Result<Socket> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_udp_socket(domain)?;
@ -60,6 +62,7 @@ pub fn new_bound_shared_udp_socket(local_address: SocketAddr) -> io::Result<Sock
Ok(socket)
}
#[instrument(level = "trace", ret, err)]
pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> io::Result<Socket> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
@ -93,6 +96,7 @@ pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> io::Result<Socke
Ok(socket)
}
#[instrument(level = "trace", ret, err)]
pub fn new_unbound_shared_tcp_socket(domain: Domain) -> io::Result<Socket> {
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
if let Err(e) = socket.set_linger(Some(core::time::Duration::from_secs(0))) {
@ -114,6 +118,7 @@ pub fn new_unbound_shared_tcp_socket(domain: Domain) -> io::Result<Socket> {
Ok(socket)
}
#[instrument(level = "trace", ret, err)]
pub fn new_bound_shared_tcp_socket(local_address: SocketAddr) -> io::Result<Socket> {
let domain = Domain::for_address(local_address);
let socket = new_unbound_shared_tcp_socket(domain)?;
@ -125,6 +130,7 @@ pub fn new_bound_shared_tcp_socket(local_address: SocketAddr) -> io::Result<Sock
Ok(socket)
}
#[instrument(level = "trace", ret, err)]
pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> io::Result<Socket> {
let domain = Domain::for_address(local_address);
@ -166,6 +172,8 @@ pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> io::Result<Socke
}
// Non-blocking connect is tricky when you want to start with a prepared socket
// Errors should not be logged as they are valid conditions for this function
#[instrument(level = "trace", ret)]
pub async fn nonblocking_connect(
socket: Socket,
addr: SocketAddr,
@ -185,7 +193,6 @@ pub async fn nonblocking_connect(
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => Ok(()),
Err(e) => Err(e),
}?;
let async_stream = Async::new(std::net::TcpStream::from(socket))?;
// The stream becomes writable when connected

View File

@ -87,11 +87,11 @@ impl RawTcpNetworkConnection {
Ok(NetworkResult::Value(out))
}
#[instrument(level = "trace", err, skip(self), fields(network_result))]
// #[instrument(level = "trace", err, skip(self), fields(network_result))]
pub async fn recv(&self) -> io::Result<NetworkResult<Vec<u8>>> {
let mut stream = self.stream.clone();
let out = Self::recv_internal(&mut stream).await?;
tracing::Span::current().record("network_result", &tracing::field::display(&out));
//tracing::Span::current().record("network_result", &tracing::field::display(&out));
Ok(out)
}
}
@ -99,30 +99,20 @@ impl RawTcpNetworkConnection {
///////////////////////////////////////////////////////////
///
struct RawTcpProtocolHandlerInner {
local_address: SocketAddr,
}
#[derive(Clone)]
pub struct RawTcpProtocolHandler
where
Self: ProtocolAcceptHandler,
{
connection_initial_timeout_ms: u32,
inner: Arc<Mutex<RawTcpProtocolHandlerInner>>,
}
impl RawTcpProtocolHandler {
fn new_inner(local_address: SocketAddr) -> RawTcpProtocolHandlerInner {
RawTcpProtocolHandlerInner { local_address }
}
pub fn new(config: VeilidConfig, local_address: SocketAddr) -> Self {
pub fn new(config: VeilidConfig) -> Self {
let c = config.get();
let connection_initial_timeout_ms = c.network.connection_initial_timeout_ms;
Self {
connection_initial_timeout_ms,
inner: Arc::new(Mutex::new(Self::new_inner(local_address))),
}
}
@ -131,6 +121,7 @@ impl RawTcpProtocolHandler {
self,
ps: AsyncPeekStream,
socket_addr: SocketAddr,
local_addr: SocketAddr,
) -> io::Result<Option<ProtocolNetworkConnection>> {
log_net!("TCP: on_accept_async: enter");
let mut peekbuf: [u8; PEEK_DETECT_LEN] = [0u8; PEEK_DETECT_LEN];
@ -147,9 +138,8 @@ impl RawTcpProtocolHandler {
SocketAddress::from_socket_addr(socket_addr),
ProtocolType::TCP,
);
let local_address = self.inner.lock().local_address;
let conn = ProtocolNetworkConnection::RawTcp(RawTcpNetworkConnection::new(
ConnectionDescriptor::new(peer_addr, SocketAddress::from_socket_addr(local_address)),
ConnectionDescriptor::new(peer_addr, SocketAddress::from_socket_addr(local_addr)),
ps,
));
@ -158,7 +148,7 @@ impl RawTcpProtocolHandler {
Ok(Some(conn))
}
#[instrument(level = "trace", err)]
#[instrument(level = "trace", ret, err)]
pub async fn connect(
local_address: Option<SocketAddr>,
socket_addr: SocketAddr,
@ -202,7 +192,8 @@ impl ProtocolAcceptHandler for RawTcpProtocolHandler {
&self,
stream: AsyncPeekStream,
peer_addr: SocketAddr,
local_addr: SocketAddr,
) -> SendPinBoxFuture<io::Result<Option<ProtocolNetworkConnection>>> {
Box::pin(self.clone().on_accept_async(stream, peer_addr))
Box::pin(self.clone().on_accept_async(stream, peer_addr, local_addr))
}
}

View File

@ -11,7 +11,7 @@ impl RawUdpProtocolHandler {
Self { socket }
}
#[instrument(level = "trace", err, skip(self, data), fields(data.len = data.len(), ret.len, ret.from))]
// #[instrument(level = "trace", err, skip(self, data), fields(data.len = data.len(), ret.len, ret.descriptor))]
pub async fn recv_message(&self, data: &mut [u8]) -> io::Result<(usize, ConnectionDescriptor)> {
let (size, descriptor) = loop {
let (size, remote_addr) = network_result_value_or_log!(debug self.socket.recv_from(data).await.into_network_result()? => continue);
@ -33,12 +33,12 @@ impl RawUdpProtocolHandler {
break (size, descriptor);
};
tracing::Span::current().record("ret.len", &size);
tracing::Span::current().record("ret.from", &format!("{:?}", descriptor).as_str());
// tracing::Span::current().record("ret.len", &size);
// tracing::Span::current().record("ret.descriptor", &format!("{:?}", descriptor).as_str());
Ok((size, descriptor))
}
#[instrument(level = "trace", err, skip(self, data), fields(data.len = data.len(), ret.len, ret.from))]
#[instrument(level = "trace", err, skip(self, data), fields(data.len = data.len(), ret.len, ret.descriptor))]
pub async fn send_message(
&self,
data: Vec<u8>,
@ -67,6 +67,8 @@ impl RawUdpProtocolHandler {
bail_io_error_other!("UDP partial send")
}
tracing::Span::current().record("ret.len", &len);
tracing::Span::current().record("ret.descriptor", &format!("{:?}", descriptor).as_str());
Ok(NetworkResult::value(descriptor))
}

View File

@ -93,7 +93,7 @@ where
Ok(out)
}
#[instrument(level = "trace", err, skip(self), fields(network_result, ret.len))]
// #[instrument(level = "trace", err, skip(self), fields(network_result, ret.len))]
pub async fn recv(&self) -> io::Result<NetworkResult<Vec<u8>>> {
let out = match self.stream.clone().next().await {
Some(Ok(Message::Binary(v))) => {
@ -120,7 +120,7 @@ where
)),
};
tracing::Span::current().record("network_result", &tracing::field::display(&out));
// tracing::Span::current().record("network_result", &tracing::field::display(&out));
Ok(out)
}
}
@ -129,7 +129,6 @@ where
///
struct WebsocketProtocolHandlerArc {
tls: bool,
local_address: SocketAddr,
request_path: Vec<u8>,
connection_initial_timeout_ms: u32,
}
@ -142,7 +141,7 @@ where
arc: Arc<WebsocketProtocolHandlerArc>,
}
impl WebsocketProtocolHandler {
pub fn new(config: VeilidConfig, tls: bool, local_address: SocketAddr) -> Self {
pub fn new(config: VeilidConfig, tls: bool) -> Self {
let c = config.get();
let path = if tls {
format!("GET /{}", c.network.protocol.ws.path.trim_end_matches('/'))
@ -158,7 +157,6 @@ impl WebsocketProtocolHandler {
Self {
arc: Arc::new(WebsocketProtocolHandlerArc {
tls,
local_address,
request_path: path.as_bytes().to_vec(),
connection_initial_timeout_ms,
}),
@ -170,6 +168,7 @@ impl WebsocketProtocolHandler {
self,
ps: AsyncPeekStream,
socket_addr: SocketAddr,
local_addr: SocketAddr,
) -> io::Result<Option<ProtocolNetworkConnection>> {
log_net!("WS: on_accept_async: enter");
let request_path_len = self.arc.request_path.len() + 2;
@ -209,10 +208,7 @@ impl WebsocketProtocolHandler {
PeerAddress::new(SocketAddress::from_socket_addr(socket_addr), protocol_type);
let conn = ProtocolNetworkConnection::WsAccepted(WebsocketNetworkConnection::new(
ConnectionDescriptor::new(
peer_addr,
SocketAddress::from_socket_addr(self.arc.local_address),
),
ConnectionDescriptor::new(peer_addr, SocketAddress::from_socket_addr(local_addr)),
ws_stream,
));
@ -221,7 +217,7 @@ impl WebsocketProtocolHandler {
Ok(Some(conn))
}
#[instrument(level = "trace", err)]
#[instrument(level = "trace", ret, err)]
pub async fn connect(
local_address: Option<SocketAddr>,
dial_info: &DialInfo,
@ -296,7 +292,8 @@ impl ProtocolAcceptHandler for WebsocketProtocolHandler {
&self,
stream: AsyncPeekStream,
peer_addr: SocketAddr,
local_addr: SocketAddr,
) -> SendPinBoxFuture<io::Result<Option<ProtocolNetworkConnection>>> {
Box::pin(self.clone().on_accept_async(stream, peer_addr))
Box::pin(self.clone().on_accept_async(stream, peer_addr, local_addr))
}
}

View File

@ -387,7 +387,7 @@ impl Network {
ip_addrs,
ws_port,
false,
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
)
.await?;
trace!("WS: listener started on {:#?}", socket_addresses);
@ -496,7 +496,7 @@ impl Network {
ip_addrs,
wss_port,
true,
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
)
.await?;
trace!("WSS: listener started on {:#?}", socket_addresses);
@ -590,7 +590,7 @@ impl Network {
ip_addrs,
tcp_port,
false,
Box::new(move |c, _, a| Box::new(RawTcpProtocolHandler::new(c, a))),
Box::new(move |c, _| Box::new(RawTcpProtocolHandler::new(c))),
)
.await?;
trace!("TCP: listener started on {:#?}", socket_addresses);

View File

@ -16,6 +16,7 @@ cfg_if::cfg_if! {
&self,
stream: AsyncPeekStream,
peer_addr: SocketAddr,
local_addr: SocketAddr,
) -> SendPinBoxFuture<io::Result<Option<ProtocolNetworkConnection>>>;
}
@ -38,7 +39,7 @@ cfg_if::cfg_if! {
}
pub type NewProtocolAcceptHandler =
dyn Fn(VeilidConfig, bool, SocketAddr) -> Box<dyn ProtocolAcceptHandler> + Send;
dyn Fn(VeilidConfig, bool) -> Box<dyn ProtocolAcceptHandler> + Send;
}
}
///////////////////////////////////////////////////////////
@ -91,7 +92,7 @@ pub struct NetworkConnection {
processor: Option<MustJoinHandle<()>>,
established_time: u64,
stats: Arc<Mutex<NetworkConnectionStats>>,
sender: flume::Sender<Vec<u8>>,
sender: flume::Sender<(Option<Id>, Vec<u8>)>,
stop_source: Option<StopSource>,
}
@ -120,9 +121,6 @@ impl NetworkConnection {
protocol_connection: ProtocolNetworkConnection,
connection_id: NetworkConnectionId,
) -> Self {
// Get timeout
let network_manager = connection_manager.network_manager();
// Get descriptor
let descriptor = protocol_connection.descriptor();
@ -181,6 +179,7 @@ impl NetworkConnection {
}
}
#[instrument(level="trace", skip(message, stats), fields(message.len = message.len()), ret, err)]
async fn send_internal(
protocol_connection: &ProtocolNetworkConnection,
stats: Arc<Mutex<NetworkConnectionStats>>,
@ -194,6 +193,8 @@ impl NetworkConnection {
Ok(NetworkResult::Value(out))
}
#[instrument(level="trace", skip(stats), fields(ret.len), err)]
async fn recv_internal(
protocol_connection: &ProtocolNetworkConnection,
stats: Arc<Mutex<NetworkConnectionStats>>,
@ -204,14 +205,18 @@ impl NetworkConnection {
let mut stats = stats.lock();
stats.last_message_recv_time.max_assign(Some(ts));
tracing::Span::current().record("ret.len", out.len());
Ok(NetworkResult::Value(out))
}
#[allow(dead_code)]
pub fn stats(&self) -> NetworkConnectionStats {
let stats = self.stats.lock();
stats.clone()
}
#[allow(dead_code)]
pub fn established_time(&self) -> u64 {
self.established_time
}
@ -223,7 +228,7 @@ impl NetworkConnection {
manager_stop_token: StopToken,
connection_id: NetworkConnectionId,
descriptor: ConnectionDescriptor,
receiver: flume::Receiver<Vec<u8>>,
receiver: flume::Receiver<(Option<Id>, Vec<u8>)>,
protocol_connection: ProtocolNetworkConnection,
stats: Arc<Mutex<NetworkConnectionStats>>,
) -> SendPinBoxFuture<()> {
@ -249,7 +254,7 @@ impl NetworkConnection {
};
let timer = MutableFuture::new(new_timer());
unord.push(system_boxed(timer.clone()));
unord.push(system_boxed(timer.clone().instrument(Span::current())));
loop {
// Add another message sender future if necessary
@ -257,13 +262,18 @@ impl NetworkConnection {
need_sender = false;
let sender_fut = receiver.recv_async().then(|res| async {
match res {
Ok(message) => {
Ok((_span_id, message)) => {
let recv_span = span!(Level::TRACE, "process_connection recv");
// xxx: causes crash (Missing otel data span extensions)
// recv_span.follows_from(span_id);
// send the packet
if let Err(e) = Self::send_internal(
&protocol_connection,
stats.clone(),
message,
)
).instrument(recv_span)
.await
{
// Sending the packet along can fail, if so, this connection is dead
@ -280,7 +290,7 @@ impl NetworkConnection {
}
}
});
unord.push(system_boxed(sender_fut));
unord.push(system_boxed(sender_fut.instrument(Span::current())));
}
// Add another message receiver future if necessary
@ -314,7 +324,7 @@ impl NetworkConnection {
}
});
unord.push(system_boxed(receiver_fut));
unord.push(system_boxed(receiver_fut.instrument(Span::current())));
}
// Process futures
@ -358,7 +368,7 @@ impl NetworkConnection {
connection_manager
.report_connection_finished(connection_id)
.await;
})
}.instrument(trace_span!("process_connection")))
}
}

View File

@ -1,6 +1,6 @@
use super::*;
use crate::dht::*;
use crate::crypto::*;
use crate::xx::*;
use futures_util::FutureExt;
use stop_token::future::FutureExt as StopFutureExt;
@ -39,119 +39,125 @@ impl NetworkManager {
// Get bootstrap nodes from hostnames concurrently
let mut unord = FuturesUnordered::new();
for bsname in bsnames {
unord.push(async move {
// look up boostrap node txt records
let bsnirecords = match intf::txt_lookup(&bsname).await {
Err(e) => {
warn!("bootstrap node txt lookup failed for {}: {}", bsname, e);
return None;
}
Ok(v) => v,
};
// for each record resolve into key/bootstraprecord pairs
let mut bootstrap_records: Vec<(DHTKey, BootstrapRecord)> = Vec::new();
for bsnirecord in bsnirecords {
// Bootstrap TXT Record Format Version 0:
// txt_version,min_version,max_version,nodeid,hostname,dialinfoshort*
//
// Split bootstrap node record by commas. Example:
// 0,0,0,7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,bootstrap-1.dev.veilid.net,T5150,U5150,W5150/ws
let records: Vec<String> = bsnirecord
.trim()
.split(',')
.map(|x| x.trim().to_owned())
.collect();
if records.len() < 6 {
warn!("invalid number of fields in bootstrap txt record");
continue;
}
// Bootstrap TXT record version
let txt_version: u8 = match records[0].parse::<u8>() {
Ok(v) => v,
unord.push(
async move {
// look up boostrap node txt records
let bsnirecords = match intf::txt_lookup(&bsname).await {
Err(e) => {
warn!(
warn!("bootstrap node txt lookup failed for {}: {}", bsname, e);
return None;
}
Ok(v) => v,
};
// for each record resolve into key/bootstraprecord pairs
let mut bootstrap_records: Vec<(DHTKey, BootstrapRecord)> = Vec::new();
for bsnirecord in bsnirecords {
// Bootstrap TXT Record Format Version 0:
// txt_version,min_version,max_version,nodeid,hostname,dialinfoshort*
//
// Split bootstrap node record by commas. Example:
// 0,0,0,7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,bootstrap-1.dev.veilid.net,T5150,U5150,W5150/ws
let records: Vec<String> = bsnirecord
.trim()
.split(',')
.map(|x| x.trim().to_owned())
.collect();
if records.len() < 6 {
warn!("invalid number of fields in bootstrap txt record");
continue;
}
// Bootstrap TXT record version
let txt_version: u8 = match records[0].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid txt_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
if txt_version != BOOTSTRAP_TXT_VERSION {
warn!("unsupported bootstrap txt record version");
continue;
}
};
if txt_version != BOOTSTRAP_TXT_VERSION {
warn!("unsupported bootstrap txt record version");
continue;
}
// Min/Max wire protocol version
let min_version: u8 = match records[1].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
// Min/Max wire protocol version
let min_version: u8 = match records[1].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid min_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
let max_version: u8 = match records[2].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
continue;
}
};
let max_version: u8 = match records[2].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid max_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
// Node Id
let node_id_str = &records[3];
let node_id_key = match DHTKey::try_decode(node_id_str) {
Ok(v) => v,
Err(e) => {
warn!(
"Invalid node id in bootstrap node record {}: {}",
node_id_str, e
);
continue;
}
};
// Hostname
let hostname_str = &records[4];
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
if self.routing_table().node_id() == node_id_key {
continue;
}
// Resolve each record and store in node dial infos list
let mut bootstrap_record = BootstrapRecord {
min_version,
max_version,
dial_info_details: Vec::new(),
};
for rec in &records[5..] {
let rec = rec.trim();
let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) {
Ok(dis) => dis,
Err(e) => {
warn!("Couldn't resolve bootstrap node dial info {}: {}", rec, e);
continue;
}
};
for di in dial_infos {
bootstrap_record.dial_info_details.push(DialInfoDetail {
dial_info: di,
class: DialInfoClass::Direct,
});
// Node Id
let node_id_str = &records[3];
let node_id_key = match DHTKey::try_decode(node_id_str) {
Ok(v) => v,
Err(e) => {
warn!(
"Invalid node id in bootstrap node record {}: {}",
node_id_str, e
);
continue;
}
};
// Hostname
let hostname_str = &records[4];
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
if self.routing_table().node_id() == node_id_key {
continue;
}
// Resolve each record and store in node dial infos list
let mut bootstrap_record = BootstrapRecord {
min_version,
max_version,
dial_info_details: Vec::new(),
};
for rec in &records[5..] {
let rec = rec.trim();
let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) {
Ok(dis) => dis,
Err(e) => {
warn!(
"Couldn't resolve bootstrap node dial info {}: {}",
rec, e
);
continue;
}
};
for di in dial_infos {
bootstrap_record.dial_info_details.push(DialInfoDetail {
dial_info: di,
class: DialInfoClass::Direct,
});
}
}
bootstrap_records.push((node_id_key, bootstrap_record));
}
bootstrap_records.push((node_id_key, bootstrap_record));
Some(bootstrap_records)
}
Some(bootstrap_records)
});
.instrument(Span::current()),
);
}
let mut bsmap = BootstrapRecordMap::new();
@ -172,6 +178,7 @@ impl NetworkManager {
}
// 'direct' bootstrap task routine for systems incapable of resolving TXT records, such as browser WASM
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn direct_bootstrap_task_routine(
self,
stop_token: StopToken,
@ -201,7 +208,8 @@ impl NetworkManager {
let routing_table = routing_table.clone();
unord.push(
// lets ask bootstrap to find ourselves now
async move { routing_table.reverse_find_node(nr, true).await },
async move { routing_table.reverse_find_node(nr, true).await }
.instrument(Span::current()),
);
}
}
@ -216,7 +224,7 @@ impl NetworkManager {
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> {
let (bootstrap, bootstrap_nodes) = {
let c = self.config.get();
let c = self.unlocked_inner.config.get();
(
c.network.bootstrap.clone(),
c.network.bootstrap_nodes.clone(),
@ -248,22 +256,26 @@ impl NetworkManager {
let mut bsmap = BootstrapRecordMap::new();
let mut bootstrap_node_dial_infos = Vec::new();
for b in bootstrap_nodes {
let ndis = NodeDialInfo::from_str(b.as_str())
.wrap_err("Invalid node dial info in bootstrap entry")?;
bootstrap_node_dial_infos.push(ndis);
let (id_str, di_str) = b
.split_once('@')
.ok_or_else(|| eyre!("Invalid node dial info in bootstrap entry"))?;
let node_id =
NodeId::from_str(id_str).wrap_err("Invalid node id in bootstrap entry")?;
let dial_info =
DialInfo::from_str(di_str).wrap_err("Invalid dial info in bootstrap entry")?;
bootstrap_node_dial_infos.push((node_id, dial_info));
}
for ndi in bootstrap_node_dial_infos {
let node_id = ndi.node_id.key;
for (node_id, dial_info) in bootstrap_node_dial_infos {
bsmap
.entry(node_id)
.entry(node_id.key)
.or_insert_with(|| BootstrapRecord {
min_version: MIN_VERSION,
max_version: MAX_VERSION,
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
dial_info_details: Vec::new(),
})
.dial_info_details
.push(DialInfoDetail {
dial_info: ndi.dial_info,
dial_info,
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
});
}
@ -287,36 +299,38 @@ impl NetworkManager {
if let Some(nr) = routing_table.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
k,
SignedNodeInfo::with_no_signature(NodeInfo {
SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo {
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled
address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
min_version: v.min_version, // Minimum protocol version specified in txt record
max_version: v.max_version, // Maximum protocol version specified in txt record
min_version: v.min_version, // Minimum crypto version specified in txt record
max_version: v.max_version, // Maximum crypto version specified in txt record
dial_info_detail_list: v.dial_info_details, // Dial info is as specified in the bootstrap list
relay_peer_info: None, // Bootstraps never require a relay themselves
}),
})),
true,
) {
// Add this our futures to process in parallel
let routing_table = routing_table.clone();
unord.push(async move {
// Need VALID signed peer info, so ask bootstrap to find_node of itself
// which will ensure it has the bootstrap's signed peer info as part of the response
let _ = routing_table.find_target(nr.clone()).await;
unord.push(
async move {
// Need VALID signed peer info, so ask bootstrap to find_node of itself
// which will ensure it has the bootstrap's signed peer info as part of the response
let _ = routing_table.find_target(nr.clone()).await;
// Ensure we got the signed peer info
if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) {
log_net!(warn
"bootstrap at {:?} did not return valid signed node info",
nr
);
// If this node info is invalid, it will time out after being unpingable
} else {
// otherwise this bootstrap is valid, lets ask it to find ourselves now
routing_table.reverse_find_node(nr, true).await
// Ensure we got the signed peer info
if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) {
log_net!(warn
"bootstrap at {:?} did not return valid signed node info",
nr
);
// If this node info is invalid, it will time out after being unpingable
} else {
// otherwise this bootstrap is valid, lets ask it to find ourselves now
routing_table.reverse_find_node(nr, true).await
}
}
});
.instrument(Span::current()),
);
}
}
@ -332,7 +346,7 @@ impl NetworkManager {
&self,
cur_ts: u64,
unord: &mut FuturesUnordered<
SendPinBoxFuture<Result<NetworkResult<Answer<SenderInfo>>, RPCError>>,
SendPinBoxFuture<Result<NetworkResult<Answer<Option<SenderInfo>>>, RPCError>>,
>,
) -> EyreResult<()> {
let rpc = self.rpc_processor();
@ -342,7 +356,7 @@ impl NetworkManager {
let node_refs = routing_table.get_nodes_needing_ping(RoutingDomain::PublicInternet, cur_ts);
// Look up any NAT mappings we may need to try to preserve with keepalives
let mut mapped_port_info = routing_table.get_mapped_port_info();
let mut mapped_port_info = routing_table.get_low_level_port_info();
// Get the PublicInternet relay if we are using one
let opt_relay_nr = routing_table.relay_node(RoutingDomain::PublicInternet);
@ -382,7 +396,11 @@ impl NetworkManager {
let nr_filtered =
nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif));
log_net!("--> Keepalive ping to {:?}", nr_filtered);
unord.push(async move { rpc.rpc_call_status(nr_filtered).await }.boxed());
unord.push(
async move { rpc.rpc_call_status(Destination::direct(nr_filtered)).await }
.instrument(Span::current())
.boxed(),
);
did_pings = true;
}
}
@ -392,7 +410,11 @@ impl NetworkManager {
// any mapped ports to preserve
if !did_pings {
let rpc = rpc.clone();
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
unord.push(
async move { rpc.rpc_call_status(Destination::direct(nr)).await }
.instrument(Span::current())
.boxed(),
);
}
}
@ -406,7 +428,7 @@ impl NetworkManager {
&self,
cur_ts: u64,
unord: &mut FuturesUnordered<
SendPinBoxFuture<Result<NetworkResult<Answer<SenderInfo>>, RPCError>>,
SendPinBoxFuture<Result<NetworkResult<Answer<Option<SenderInfo>>>, RPCError>>,
>,
) -> EyreResult<()> {
let rpc = self.rpc_processor();
@ -420,7 +442,11 @@ impl NetworkManager {
let rpc = rpc.clone();
// Just do a single ping with the best protocol for all the nodes
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
unord.push(
async move { rpc.rpc_call_status(Destination::direct(nr)).await }
.instrument(Span::current())
.boxed(),
);
}
Ok(())
@ -464,7 +490,7 @@ impl NetworkManager {
let routing_table = self.routing_table();
let mut ord = FuturesOrdered::new();
let min_peer_count = {
let c = self.config.get();
let c = self.unlocked_inner.config.get();
c.network.dht.min_peer_count as usize
};
@ -472,14 +498,17 @@ impl NetworkManager {
// even the unreliable ones, and ask them to find nodes close to our node too
let noderefs = routing_table.find_fastest_nodes(
min_peer_count,
|_k, _v| true,
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
VecDeque::new(),
|_rti, k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(routing_table.clone(), k, v.unwrap().clone(), None)
},
);
for nr in noderefs {
let routing_table = routing_table.clone();
ord.push_back(async move { routing_table.reverse_find_node(nr, false).await });
ord.push_back(
async move { routing_table.reverse_find_node(nr, false).await }
.instrument(Span::current()),
);
}
// do peer minimum search in order from fastest to slowest
@ -498,8 +527,9 @@ impl NetworkManager {
) -> EyreResult<()> {
// Get our node's current node info and network class and do the right thing
let routing_table = self.routing_table();
let node_info = routing_table.get_own_node_info(RoutingDomain::PublicInternet);
let network_class = self.get_network_class(RoutingDomain::PublicInternet);
let own_peer_info = routing_table.get_own_peer_info(RoutingDomain::PublicInternet);
let own_node_info = own_peer_info.signed_node_info.node_info();
let network_class = routing_table.get_network_class(RoutingDomain::PublicInternet);
// Get routing domain editor
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
@ -515,7 +545,7 @@ impl NetworkManager {
info!("Relay node died, dropping relay {}", relay_node);
editor.clear_relay_node();
false
} else if !node_info.requires_relay() {
} else if !own_node_info.requires_relay() {
info!(
"Relay node no longer required, dropping relay {}",
relay_node
@ -531,8 +561,9 @@ impl NetworkManager {
};
// Do we need a relay?
if !has_relay && node_info.requires_relay() {
// Do we need an outbound relay?
if !has_relay && own_node_info.requires_relay() {
// Do we want an outbound relay?
let mut got_outbound_relay = false;
if network_class.outbound_wants_relay() {
// The outbound relay is the host of the PWA
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
@ -545,10 +576,11 @@ impl NetworkManager {
) {
info!("Outbound relay node selected: {}", nr);
editor.set_relay_node(nr);
got_outbound_relay = true;
}
}
// Otherwise we must need an inbound relay
} else {
}
if !got_outbound_relay {
// Find a node in our routing table that is an acceptable inbound relay
if let Some(nr) =
routing_table.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts)
@ -566,6 +598,34 @@ impl NetworkManager {
Ok(())
}
// Keep private routes assigned and accessible
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn private_route_management_task_routine(
self,
_stop_token: StopToken,
_last_ts: u64,
cur_ts: u64,
) -> EyreResult<()> {
// Get our node's current node info and network class and do the right thing
let routing_table = self.routing_table();
let own_peer_info = routing_table.get_own_peer_info(RoutingDomain::PublicInternet);
let network_class = routing_table.get_network_class(RoutingDomain::PublicInternet);
// Get routing domain editor
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
// Do we know our network class yet?
if let Some(network_class) = network_class {
// see if we have any routes that need extending
}
// Commit the changes
editor.commit().await;
Ok(())
}
// Compute transfer statistics for the low level network
#[instrument(level = "trace", skip(self), err)]
pub(super) async fn rolling_transfers_task_routine(

View File

@ -52,6 +52,7 @@ pub async fn test_add_get_remove() {
);
let c1 = NetworkConnection::dummy(1, a1);
let c1b = NetworkConnection::dummy(10, a1);
let c1h = c1.get_handle();
let c2 = NetworkConnection::dummy(2, a2);
let c3 = NetworkConnection::dummy(3, a3);
@ -65,6 +66,7 @@ pub async fn test_add_get_remove() {
assert_eq!(table.connection_count(), 0);
assert_eq!(table.get_connection_by_descriptor(a1), None);
table.add_connection(c1).unwrap();
assert!(table.add_connection(c1b).is_err());
assert_eq!(table.connection_count(), 1);
assert!(table.remove_connection_by_id(4).is_none());

View File

@ -10,25 +10,43 @@ use std::io;
/////////////////////////////////////////////////////////////////
struct NetworkInner {
network_manager: NetworkManager,
network_started: bool,
network_needs_restart: bool,
protocol_config: Option<ProtocolConfig>,
protocol_config: ProtocolConfig,
}
struct NetworkUnlockedInner {
// Accessors
routing_table: RoutingTable,
network_manager: NetworkManager,
connection_manager: ConnectionManager,
}
#[derive(Clone)]
pub struct Network {
config: VeilidConfig,
inner: Arc<Mutex<NetworkInner>>,
unlocked_inner: Arc<NetworkUnlockedInner>,
}
impl Network {
fn new_inner(network_manager: NetworkManager) -> NetworkInner {
fn new_inner() -> NetworkInner {
NetworkInner {
network_manager,
network_started: false,
network_needs_restart: false,
protocol_config: None, //join_handle: None,
protocol_config: Default::default(),
}
}
fn new_unlocked_inner(
network_manager: NetworkManager,
routing_table: RoutingTable,
connection_manager: ConnectionManager,
) -> NetworkUnlockedInner {
NetworkUnlockedInner {
network_manager,
routing_table,
connection_manager,
}
}
@ -39,15 +57,23 @@ impl Network {
) -> Self {
Self {
config: network_manager.config(),
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
inner: Arc::new(Mutex::new(Self::new_inner())),
unlocked_inner: Arc::new(Self::new_unlocked_inner(
network_manager,
routing_table,
connection_manager,
)),
}
}
fn network_manager(&self) -> NetworkManager {
self.inner.lock().network_manager.clone()
self.unlocked_inner.network_manager.clone()
}
fn routing_table(&self) -> RoutingTable {
self.unlocked_inner.routing_table.clone()
}
fn connection_manager(&self) -> ConnectionManager {
self.inner.lock().network_manager.connection_manager()
self.unlocked_inner.connection_manager.clone()
}
/////////////////////////////////////////////////////////////////
@ -225,7 +251,7 @@ impl Network {
pub async fn startup(&self) -> EyreResult<()> {
// get protocol config
self.inner.lock().protocol_config = Some({
self.inner.lock().protocol_config = {
let c = self.config.get();
let inbound = ProtocolTypeSet::new();
let mut outbound = ProtocolTypeSet::new();
@ -247,7 +273,7 @@ impl Network {
family_global,
family_local,
}
});
};
self.inner.lock().network_started = true;
Ok(())
@ -269,20 +295,26 @@ impl Network {
trace!("stopping network");
// Reset state
let network_manager = self.inner.lock().network_manager.clone();
let routing_table = network_manager.routing_table();
let routing_table = self.routing_table();
// Drop all dial info
routing_table.clear_dial_info_details(RoutingDomain::PublicInternet);
routing_table.clear_dial_info_details(RoutingDomain::LocalNetwork);
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
editor.disable_node_info_updates();
editor.clear_dial_info_details();
editor.commit().await;
let mut editor = routing_table.edit_routing_domain(RoutingDomain::LocalNetwork);
editor.disable_node_info_updates();
editor.clear_dial_info_details();
editor.commit().await;
// Cancels all async background tasks by dropping join handles
*self.inner.lock() = Self::new_inner(network_manager);
*self.inner.lock() = Self::new_inner();
trace!("network stopped");
}
pub fn is_usable_interface_address(&self, addr: IpAddr) -> bool {
pub fn is_usable_interface_address(&self, _addr: IpAddr) -> bool {
false
}
@ -291,12 +323,15 @@ impl Network {
}
//////////////////////////////////////////
pub fn set_needs_public_dial_info_check(&self, _punishment: Option<Box<dyn FnOnce() + Send + 'static>>) {
pub fn set_needs_public_dial_info_check(
&self,
_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
) {
//
}
pub fn doing_public_dial_info_check(&self) -> bool {
pub fn needs_public_dial_info_check(&self) -> bool {
false
}
@ -309,7 +344,7 @@ impl Network {
};
}
pub fn get_protocol_config(&self) -> Option<ProtocolConfig> {
pub fn get_protocol_config(&self) -> ProtocolConfig {
self.inner.lock().protocol_config.clone()
}

View File

@ -75,7 +75,7 @@ impl WebsocketNetworkConnection {
Ok(out)
}
#[instrument(level = "trace", err, skip(self), fields(network_result, ret.len))]
// #[instrument(level = "trace", err, skip(self), fields(network_result, ret.len))]
pub async fn recv(&self) -> io::Result<NetworkResult<Vec<u8>>> {
let out = match SendWrapper::new(self.inner.ws_stream.clone().next()).await {
Some(WsMessage::Binary(v)) => {
@ -95,7 +95,7 @@ impl WebsocketNetworkConnection {
bail_io_error_other!("WS stream closed");
}
};
tracing::Span::current().record("network_result", &tracing::field::display(&out));
// tracing::Span::current().record("network_result", &tracing::field::display(&out));
Ok(out)
}
}
@ -106,13 +106,13 @@ impl WebsocketNetworkConnection {
pub struct WebsocketProtocolHandler {}
impl WebsocketProtocolHandler {
#[instrument(level = "trace", err)]
#[instrument(level = "trace", ret, err)]
pub async fn connect(
dial_info: &DialInfo,
timeout_ms: u32,
) -> io::Result<NetworkResult<ProtocolNetworkConnection>> {
// Split dial info up
let (tls, scheme) = match dial_info {
let (_tls, scheme) = match dial_info {
DialInfo::WS(_) => (false, "ws"),
DialInfo::WSS(_) => (true, "wss"),
_ => panic!("invalid dialinfo for WS/WSS protocol"),

View File

@ -1,20 +1,30 @@
use crate::*;
use core::fmt;
use dht::*;
use crypto::*;
use futures_util::stream::{FuturesUnordered, StreamExt};
use network_manager::*;
use routing_table::*;
use stop_token::future::FutureExt;
use xx::*;
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug)]
pub enum ReceiptEvent {
ReturnedOutOfBand,
ReturnedInBand { inbound_noderef: NodeRef },
ReturnedSafety,
ReturnedPrivate { private_route: DHTKey },
Expired,
Cancelled,
}
#[derive(Clone, Debug)]
pub enum ReceiptReturned {
OutOfBand,
InBand { inbound_noderef: NodeRef },
Safety,
Private { private_route: DHTKey },
}
pub trait ReceiptCallback: Send + 'static {
fn call(
&self,
@ -246,7 +256,7 @@ impl ReceiptManager {
if let Some(callback) =
Self::perform_callback(ReceiptEvent::Expired, &mut expired_record_mut)
{
callbacks.push(callback)
callbacks.push(callback.instrument(Span::current()))
}
}
@ -394,17 +404,18 @@ impl ReceiptManager {
pub async fn handle_receipt(
&self,
receipt: Receipt,
inbound_noderef: Option<NodeRef>,
receipt_returned: ReceiptReturned,
) -> NetworkResult<()> {
let receipt_nonce = receipt.get_nonce();
let extra_data = receipt.get_extra_data();
log_rpc!(debug "<<== RECEIPT {} <- {}{}",
receipt_nonce.encode(),
if let Some(nr) = &inbound_noderef {
nr.to_string()
} else {
"DIRECT".to_owned()
match receipt_returned {
ReceiptReturned::OutOfBand => "OutOfBand".to_owned(),
ReceiptReturned::InBand { ref inbound_noderef } => format!("InBand({})", inbound_noderef),
ReceiptReturned::Safety => "Safety".to_owned(),
ReceiptReturned::Private { ref private_route } => format!("Private({})", private_route),
},
if extra_data.is_empty() {
"".to_owned()
@ -435,10 +446,17 @@ impl ReceiptManager {
record_mut.returns_so_far += 1;
// Get the receipt event to return
let receipt_event = if let Some(inbound_noderef) = inbound_noderef {
ReceiptEvent::ReturnedInBand { inbound_noderef }
} else {
ReceiptEvent::ReturnedOutOfBand
let receipt_event = match receipt_returned {
ReceiptReturned::OutOfBand => ReceiptEvent::ReturnedOutOfBand,
ReceiptReturned::Safety => ReceiptEvent::ReturnedSafety,
ReceiptReturned::InBand {
ref inbound_noderef,
} => ReceiptEvent::ReturnedInBand {
inbound_noderef: inbound_noderef.clone(),
},
ReceiptReturned::Private { ref private_route } => ReceiptEvent::ReturnedPrivate {
private_route: private_route.clone(),
},
};
let callback_future = Self::perform_callback(receipt_event, &mut record_mut);

View File

@ -1,5 +1,6 @@
use super::*;
use core::sync::atomic::Ordering;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
pub struct Bucket {
routing_table: RoutingTable,
@ -8,6 +9,20 @@ pub struct Bucket {
}
pub(super) type EntriesIter<'a> = alloc::collections::btree_map::Iter<'a, DHTKey, Arc<BucketEntry>>;
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
struct BucketEntryData {
key: DHTKey,
value: Vec<u8>,
}
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
struct BucketData {
entries: Vec<BucketEntryData>,
newest_entry: Option<DHTKey>,
}
fn state_ordering(state: BucketEntryState) -> usize {
match state {
BucketEntryState::Dead => 0,
@ -25,6 +40,36 @@ impl Bucket {
}
}
pub(super) fn load_bucket(&mut self, data: Vec<u8>) -> EyreResult<()> {
let bucket_data: BucketData = from_rkyv(data)?;
for e in bucket_data.entries {
let entryinner = from_rkyv(e.value).wrap_err("failed to deserialize bucket entry")?;
self.entries
.insert(e.key, Arc::new(BucketEntry::new_with_inner(entryinner)));
}
self.newest_entry = bucket_data.newest_entry;
Ok(())
}
pub(super) fn save_bucket(&self) -> EyreResult<Vec<u8>> {
let mut entries = Vec::new();
for (k, v) in &self.entries {
let entry_bytes = v.with_inner(|e| to_rkyv(e))?;
entries.push(BucketEntryData {
key: *k,
value: entry_bytes,
});
}
let bucket_data = BucketData {
entries,
newest_entry: self.newest_entry.clone(),
};
let out = to_rkyv(&bucket_data)?;
Ok(out)
}
pub(super) fn add_entry(&mut self, node_id: DHTKey) -> NodeRef {
log_rtab!("Node added: {}", node_id.encode());
@ -48,13 +93,6 @@ impl Bucket {
// newest_entry is updated by kick_bucket()
}
pub(super) fn roll_transfers(&self, last_ts: u64, cur_ts: u64) {
// Called every ROLLING_TRANSFERS_INTERVAL_SECS
for (_k, v) in &self.entries {
v.with_mut(|e| e.roll_transfers(last_ts, cur_ts));
}
}
pub(super) fn entry(&self, key: &DHTKey) -> Option<Arc<BucketEntry>> {
self.entries.get(key).cloned()
}
@ -87,8 +125,8 @@ impl Bucket {
if a.0 == b.0 {
return core::cmp::Ordering::Equal;
}
a.1.with(|ea| {
b.1.with(|eb| {
a.1.with_inner(|ea| {
b.1.with_inner(|eb| {
let astate = state_ordering(ea.state(cur_ts));
let bstate = state_ordering(eb.state(cur_ts));
// first kick dead nodes, then unreliable nodes

View File

@ -1,5 +1,8 @@
use super::*;
use core::sync::atomic::{AtomicU32, Ordering};
use rkyv::{
with::Skip, Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize,
};
/// Reliable pings are done with increased spacing between pings
@ -39,10 +42,11 @@ pub enum BucketEntryState {
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
struct LastConnectionKey(ProtocolType, AddressType);
pub struct LastConnectionKey(ProtocolType, AddressType);
/// Bucket entry information specific to the LocalNetwork RoutingDomain
#[derive(Debug)]
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct BucketEntryPublicInternet {
/// The PublicInternet node info
signed_node_info: Option<Box<SignedNodeInfo>>,
@ -53,7 +57,8 @@ pub struct BucketEntryPublicInternet {
}
/// Bucket entry information specific to the LocalNetwork RoutingDomain
#[derive(Debug)]
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct BucketEntryLocalNetwork {
/// The LocalNetwork node info
signed_node_info: Option<Box<SignedNodeInfo>>,
@ -63,19 +68,51 @@ pub struct BucketEntryLocalNetwork {
node_status: Option<LocalNetworkNodeStatus>,
}
#[derive(Debug)]
/// A range of cryptography versions supported by this entry
#[derive(Copy, Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct VersionRange {
/// The minimum cryptography version supported by this entry
pub min: u8,
/// The maximum cryptography version supported by this entry
pub max: u8,
}
/// The data associated with each bucket entry
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct BucketEntryInner {
min_max_version: Option<(u8, u8)>,
/// The minimum and maximum range of cryptography versions supported by the node,
/// inclusive of the requirements of any relay the node may be using
min_max_version: Option<VersionRange>,
/// If this node has updated it's SignedNodeInfo since our network
/// and dial info has last changed, for example when our IP address changes
/// Used to determine if we should make this entry 'live' again when we receive a signednodeinfo update that
/// has the same timestamp, because if we change our own IP address or network class it may be possible for nodes that were
/// unreachable may now be reachable with the same SignedNodeInfo/DialInfo
updated_since_last_network_change: bool,
/// The last connection descriptors used to contact this node, per protocol type
#[with(Skip)]
last_connections: BTreeMap<LastConnectionKey, (ConnectionDescriptor, u64)>,
/// The node info for this entry on the publicinternet routing domain
public_internet: BucketEntryPublicInternet,
/// The node info for this entry on the localnetwork routing domain
local_network: BucketEntryLocalNetwork,
/// Statistics gathered for the peer
peer_stats: PeerStats,
/// The accounting for the latency statistics
#[with(Skip)]
latency_stats_accounting: LatencyStatsAccounting,
/// The accounting for the transfer statistics
#[with(Skip)]
transfer_stats_accounting: TransferStatsAccounting,
/// Tracking identifier for NodeRef debugging
#[cfg(feature = "tracking")]
#[with(Skip)]
next_track_id: usize,
/// Backtraces for NodeRef debugging
#[cfg(feature = "tracking")]
#[with(Skip)]
node_ref_tracks: HashMap<usize, backtrace::Backtrace>,
}
@ -132,6 +169,28 @@ impl BucketEntryInner {
}
}
// Less is more reliable then older
pub fn cmp_oldest_reliable(cur_ts: u64, e1: &Self, e2: &Self) -> std::cmp::Ordering {
// Reverse compare so most reliable is at front
let ret = e2.state(cur_ts).cmp(&e1.state(cur_ts));
if ret != std::cmp::Ordering::Equal {
return ret;
}
// Lower timestamp to the front, recent or no timestamp is at the end
if let Some(e1_ts) = &e1.peer_stats.rpc_stats.first_consecutive_seen_ts {
if let Some(e2_ts) = &e2.peer_stats.rpc_stats.first_consecutive_seen_ts {
e1_ts.cmp(&e2_ts)
} else {
std::cmp::Ordering::Less
}
} else if e2.peer_stats.rpc_stats.first_consecutive_seen_ts.is_some() {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
pub fn sort_fastest_reliable_fn(cur_ts: u64) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering {
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
}
@ -159,28 +218,42 @@ impl BucketEntryInner {
// See if we have an existing signed_node_info to update or not
if let Some(current_sni) = opt_current_sni {
// If the timestamp hasn't changed or is less, ignore this update
if signed_node_info.timestamp <= current_sni.timestamp {
// If we received a node update with the same timestamp
// we can make this node live again, but only if our network has recently changed
// which may make nodes that were unreachable now reachable with the same dialinfo
if !self.updated_since_last_network_change
&& signed_node_info.timestamp == current_sni.timestamp
{
// No need to update the signednodeinfo though since the timestamp is the same
// Touch the node and let it try to live again
self.updated_since_last_network_change = true;
self.touch_last_seen(intf::get_timestamp());
// Always allow overwriting invalid/unsigned node
if current_sni.has_valid_signature() {
// If the timestamp hasn't changed or is less, ignore this update
if signed_node_info.timestamp() <= current_sni.timestamp() {
// If we received a node update with the same timestamp
// we can make this node live again, but only if our network has recently changed
// which may make nodes that were unreachable now reachable with the same dialinfo
if !self.updated_since_last_network_change
&& signed_node_info.timestamp() == current_sni.timestamp()
{
// No need to update the signednodeinfo though since the timestamp is the same
// Touch the node and let it try to live again
self.updated_since_last_network_change = true;
self.touch_last_seen(intf::get_timestamp());
}
return;
}
return;
}
}
// Update the protocol min/max version we have
self.min_max_version = Some((
signed_node_info.node_info.min_version,
signed_node_info.node_info.max_version,
));
// Update the protocol min/max version we have to use, to include relay requirements if needed
let mut version_range = VersionRange {
min: signed_node_info.node_info().min_version,
max: signed_node_info.node_info().max_version,
};
if let Some(relay_info) = signed_node_info.relay_info() {
version_range.min.max_assign(relay_info.min_version);
version_range.max.min_assign(relay_info.max_version);
}
if version_range.min <= version_range.max {
// Can be reached with at least one crypto version
self.min_max_version = Some(version_range);
} else {
// No valid crypto version in range
self.min_max_version = None;
}
// Update the signed node info
*opt_current_sni = Some(Box::new(signed_node_info));
@ -207,7 +280,7 @@ impl BucketEntryInner {
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
};
opt_current_sni.as_ref().map(|s| &s.node_info)
opt_current_sni.as_ref().map(|s| s.node_info())
}
pub fn signed_node_info(&self, routing_domain: RoutingDomain) -> Option<&SignedNodeInfo> {
@ -264,37 +337,54 @@ impl BucketEntryInner {
self.last_connections.clear();
}
// Gets the best 'last connection' that matches a set of routing domain, protocol types and address types
pub(super) fn last_connection(
// Gets all the 'last connections' that match a particular filter
pub(super) fn last_connections(
&self,
routing_table_inner: &RoutingTableInner,
node_ref_filter: Option<NodeRefFilter>,
) -> Option<(ConnectionDescriptor, u64)> {
// Iterate peer scopes and protocol types and address type in order to ensure we pick the preferred protocols if all else is the same
let nrf = node_ref_filter.unwrap_or_default();
for pt in nrf.dial_info_filter.protocol_type_set {
for at in nrf.dial_info_filter.address_type_set {
let key = LastConnectionKey(pt, at);
if let Some(v) = self.last_connections.get(&key) {
// Verify this connection could be in the filtered routing domain
let address = v.0.remote_address().address();
if let Some(rd) =
RoutingTable::routing_domain_for_address_inner(routing_table_inner, address)
{
if nrf.routing_domain_set.contains(rd) {
return Some(*v);
rti: &RoutingTableInner,
filter: Option<NodeRefFilter>,
) -> Vec<(ConnectionDescriptor, u64)> {
let mut out: Vec<(ConnectionDescriptor, u64)> = self
.last_connections
.iter()
.filter_map(|(k, v)| {
let include = if let Some(filter) = &filter {
let remote_address = v.0.remote_address().address();
if let Some(routing_domain) = rti.routing_domain_for_address(remote_address) {
if filter.routing_domain_set.contains(routing_domain)
&& filter.dial_info_filter.protocol_type_set.contains(k.0)
&& filter.dial_info_filter.address_type_set.contains(k.1)
{
// matches filter
true
} else {
// does not match filter
false
}
} else {
// no valid routing domain
false
}
} else {
// no filter
true
};
if include {
Some(v.clone())
} else {
None
}
}
}
None
})
.collect();
// Sort with newest timestamps first
out.sort_by(|a, b| b.1.cmp(&a.1));
out
}
pub fn set_min_max_version(&mut self, min_max_version: (u8, u8)) {
pub fn set_min_max_version(&mut self, min_max_version: VersionRange) {
self.min_max_version = Some(min_max_version);
}
pub fn min_max_version(&self) -> Option<(u8, u8)> {
pub fn min_max_version(&self) -> Option<VersionRange> {
self.min_max_version
}
@ -409,14 +499,17 @@ impl BucketEntryInner {
}
}
fn needs_constant_ping(&self, cur_ts: u64, interval: u64) -> bool {
// If we have not either seen the node, nor asked it a question in the last 'interval'
// then we should ping it
let latest_contact_time = self
.peer_stats
/// Return the last time we either saw a node, or asked it a question
fn latest_contact_time(&self) -> Option<u64> {
self.peer_stats
.rpc_stats
.last_seen_ts
.max(self.peer_stats.rpc_stats.last_question);
.max(self.peer_stats.rpc_stats.last_question)
}
fn needs_constant_ping(&self, cur_ts: u64, interval: u64) -> bool {
// If we have not either seen the node in the last 'interval' then we should ping it
let latest_contact_time = self.latest_contact_time();
match latest_contact_time {
None => true,
@ -438,14 +531,19 @@ impl BucketEntryInner {
return self.needs_constant_ping(cur_ts, KEEPALIVE_PING_INTERVAL_SECS as u64);
}
// If we don't have node status for this node, then we should ping it to get some node status
for routing_domain in RoutingDomainSet::all() {
if self.has_node_info(routing_domain.into()) {
if self.node_status(routing_domain).is_none() {
return true;
}
}
}
match state {
BucketEntryState::Reliable => {
// If we are in a reliable state, we need a ping on an exponential scale
let latest_contact_time = self
.peer_stats
.rpc_stats
.last_seen_ts
.max(self.peer_stats.rpc_stats.last_question);
let latest_contact_time = self.latest_contact_time();
match latest_contact_time {
None => {
@ -607,7 +705,37 @@ impl BucketEntry {
}
}
pub(super) fn with<F, R>(&self, f: F) -> R
pub(super) fn new_with_inner(inner: BucketEntryInner) -> Self {
Self {
ref_count: AtomicU32::new(0),
inner: RwLock::new(inner),
}
}
// Note, that this requires -also- holding the RoutingTable read lock, as an
// immutable reference to RoutingTableInner must be passed in to get this
// This ensures that an operation on the routing table can not change entries
// while it is being read from
pub fn with<F, R>(&self, rti: &RoutingTableInner, f: F) -> R
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> R,
{
let inner = self.inner.read();
f(rti, &*inner)
}
// Note, that this requires -also- holding the RoutingTable write lock, as a
// mutable reference to RoutingTableInner must be passed in to get this
pub fn with_mut<F, R>(&self, rti: &mut RoutingTableInner, f: F) -> R
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> R,
{
let mut inner = self.inner.write();
f(rti, &mut *inner)
}
// Internal inner access for RoutingTableInner only
pub(super) fn with_inner<F, R>(&self, f: F) -> R
where
F: FnOnce(&BucketEntryInner) -> R,
{
@ -615,7 +743,8 @@ impl BucketEntry {
f(&*inner)
}
pub(super) fn with_mut<F, R>(&self, f: F) -> R
// Internal inner access for RoutingTableInner only
pub(super) fn with_mut_inner<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut BucketEntryInner) -> R,
{

View File

@ -1,7 +1,7 @@
use super::*;
impl RoutingTable {
pub fn debug_info_nodeinfo(&self) -> String {
pub(crate) fn debug_info_nodeinfo(&self) -> String {
let mut out = String::new();
let inner = self.inner.read();
out += "Routing Table Info:\n";
@ -23,7 +23,7 @@ impl RoutingTable {
out
}
pub async fn debug_info_txtrecord(&self) -> String {
pub(crate) async fn debug_info_txtrecord(&self) -> String {
let mut out = String::new();
let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
@ -58,8 +58,8 @@ impl RoutingTable {
out += &format!(
"{},{},{},{},{}",
BOOTSTRAP_TXT_VERSION,
MIN_VERSION,
MAX_VERSION,
MIN_CRYPTO_VERSION,
MAX_CRYPTO_VERSION,
self.node_id().encode(),
some_hostname.unwrap()
);
@ -71,7 +71,7 @@ impl RoutingTable {
out
}
pub fn debug_info_dialinfo(&self) -> String {
pub(crate) fn debug_info_dialinfo(&self) -> String {
let ldis = self.dial_info_details(RoutingDomain::LocalNetwork);
let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
let mut out = String::new();
@ -100,8 +100,9 @@ impl RoutingTable {
out
}
pub fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String {
pub(crate) fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String {
let inner = self.inner.read();
let inner = &*inner;
let cur_ts = intf::get_timestamp();
let mut out = String::new();
@ -114,14 +115,14 @@ impl RoutingTable {
let filtered_entries: Vec<(&DHTKey, &Arc<BucketEntry>)> = inner.buckets[b]
.entries()
.filter(|e| {
let state = e.1.with(|e| e.state(cur_ts));
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
state >= min_state
})
.collect();
if !filtered_entries.is_empty() {
out += &format!(" Bucket #{}:\n", b);
for e in filtered_entries {
let state = e.1.with(|e| e.state(cur_ts));
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
out += &format!(
" {} [{}]\n",
e.0.encode(),
@ -147,7 +148,7 @@ impl RoutingTable {
out
}
pub fn debug_info_entry(&self, node_id: DHTKey) -> String {
pub(crate) fn debug_info_entry(&self, node_id: DHTKey) -> String {
let mut out = String::new();
out += &format!("Entry {:?}:\n", node_id);
if let Some(nr) = self.lookup_node_ref(node_id) {
@ -159,8 +160,9 @@ impl RoutingTable {
out
}
pub fn debug_info_buckets(&self, min_state: BucketEntryState) -> String {
pub(crate) fn debug_info_buckets(&self, min_state: BucketEntryState) -> String {
let inner = self.inner.read();
let inner = &*inner;
let cur_ts = intf::get_timestamp();
let mut out = String::new();
@ -175,7 +177,7 @@ impl RoutingTable {
while c < COLS {
let mut cnt = 0;
for e in inner.buckets[b].entries() {
if e.1.with(|e| e.state(cur_ts) >= min_state) {
if e.1.with(inner, |_rti, e| e.state(cur_ts) >= min_state) {
cnt += 1;
}
}

View File

@ -1,615 +0,0 @@
use super::*;
use crate::dht::*;
use crate::xx::*;
use crate::*;
pub type LowLevelProtocolPorts = BTreeSet<(LowLevelProtocolType, AddressType, u16)>;
pub type ProtocolToPortMapping = BTreeMap<(ProtocolType, AddressType), (LowLevelProtocolType, u16)>;
#[derive(Clone, Debug)]
pub struct MappedPortInfo {
pub low_level_protocol_ports: LowLevelProtocolPorts,
pub protocol_to_port: ProtocolToPortMapping,
}
impl RoutingTable {
// Makes a filter that finds nodes with a matching inbound dialinfo
pub fn make_inbound_dial_info_entry_filter(
routing_domain: RoutingDomain,
dial_info_filter: DialInfoFilter,
) -> impl FnMut(&BucketEntryInner) -> bool {
// does it have matching public dial info?
move |e| {
if let Some(ni) = e.node_info(routing_domain) {
if ni
.first_filtered_dial_info_detail(|did| did.matches_filter(&dial_info_filter))
.is_some()
{
return true;
}
}
false
}
}
// Makes a filter that finds nodes capable of dialing a particular outbound dialinfo
pub fn make_outbound_dial_info_entry_filter(
routing_domain: RoutingDomain,
dial_info: DialInfo,
) -> impl FnMut(&BucketEntryInner) -> bool {
// does the node's outbound capabilities match the dialinfo?
move |e| {
if let Some(ni) = e.node_info(routing_domain) {
let dif = DialInfoFilter::all()
.with_protocol_type_set(ni.outbound_protocols)
.with_address_type_set(ni.address_types);
if dial_info.matches_filter(&dif) {
return true;
}
}
false
}
}
// Make a filter that wraps another filter
pub fn combine_entry_filters<F, G>(
mut f1: F,
mut f2: G,
) -> impl FnMut(&BucketEntryInner) -> bool
where
F: FnMut(&BucketEntryInner) -> bool,
G: FnMut(&BucketEntryInner) -> bool,
{
move |e| {
if !f1(e) {
return false;
}
if !f2(e) {
return false;
}
true
}
}
// Retrieve the fastest nodes in the routing table matching an entry filter
pub fn find_fast_public_nodes_filtered<F>(
&self,
node_count: usize,
mut entry_filter: F,
) -> Vec<NodeRef>
where
F: FnMut(&BucketEntryInner) -> bool,
{
self.find_fastest_nodes(
// count
node_count,
// filter
|_k: DHTKey, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap();
entry.with(|e| {
// skip nodes on local network
if e.node_info(RoutingDomain::LocalNetwork).is_some() {
return false;
}
// skip nodes not on public internet
if e.node_info(RoutingDomain::PublicInternet).is_none() {
return false;
}
// skip nodes that dont match entry filter
entry_filter(e)
})
},
// transform
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
},
)
}
// Retrieve up to N of each type of protocol capable nodes
pub fn find_bootstrap_nodes_filtered(&self, max_per_type: usize) -> Vec<NodeRef> {
let protocol_types = vec![
ProtocolType::UDP,
ProtocolType::TCP,
ProtocolType::WS,
ProtocolType::WSS,
];
let mut nodes_proto_v4 = vec![0usize, 0usize, 0usize, 0usize];
let mut nodes_proto_v6 = vec![0usize, 0usize, 0usize, 0usize];
self.find_fastest_nodes(
// count
protocol_types.len() * 2 * max_per_type,
// filter
move |_k: DHTKey, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap();
entry.with(|e| {
// skip nodes on our local network here
if e.has_node_info(RoutingDomain::LocalNetwork.into()) {
return false;
}
// does it have some dial info we need?
let filter = |n: &NodeInfo| {
let mut keep = false;
for did in &n.dial_info_detail_list {
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v4[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v4[n] += 1;
keep = true;
}
}
} else if matches!(did.dial_info.address_type(), AddressType::IPV6) {
for (n, protocol_type) in protocol_types.iter().enumerate() {
if nodes_proto_v6[n] < max_per_type
&& did.dial_info.protocol_type() == *protocol_type
{
nodes_proto_v6[n] += 1;
keep = true;
}
}
}
}
keep
};
e.node_info(RoutingDomain::PublicInternet)
.map(filter)
.unwrap_or(false)
})
},
// transform
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
},
)
}
pub fn filter_has_valid_signed_node_info(
&self,
routing_domain: RoutingDomain,
v: Option<Arc<BucketEntry>>,
) -> bool {
match v {
None => self.has_valid_own_node_info(routing_domain),
Some(entry) => entry.with(|e| {
e.signed_node_info(routing_domain.into())
.map(|sni| sni.has_valid_signature())
.unwrap_or(false)
}),
}
}
pub fn transform_to_peer_info(
&self,
routing_domain: RoutingDomain,
k: DHTKey,
v: Option<Arc<BucketEntry>>,
) -> PeerInfo {
match v {
None => self.get_own_peer_info(routing_domain),
Some(entry) => entry.with(|e| e.make_peer_info(k, routing_domain).unwrap()),
}
}
pub fn find_peers_with_sort_and_filter<F, C, T, O>(
&self,
node_count: usize,
cur_ts: u64,
mut filter: F,
compare: C,
mut transform: T,
) -> Vec<O>
where
F: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> bool,
C: FnMut(
&(DHTKey, Option<Arc<BucketEntry>>),
&(DHTKey, Option<Arc<BucketEntry>>),
) -> core::cmp::Ordering,
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
{
let inner = self.inner.read();
let self_node_id = self.unlocked_inner.node_id;
// collect all the nodes for sorting
let mut nodes =
Vec::<(DHTKey, Option<Arc<BucketEntry>>)>::with_capacity(inner.bucket_entry_count + 1);
// add our own node (only one of there with the None entry)
if filter(self_node_id, None) {
nodes.push((self_node_id, None));
}
// add all nodes from buckets
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
// Apply filter
if filter(k, Some(v.clone())) {
nodes.push((k, Some(v.clone())));
}
Option::<()>::None
});
// sort by preference for returning nodes
nodes.sort_by(compare);
// return transformed vector for filtered+sorted nodes
let cnt = usize::min(node_count, nodes.len());
let mut out = Vec::<O>::with_capacity(cnt);
for node in nodes {
let val = transform(node.0, node.1);
out.push(val);
}
out
}
pub fn find_fastest_nodes<T, F, O>(
&self,
node_count: usize,
mut filter: F,
transform: T,
) -> Vec<O>
where
F: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> bool,
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
{
let cur_ts = intf::get_timestamp();
let out = self.find_peers_with_sort_and_filter(
node_count,
cur_ts,
// filter
|k, v| {
if let Some(entry) = &v {
// always filter out dead nodes
if entry.with(|e| e.state(cur_ts) == BucketEntryState::Dead) {
false
} else {
filter(k, v)
}
} else {
// always filter out self peer, as it is irrelevant to the 'fastest nodes' search
false
}
},
// sort
|(a_key, a_entry), (b_key, b_entry)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// our own node always comes last (should not happen, here for completeness)
if a_entry.is_none() {
return core::cmp::Ordering::Greater;
}
if b_entry.is_none() {
return core::cmp::Ordering::Less;
}
// reliable nodes come first
let ae = a_entry.as_ref().unwrap();
let be = b_entry.as_ref().unwrap();
ae.with(|ae| {
be.with(|be| {
let ra = ae.check_reliable(cur_ts);
let rb = be.check_reliable(cur_ts);
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// latency is the next metric, closer nodes first
let a_latency = match ae.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Greater;
}
Some(l) => l,
};
let b_latency = match be.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Less;
}
Some(l) => l,
};
// Sort by average latency
a_latency.average.cmp(&b_latency.average)
})
})
},
// transform,
transform,
);
out
}
pub fn find_closest_nodes<F, T, O>(
&self,
node_id: DHTKey,
filter: F,
mut transform: T,
) -> Vec<O>
where
F: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> bool,
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
{
let cur_ts = intf::get_timestamp();
let node_count = {
let c = self.unlocked_inner.config.get();
c.network.dht.max_find_node_count as usize
};
let out = self.find_peers_with_sort_and_filter(
node_count,
cur_ts,
// filter
filter,
// sort
|(a_key, a_entry), (b_key, b_entry)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// reliable nodes come first, pessimistically treating our own node as unreliable
let ra = a_entry
.as_ref()
.map_or(false, |x| x.with(|x| x.check_reliable(cur_ts)));
let rb = b_entry
.as_ref()
.map_or(false, |x| x.with(|x| x.check_reliable(cur_ts)));
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// distance is the next metric, closer nodes first
let da = distance(a_key, &node_id);
let db = distance(b_key, &node_id);
da.cmp(&db)
},
// transform,
&mut transform,
);
log_rtab!(">> find_closest_nodes: node count = {}", out.len());
out
}
// Build a map of protocols to low level ports
// This way we can get the set of protocols required to keep our NAT mapping alive for keepalive pings
// Only one protocol per low level protocol/port combination is required
// For example, if WS/WSS and TCP protocols are on the same low-level TCP port, only TCP keepalives will be required
// and we do not need to do WS/WSS keepalive as well. If they are on different ports, then we will need WS/WSS keepalives too.
pub fn get_mapped_port_info(&self) -> MappedPortInfo {
let mut low_level_protocol_ports =
BTreeSet::<(LowLevelProtocolType, AddressType, u16)>::new();
let mut protocol_to_port =
BTreeMap::<(ProtocolType, AddressType), (LowLevelProtocolType, u16)>::new();
let our_dids = self.all_filtered_dial_info_details(
RoutingDomain::PublicInternet.into(),
&DialInfoFilter::all(),
);
for did in our_dids {
low_level_protocol_ports.insert((
did.dial_info.protocol_type().low_level_protocol_type(),
did.dial_info.address_type(),
did.dial_info.socket_address().port(),
));
protocol_to_port.insert(
(did.dial_info.protocol_type(), did.dial_info.address_type()),
(
did.dial_info.protocol_type().low_level_protocol_type(),
did.dial_info.socket_address().port(),
),
);
}
MappedPortInfo {
low_level_protocol_ports,
protocol_to_port,
}
}
fn make_public_internet_relay_node_filter(&self) -> impl Fn(&BucketEntryInner) -> bool {
// Get all our outbound protocol/address types
let outbound_dif = self
.network_manager()
.get_outbound_dial_info_filter(RoutingDomain::PublicInternet);
let mapped_port_info = self.get_mapped_port_info();
move |e: &BucketEntryInner| {
// Ensure this node is not on the local network
if e.has_node_info(RoutingDomain::LocalNetwork.into()) {
return false;
}
// Disqualify nodes that don't cover all our inbound ports for tcp and udp
// as we need to be able to use the relay for keepalives for all nat mappings
let mut low_level_protocol_ports = mapped_port_info.low_level_protocol_ports.clone();
let can_serve_as_relay = e
.node_info(RoutingDomain::PublicInternet)
.map(|n| {
let dids =
n.all_filtered_dial_info_details(|did| did.matches_filter(&outbound_dif));
for did in &dids {
let pt = did.dial_info.protocol_type();
let at = did.dial_info.address_type();
if let Some((llpt, port)) = mapped_port_info.protocol_to_port.get(&(pt, at))
{
low_level_protocol_ports.remove(&(*llpt, at, *port));
}
}
low_level_protocol_ports.is_empty()
})
.unwrap_or(false);
if !can_serve_as_relay {
return false;
}
true
}
}
#[instrument(level = "trace", skip(self), ret)]
pub fn find_inbound_relay(
&self,
routing_domain: RoutingDomain,
cur_ts: u64,
) -> Option<NodeRef> {
// Get relay filter function
let relay_node_filter = match routing_domain {
RoutingDomain::PublicInternet => self.make_public_internet_relay_node_filter(),
RoutingDomain::LocalNetwork => {
unimplemented!();
}
};
// Go through all entries and find fastest entry that matches filter function
let inner = self.inner.read();
let inner = &*inner;
let mut best_inbound_relay: Option<(DHTKey, Arc<BucketEntry>)> = None;
// Iterate all known nodes for candidates
Self::with_entries(inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
let v2 = v.clone();
v.with(|e| {
// Ensure we have the node's status
if let Some(node_status) = e.node_status(routing_domain) {
// Ensure the node will relay
if node_status.will_relay() {
// Compare against previous candidate
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
// Less is faster
let better = best_inbound_relay.1.with(|best| {
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
== std::cmp::Ordering::Less
});
// Now apply filter function and see if this node should be included
if better && relay_node_filter(e) {
*best_inbound_relay = (k, v2);
}
} else if relay_node_filter(e) {
// Always store the first candidate
best_inbound_relay = Some((k, v2));
}
}
}
});
// Don't end early, iterate through all entries
Option::<()>::None
});
// Return the best inbound relay noderef
best_inbound_relay.map(|(k, e)| NodeRef::new(self.clone(), k, e, None))
}
#[instrument(level = "trace", skip(self), ret)]
pub fn register_find_node_answer(&self, peers: Vec<PeerInfo>) -> Vec<NodeRef> {
let node_id = self.node_id();
// register nodes we'd found
let mut out = Vec::<NodeRef>::with_capacity(peers.len());
for p in peers {
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
if p.node_id.key == node_id {
continue;
}
// node can not be its own relay
if let Some(rpi) = &p.signed_node_info.node_info.relay_peer_info {
if rpi.node_id == p.node_id {
continue;
}
}
// register the node if it's new
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
p.node_id.key,
p.signed_node_info.clone(),
false,
) {
out.push(nr);
}
}
out
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_node(
&self,
node_ref: NodeRef,
node_id: DHTKey,
) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let rpc_processor = self.rpc_processor();
let res = network_result_try!(
rpc_processor
.clone()
.rpc_call_find_node(Destination::direct(node_ref), node_id)
.await?
);
// register nodes we'd found
Ok(NetworkResult::value(
self.register_find_node_answer(res.answer),
))
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_self(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let node_id = self.node_id();
self.find_node(node_ref, node_id).await
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_target(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let node_id = node_ref.node_id();
self.find_node(node_ref, node_id).await
}
#[instrument(level = "trace", skip(self))]
pub async fn reverse_find_node(&self, node_ref: NodeRef, wide: bool) {
// Ask bootstrap node to 'find' our own node so we can get some more nodes near ourselves
// and then contact those nodes to inform -them- that we exist
// Ask bootstrap server for nodes closest to our own node
let closest_nodes = network_result_value_or_log!(debug match self.find_self(node_ref.clone()).await {
Err(e) => {
log_rtab!(error
"find_self failed for {:?}: {:?}",
&node_ref, e
);
return;
}
Ok(v) => v,
} => {
return;
});
// Ask each node near us to find us as well
if wide {
for closest_nr in closest_nodes {
network_result_value_or_log!(debug match self.find_self(closest_nr.clone()).await {
Err(e) => {
log_rtab!(error
"find_self failed for {:?}: {:?}",
&closest_nr, e
);
continue;
}
Ok(v) => v,
} => {
// Do nothing with non-values
continue;
});
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,80 +1,360 @@
use super::*;
use crate::dht::*;
use crate::crypto::*;
use alloc::fmt;
// Connectionless protocols like UDP are dependent on a NAT translation timeout
// We should ping them with some frequency and 30 seconds is typical timeout
const CONNECTIONLESS_TIMEOUT_SECS: u32 = 29;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct NodeRefFilter {
pub routing_domain_set: RoutingDomainSet,
pub dial_info_filter: DialInfoFilter,
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
impl Default for NodeRefFilter {
fn default() -> Self {
Self::new()
}
}
impl NodeRefFilter {
pub fn new() -> Self {
Self {
routing_domain_set: RoutingDomainSet::all(),
dial_info_filter: DialInfoFilter::all(),
}
}
pub fn with_routing_domain(mut self, routing_domain: RoutingDomain) -> Self {
self.routing_domain_set = routing_domain.into();
self
}
pub fn with_routing_domain_set(mut self, routing_domain_set: RoutingDomainSet) -> Self {
self.routing_domain_set = routing_domain_set;
self
}
pub fn with_dial_info_filter(mut self, dial_info_filter: DialInfoFilter) -> Self {
self.dial_info_filter = dial_info_filter;
self
}
pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type(protocol_type);
self
}
pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type_set(protocol_set);
self
}
pub fn with_address_type(mut self, address_type: AddressType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type(address_type);
self
}
pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set);
self
}
pub fn filtered(mut self, other_filter: &NodeRefFilter) -> Self {
self.routing_domain_set &= other_filter.routing_domain_set;
self.dial_info_filter = self
.dial_info_filter
.filtered(&other_filter.dial_info_filter);
self
}
pub fn is_dead(&self) -> bool {
self.dial_info_filter.is_dead() || self.routing_domain_set.is_empty()
}
}
pub struct NodeRef {
pub struct NodeRefBaseCommon {
routing_table: RoutingTable,
node_id: DHTKey,
entry: Arc<BucketEntry>,
filter: Option<NodeRefFilter>,
sequencing: Sequencing,
#[cfg(feature = "tracking")]
track_id: usize,
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
pub trait NodeRefBase: Sized {
// Common field access
fn common(&self) -> &NodeRefBaseCommon;
fn common_mut(&mut self) -> &mut NodeRefBaseCommon;
// Implementation-specific operators
fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T;
fn operate_mut<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T;
// Filtering
fn filter_ref(&self) -> Option<&NodeRefFilter> {
self.common().filter.as_ref()
}
fn take_filter(&mut self) -> Option<NodeRefFilter> {
self.common_mut().filter.take()
}
fn set_filter(&mut self, filter: Option<NodeRefFilter>) {
self.common_mut().filter = filter
}
fn set_sequencing(&mut self, sequencing: Sequencing) {
self.common_mut().sequencing = sequencing;
}
fn sequencing(&self) -> Sequencing {
self.common().sequencing
}
fn merge_filter(&mut self, filter: NodeRefFilter) {
let common_mut = self.common_mut();
if let Some(self_filter) = common_mut.filter.take() {
common_mut.filter = Some(self_filter.filtered(&filter));
} else {
common_mut.filter = Some(filter);
}
}
fn is_filter_dead(&self) -> bool {
if let Some(filter) = &self.common().filter {
filter.is_dead()
} else {
false
}
}
fn routing_domain_set(&self) -> RoutingDomainSet {
self.common()
.filter
.as_ref()
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all())
}
fn dial_info_filter(&self) -> DialInfoFilter {
self.common()
.filter
.as_ref()
.map(|f| f.dial_info_filter.clone())
.unwrap_or(DialInfoFilter::all())
}
fn best_routing_domain(&self) -> Option<RoutingDomain> {
self.operate(|_rti, e| {
e.best_routing_domain(
self.common()
.filter
.as_ref()
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all()),
)
})
}
// Accessors
fn routing_table(&self) -> RoutingTable {
self.common().routing_table.clone()
}
fn node_id(&self) -> DHTKey {
self.common().node_id
}
fn has_updated_since_last_network_change(&self) -> bool {
self.operate(|_rti, e| e.has_updated_since_last_network_change())
}
fn set_updated_since_last_network_change(&self) {
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
}
fn update_node_status(&self, node_status: NodeStatus) {
self.operate_mut(|_rti, e| {
e.update_node_status(node_status);
});
}
fn min_max_version(&self) -> Option<VersionRange> {
self.operate(|_rti, e| e.min_max_version())
}
fn set_min_max_version(&self, min_max_version: VersionRange) {
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
}
fn state(&self, cur_ts: u64) -> BucketEntryState {
self.operate(|_rti, e| e.state(cur_ts))
}
fn peer_stats(&self) -> PeerStats {
self.operate(|_rti, e| e.peer_stats().clone())
}
// Per-RoutingDomain accessors
fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.operate(|_rti, e| e.make_peer_info(self.node_id(), routing_domain))
}
fn node_info(&self, routing_domain: RoutingDomain) -> Option<NodeInfo> {
self.operate(|_rti, e| e.node_info(routing_domain).cloned())
}
fn signed_node_info_has_valid_signature(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| {
e.signed_node_info(routing_domain)
.map(|sni| sni.has_valid_signature())
.unwrap_or(false)
})
}
fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
}
fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
}
fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
}
fn outbound_protocols(&self, routing_domain: RoutingDomain) -> Option<ProtocolTypeSet> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols))
}
fn address_types(&self, routing_domain: RoutingDomain) -> Option<AddressTypeSet> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types))
}
fn node_info_outbound_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
let mut dif = DialInfoFilter::all();
if let Some(outbound_protocols) = self.outbound_protocols(routing_domain) {
dif = dif.with_protocol_type_set(outbound_protocols);
}
if let Some(address_types) = self.address_types(routing_domain) {
dif = dif.with_address_type_set(address_types);
}
dif
}
fn relay(&self, routing_domain: RoutingDomain) -> Option<NodeRef> {
self.operate_mut(|rti, e| {
e.signed_node_info(routing_domain)
.and_then(|n| n.relay_peer_info())
.and_then(|t| {
// If relay is ourselves, then return None, because we can't relay through ourselves
// and to contact this node we should have had an existing inbound connection
if t.node_id.key == rti.unlocked_inner.node_id {
return None;
}
// Register relay node and return noderef
rti.register_node_with_signed_node_info(
self.routing_table(),
routing_domain,
t.node_id.key,
t.signed_node_info,
false,
)
})
})
}
// Filtered accessors
fn first_filtered_dial_info_detail(&self) -> Option<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
let dial_info_filter = self.dial_info_filter();
let (sort, dial_info_filter) = match self.common().sequencing {
Sequencing::NoPreference => (None, dial_info_filter),
Sequencing::PreferOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter,
),
Sequencing::EnsureOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter.filtered(
&DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
),
),
};
self.operate(|_rt, e| {
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
if let Some(did) = ni.first_filtered_dial_info_detail(sort, filter) {
return Some(did);
}
}
}
None
})
}
fn all_filtered_dial_info_details<F>(&self) -> Vec<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
let dial_info_filter = self.dial_info_filter();
let (sort, dial_info_filter) = match self.common().sequencing {
Sequencing::NoPreference => (None, dial_info_filter),
Sequencing::PreferOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter,
),
Sequencing::EnsureOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter.filtered(
&DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
),
),
};
let mut out = Vec::new();
self.operate(|_rt, e| {
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
if let Some(did) = ni.first_filtered_dial_info_detail(sort, filter) {
out.push(did);
}
}
}
});
out.remove_duplicates();
out
}
fn last_connection(&self) -> Option<ConnectionDescriptor> {
// Get the last connections and the last time we saw anything with this connection
// Filtered first and then sorted by most recent
self.operate(|rti, e| {
let last_connections = e.last_connections(rti, self.common().filter.clone());
// Do some checks to ensure these are possibly still 'live'
for (last_connection, last_seen) in last_connections {
// Should we check the connection table?
if last_connection.protocol_type().is_connection_oriented() {
// Look the connection up in the connection manager and see if it's still there
let connection_manager =
rti.unlocked_inner.network_manager.connection_manager();
if connection_manager.get_connection(last_connection).is_some() {
return Some(last_connection);
}
} else {
// If this is not connection oriented, then we check our last seen time
// to see if this mapping has expired (beyond our timeout)
let cur_ts = intf::get_timestamp();
if (last_seen + (CONNECTIONLESS_TIMEOUT_SECS as u64 * 1_000_000u64)) >= cur_ts {
return Some(last_connection);
}
}
}
None
})
}
fn clear_last_connections(&self) {
self.operate_mut(|_rti, e| e.clear_last_connections())
}
fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: u64) {
self.operate_mut(|rti, e| {
e.set_last_connection(connection_descriptor, ts);
rti.touch_recent_peer(self.common().node_id, connection_descriptor);
})
}
fn has_any_dial_info(&self) -> bool {
self.operate(|_rti, e| {
for rtd in RoutingDomain::all() {
if let Some(sni) = e.signed_node_info(rtd) {
if sni.has_any_dial_info() {
return true;
}
}
}
false
})
}
fn stats_question_sent(&self, ts: u64, bytes: u64, expects_answer: bool) {
self.operate_mut(|rti, e| {
rti.transfer_stats_accounting().add_up(bytes);
e.question_sent(ts, bytes, expects_answer);
})
}
fn stats_question_rcvd(&self, ts: u64, bytes: u64) {
self.operate_mut(|rti, e| {
rti.transfer_stats_accounting().add_down(bytes);
e.question_rcvd(ts, bytes);
})
}
fn stats_answer_sent(&self, bytes: u64) {
self.operate_mut(|rti, e| {
rti.transfer_stats_accounting().add_up(bytes);
e.answer_sent(bytes);
})
}
fn stats_answer_rcvd(&self, send_ts: u64, recv_ts: u64, bytes: u64) {
self.operate_mut(|rti, e| {
rti.transfer_stats_accounting().add_down(bytes);
rti.latency_stats_accounting()
.record_latency(recv_ts - send_ts);
e.answer_rcvd(send_ts, recv_ts, bytes);
})
}
fn stats_question_lost(&self) {
self.operate_mut(|_rti, e| {
e.question_lost();
})
}
fn stats_failed_to_send(&self, ts: u64, expects_answer: bool) {
self.operate_mut(|_rti, e| {
e.failed_to_send(ts, expects_answer);
})
}
}
////////////////////////////////////////////////////////////////////////////////////
/// Reference to a routing table entry
/// Keeps entry in the routing table until all references are gone
pub struct NodeRef {
common: NodeRefBaseCommon,
}
impl NodeRef {
pub fn new(
routing_table: RoutingTable,
@ -85,52 +365,15 @@ impl NodeRef {
entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
Self {
routing_table,
node_id,
entry,
filter,
#[cfg(feature = "tracking")]
track_id: entry.track(),
}
}
// Operate on entry accessors
pub(super) fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
{
let inner = &*self.routing_table.inner.read();
self.entry.with(|e| f(inner, e))
}
pub(super) fn operate_mut<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
{
let inner = &mut *self.routing_table.inner.write();
self.entry.with_mut(|e| f(inner, e))
}
// Filtering
pub fn filter_ref(&self) -> Option<&NodeRefFilter> {
self.filter.as_ref()
}
pub fn take_filter(&mut self) -> Option<NodeRefFilter> {
self.filter.take()
}
pub fn set_filter(&mut self, filter: Option<NodeRefFilter>) {
self.filter = filter
}
pub fn merge_filter(&mut self, filter: NodeRefFilter) {
if let Some(self_filter) = self.filter.take() {
self.filter = Some(self_filter.filtered(&filter));
} else {
self.filter = Some(filter);
common: NodeRefBaseCommon {
routing_table,
node_id,
entry,
filter,
sequencing: Sequencing::NoPreference,
#[cfg(feature = "tracking")]
track_id: entry.track(),
},
}
}
@ -140,281 +383,73 @@ impl NodeRef {
out
}
pub fn is_filter_dead(&self) -> bool {
if let Some(filter) = &self.filter {
filter.is_dead()
} else {
false
}
pub fn locked<'a>(&self, rti: &'a RoutingTableInner) -> NodeRefLocked<'a> {
NodeRefLocked::new(rti, self.clone())
}
pub fn locked_mut<'a>(&self, rti: &'a mut RoutingTableInner) -> NodeRefLockedMut<'a> {
NodeRefLockedMut::new(rti, self.clone())
}
}
impl NodeRefBase for NodeRef {
fn common(&self) -> &NodeRefBaseCommon {
&self.common
}
pub fn routing_domain_set(&self) -> RoutingDomainSet {
self.filter
.as_ref()
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all())
fn common_mut(&mut self) -> &mut NodeRefBaseCommon {
&mut self.common
}
pub fn dial_info_filter(&self) -> DialInfoFilter {
self.filter
.as_ref()
.map(|f| f.dial_info_filter.clone())
.unwrap_or(DialInfoFilter::all())
fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
{
let inner = &*self.common.routing_table.inner.read();
self.common.entry.with(inner, f)
}
pub fn best_routing_domain(&self) -> Option<RoutingDomain> {
self.operate(|_rti, e| {
e.best_routing_domain(
self.filter
.as_ref()
.map(|f| f.routing_domain_set)
.unwrap_or(RoutingDomainSet::all()),
)
})
}
// Accessors
pub fn routing_table(&self) -> RoutingTable {
self.routing_table.clone()
}
pub fn node_id(&self) -> DHTKey {
self.node_id
}
pub fn has_updated_since_last_network_change(&self) -> bool {
self.operate(|_rti, e| e.has_updated_since_last_network_change())
}
pub fn set_updated_since_last_network_change(&self) {
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
}
pub fn update_node_status(&self, node_status: NodeStatus) {
self.operate_mut(|_rti, e| {
e.update_node_status(node_status);
});
}
pub fn min_max_version(&self) -> Option<(u8, u8)> {
self.operate(|_rti, e| e.min_max_version())
}
pub fn set_min_max_version(&self, min_max_version: (u8, u8)) {
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
}
pub fn state(&self, cur_ts: u64) -> BucketEntryState {
self.operate(|_rti, e| e.state(cur_ts))
}
pub fn peer_stats(&self) -> PeerStats {
self.operate(|_rti, e| e.peer_stats().clone())
}
// Per-RoutingDomain accessors
pub fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.operate(|_rti, e| e.make_peer_info(self.node_id(), routing_domain))
}
pub fn signed_node_info_has_valid_signature(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| {
e.signed_node_info(routing_domain)
.map(|sni| sni.has_valid_signature())
.unwrap_or(false)
})
}
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
}
pub fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
}
pub fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
}
pub fn outbound_protocols(&self, routing_domain: RoutingDomain) -> Option<ProtocolTypeSet> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols))
}
pub fn address_types(&self, routing_domain: RoutingDomain) -> Option<AddressTypeSet> {
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types))
}
pub fn node_info_outbound_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
let mut dif = DialInfoFilter::all();
if let Some(outbound_protocols) = self.outbound_protocols(routing_domain) {
dif = dif.with_protocol_type_set(outbound_protocols);
}
if let Some(address_types) = self.address_types(routing_domain) {
dif = dif.with_address_type_set(address_types);
}
dif
}
pub fn relay(&self, routing_domain: RoutingDomain) -> Option<NodeRef> {
let target_rpi = self.operate(|_rti, e| {
e.node_info(routing_domain)
.map(|n| n.relay_peer_info.as_ref().map(|pi| pi.as_ref().clone()))
})?;
target_rpi.and_then(|t| {
// If relay is ourselves, then return None, because we can't relay through ourselves
// and to contact this node we should have had an existing inbound connection
if t.node_id.key == self.routing_table.node_id() {
return None;
}
// Register relay node and return noderef
self.routing_table.register_node_with_signed_node_info(
routing_domain,
t.node_id.key,
t.signed_node_info,
false,
)
})
}
// Filtered accessors
pub fn first_filtered_dial_info_detail(&self) -> Option<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
let dial_info_filter = self.dial_info_filter();
self.operate(|_rt, e| {
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
return Some(did);
}
}
}
None
})
}
pub fn all_filtered_dial_info_details<F>(&self) -> Vec<DialInfoDetail> {
let routing_domain_set = self.routing_domain_set();
let dial_info_filter = self.dial_info_filter();
let mut out = Vec::new();
self.operate(|_rt, e| {
for routing_domain in routing_domain_set {
if let Some(ni) = e.node_info(routing_domain) {
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
out.push(did);
}
}
}
});
out.remove_duplicates();
out
}
pub fn last_connection(&self) -> Option<ConnectionDescriptor> {
// Get the last connection and the last time we saw anything with this connection
let (last_connection, last_seen) =
self.operate(|rti, e| e.last_connection(rti, self.filter.clone()))?;
// Should we check the connection table?
if last_connection.protocol_type().is_connection_oriented() {
// Look the connection up in the connection manager and see if it's still there
let connection_manager = self.routing_table.network_manager().connection_manager();
connection_manager.get_connection(last_connection)?;
} else {
// If this is not connection oriented, then we check our last seen time
// to see if this mapping has expired (beyond our timeout)
let cur_ts = intf::get_timestamp();
if (last_seen + (CONNECTIONLESS_TIMEOUT_SECS as u64 * 1_000_000u64)) < cur_ts {
return None;
}
}
Some(last_connection)
}
pub fn clear_last_connections(&self) {
self.operate_mut(|_rti, e| e.clear_last_connections())
}
pub fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: u64) {
self.operate_mut(|_rti, e| e.set_last_connection(connection_descriptor, ts));
self.routing_table
.touch_recent_peer(self.node_id(), connection_descriptor);
}
pub fn has_any_dial_info(&self) -> bool {
self.operate(|_rti, e| {
for rtd in RoutingDomain::all() {
if let Some(ni) = e.node_info(rtd) {
if ni.has_any_dial_info() {
return true;
}
}
}
false
})
}
pub fn stats_question_sent(&self, ts: u64, bytes: u64, expects_answer: bool) {
self.operate_mut(|rti, e| {
rti.self_transfer_stats_accounting.add_up(bytes);
e.question_sent(ts, bytes, expects_answer);
})
}
pub fn stats_question_rcvd(&self, ts: u64, bytes: u64) {
self.operate_mut(|rti, e| {
rti.self_transfer_stats_accounting.add_down(bytes);
e.question_rcvd(ts, bytes);
})
}
pub fn stats_answer_sent(&self, bytes: u64) {
self.operate_mut(|rti, e| {
rti.self_transfer_stats_accounting.add_up(bytes);
e.answer_sent(bytes);
})
}
pub fn stats_answer_rcvd(&self, send_ts: u64, recv_ts: u64, bytes: u64) {
self.operate_mut(|rti, e| {
rti.self_transfer_stats_accounting.add_down(bytes);
rti.self_latency_stats_accounting
.record_latency(recv_ts - send_ts);
e.answer_rcvd(send_ts, recv_ts, bytes);
})
}
pub fn stats_question_lost(&self) {
self.operate_mut(|_rti, e| {
e.question_lost();
})
}
pub fn stats_failed_to_send(&self, ts: u64, expects_answer: bool) {
self.operate_mut(|_rti, e| {
e.failed_to_send(ts, expects_answer);
})
fn operate_mut<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
{
let inner = &mut *self.common.routing_table.inner.write();
self.common.entry.with_mut(inner, f)
}
}
impl Clone for NodeRef {
fn clone(&self) -> Self {
self.entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
self.common
.entry
.ref_count
.fetch_add(1u32, Ordering::Relaxed);
Self {
routing_table: self.routing_table.clone(),
node_id: self.node_id,
entry: self.entry.clone(),
filter: self.filter.clone(),
#[cfg(feature = "tracking")]
track_id: e.track(),
common: NodeRefBaseCommon {
routing_table: self.common.routing_table.clone(),
node_id: self.common.node_id,
entry: self.common.entry.clone(),
filter: self.common.filter.clone(),
sequencing: self.common.sequencing,
#[cfg(feature = "tracking")]
track_id: self.common.entry.write().track(),
},
}
}
}
impl PartialEq for NodeRef {
fn eq(&self, other: &Self) -> bool {
self.node_id == other.node_id
}
}
impl Eq for NodeRef {}
impl fmt::Display for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.node_id.encode())
write!(f, "{}", self.common.node_id.encode())
}
}
impl fmt::Debug for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeRef")
.field("node_id", &self.node_id)
.field("filter", &self.filter)
.field("node_id", &self.common.node_id)
.field("filter", &self.common.filter)
.field("sequencing", &self.common.sequencing)
.finish()
}
}
@ -422,12 +457,138 @@ impl fmt::Debug for NodeRef {
impl Drop for NodeRef {
fn drop(&mut self) {
#[cfg(feature = "tracking")]
self.operate(|e| e.untrack(self.track_id));
self.common.entry.write().untrack(self.track_id);
// drop the noderef and queue a bucket kick if it was the last one
let new_ref_count = self.entry.ref_count.fetch_sub(1u32, Ordering::Relaxed) - 1;
let new_ref_count = self
.common
.entry
.ref_count
.fetch_sub(1u32, Ordering::Relaxed)
- 1;
if new_ref_count == 0 {
self.routing_table.queue_bucket_kick(self.node_id);
self.common
.routing_table
.queue_bucket_kick(self.common.node_id);
}
}
}
////////////////////////////////////////////////////////////////////////////////////
/// Locked reference to a routing table entry
/// For internal use inside the RoutingTable module where you have
/// already locked a RoutingTableInner
/// Keeps entry in the routing table until all references are gone
pub struct NodeRefLocked<'a> {
inner: Mutex<&'a RoutingTableInner>,
nr: NodeRef,
}
impl<'a> NodeRefLocked<'a> {
pub fn new(inner: &'a RoutingTableInner, nr: NodeRef) -> Self {
Self {
inner: Mutex::new(inner),
nr,
}
}
}
impl<'a> NodeRefBase for NodeRefLocked<'a> {
fn common(&self) -> &NodeRefBaseCommon {
&self.nr.common
}
fn common_mut(&mut self) -> &mut NodeRefBaseCommon {
&mut self.nr.common
}
fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
{
let inner = &*self.inner.lock();
self.nr.common.entry.with(inner, f)
}
fn operate_mut<T, F>(&self, _f: F) -> T
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
{
panic!("need to locked_mut() for this operation")
}
}
impl<'a> fmt::Display for NodeRefLocked<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.nr)
}
}
impl<'a> fmt::Debug for NodeRefLocked<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeRefLocked")
.field("nr", &self.nr)
.finish()
}
}
////////////////////////////////////////////////////////////////////////////////////
/// Mutable locked reference to a routing table entry
/// For internal use inside the RoutingTable module where you have
/// already locked a RoutingTableInner
/// Keeps entry in the routing table until all references are gone
pub struct NodeRefLockedMut<'a> {
inner: Mutex<&'a mut RoutingTableInner>,
nr: NodeRef,
}
impl<'a> NodeRefLockedMut<'a> {
pub fn new(inner: &'a mut RoutingTableInner, nr: NodeRef) -> Self {
Self {
inner: Mutex::new(inner),
nr,
}
}
}
impl<'a> NodeRefBase for NodeRefLockedMut<'a> {
fn common(&self) -> &NodeRefBaseCommon {
&self.nr.common
}
fn common_mut(&mut self) -> &mut NodeRefBaseCommon {
&mut self.nr.common
}
fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
{
let inner = &*self.inner.lock();
self.nr.common.entry.with(inner, f)
}
fn operate_mut<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
{
let inner = &mut *self.inner.lock();
self.nr.common.entry.with_mut(inner, f)
}
}
impl<'a> fmt::Display for NodeRefLockedMut<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.nr)
}
}
impl<'a> fmt::Debug for NodeRefLockedMut<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeRefLockedMut")
.field("nr", &self.nr)
.finish()
}
}

View File

@ -0,0 +1,61 @@
use super::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct NodeRefFilter {
pub routing_domain_set: RoutingDomainSet,
pub dial_info_filter: DialInfoFilter,
}
impl Default for NodeRefFilter {
fn default() -> Self {
Self::new()
}
}
impl NodeRefFilter {
pub fn new() -> Self {
Self {
routing_domain_set: RoutingDomainSet::all(),
dial_info_filter: DialInfoFilter::all(),
}
}
pub fn with_routing_domain(mut self, routing_domain: RoutingDomain) -> Self {
self.routing_domain_set = routing_domain.into();
self
}
pub fn with_routing_domain_set(mut self, routing_domain_set: RoutingDomainSet) -> Self {
self.routing_domain_set = routing_domain_set;
self
}
pub fn with_dial_info_filter(mut self, dial_info_filter: DialInfoFilter) -> Self {
self.dial_info_filter = dial_info_filter;
self
}
pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type(protocol_type);
self
}
pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_protocol_type_set(protocol_set);
self
}
pub fn with_address_type(mut self, address_type: AddressType) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type(address_type);
self
}
pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self {
self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set);
self
}
pub fn filtered(mut self, other_filter: &NodeRefFilter) -> Self {
self.routing_domain_set &= other_filter.routing_domain_set;
self.dial_info_filter = self
.dial_info_filter
.filtered(&other_filter.dial_info_filter);
self
}
pub fn is_dead(&self) -> bool {
self.dial_info_filter.is_dead() || self.routing_domain_set.is_empty()
}
}

View File

@ -0,0 +1,174 @@
use super::*;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Compiled Privacy Objects
/// An encrypted private/safety route hop
#[derive(Clone, Debug)]
pub struct RouteHopData {
/// The nonce used in the encryption ENC(Xn,DH(PKn,SKapr))
pub nonce: Nonce,
/// The encrypted blob
pub blob: Vec<u8>,
}
/// How to find a route node
#[derive(Clone, Debug)]
pub enum RouteNode {
/// Route node is optimized, no contact method information as this node id has been seen before
NodeId(NodeId),
/// Route node with full contact method information to ensure the peer is reachable
PeerInfo(PeerInfo),
}
impl fmt::Display for RouteNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
RouteNode::NodeId(x) => x.key.encode(),
RouteNode::PeerInfo(pi) => pi.node_id.key.encode(),
}
)
}
}
/// An unencrypted private/safety route hop
#[derive(Clone, Debug)]
pub struct RouteHop {
/// The location of the hop
pub node: RouteNode,
/// The encrypted blob to pass to the next hop as its data (None for stubs)
pub next_hop: Option<RouteHopData>,
}
/// The kind of hops a private route can have
#[derive(Clone, Debug)]
pub enum PrivateRouteHops {
/// The first hop of a private route, unencrypted, route_hops == total hop count
FirstHop(RouteHop),
/// Private route internal node. Has > 0 private route hops left but < total hop count
Data(RouteHopData),
/// Private route has ended (hop count = 0)
Empty,
}
/// A private route for receiver privacy
#[derive(Clone, Debug)]
pub struct PrivateRoute {
/// The public key used for the entire route
pub public_key: DHTKey,
pub hop_count: u8,
pub hops: PrivateRouteHops,
}
impl PrivateRoute {
/// Empty private route is the form used when receiving the last hop
pub fn new_empty(public_key: DHTKey) -> Self {
Self {
public_key,
hop_count: 0,
hops: PrivateRouteHops::Empty,
}
}
/// Stub route is the form used when no privacy is required, but you need to specify the destination for a safety route
pub fn new_stub(public_key: DHTKey, node: RouteNode) -> Self {
Self {
public_key,
hop_count: 1,
hops: PrivateRouteHops::FirstHop(RouteHop {
node,
next_hop: None,
}),
}
}
/// Remove the first unencrypted hop if possible
pub fn pop_first_hop(&mut self) -> Option<RouteNode> {
match &mut self.hops {
PrivateRouteHops::FirstHop(first_hop) => {
let first_hop_node = first_hop.node.clone();
// Reduce hop count
if self.hop_count > 0 {
self.hop_count -= 1;
} else {
error!("hop count should not be 0 for first hop");
}
// Go to next hop
self.hops = match first_hop.next_hop.take() {
Some(rhd) => PrivateRouteHops::Data(rhd),
None => PrivateRouteHops::Empty,
};
return Some(first_hop_node);
}
PrivateRouteHops::Data(_) => return None,
PrivateRouteHops::Empty => return None,
}
}
}
impl fmt::Display for PrivateRoute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"PR({:?}+{}{})",
self.public_key,
self.hop_count,
match &self.hops {
PrivateRouteHops::FirstHop(fh) => {
format!("->{}", fh.node)
}
PrivateRouteHops::Data(_) => {
"->?".to_owned()
}
PrivateRouteHops::Empty => {
"".to_owned()
}
}
)
}
}
#[derive(Clone, Debug)]
pub enum SafetyRouteHops {
/// Has >= 1 safety route hops
Data(RouteHopData),
/// Has 0 safety route hops
Private(PrivateRoute),
}
#[derive(Clone, Debug)]
pub struct SafetyRoute {
pub public_key: DHTKey,
pub hop_count: u8,
pub hops: SafetyRouteHops,
}
impl SafetyRoute {
pub fn new_stub(public_key: DHTKey, private_route: PrivateRoute) -> Self {
assert!(matches!(private_route.hops, PrivateRouteHops::Data(_)));
Self {
public_key,
hop_count: 0,
hops: SafetyRouteHops::Private(private_route),
}
}
}
impl fmt::Display for SafetyRoute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"SR({:?}+{}{})",
self.public_key,
self.hop_count,
match &self.hops {
SafetyRouteHops::Data(_) => "".to_owned(),
SafetyRouteHops::Private(p) => format!("->{}", p),
}
)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,20 @@ use super::*;
enum RoutingDomainChange {
ClearDialInfoDetails,
ClearRelayNode,
SetRelayNode { relay_node: NodeRef },
AddDialInfoDetail { dial_info_detail: DialInfoDetail },
SetRelayNode {
relay_node: NodeRef,
},
AddDialInfoDetail {
dial_info_detail: DialInfoDetail,
},
SetupNetwork {
outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet,
},
SetNetworkClass {
network_class: Option<NetworkClass>,
},
}
pub struct RoutingDomainEditor {
@ -67,31 +79,54 @@ impl RoutingDomainEditor {
Ok(())
}
#[instrument(level = "debug", skip(self))]
pub fn setup_network(
&mut self,
outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet,
) {
self.changes.push(RoutingDomainChange::SetupNetwork {
outbound_protocols,
inbound_protocols,
address_types,
})
}
#[instrument(level = "debug", skip(self))]
pub fn set_network_class(&mut self, network_class: Option<NetworkClass>) {
self.changes
.push(RoutingDomainChange::SetNetworkClass { network_class })
}
#[instrument(level = "debug", skip(self))]
pub async fn commit(self) {
// No locking if we have nothing to do
if self.changes.is_empty() {
return;
}
let mut changed = false;
{
let node_id = self.routing_table.node_id();
let mut inner = self.routing_table.inner.write();
let inner = &mut *inner;
RoutingTable::with_routing_domain_mut(inner, self.routing_domain, |detail| {
inner.with_routing_domain_mut(self.routing_domain, |detail| {
for change in self.changes {
match change {
RoutingDomainChange::ClearDialInfoDetails => {
debug!("[{:?}] cleared dial info details", self.routing_domain);
detail.clear_dial_info_details();
detail.common_mut().clear_dial_info_details();
changed = true;
}
RoutingDomainChange::ClearRelayNode => {
debug!("[{:?}] cleared relay node", self.routing_domain);
detail.set_relay_node(None);
detail.common_mut().set_relay_node(None);
changed = true;
}
RoutingDomainChange::SetRelayNode { relay_node } => {
debug!("[{:?}] set relay node: {}", self.routing_domain, relay_node);
detail.set_relay_node(Some(relay_node));
detail.common_mut().set_relay_node(Some(relay_node));
changed = true;
}
RoutingDomainChange::AddDialInfoDetail { dial_info_detail } => {
@ -99,27 +134,85 @@ impl RoutingDomainEditor {
"[{:?}] add dial info detail: {:?}",
self.routing_domain, dial_info_detail
);
detail.add_dial_info_detail(dial_info_detail.clone());
detail
.common_mut()
.add_dial_info_detail(dial_info_detail.clone());
info!(
"{:?} Dial Info: {}",
"{:?} Dial Info: {}@{}",
self.routing_domain,
NodeDialInfo {
node_id: NodeId::new(node_id),
dial_info: dial_info_detail.dial_info
}
.to_string(),
NodeId::new(node_id),
dial_info_detail.dial_info
);
changed = true;
}
RoutingDomainChange::SetupNetwork {
outbound_protocols,
inbound_protocols,
address_types,
} => {
let old_outbound_protocols = detail.common().outbound_protocols();
let old_inbound_protocols = detail.common().inbound_protocols();
let old_address_types = detail.common().address_types();
let this_changed = old_outbound_protocols != outbound_protocols
|| old_inbound_protocols != inbound_protocols
|| old_address_types != address_types;
debug!(
"[{:?}] setup network: {:?} {:?} {:?}",
self.routing_domain,
outbound_protocols,
inbound_protocols,
address_types
);
detail.common_mut().setup_network(
outbound_protocols,
inbound_protocols,
address_types,
);
if this_changed {
changed = true;
}
}
RoutingDomainChange::SetNetworkClass { network_class } => {
let old_network_class = detail.common().network_class();
let this_changed = old_network_class != network_class;
debug!(
"[{:?}] set network class: {:?}",
self.routing_domain, network_class,
);
detail.common_mut().set_network_class(network_class);
if this_changed {
changed = true;
}
}
}
}
if changed {
// Clear our 'peer info' cache, the peerinfo for this routing domain will get regenerated next time it is asked for
detail.common_mut().clear_cache()
}
});
if changed {
RoutingTable::reset_all_seen_our_node_info(inner, self.routing_domain);
RoutingTable::reset_all_updated_since_last_network_change(inner);
// Mark that nothing in the routing table has seen our new node info
inner.reset_all_seen_our_node_info(self.routing_domain);
//
inner.reset_all_updated_since_last_network_change();
}
}
// Clear the routespecstore cache if our PublicInternet dial info has changed
if changed {
if self.routing_domain == RoutingDomain::PublicInternet {
let rss = self.routing_table.route_spec_store();
rss.reset();
}
}
// Send our updated node info to all the nodes in the routing table
if changed && self.send_node_info_updates {
let network_manager = self.routing_table.unlocked_inner.network_manager.clone();
network_manager

View File

@ -1,62 +1,422 @@
use super::*;
/// Mechanism required to contact another node
#[derive(Clone, Debug)]
pub enum ContactMethod {
/// Node is not reachable by any means
Unreachable,
/// Connection should have already existed
Existing,
/// Contact the node directly
Direct(DialInfo),
/// Request via signal the node connect back directly (relay, target)
SignalReverse(DHTKey, DHTKey),
/// Request via signal the node negotiate a hole punch (relay, target_node)
SignalHolePunch(DHTKey, DHTKey),
/// Must use an inbound relay to reach the node
InboundRelay(DHTKey),
/// Must use outbound relay to reach the node
OutboundRelay(DHTKey),
}
#[derive(Debug)]
pub struct RoutingDomainDetailCommon {
routing_domain: RoutingDomain,
network_class: Option<NetworkClass>,
outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet,
relay_node: Option<NodeRef>,
dial_info_details: Vec<DialInfoDetail>,
// caches
cached_peer_info: Mutex<Option<PeerInfo>>,
}
impl RoutingDomainDetailCommon {
pub fn new(routing_domain: RoutingDomain) -> Self {
Self {
routing_domain,
network_class: Default::default(),
outbound_protocols: Default::default(),
inbound_protocols: Default::default(),
address_types: Default::default(),
relay_node: Default::default(),
dial_info_details: Default::default(),
cached_peer_info: Mutex::new(Default::default()),
}
}
// Set from network manager
pub(super) fn setup_network(
&mut self,
outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet,
) {
self.outbound_protocols = outbound_protocols;
self.inbound_protocols = inbound_protocols;
self.address_types = address_types;
self.clear_cache();
}
pub(super) fn set_network_class(&mut self, network_class: Option<NetworkClass>) {
self.network_class = network_class;
self.clear_cache();
}
pub fn network_class(&self) -> Option<NetworkClass> {
self.network_class
}
pub fn outbound_protocols(&self) -> ProtocolTypeSet {
self.outbound_protocols
}
pub fn inbound_protocols(&self) -> ProtocolTypeSet {
self.inbound_protocols
}
pub fn address_types(&self) -> AddressTypeSet {
self.address_types
}
pub fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone()
}
pub(super) fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
self.relay_node = opt_relay_node.map(|nr| {
nr.filtered_clone(NodeRefFilter::new().with_routing_domain(self.routing_domain))
});
self.clear_cache();
}
pub fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
&self.dial_info_details
}
pub(super) fn clear_dial_info_details(&mut self) {
self.dial_info_details.clear();
self.clear_cache();
}
pub(super) fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
self.dial_info_details.push(did);
self.dial_info_details.sort();
self.clear_cache();
}
pub fn has_valid_own_node_info(&self) -> bool {
self.network_class.unwrap_or(NetworkClass::Invalid) != NetworkClass::Invalid
}
fn make_peer_info(&self, rti: &RoutingTableInner) -> PeerInfo {
let node_info = NodeInfo {
network_class: self.network_class.unwrap_or(NetworkClass::Invalid),
outbound_protocols: self.outbound_protocols,
address_types: self.address_types,
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
dial_info_detail_list: self.dial_info_details.clone(),
};
let relay_info = self
.relay_node
.as_ref()
.and_then(|rn| {
let opt_relay_pi = rn.locked(rti).make_peer_info(self.routing_domain);
if let Some(relay_pi) = opt_relay_pi {
match relay_pi.signed_node_info {
SignedNodeInfo::Direct(d) => Some((relay_pi.node_id, d)),
SignedNodeInfo::Relayed(_) => {
warn!("relay node should not have a relay itself! if this happens, a relay updated its signed node info and became a relay, which should cause the relay to be dropped");
None
},
}
} else {
None
}
});
let signed_node_info = match relay_info {
Some((relay_id, relay_sdni)) => SignedNodeInfo::Relayed(
SignedRelayedNodeInfo::with_secret(
NodeId::new(rti.unlocked_inner.node_id),
node_info,
relay_id,
relay_sdni,
&rti.unlocked_inner.node_id_secret,
)
.unwrap(),
),
None => SignedNodeInfo::Direct(
SignedDirectNodeInfo::with_secret(
NodeId::new(rti.unlocked_inner.node_id),
node_info,
&rti.unlocked_inner.node_id_secret,
)
.unwrap()
),
};
PeerInfo::new(NodeId::new(rti.unlocked_inner.node_id), signed_node_info)
}
pub fn with_peer_info<F, R>(&self, rti: &RoutingTableInner, f: F) -> R
where
F: FnOnce(&PeerInfo) -> R,
{
let mut cpi = self.cached_peer_info.lock();
if cpi.is_none() {
// Regenerate peer info
let pi = self.make_peer_info(rti);
// Cache the peer info
*cpi = Some(pi);
}
f(cpi.as_ref().unwrap())
}
pub fn inbound_dial_info_filter(&self) -> DialInfoFilter {
DialInfoFilter::all()
.with_protocol_type_set(self.inbound_protocols)
.with_address_type_set(self.address_types)
}
pub fn outbound_dial_info_filter(&self) -> DialInfoFilter {
DialInfoFilter::all()
.with_protocol_type_set(self.outbound_protocols)
.with_address_type_set(self.address_types)
}
pub(super) fn clear_cache(&self) {
*self.cached_peer_info.lock() = None;
}
}
/// General trait for all routing domains
pub trait RoutingDomainDetail {
// Common accessors
fn common(&self) -> &RoutingDomainDetailCommon;
fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon;
/// Can this routing domain contain a particular address
fn can_contain_address(&self, address: Address) -> bool;
fn relay_node(&self) -> Option<NodeRef>;
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>);
fn dial_info_details(&self) -> &Vec<DialInfoDetail>;
fn clear_dial_info_details(&mut self);
fn add_dial_info_detail(&mut self, did: DialInfoDetail);
/// Get the contact method required for node A to reach node B in this routing domain
/// Routing table must be locked for reading to use this function
fn get_contact_method(
&self,
rti: &RoutingTableInner,
peer_a: &PeerInfo,
peer_b: &PeerInfo,
dial_info_filter: DialInfoFilter,
sequencing: Sequencing,
) -> ContactMethod;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Public Internet routing domain internals
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct PublicInternetRoutingDomainDetail {
/// An optional node we relay through for this domain
relay_node: Option<NodeRef>,
/// The dial infos on this domain we can be reached by
dial_info_details: Vec<DialInfoDetail>,
/// Common implementation for all routing domains
common: RoutingDomainDetailCommon,
}
impl Default for PublicInternetRoutingDomainDetail {
fn default() -> Self {
Self {
common: RoutingDomainDetailCommon::new(RoutingDomain::PublicInternet),
}
}
}
fn first_filtered_dial_info_detail(
from_node: &NodeInfo,
to_node: &NodeInfo,
dial_info_filter: &DialInfoFilter,
sequencing: Sequencing,
) -> Option<DialInfoDetail> {
let dial_info_filter = dial_info_filter.clone().filtered(
&DialInfoFilter::all()
.with_address_type_set(from_node.address_types)
.with_protocol_type_set(from_node.outbound_protocols),
);
// Get first filtered dialinfo
let (sort, dial_info_filter) = match sequencing {
Sequencing::NoPreference => (None, dial_info_filter),
Sequencing::PreferOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter,
),
Sequencing::EnsureOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter.filtered(
&DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
),
),
};
// If the filter is dead then we won't be able to connect
if dial_info_filter.is_dead() {
return None;
}
let direct_filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
// Get the best match dial info for node B if we have it
to_node.first_filtered_dial_info_detail(sort, direct_filter)
}
impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
fn common(&self) -> &RoutingDomainDetailCommon {
&self.common
}
fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon {
&mut self.common
}
fn can_contain_address(&self, address: Address) -> bool {
address.is_global()
}
fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone()
}
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
self.relay_node = opt_relay_node.map(|nr| {
nr.filtered_clone(
NodeRefFilter::new().with_routing_domain(RoutingDomain::PublicInternet),
fn get_contact_method(
&self,
_rti: &RoutingTableInner,
peer_a: &PeerInfo,
peer_b: &PeerInfo,
dial_info_filter: DialInfoFilter,
sequencing: Sequencing,
) -> ContactMethod {
// Get the nodeinfos for convenience
let node_a = peer_a.signed_node_info.node_info();
let node_b = peer_b.signed_node_info.node_info();
// Get the best match dial info for node B if we have it
if let Some(target_did) =
first_filtered_dial_info_detail(node_a, node_b, &dial_info_filter, sequencing)
{
// Do we need to signal before going inbound?
if !target_did.class.requires_signal() {
// Go direct without signaling
return ContactMethod::Direct(target_did.dial_info);
}
// Get the target's inbound relay, it must have one or it is not reachable
if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
// Note that relay_peer_info could be node_a, in which case a connection already exists
// and we shouldn't have even gotten here
if node_b_relay_id.key == peer_a.node_id.key {
return ContactMethod::Existing;
}
// Can node A reach the inbound relay directly?
if first_filtered_dial_info_detail(
node_a,
node_b_relay,
&dial_info_filter,
sequencing,
)
.is_some()
{
// Can node A receive anything inbound ever?
if matches!(node_a.network_class, NetworkClass::InboundCapable) {
///////// Reverse connection
// Get the best match dial info for an reverse inbound connection from node B to node A
if let Some(reverse_did) = first_filtered_dial_info_detail(
node_b,
node_a,
&dial_info_filter,
sequencing,
) {
// Ensure we aren't on the same public IP address (no hairpin nat)
if reverse_did.dial_info.to_ip_addr()
!= target_did.dial_info.to_ip_addr()
{
// Can we receive a direct reverse connection?
if !reverse_did.class.requires_signal() {
return ContactMethod::SignalReverse(
node_b_relay_id.key,
peer_b.node_id.key,
);
}
}
}
///////// UDP hole-punch
// Does node B have a direct udp dialinfo node A can reach?
let udp_dial_info_filter = dial_info_filter
.clone()
.filtered(&DialInfoFilter::all().with_protocol_type(ProtocolType::UDP));
if let Some(target_udp_did) = first_filtered_dial_info_detail(
node_a,
node_b,
&udp_dial_info_filter,
sequencing,
) {
// Does node A have a direct udp dialinfo that node B can reach?
if let Some(reverse_udp_did) = first_filtered_dial_info_detail(
node_b,
node_a,
&udp_dial_info_filter,
sequencing,
) {
// Ensure we aren't on the same public IP address (no hairpin nat)
if reverse_udp_did.dial_info.to_ip_addr()
!= target_udp_did.dial_info.to_ip_addr()
{
// The target and ourselves have a udp dialinfo that they can reach
return ContactMethod::SignalHolePunch(
node_b_relay_id.key,
peer_a.node_id.key,
);
}
}
}
// Otherwise we have to inbound relay
}
return ContactMethod::InboundRelay(node_b_relay_id.key);
}
}
}
// If the node B has no direct dial info, it needs to have an inbound relay
else if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
// Can we reach the full relay?
if first_filtered_dial_info_detail(
node_a,
&node_b_relay,
&dial_info_filter,
sequencing,
)
})
}
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
&self.dial_info_details
}
fn clear_dial_info_details(&mut self) {
self.dial_info_details.clear();
}
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
self.dial_info_details.push(did);
self.dial_info_details.sort();
.is_some()
{
return ContactMethod::InboundRelay(node_b_relay_id.key);
}
}
// If node A can't reach the node by other means, it may need to use its own relay
if let Some(node_a_relay_id) = peer_a.signed_node_info.relay_id() {
return ContactMethod::OutboundRelay(node_a_relay_id.key);
}
ContactMethod::Unreachable
}
}
/// Local Network routing domain internals
#[derive(Debug, Default)]
pub struct LocalInternetRoutingDomainDetail {
/// An optional node we relay through for this domain
relay_node: Option<NodeRef>,
/// The dial infos on this domain we can be reached by
dial_info_details: Vec<DialInfoDetail>,
#[derive(Debug)]
pub struct LocalNetworkRoutingDomainDetail {
/// The local networks this domain will communicate with
local_networks: Vec<(IpAddr, IpAddr)>,
/// Common implementation for all routing domains
common: RoutingDomainDetailCommon,
}
impl LocalInternetRoutingDomainDetail {
impl Default for LocalNetworkRoutingDomainDetail {
fn default() -> Self {
Self {
local_networks: Default::default(),
common: RoutingDomainDetailCommon::new(RoutingDomain::LocalNetwork),
}
}
}
impl LocalNetworkRoutingDomainDetail {
pub fn set_local_networks(&mut self, mut local_networks: Vec<(IpAddr, IpAddr)>) -> bool {
local_networks.sort();
if local_networks == self.local_networks {
@ -67,7 +427,13 @@ impl LocalInternetRoutingDomainDetail {
}
}
impl RoutingDomainDetail for LocalInternetRoutingDomainDetail {
impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
fn common(&self) -> &RoutingDomainDetailCommon {
&self.common
}
fn common_mut(&mut self) -> &mut RoutingDomainDetailCommon {
&mut self.common
}
fn can_contain_address(&self, address: Address) -> bool {
let ip = address.to_ip_addr();
for localnet in &self.local_networks {
@ -77,22 +443,48 @@ impl RoutingDomainDetail for LocalInternetRoutingDomainDetail {
}
false
}
fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone()
}
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
self.relay_node = opt_relay_node.map(|nr| {
nr.filtered_clone(NodeRefFilter::new().with_routing_domain(RoutingDomain::LocalNetwork))
});
}
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
&self.dial_info_details
}
fn clear_dial_info_details(&mut self) {
self.dial_info_details.clear();
}
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
self.dial_info_details.push(did);
self.dial_info_details.sort();
fn get_contact_method(
&self,
_rti: &RoutingTableInner,
peer_a: &PeerInfo,
peer_b: &PeerInfo,
dial_info_filter: DialInfoFilter,
sequencing: Sequencing,
) -> ContactMethod {
// Scope the filter down to protocols node A can do outbound
let dial_info_filter = dial_info_filter.filtered(
&DialInfoFilter::all()
.with_address_type_set(peer_a.signed_node_info.node_info().address_types)
.with_protocol_type_set(peer_a.signed_node_info.node_info().outbound_protocols),
);
// Get first filtered dialinfo
let (sort, dial_info_filter) = match sequencing {
Sequencing::NoPreference => (None, dial_info_filter),
Sequencing::PreferOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter,
),
Sequencing::EnsureOrdered => (
Some(DialInfoDetail::ordered_sequencing_sort),
dial_info_filter.filtered(
&DialInfoFilter::all().with_protocol_type_set(ProtocolType::all_ordered_set()),
),
),
};
// If the filter is dead then we won't be able to connect
if dial_info_filter.is_dead() {
return ContactMethod::Unreachable;
}
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
let opt_target_did = peer_b.signed_node_info.node_info().first_filtered_dial_info_detail(sort, filter);
if let Some(target_did) = opt_target_did {
return ContactMethod::Direct(target_did.dial_info);
}
ContactMethod::Unreachable
}
}

View File

@ -0,0 +1,999 @@
use super::*;
const RECENT_PEERS_TABLE_SIZE: usize = 64;
//////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, Copy)]
pub struct RecentPeersEntry {
pub last_connection: ConnectionDescriptor,
}
/// RoutingTable rwlock-internal data
pub struct RoutingTableInner {
/// Extra pointer to unlocked members to simplify access
pub(super) unlocked_inner: Arc<RoutingTableUnlockedInner>,
/// Routing table buckets that hold entries
pub(super) buckets: Vec<Bucket>,
/// A fast counter for the number of entries in the table, total
pub(super) bucket_entry_count: usize,
/// The public internet routing domain
pub(super) public_internet_routing_domain: PublicInternetRoutingDomainDetail,
/// The dial info we use on the local network
pub(super) local_network_routing_domain: LocalNetworkRoutingDomainDetail,
/// Interim accounting mechanism for this node's RPC latency to any other node
pub(super) self_latency_stats_accounting: LatencyStatsAccounting,
/// Interim accounting mechanism for the total bandwidth to/from this node
pub(super) self_transfer_stats_accounting: TransferStatsAccounting,
/// Statistics about the total bandwidth to/from this node
pub(super) self_transfer_stats: TransferStatsDownUp,
/// Peers we have recently communicated with
pub(super) recent_peers: LruCache<DHTKey, RecentPeersEntry>,
/// Storage for private/safety RouteSpecs
pub(super) route_spec_store: Option<RouteSpecStore>,
}
impl RoutingTableInner {
pub(super) fn new(unlocked_inner: Arc<RoutingTableUnlockedInner>) -> RoutingTableInner {
RoutingTableInner {
unlocked_inner,
buckets: Vec::new(),
public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(),
local_network_routing_domain: LocalNetworkRoutingDomainDetail::default(),
bucket_entry_count: 0,
self_latency_stats_accounting: LatencyStatsAccounting::new(),
self_transfer_stats_accounting: TransferStatsAccounting::new(),
self_transfer_stats: TransferStatsDownUp::default(),
recent_peers: LruCache::new(RECENT_PEERS_TABLE_SIZE),
route_spec_store: None,
}
}
pub fn network_manager(&self) -> NetworkManager {
self.unlocked_inner.network_manager.clone()
}
pub fn rpc_processor(&self) -> RPCProcessor {
self.network_manager().rpc_processor()
}
pub fn node_id(&self) -> DHTKey {
self.unlocked_inner.node_id
}
pub fn node_id_secret(&self) -> DHTKeySecret {
self.unlocked_inner.node_id_secret
}
pub fn config(&self) -> VeilidConfig {
self.unlocked_inner.config.clone()
}
pub fn transfer_stats_accounting(&mut self) -> &mut TransferStatsAccounting {
&mut self.self_transfer_stats_accounting
}
pub fn latency_stats_accounting(&mut self) -> &mut LatencyStatsAccounting {
&mut self.self_latency_stats_accounting
}
pub fn routing_domain_for_address(&self, address: Address) -> Option<RoutingDomain> {
for rd in RoutingDomain::all() {
let can_contain = self.with_routing_domain(rd, |rdd| rdd.can_contain_address(address));
if can_contain {
return Some(rd);
}
}
None
}
pub fn with_routing_domain<F, R>(&self, domain: RoutingDomain, f: F) -> R
where
F: FnOnce(&dyn RoutingDomainDetail) -> R,
{
match domain {
RoutingDomain::PublicInternet => f(&self.public_internet_routing_domain),
RoutingDomain::LocalNetwork => f(&self.local_network_routing_domain),
}
}
pub fn with_routing_domain_mut<F, R>(&mut self, domain: RoutingDomain, f: F) -> R
where
F: FnOnce(&mut dyn RoutingDomainDetail) -> R,
{
match domain {
RoutingDomain::PublicInternet => f(&mut self.public_internet_routing_domain),
RoutingDomain::LocalNetwork => f(&mut self.local_network_routing_domain),
}
}
pub fn relay_node(&self, domain: RoutingDomain) -> Option<NodeRef> {
self.with_routing_domain(domain, |rd| rd.common().relay_node())
}
pub fn has_dial_info(&self, domain: RoutingDomain) -> bool {
self.with_routing_domain(domain, |rd| !rd.common().dial_info_details().is_empty())
}
pub fn dial_info_details(&self, domain: RoutingDomain) -> Vec<DialInfoDetail> {
self.with_routing_domain(domain, |rd| rd.common().dial_info_details().clone())
}
pub fn first_filtered_dial_info_detail(
&self,
routing_domain_set: RoutingDomainSet,
filter: &DialInfoFilter,
) -> Option<DialInfoDetail> {
for routing_domain in routing_domain_set {
let did = self.with_routing_domain(routing_domain, |rd| {
for did in rd.common().dial_info_details() {
if did.matches_filter(filter) {
return Some(did.clone());
}
}
None
});
if did.is_some() {
return did;
}
}
None
}
pub fn all_filtered_dial_info_details(
&self,
routing_domain_set: RoutingDomainSet,
filter: &DialInfoFilter,
) -> Vec<DialInfoDetail> {
let mut ret = Vec::new();
for routing_domain in routing_domain_set {
self.with_routing_domain(routing_domain, |rd| {
for did in rd.common().dial_info_details() {
if did.matches_filter(filter) {
ret.push(did.clone());
}
}
});
}
ret.remove_duplicates();
ret
}
pub fn ensure_dial_info_is_valid(&self, domain: RoutingDomain, dial_info: &DialInfo) -> bool {
let address = dial_info.socket_address().address();
let can_contain_address =
self.with_routing_domain(domain, |rd| rd.can_contain_address(address));
if !can_contain_address {
log_rtab!(debug "can not add dial info to this routing domain");
return false;
}
if !dial_info.is_valid() {
log_rtab!(debug
"shouldn't be registering invalid addresses: {:?}",
dial_info
);
return false;
}
true
}
pub fn node_info_is_valid_in_routing_domain(
&self,
routing_domain: RoutingDomain,
node_info: &NodeInfo,
) -> bool {
// Should not be passing around nodeinfo with an invalid network class
if matches!(node_info.network_class, NetworkClass::Invalid) {
return false;
}
// Ensure all of the dial info works in this routing domain
for did in &node_info.dial_info_detail_list {
if !self.ensure_dial_info_is_valid(routing_domain, &did.dial_info) {
return false;
}
}
true
}
pub fn signed_node_info_is_valid_in_routing_domain(
&self,
routing_domain: RoutingDomain,
signed_node_info: &SignedNodeInfo,
) -> bool {
if !self.node_info_is_valid_in_routing_domain(routing_domain, signed_node_info.node_info())
{
return false;
}
// Ensure the relay is also valid in this routing domain if it is provided
if let Some(relay_ni) = signed_node_info.relay_info() {
if !self.node_info_is_valid_in_routing_domain(routing_domain, relay_ni) {
return false;
}
}
true
}
#[instrument(level = "trace", skip(self), ret)]
pub fn get_contact_method(
&self,
routing_domain: RoutingDomain,
peer_a: &PeerInfo,
peer_b: &PeerInfo,
dial_info_filter: DialInfoFilter,
sequencing: Sequencing,
) -> ContactMethod {
self.with_routing_domain(routing_domain, |rdd| {
rdd.get_contact_method(self, peer_a, peer_b, dial_info_filter, sequencing)
})
}
pub fn reset_all_seen_our_node_info(&mut self, routing_domain: RoutingDomain) {
let cur_ts = intf::get_timestamp();
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, v| {
v.with_mut(rti, |_rti, e| {
e.set_seen_our_node_info(routing_domain, false);
});
Option::<()>::None
});
}
pub fn reset_all_updated_since_last_network_change(&mut self) {
let cur_ts = intf::get_timestamp();
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, v| {
v.with_mut(rti, |_rti, e| {
e.set_updated_since_last_network_change(false)
});
Option::<()>::None
});
}
/// Return a copy of our node's peerinfo
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
self.with_routing_domain(routing_domain, |rdd| {
rdd.common().with_peer_info(self, |pi| pi.clone())
})
}
/// Return our currently registered network class
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
self.with_routing_domain(routing_domain, |rdd| rdd.common().has_valid_own_node_info())
}
/// Return the domain's currently registered network class
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
self.with_routing_domain(routing_domain, |rdd| rdd.common().network_class())
}
/// Return the domain's filter for what we can receivein the form of a dial info filter
pub fn get_inbound_dial_info_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
self.with_routing_domain(routing_domain, |rdd| {
rdd.common().inbound_dial_info_filter()
})
}
/// Return the domain's filter for what we can receive in the form of a node ref filter
pub fn get_inbound_node_ref_filter(&self, routing_domain: RoutingDomain) -> NodeRefFilter {
let dif = self.get_inbound_dial_info_filter(routing_domain);
NodeRefFilter::new()
.with_routing_domain(routing_domain)
.with_dial_info_filter(dif)
}
/// Return the domain's filter for what we can send out in the form of a dial info filter
pub fn get_outbound_dial_info_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
self.with_routing_domain(routing_domain, |rdd| {
rdd.common().outbound_dial_info_filter()
})
}
/// Return the domain's filter for what we can receive in the form of a node ref filter
pub fn get_outbound_node_ref_filter(&self, routing_domain: RoutingDomain) -> NodeRefFilter {
let dif = self.get_outbound_dial_info_filter(routing_domain);
NodeRefFilter::new()
.with_routing_domain(routing_domain)
.with_dial_info_filter(dif)
}
fn bucket_depth(index: usize) -> usize {
match index {
0 => 256,
1 => 128,
2 => 64,
3 => 32,
4 => 16,
5 => 8,
6 => 4,
7 => 4,
8 => 4,
9 => 4,
_ => 4,
}
}
pub fn init_buckets(&mut self, routing_table: RoutingTable) {
// Size the buckets (one per bit)
self.buckets.clear();
self.buckets.reserve(DHT_KEY_LENGTH * 8);
for _ in 0..DHT_KEY_LENGTH * 8 {
let bucket = Bucket::new(routing_table.clone());
self.buckets.push(bucket);
}
}
pub fn configure_local_network_routing_domain(
&mut self,
local_networks: Vec<(IpAddr, IpAddr)>,
) {
log_net!(debug "configure_local_network_routing_domain: {:#?}", local_networks);
let changed = self
.local_network_routing_domain
.set_local_networks(local_networks);
// If the local network topology has changed, nuke the existing local node info and let new local discovery happen
if changed {
let cur_ts = intf::get_timestamp();
self.with_entries_mut(cur_ts, BucketEntryState::Dead, |rti, _, e| {
e.with_mut(rti, |_rti, e| {
e.clear_signed_node_info(RoutingDomain::LocalNetwork);
e.set_seen_our_node_info(RoutingDomain::LocalNetwork, false);
e.set_updated_since_last_network_change(false);
});
Option::<()>::None
});
}
}
/// Attempt to empty the routing table
/// should only be performed when there are no node_refs (detached)
pub fn purge_buckets(&mut self) {
log_rtab!(
"Starting routing table buckets purge. Table currently has {} nodes",
self.bucket_entry_count
);
for bucket in &mut self.buckets {
bucket.kick(0);
}
log_rtab!(debug
"Routing table buckets purge complete. Routing table now has {} nodes",
self.bucket_entry_count
);
}
/// Attempt to remove last_connections from entries
pub fn purge_last_connections(&mut self) {
log_rtab!(
"Starting routing table last_connections purge. Table currently has {} nodes",
self.bucket_entry_count
);
for bucket in &self.buckets {
for entry in bucket.entries() {
entry.1.with_mut_inner(|e| {
e.clear_last_connections();
});
}
}
log_rtab!(debug
"Routing table last_connections purge complete. Routing table now has {} nodes",
self.bucket_entry_count
);
}
/// Attempt to settle buckets and remove entries down to the desired number
/// which may not be possible due extant NodeRefs
pub fn kick_bucket(&mut self, idx: usize) {
let bucket = &mut self.buckets[idx];
let bucket_depth = Self::bucket_depth(idx);
if let Some(dead_node_ids) = bucket.kick(bucket_depth) {
// Remove counts
self.bucket_entry_count -= dead_node_ids.len();
log_rtab!(debug "Routing table now has {} nodes", self.bucket_entry_count);
// Now purge the routing table inner vectors
//let filter = |k: &DHTKey| dead_node_ids.contains(k);
//inner.closest_reliable_nodes.retain(filter);
//inner.fastest_reliable_nodes.retain(filter);
//inner.closest_nodes.retain(filter);
//inner.fastest_nodes.retain(filter);
}
}
pub fn find_bucket_index(&self, node_id: DHTKey) -> usize {
distance(&node_id, &self.unlocked_inner.node_id)
.first_nonzero_bit()
.unwrap()
}
pub fn get_entry_count(
&self,
routing_domain_set: RoutingDomainSet,
min_state: BucketEntryState,
) -> usize {
let mut count = 0usize;
let cur_ts = intf::get_timestamp();
self.with_entries(cur_ts, min_state, |rti, _, e| {
if e.with(rti, |_rti, e| e.best_routing_domain(routing_domain_set))
.is_some()
{
count += 1;
}
Option::<()>::None
});
count
}
pub fn with_entries<T, F: FnMut(&RoutingTableInner, DHTKey, Arc<BucketEntry>) -> Option<T>>(
&self,
cur_ts: u64,
min_state: BucketEntryState,
mut f: F,
) -> Option<T> {
let mut entryvec = Vec::with_capacity(self.bucket_entry_count);
for bucket in &self.buckets {
for entry in bucket.entries() {
if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
entryvec.push((*entry.0, entry.1.clone()));
}
}
}
for entry in entryvec {
if let Some(out) = f(self, entry.0, entry.1) {
return Some(out);
}
}
None
}
pub fn with_entries_mut<
T,
F: FnMut(&mut RoutingTableInner, DHTKey, Arc<BucketEntry>) -> Option<T>,
>(
&mut self,
cur_ts: u64,
min_state: BucketEntryState,
mut f: F,
) -> Option<T> {
let mut entryvec = Vec::with_capacity(self.bucket_entry_count);
for bucket in &self.buckets {
for entry in bucket.entries() {
if entry.1.with(self, |_rti, e| e.state(cur_ts) >= min_state) {
entryvec.push((*entry.0, entry.1.clone()));
}
}
}
for entry in entryvec {
if let Some(out) = f(self, entry.0, entry.1) {
return Some(out);
}
}
None
}
pub fn get_nodes_needing_updates(
&self,
outer_self: RoutingTable,
routing_domain: RoutingDomain,
cur_ts: u64,
all: bool,
) -> Vec<NodeRef> {
let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count);
self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
// Only update nodes that haven't seen our node info yet
if all || !v.with(rti, |_rti, e| e.has_seen_our_node_info(routing_domain)) {
node_refs.push(NodeRef::new(
outer_self.clone(),
k,
v,
Some(NodeRefFilter::new().with_routing_domain(routing_domain)),
));
}
Option::<()>::None
});
node_refs
}
pub fn get_nodes_needing_ping(
&self,
outer_self: RoutingTable,
routing_domain: RoutingDomain,
cur_ts: u64,
) -> Vec<NodeRef> {
// Collect relay nodes
let opt_relay_id = self.with_routing_domain(routing_domain, |rd| {
rd.common().relay_node().map(|rn| rn.node_id())
});
// Collect all entries that are 'needs_ping' and have some node info making them reachable somehow
let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count);
self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
if v.with(rti, |_rti, e| {
e.has_node_info(routing_domain.into())
&& e.needs_ping(cur_ts, opt_relay_id == Some(k))
}) {
node_refs.push(NodeRef::new(
outer_self.clone(),
k,
v,
Some(NodeRefFilter::new().with_routing_domain(routing_domain)),
));
}
Option::<()>::None
});
node_refs
}
pub fn get_all_nodes(&self, outer_self: RoutingTable, cur_ts: u64) -> Vec<NodeRef> {
let mut node_refs = Vec::<NodeRef>::with_capacity(self.bucket_entry_count);
self.with_entries(cur_ts, BucketEntryState::Unreliable, |_rti, k, v| {
node_refs.push(NodeRef::new(outer_self.clone(), k, v, None));
Option::<()>::None
});
node_refs
}
/// Create a node reference, possibly creating a bucket entry
/// the 'update_func' closure is called on the node, and, if created,
/// in a locked fashion as to ensure the bucket entry state is always valid
pub fn create_node_ref<F>(
&mut self,
outer_self: RoutingTable,
node_id: DHTKey,
update_func: F,
) -> Option<NodeRef>
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner),
{
// Ensure someone isn't trying register this node itself
if node_id == self.node_id() {
log_rtab!(debug "can't register own node");
return None;
}
// Look up existing entry
let idx = self.find_bucket_index(node_id);
let noderef = {
let bucket = &self.buckets[idx];
let entry = bucket.entry(&node_id);
entry.map(|e| NodeRef::new(outer_self.clone(), node_id, e, None))
};
// If one doesn't exist, insert into bucket, possibly evicting a bucket member
let noderef = match noderef {
None => {
// Make new entry
self.bucket_entry_count += 1;
let cnt = self.bucket_entry_count;
let bucket = &mut self.buckets[idx];
let nr = bucket.add_entry(node_id);
// Update the entry
let entry = bucket.entry(&node_id).unwrap();
entry.with_mut(self, update_func);
// Kick the bucket
self.unlocked_inner.kick_queue.lock().insert(idx);
log_rtab!(debug "Routing table now has {} nodes, {} live", cnt, self.get_entry_count(RoutingDomainSet::all(), BucketEntryState::Unreliable));
nr
}
Some(nr) => {
// Update the entry
let bucket = &mut self.buckets[idx];
let entry = bucket.entry(&node_id).unwrap();
entry.with_mut(self, update_func);
nr
}
};
Some(noderef)
}
/// Resolve an existing routing table entry and return a reference to it
pub fn lookup_node_ref(&self, outer_self: RoutingTable, node_id: DHTKey) -> Option<NodeRef> {
if node_id == self.unlocked_inner.node_id {
log_rtab!(error "can't look up own node id in routing table");
return None;
}
let idx = self.find_bucket_index(node_id);
let bucket = &self.buckets[idx];
bucket
.entry(&node_id)
.map(|e| NodeRef::new(outer_self, node_id, e, None))
}
/// Resolve an existing routing table entry and return a filtered reference to it
pub fn lookup_and_filter_noderef(
&self,
outer_self: RoutingTable,
node_id: DHTKey,
routing_domain_set: RoutingDomainSet,
dial_info_filter: DialInfoFilter,
) -> Option<NodeRef> {
let nr = self.lookup_node_ref(outer_self, node_id)?;
Some(
nr.filtered_clone(
NodeRefFilter::new()
.with_dial_info_filter(dial_info_filter)
.with_routing_domain_set(routing_domain_set),
),
)
}
/// Resolve an existing routing table entry and call a function on its entry without using a noderef
pub fn with_node_entry<F, R>(&self, node_id: DHTKey, f: F) -> Option<R>
where
F: FnOnce(Arc<BucketEntry>) -> R,
{
if node_id == self.unlocked_inner.node_id {
log_rtab!(error "can't look up own node id in routing table");
return None;
}
let idx = self.find_bucket_index(node_id);
let bucket = &self.buckets[idx];
if let Some(e) = bucket.entry(&node_id) {
return Some(f(e));
}
None
}
/// Shortcut function to add a node to our routing table if it doesn't exist
/// and add the dial info we have for it. Returns a noderef filtered to
/// the routing domain in which this node was registered for convenience.
pub fn register_node_with_signed_node_info(
&mut self,
outer_self: RoutingTable,
routing_domain: RoutingDomain,
node_id: DHTKey,
signed_node_info: SignedNodeInfo,
allow_invalid: bool,
) -> Option<NodeRef> {
// validate signed node info is not something malicious
if node_id == self.node_id() {
log_rtab!(debug "can't register own node id in routing table");
return None;
}
if let Some(relay_id) = signed_node_info.relay_id() {
if relay_id.key == node_id {
log_rtab!(debug "node can not be its own relay");
return None;
}
}
if !allow_invalid {
// verify signature
if !signed_node_info.has_valid_signature() {
log_rtab!(debug "signed node info for {} has invalid signature", node_id);
return None;
}
// verify signed node info is valid in this routing domain
if !self.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info)
{
log_rtab!(debug "signed node info for {} not valid in the {:?} routing domain", node_id, routing_domain);
return None;
}
}
self.create_node_ref(outer_self, node_id, |_rti, e| {
e.update_signed_node_info(routing_domain, signed_node_info);
})
.map(|mut nr| {
nr.set_filter(Some(
NodeRefFilter::new().with_routing_domain(routing_domain),
));
nr
})
}
/// Shortcut function to add a node to our routing table if it doesn't exist
/// and add the last peer address we have for it, since that's pretty common
pub fn register_node_with_existing_connection(
&mut self,
outer_self: RoutingTable,
node_id: DHTKey,
descriptor: ConnectionDescriptor,
timestamp: u64,
) -> Option<NodeRef> {
let out = self.create_node_ref(outer_self, node_id, |_rti, e| {
// this node is live because it literally just connected to us
e.touch_last_seen(timestamp);
});
if let Some(nr) = &out {
// set the most recent node address for connection finding and udp replies
nr.locked_mut(self)
.set_last_connection(descriptor, timestamp);
}
out
}
//////////////////////////////////////////////////////////////////////
// Routing Table Health Metrics
pub fn get_routing_table_health(&self) -> RoutingTableHealth {
let mut health = RoutingTableHealth::default();
let cur_ts = intf::get_timestamp();
for bucket in &self.buckets {
for (_, v) in bucket.entries() {
match v.with(self, |_rti, e| e.state(cur_ts)) {
BucketEntryState::Reliable => {
health.reliable_entry_count += 1;
}
BucketEntryState::Unreliable => {
health.unreliable_entry_count += 1;
}
BucketEntryState::Dead => {
health.dead_entry_count += 1;
}
}
}
}
health
}
pub fn touch_recent_peer(&mut self, node_id: DHTKey, last_connection: ConnectionDescriptor) {
self.recent_peers
.insert(node_id, RecentPeersEntry { last_connection });
}
//////////////////////////////////////////////////////////////////////
// Find Nodes
// Retrieve the fastest nodes in the routing table matching an entry filter
pub fn find_fast_public_nodes_filtered(
&self,
outer_self: RoutingTable,
node_count: usize,
mut filters: VecDeque<RoutingTableEntryFilter>,
) -> Vec<NodeRef> {
let public_node_filter = Box::new(
|rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap();
entry.with(rti, |_rti, e| {
// skip nodes on local network
if e.node_info(RoutingDomain::LocalNetwork).is_some() {
return false;
}
// skip nodes not on public internet
if e.node_info(RoutingDomain::PublicInternet).is_none() {
return false;
}
true
})
},
) as RoutingTableEntryFilter;
filters.push_front(public_node_filter);
self.find_fastest_nodes(
node_count,
filters,
|_rti: &RoutingTableInner, k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(outer_self.clone(), k, v.unwrap().clone(), None)
},
)
}
pub fn filter_has_valid_signed_node_info(
&self,
routing_domain: RoutingDomain,
has_valid_own_node_info: bool,
v: Option<Arc<BucketEntry>>,
) -> bool {
match v {
None => has_valid_own_node_info,
Some(entry) => entry.with(self, |_rti, e| {
e.signed_node_info(routing_domain.into())
.map(|sni| sni.has_valid_signature())
.unwrap_or(false)
}),
}
}
pub fn transform_to_peer_info(
&self,
routing_domain: RoutingDomain,
own_peer_info: PeerInfo,
k: DHTKey,
v: Option<Arc<BucketEntry>>,
) -> PeerInfo {
match v {
None => own_peer_info,
Some(entry) => entry.with(self, |_rti, e| e.make_peer_info(k, routing_domain).unwrap()),
}
}
pub fn find_peers_with_sort_and_filter<C, T, O>(
&self,
node_count: usize,
cur_ts: u64,
mut filters: VecDeque<RoutingTableEntryFilter>,
mut compare: C,
mut transform: T,
) -> Vec<O>
where
C: for<'a, 'b> FnMut(
&'a RoutingTableInner,
&'b (DHTKey, Option<Arc<BucketEntry>>),
&'b (DHTKey, Option<Arc<BucketEntry>>),
) -> core::cmp::Ordering,
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O,
{
// collect all the nodes for sorting
let mut nodes =
Vec::<(DHTKey, Option<Arc<BucketEntry>>)>::with_capacity(self.bucket_entry_count + 1);
// add our own node (only one of there with the None entry)
let mut filtered = false;
for filter in &mut filters {
if !filter(self, self.unlocked_inner.node_id, None) {
filtered = true;
break;
}
}
if !filtered {
nodes.push((self.unlocked_inner.node_id, None));
}
// add all nodes from buckets
self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
// Apply filter
for filter in &mut filters {
if filter(rti, k, Some(v.clone())) {
nodes.push((k, Some(v.clone())));
break;
}
}
Option::<()>::None
});
// sort by preference for returning nodes
nodes.sort_by(|a, b| compare(self, a, b));
// return transformed vector for filtered+sorted nodes
let cnt = usize::min(node_count, nodes.len());
let mut out = Vec::<O>::with_capacity(cnt);
for node in nodes {
let val = transform(self, node.0, node.1);
out.push(val);
}
out
}
pub fn find_fastest_nodes<T, O>(
&self,
node_count: usize,
mut filters: VecDeque<RoutingTableEntryFilter>,
transform: T,
) -> Vec<O>
where
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O,
{
let cur_ts = intf::get_timestamp();
// Add filter to remove dead nodes always
let filter_dead = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
if let Some(entry) = &v {
// always filter out dead nodes
if entry.with(rti, |_rti, e| e.state(cur_ts) == BucketEntryState::Dead) {
false
} else {
true
}
} else {
// always filter out self peer, as it is irrelevant to the 'fastest nodes' search
false
}
},
) as RoutingTableEntryFilter;
filters.push_front(filter_dead);
// Fastest sort
let sort = |rti: &RoutingTableInner,
(a_key, a_entry): &(DHTKey, Option<Arc<BucketEntry>>),
(b_key, b_entry): &(DHTKey, Option<Arc<BucketEntry>>)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// our own node always comes last (should not happen, here for completeness)
if a_entry.is_none() {
return core::cmp::Ordering::Greater;
}
if b_entry.is_none() {
return core::cmp::Ordering::Less;
}
// reliable nodes come first
let ae = a_entry.as_ref().unwrap();
let be = b_entry.as_ref().unwrap();
ae.with(rti, |rti, ae| {
be.with(rti, |_rti, be| {
let ra = ae.check_reliable(cur_ts);
let rb = be.check_reliable(cur_ts);
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// latency is the next metric, closer nodes first
let a_latency = match ae.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Greater;
}
Some(l) => l,
};
let b_latency = match be.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Less;
}
Some(l) => l,
};
// Sort by average latency
a_latency.average.cmp(&b_latency.average)
})
})
};
let out =
self.find_peers_with_sort_and_filter(node_count, cur_ts, filters, sort, transform);
out
}
pub fn find_closest_nodes<T, O>(
&self,
node_id: DHTKey,
filters: VecDeque<RoutingTableEntryFilter>,
transform: T,
) -> Vec<O>
where
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O,
{
let cur_ts = intf::get_timestamp();
let node_count = {
let config = self.config();
let c = config.get();
c.network.dht.max_find_node_count as usize
};
// closest sort
let sort = |rti: &RoutingTableInner,
(a_key, a_entry): &(DHTKey, Option<Arc<BucketEntry>>),
(b_key, b_entry): &(DHTKey, Option<Arc<BucketEntry>>)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// reliable nodes come first, pessimistically treating our own node as unreliable
let ra = a_entry
.as_ref()
.map_or(false, |x| x.with(rti, |_rti, x| x.check_reliable(cur_ts)));
let rb = b_entry
.as_ref()
.map_or(false, |x| x.with(rti, |_rti, x| x.check_reliable(cur_ts)));
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// distance is the next metric, closer nodes first
let da = distance(a_key, &node_id);
let db = distance(b_key, &node_id);
da.cmp(&db)
};
let out =
self.find_peers_with_sort_and_filter(node_count, cur_ts, filters, sort, transform);
log_rtab!(">> find_closest_nodes: node count = {}", out.len());
out
}
}

View File

@ -22,8 +22,13 @@ impl RoutingTable {
);
// Roll all bucket entry transfers
for b in &mut inner.buckets {
b.roll_transfers(last_ts, cur_ts);
let entries: Vec<Arc<BucketEntry>> = inner
.buckets
.iter()
.flat_map(|b| b.entries().map(|(_k, v)| v.clone()))
.collect();
for v in entries {
v.with_mut(inner, |_rti, e| e.roll_transfers(last_ts, cur_ts));
}
Ok(())
}
@ -42,7 +47,7 @@ impl RoutingTable {
.collect();
let mut inner = self.inner.write();
for idx in kick_queue {
Self::kick_bucket(&mut *inner, idx)
inner.kick_bucket(idx)
}
Ok(())
}

View File

@ -1,41 +0,0 @@
use crate::dht::*;
use crate::*;
use core::convert::TryInto;
use rpc_processor::*;
pub fn decode_block_id(public_key: &veilid_capnp::b_l_a_k_e3_hash::Reader) -> DHTKey {
let u0 = public_key.get_u0().to_be_bytes();
let u1 = public_key.get_u1().to_be_bytes();
let u2 = public_key.get_u2().to_be_bytes();
let u3 = public_key.get_u3().to_be_bytes();
let mut x: [u8; 32] = Default::default();
x[0..8].copy_from_slice(&u0);
x[8..16].copy_from_slice(&u1);
x[16..24].copy_from_slice(&u2);
x[24..32].copy_from_slice(&u3);
DHTKey::new(x)
}
pub fn encode_block_id(
key: &DHTKey,
builder: &mut veilid_capnp::b_l_a_k_e3_hash::Builder,
) -> Result<(), RPCError> {
if !key.valid {
return Err(RPCError::protocol("invalid key"));
}
builder.set_u0(u64::from_be_bytes(
key.bytes[0..8].try_into().map_err(RPCError::internal)?,
));
builder.set_u1(u64::from_be_bytes(
key.bytes[8..16].try_into().map_err(RPCError::internal)?,
));
builder.set_u2(u64::from_be_bytes(
key.bytes[16..24].try_into().map_err(RPCError::internal)?,
));
builder.set_u3(u64::from_be_bytes(
key.bytes[24..32].try_into().map_err(RPCError::internal)?,
));
Ok(())
}

View File

@ -1,9 +1,9 @@
use crate::dht::*;
use crate::crypto::*;
use crate::*;
use core::convert::TryInto;
use rpc_processor::*;
pub fn decode_public_key(public_key: &veilid_capnp::curve25519_public_key::Reader) -> DHTKey {
pub fn decode_dht_key(public_key: &veilid_capnp::key256::Reader) -> DHTKey {
let u0 = public_key.get_u0().to_be_bytes();
let u1 = public_key.get_u1().to_be_bytes();
let u2 = public_key.get_u2().to_be_bytes();
@ -18,13 +18,10 @@ pub fn decode_public_key(public_key: &veilid_capnp::curve25519_public_key::Reade
DHTKey::new(x)
}
pub fn encode_public_key(
pub fn encode_dht_key(
key: &DHTKey,
builder: &mut veilid_capnp::curve25519_public_key::Builder,
builder: &mut veilid_capnp::key256::Builder,
) -> Result<(), RPCError> {
if !key.valid {
return Err(RPCError::protocol("invalid key"));
}
builder.set_u0(u64::from_be_bytes(
key.bytes[0..8]
.try_into()

View File

@ -1,14 +1,7 @@
use crate::*;
use rpc_processor::*;
pub fn encode_signature(
sig: &DHTSignature,
builder: &mut veilid_capnp::ed25519_signature::Builder,
) {
if !sig.valid {
panic!("don't encode invalid signatures");
}
pub fn encode_signature(sig: &DHTSignature, builder: &mut veilid_capnp::signature512::Builder) {
let sig = &sig.bytes;
builder.set_u0(u64::from_be_bytes(
@ -37,7 +30,7 @@ pub fn encode_signature(
));
}
pub fn decode_signature(reader: &veilid_capnp::ed25519_signature::Reader) -> DHTSignature {
pub fn decode_signature(reader: &veilid_capnp::signature512::Reader) -> DHTSignature {
let u0 = reader.get_u0().to_be_bytes();
let u1 = reader.get_u1().to_be_bytes();
let u2 = reader.get_u2().to_be_bytes();

View File

@ -1,11 +1,11 @@
mod address;
mod address_type_set;
mod block_id;
mod dht_key;
mod dht_signature;
mod dial_info;
mod dial_info_class;
mod dial_info_detail;
mod network_class;
mod node_dial_info;
mod node_info;
mod node_status;
mod nonce;
@ -13,11 +13,11 @@ mod operations;
mod peer_info;
mod private_safety_route;
mod protocol_type_set;
mod public_key;
mod sender_info;
mod signal_info;
mod signature;
mod signed_direct_node_info;
mod signed_node_info;
mod signed_relayed_node_info;
mod socket_address;
mod tunnel;
mod value_data;
@ -25,12 +25,12 @@ mod value_key;
pub use address::*;
pub use address_type_set::*;
pub use block_id::*;
pub use dht_key::*;
pub use dht_signature::*;
pub use dial_info::*;
pub use dial_info_class::*;
pub use dial_info_detail::*;
pub use network_class::*;
pub use node_dial_info::*;
pub use node_info::*;
pub use node_status::*;
pub use nonce::*;
@ -38,11 +38,11 @@ pub use operations::*;
pub use peer_info::*;
pub use private_safety_route::*;
pub use protocol_type_set::*;
pub use public_key::*;
pub use sender_info::*;
pub use signal_info::*;
pub use signature::*;
pub use signed_direct_node_info::*;
pub use signed_node_info::*;
pub use signed_relayed_node_info::*;
pub use socket_address::*;
pub use tunnel::*;
pub use value_data::*;

View File

@ -5,7 +5,7 @@ pub fn encode_network_class(network_class: NetworkClass) -> veilid_capnp::Networ
NetworkClass::InboundCapable => veilid_capnp::NetworkClass::InboundCapable,
NetworkClass::OutboundOnly => veilid_capnp::NetworkClass::OutboundOnly,
NetworkClass::WebApp => veilid_capnp::NetworkClass::WebApp,
NetworkClass::Invalid => panic!("invalid network class should not be encoded"),
NetworkClass::Invalid => veilid_capnp::NetworkClass::Invalid,
}
}
@ -14,5 +14,6 @@ pub fn decode_network_class(network_class: veilid_capnp::NetworkClass) -> Networ
veilid_capnp::NetworkClass::InboundCapable => NetworkClass::InboundCapable,
veilid_capnp::NetworkClass::OutboundOnly => NetworkClass::OutboundOnly,
veilid_capnp::NetworkClass::WebApp => NetworkClass::WebApp,
veilid_capnp::NetworkClass::Invalid => NetworkClass::Invalid,
}
}

View File

@ -1,29 +0,0 @@
use crate::*;
use rpc_processor::*;
pub fn encode_node_dial_info(
ndis: &NodeDialInfo,
builder: &mut veilid_capnp::node_dial_info::Builder,
) -> Result<(), RPCError> {
let mut ni_builder = builder.reborrow().init_node_id();
encode_public_key(&ndis.node_id.key, &mut ni_builder)?;
let mut di_builder = builder.reborrow().init_dial_info();
encode_dial_info(&ndis.dial_info, &mut di_builder)?;
Ok(())
}
pub fn decode_node_dial_info(
reader: &veilid_capnp::node_dial_info::Reader,
) -> Result<NodeDialInfo, RPCError> {
let node_id = decode_public_key(&reader.get_node_id().map_err(RPCError::map_protocol(
"invalid public key in node_dial_info",
))?);
let dial_info = decode_dial_info(&reader.get_dial_info().map_err(RPCError::map_protocol(
"invalid dial_info in node_dial_info",
))?)?;
Ok(NodeDialInfo {
node_id: NodeId::new(node_id),
dial_info,
})
}

View File

@ -31,18 +31,10 @@ pub fn encode_node_info(
encode_dial_info_detail(&node_info.dial_info_detail_list[idx], &mut did_builder)?;
}
if let Some(rpi) = &node_info.relay_peer_info {
let mut rpi_builder = builder.reborrow().init_relay_peer_info();
encode_peer_info(rpi, &mut rpi_builder)?;
}
Ok(())
}
pub fn decode_node_info(
reader: &veilid_capnp::node_info::Reader,
allow_relay_peer_info: bool,
) -> Result<NodeInfo, RPCError> {
pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<NodeInfo, RPCError> {
let network_class = decode_network_class(
reader
.reborrow()
@ -81,22 +73,6 @@ pub fn decode_node_info(
dial_info_detail_list.push(decode_dial_info_detail(&did)?)
}
let relay_peer_info = if allow_relay_peer_info {
if reader.has_relay_peer_info() {
Some(Box::new(decode_peer_info(
&reader
.reborrow()
.get_relay_peer_info()
.map_err(RPCError::protocol)?,
false,
)?))
} else {
None
}
} else {
None
};
Ok(NodeInfo {
network_class,
outbound_protocols,
@ -104,6 +80,5 @@ pub fn decode_node_info(
min_version,
max_version,
dial_info_detail_list,
relay_peer_info,
})
}

View File

@ -1,10 +1,7 @@
use crate::*;
use rpc_processor::*;
pub fn encode_nonce(
nonce: &Nonce,
builder: &mut veilid_capnp::x_cha_cha20_poly1305_nonce::Builder,
) {
pub fn encode_nonce(nonce: &Nonce, builder: &mut veilid_capnp::nonce24::Builder) {
builder.set_u0(u64::from_be_bytes(
nonce[0..8].try_into().expect("slice with incorrect length"),
));
@ -20,7 +17,7 @@ pub fn encode_nonce(
));
}
pub fn decode_nonce(reader: &veilid_capnp::x_cha_cha20_poly1305_nonce::Reader) -> Nonce {
pub fn decode_nonce(reader: &veilid_capnp::nonce24::Reader) -> Nonce {
let u0 = reader.get_u0().to_be_bytes();
let u1 = reader.get_u1().to_be_bytes();
let u2 = reader.get_u2().to_be_bytes();

View File

@ -11,9 +11,6 @@ impl RPCAnswer {
pub fn new(detail: RPCAnswerDetail) -> Self {
Self { detail }
}
// pub fn detail(&self) -> &RPCAnswerDetail {
// &self.detail
// }
pub fn into_detail(self) -> RPCAnswerDetail {
self.detail
}
@ -35,6 +32,7 @@ impl RPCAnswer {
pub enum RPCAnswerDetail {
StatusA(RPCOperationStatusA),
FindNodeA(RPCOperationFindNodeA),
AppCallA(RPCOperationAppCallA),
GetValueA(RPCOperationGetValueA),
SetValueA(RPCOperationSetValueA),
WatchValueA(RPCOperationWatchValueA),
@ -50,6 +48,7 @@ impl RPCAnswerDetail {
match self {
RPCAnswerDetail::StatusA(_) => "StatusA",
RPCAnswerDetail::FindNodeA(_) => "FindNodeA",
RPCAnswerDetail::AppCallA(_) => "AppCallA",
RPCAnswerDetail::GetValueA(_) => "GetValueA",
RPCAnswerDetail::SetValueA(_) => "SetValueA",
RPCAnswerDetail::WatchValueA(_) => "WatchValueA",
@ -76,6 +75,11 @@ impl RPCAnswerDetail {
let out = RPCOperationFindNodeA::decode(&op_reader)?;
RPCAnswerDetail::FindNodeA(out)
}
veilid_capnp::answer::detail::AppCallA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationAppCallA::decode(&op_reader)?;
RPCAnswerDetail::AppCallA(out)
}
veilid_capnp::answer::detail::GetValueA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationGetValueA::decode(&op_reader)?;
@ -126,6 +130,7 @@ impl RPCAnswerDetail {
match self {
RPCAnswerDetail::StatusA(d) => d.encode(&mut builder.reborrow().init_status_a()),
RPCAnswerDetail::FindNodeA(d) => d.encode(&mut builder.reborrow().init_find_node_a()),
RPCAnswerDetail::AppCallA(d) => d.encode(&mut builder.reborrow().init_app_call_a()),
RPCAnswerDetail::GetValueA(d) => d.encode(&mut builder.reborrow().init_get_value_a()),
RPCAnswerDetail::SetValueA(d) => d.encode(&mut builder.reborrow().init_set_value_a()),
RPCAnswerDetail::WatchValueA(d) => {

View File

@ -1,5 +1,7 @@
mod answer;
mod operation;
mod operation_app_call;
mod operation_app_message;
mod operation_cancel_tunnel;
mod operation_complete_tunnel;
mod operation_find_block;
@ -22,6 +24,8 @@ mod statement;
pub use answer::*;
pub use operation::*;
pub use operation_app_call::*;
pub use operation_app_message::*;
pub use operation_cancel_tunnel::*;
pub use operation_complete_tunnel::*;
pub use operation_find_block::*;

View File

@ -19,7 +19,7 @@ impl RPCOperationKind {
pub fn decode(
kind_reader: &veilid_capnp::operation::kind::Reader,
sender_node_id: &DHTKey,
opt_sender_node_id: Option<&DHTKey>,
) -> Result<Self, RPCError> {
let which_reader = kind_reader.which().map_err(RPCError::protocol)?;
let out = match which_reader {
@ -30,7 +30,7 @@ impl RPCOperationKind {
}
veilid_capnp::operation::kind::Which::Statement(r) => {
let q_reader = r.map_err(RPCError::protocol)?;
let out = RPCStatement::decode(&q_reader, sender_node_id)?;
let out = RPCStatement::decode(&q_reader, opt_sender_node_id)?;
RPCOperationKind::Statement(out)
}
veilid_capnp::operation::kind::Which::Answer(r) => {
@ -111,22 +111,26 @@ impl RPCOperation {
pub fn decode(
operation_reader: &veilid_capnp::operation::Reader,
sender_node_id: &DHTKey,
opt_sender_node_id: Option<&DHTKey>,
) -> Result<Self, RPCError> {
let op_id = operation_reader.get_op_id();
let sender_node_info = if operation_reader.has_sender_node_info() {
let sni_reader = operation_reader
.get_sender_node_info()
.map_err(RPCError::protocol)?;
let sni = decode_signed_node_info(&sni_reader, sender_node_id, true)?;
Some(sni)
if let Some(sender_node_id) = opt_sender_node_id {
let sni_reader = operation_reader
.get_sender_node_info()
.map_err(RPCError::protocol)?;
let sni = decode_signed_node_info(&sni_reader, sender_node_id)?;
Some(sni)
} else {
None
}
} else {
None
};
let kind_reader = operation_reader.get_kind();
let kind = RPCOperationKind::decode(&kind_reader, sender_node_id)?;
let kind = RPCOperationKind::decode(&kind_reader, opt_sender_node_id)?;
Ok(RPCOperation {
op_id,

View File

@ -0,0 +1,44 @@
use crate::*;
use rpc_processor::*;
#[derive(Debug, Clone)]
pub struct RPCOperationAppCallQ {
pub message: Vec<u8>,
}
impl RPCOperationAppCallQ {
pub fn decode(
reader: &veilid_capnp::operation_app_call_q::Reader,
) -> Result<RPCOperationAppCallQ, RPCError> {
let message = reader.get_message().map_err(RPCError::protocol)?.to_vec();
Ok(RPCOperationAppCallQ { message })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_app_call_q::Builder,
) -> Result<(), RPCError> {
builder.set_message(&self.message);
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct RPCOperationAppCallA {
pub message: Vec<u8>,
}
impl RPCOperationAppCallA {
pub fn decode(
reader: &veilid_capnp::operation_app_call_a::Reader,
) -> Result<RPCOperationAppCallA, RPCError> {
let message = reader.get_message().map_err(RPCError::protocol)?.to_vec();
Ok(RPCOperationAppCallA { message })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_app_call_a::Builder,
) -> Result<(), RPCError> {
builder.set_message(&self.message);
Ok(())
}
}

View File

@ -0,0 +1,23 @@
use crate::*;
use rpc_processor::*;
#[derive(Debug, Clone)]
pub struct RPCOperationAppMessage {
pub message: Vec<u8>,
}
impl RPCOperationAppMessage {
pub fn decode(
reader: &veilid_capnp::operation_app_message::Reader,
) -> Result<RPCOperationAppMessage, RPCError> {
let message = reader.get_message().map_err(RPCError::protocol)?.to_vec();
Ok(RPCOperationAppMessage { message })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_app_message::Builder,
) -> Result<(), RPCError> {
builder.set_message(&self.message);
Ok(())
}
}

View File

@ -11,7 +11,7 @@ impl RPCOperationFindBlockQ {
reader: &veilid_capnp::operation_find_block_q::Reader,
) -> Result<RPCOperationFindBlockQ, RPCError> {
let bi_reader = reader.get_block_id().map_err(RPCError::protocol)?;
let block_id = decode_block_id(&bi_reader);
let block_id = decode_dht_key(&bi_reader);
Ok(RPCOperationFindBlockQ { block_id })
}
@ -20,7 +20,7 @@ impl RPCOperationFindBlockQ {
builder: &mut veilid_capnp::operation_find_block_q::Builder,
) -> Result<(), RPCError> {
let mut bi_builder = builder.reborrow().init_block_id();
encode_block_id(&self.block_id, &mut bi_builder)?;
encode_dht_key(&self.block_id, &mut bi_builder)?;
Ok(())
}
@ -47,7 +47,7 @@ impl RPCOperationFindBlockA {
.map_err(RPCError::map_internal("too many suppliers"))?,
);
for s in suppliers_reader.iter() {
let peer_info = decode_peer_info(&s, true)?;
let peer_info = decode_peer_info(&s)?;
suppliers.push(peer_info);
}
@ -59,7 +59,7 @@ impl RPCOperationFindBlockA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -11,7 +11,7 @@ impl RPCOperationFindNodeQ {
reader: &veilid_capnp::operation_find_node_q::Reader,
) -> Result<RPCOperationFindNodeQ, RPCError> {
let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?;
let node_id = decode_public_key(&ni_reader);
let node_id = decode_dht_key(&ni_reader);
Ok(RPCOperationFindNodeQ { node_id })
}
pub fn encode(
@ -19,7 +19,7 @@ impl RPCOperationFindNodeQ {
builder: &mut veilid_capnp::operation_find_node_q::Builder,
) -> Result<(), RPCError> {
let mut ni_builder = builder.reborrow().init_node_id();
encode_public_key(&self.node_id, &mut ni_builder)?;
encode_dht_key(&self.node_id, &mut ni_builder)?;
Ok(())
}
}
@ -41,7 +41,7 @@ impl RPCOperationFindNodeA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -48,7 +48,7 @@ impl RPCOperationGetValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -9,10 +9,16 @@ pub struct RPCOperationNodeInfoUpdate {
impl RPCOperationNodeInfoUpdate {
pub fn decode(
reader: &veilid_capnp::operation_node_info_update::Reader,
sender_node_id: &DHTKey,
opt_sender_node_id: Option<&DHTKey>,
) -> Result<RPCOperationNodeInfoUpdate, RPCError> {
if opt_sender_node_id.is_none() {
return Err(RPCError::protocol(
"can't decode node info update without sender node id",
));
}
let sender_node_id = opt_sender_node_id.unwrap();
let sni_reader = reader.get_signed_node_info().map_err(RPCError::protocol)?;
let signed_node_info = decode_signed_node_info(&sni_reader, sender_node_id, true)?;
let signed_node_info = decode_signed_node_info(&sni_reader, sender_node_id)?;
Ok(RPCOperationNodeInfoUpdate { signed_node_info })
}

View File

@ -3,14 +3,16 @@ use rpc_processor::*;
#[derive(Debug, Clone)]
pub struct RoutedOperation {
pub version: u8,
pub signatures: Vec<DHTSignature>,
pub nonce: Nonce,
pub data: Vec<u8>,
}
impl RoutedOperation {
pub fn new(nonce: Nonce, data: Vec<u8>) -> Self {
pub fn new(version: u8, nonce: Nonce, data: Vec<u8>) -> Self {
Self {
version,
signatures: Vec::new(),
nonce,
data,
@ -32,11 +34,13 @@ impl RoutedOperation {
signatures.push(sig);
}
let version = reader.get_version();
let n_reader = reader.get_nonce().map_err(RPCError::protocol)?;
let nonce = decode_nonce(&n_reader);
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
Ok(RoutedOperation {
version,
signatures,
nonce,
data,
@ -47,6 +51,7 @@ impl RoutedOperation {
&self,
builder: &mut veilid_capnp::routed_operation::Builder,
) -> Result<(), RPCError> {
builder.reborrow().set_version(self.version);
let mut sigs_builder = builder.reborrow().init_signatures(
self.signatures
.len()

View File

@ -53,7 +53,7 @@ impl RPCOperationSetValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -3,42 +3,59 @@ use rpc_processor::*;
#[derive(Debug, Clone)]
pub struct RPCOperationStatusQ {
pub node_status: NodeStatus,
pub node_status: Option<NodeStatus>,
}
impl RPCOperationStatusQ {
pub fn decode(
reader: &veilid_capnp::operation_status_q::Reader,
) -> Result<RPCOperationStatusQ, RPCError> {
let ns_reader = reader.get_node_status().map_err(RPCError::protocol)?;
let node_status = decode_node_status(&ns_reader)?;
let node_status = if reader.has_node_status() {
let ns_reader = reader.get_node_status().map_err(RPCError::protocol)?;
let node_status = decode_node_status(&ns_reader)?;
Some(node_status)
} else {
None
};
Ok(RPCOperationStatusQ { node_status })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_status_q::Builder,
) -> Result<(), RPCError> {
let mut ns_builder = builder.reborrow().init_node_status();
encode_node_status(&self.node_status, &mut ns_builder)?;
if let Some(ns) = &self.node_status {
let mut ns_builder = builder.reborrow().init_node_status();
encode_node_status(&ns, &mut ns_builder)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct RPCOperationStatusA {
pub node_status: NodeStatus,
pub sender_info: SenderInfo,
pub node_status: Option<NodeStatus>,
pub sender_info: Option<SenderInfo>,
}
impl RPCOperationStatusA {
pub fn decode(
reader: &veilid_capnp::operation_status_a::Reader,
) -> Result<RPCOperationStatusA, RPCError> {
let ns_reader = reader.get_node_status().map_err(RPCError::protocol)?;
let node_status = decode_node_status(&ns_reader)?;
let node_status = if reader.has_node_status() {
let ns_reader = reader.get_node_status().map_err(RPCError::protocol)?;
let node_status = decode_node_status(&ns_reader)?;
Some(node_status)
} else {
None
};
let si_reader = reader.get_sender_info().map_err(RPCError::protocol)?;
let sender_info = decode_sender_info(&si_reader)?;
let sender_info = if reader.has_sender_info() {
let si_reader = reader.get_sender_info().map_err(RPCError::protocol)?;
let sender_info = decode_sender_info(&si_reader)?;
Some(sender_info)
} else {
None
};
Ok(RPCOperationStatusA {
node_status,
@ -49,10 +66,14 @@ impl RPCOperationStatusA {
&self,
builder: &mut veilid_capnp::operation_status_a::Builder,
) -> Result<(), RPCError> {
let mut ns_builder = builder.reborrow().init_node_status();
encode_node_status(&self.node_status, &mut ns_builder)?;
let mut si_builder = builder.reborrow().init_sender_info();
encode_sender_info(&self.sender_info, &mut si_builder)?;
if let Some(ns) = &self.node_status {
let mut ns_builder = builder.reborrow().init_node_status();
encode_node_status(&ns, &mut ns_builder)?;
}
if let Some(si) = &self.sender_info {
let mut si_builder = builder.reborrow().init_sender_info();
encode_sender_info(&si, &mut si_builder)?;
}
Ok(())
}
}

View File

@ -11,7 +11,7 @@ impl RPCOperationSupplyBlockQ {
reader: &veilid_capnp::operation_supply_block_q::Reader,
) -> Result<RPCOperationSupplyBlockQ, RPCError> {
let bi_reader = reader.get_block_id().map_err(RPCError::protocol)?;
let block_id = decode_block_id(&bi_reader);
let block_id = decode_dht_key(&bi_reader);
Ok(RPCOperationSupplyBlockQ { block_id })
}
@ -20,7 +20,7 @@ impl RPCOperationSupplyBlockQ {
builder: &mut veilid_capnp::operation_supply_block_q::Builder,
) -> Result<(), RPCError> {
let mut bi_builder = builder.reborrow().init_block_id();
encode_block_id(&self.block_id, &mut bi_builder)?;
encode_dht_key(&self.block_id, &mut bi_builder)?;
Ok(())
}
@ -49,7 +49,7 @@ impl RPCOperationSupplyBlockA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -43,7 +43,7 @@ impl RPCOperationWatchValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p, true)?;
let peer_info = decode_peer_info(&p)?;
peers.push(peer_info);
}

View File

@ -40,6 +40,7 @@ impl RPCQuestion {
pub enum RPCQuestionDetail {
StatusQ(RPCOperationStatusQ),
FindNodeQ(RPCOperationFindNodeQ),
AppCallQ(RPCOperationAppCallQ),
GetValueQ(RPCOperationGetValueQ),
SetValueQ(RPCOperationSetValueQ),
WatchValueQ(RPCOperationWatchValueQ),
@ -55,6 +56,7 @@ impl RPCQuestionDetail {
match self {
RPCQuestionDetail::StatusQ(_) => "StatusQ",
RPCQuestionDetail::FindNodeQ(_) => "FindNodeQ",
RPCQuestionDetail::AppCallQ(_) => "AppCallQ",
RPCQuestionDetail::GetValueQ(_) => "GetValueQ",
RPCQuestionDetail::SetValueQ(_) => "SetValueQ",
RPCQuestionDetail::WatchValueQ(_) => "WatchValueQ",
@ -81,6 +83,11 @@ impl RPCQuestionDetail {
let out = RPCOperationFindNodeQ::decode(&op_reader)?;
RPCQuestionDetail::FindNodeQ(out)
}
veilid_capnp::question::detail::Which::AppCallQ(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationAppCallQ::decode(&op_reader)?;
RPCQuestionDetail::AppCallQ(out)
}
veilid_capnp::question::detail::GetValueQ(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationGetValueQ::decode(&op_reader)?;
@ -131,6 +138,7 @@ impl RPCQuestionDetail {
match self {
RPCQuestionDetail::StatusQ(d) => d.encode(&mut builder.reborrow().init_status_q()),
RPCQuestionDetail::FindNodeQ(d) => d.encode(&mut builder.reborrow().init_find_node_q()),
RPCQuestionDetail::AppCallQ(d) => d.encode(&mut builder.reborrow().init_app_call_q()),
RPCQuestionDetail::GetValueQ(d) => d.encode(&mut builder.reborrow().init_get_value_q()),
RPCQuestionDetail::SetValueQ(d) => d.encode(&mut builder.reborrow().init_set_value_q()),
RPCQuestionDetail::WatchValueQ(d) => {

View File

@ -22,10 +22,10 @@ impl RPCStatement {
}
pub fn decode(
reader: &veilid_capnp::statement::Reader,
sender_node_id: &DHTKey,
opt_sender_node_id: Option<&DHTKey>,
) -> Result<RPCStatement, RPCError> {
let d_reader = reader.get_detail();
let detail = RPCStatementDetail::decode(&d_reader, sender_node_id)?;
let detail = RPCStatementDetail::decode(&d_reader, opt_sender_node_id)?;
Ok(RPCStatement { detail })
}
pub fn encode(&self, builder: &mut veilid_capnp::statement::Builder) -> Result<(), RPCError> {
@ -42,6 +42,7 @@ pub enum RPCStatementDetail {
ValueChanged(RPCOperationValueChanged),
Signal(RPCOperationSignal),
ReturnReceipt(RPCOperationReturnReceipt),
AppMessage(RPCOperationAppMessage),
}
impl RPCStatementDetail {
@ -53,11 +54,12 @@ impl RPCStatementDetail {
RPCStatementDetail::ValueChanged(_) => "ValueChanged",
RPCStatementDetail::Signal(_) => "Signal",
RPCStatementDetail::ReturnReceipt(_) => "ReturnReceipt",
RPCStatementDetail::AppMessage(_) => "AppMessage",
}
}
pub fn decode(
reader: &veilid_capnp::statement::detail::Reader,
sender_node_id: &DHTKey,
opt_sender_node_id: Option<&DHTKey>,
) -> Result<RPCStatementDetail, RPCError> {
let which_reader = reader.which().map_err(RPCError::protocol)?;
let out = match which_reader {
@ -73,7 +75,7 @@ impl RPCStatementDetail {
}
veilid_capnp::statement::detail::NodeInfoUpdate(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationNodeInfoUpdate::decode(&op_reader, sender_node_id)?;
let out = RPCOperationNodeInfoUpdate::decode(&op_reader, opt_sender_node_id)?;
RPCStatementDetail::NodeInfoUpdate(out)
}
veilid_capnp::statement::detail::ValueChanged(r) => {
@ -91,6 +93,11 @@ impl RPCStatementDetail {
let out = RPCOperationReturnReceipt::decode(&op_reader)?;
RPCStatementDetail::ReturnReceipt(out)
}
veilid_capnp::statement::detail::AppMessage(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationAppMessage::decode(&op_reader)?;
RPCStatementDetail::AppMessage(out)
}
};
Ok(out)
}
@ -113,6 +120,9 @@ impl RPCStatementDetail {
RPCStatementDetail::ReturnReceipt(d) => {
d.encode(&mut builder.reborrow().init_return_receipt())
}
RPCStatementDetail::AppMessage(d) => {
d.encode(&mut builder.reborrow().init_app_message())
}
}
}
}

View File

@ -7,17 +7,14 @@ pub fn encode_peer_info(
) -> Result<(), RPCError> {
//
let mut nid_builder = builder.reborrow().init_node_id();
encode_public_key(&peer_info.node_id.key, &mut nid_builder)?;
encode_dht_key(&peer_info.node_id.key, &mut nid_builder)?;
let mut sni_builder = builder.reborrow().init_signed_node_info();
encode_signed_node_info(&peer_info.signed_node_info, &mut sni_builder)?;
Ok(())
}
pub fn decode_peer_info(
reader: &veilid_capnp::peer_info::Reader,
allow_relay_peer_info: bool,
) -> Result<PeerInfo, RPCError> {
pub fn decode_peer_info(reader: &veilid_capnp::peer_info::Reader) -> Result<PeerInfo, RPCError> {
let nid_reader = reader
.reborrow()
.get_node_id()
@ -26,9 +23,8 @@ pub fn decode_peer_info(
.reborrow()
.get_signed_node_info()
.map_err(RPCError::protocol)?;
let node_id = NodeId::new(decode_public_key(&nid_reader));
let signed_node_info =
decode_signed_node_info(&sni_reader, &node_id.key, allow_relay_peer_info)?;
let node_id = NodeId::new(decode_dht_key(&nid_reader));
let signed_node_info = decode_signed_node_info(&sni_reader, &node_id.key)?;
Ok(PeerInfo {
node_id,

View File

@ -2,80 +2,6 @@ use super::*;
////////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Clone, Debug)]
pub struct RouteHopData {
pub nonce: Nonce,
pub blob: Vec<u8>,
}
#[derive(Clone, Debug)]
pub struct RouteHop {
pub dial_info: NodeDialInfo,
pub next_hop: Option<RouteHopData>,
}
#[derive(Clone, Debug)]
pub struct PrivateRoute {
pub public_key: DHTKey,
pub hop_count: u8,
pub hops: Option<RouteHop>,
}
impl PrivateRoute {
pub fn new_stub(public_key: DHTKey) -> Self {
Self {
public_key,
hop_count: 0,
hops: None,
}
}
}
impl fmt::Display for PrivateRoute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"PR({:?}+{}{})",
self.public_key,
self.hop_count,
if let Some(hops) = &self.hops {
format!("->{}", hops.dial_info)
} else {
"".to_owned()
}
)
}
}
#[derive(Clone, Debug)]
pub enum SafetyRouteHops {
Data(RouteHopData),
Private(PrivateRoute),
}
#[derive(Clone, Debug)]
pub struct SafetyRoute {
pub public_key: DHTKey,
pub hop_count: u8,
pub hops: SafetyRouteHops,
}
impl fmt::Display for SafetyRoute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"SR({:?}+{}{})",
self.public_key,
self.hop_count,
match &self.hops {
SafetyRouteHops::Data(_) => "".to_owned(),
SafetyRouteHops::Private(p) => format!("->{}", p),
}
)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
pub fn encode_route_hop_data(
route_hop_data: &RouteHopData,
builder: &mut veilid_capnp::route_hop_data::Builder,
@ -98,62 +24,6 @@ pub fn encode_route_hop_data(
Ok(())
}
pub fn encode_route_hop(
route_hop: &RouteHop,
builder: &mut veilid_capnp::route_hop::Builder,
) -> Result<(), RPCError> {
encode_node_dial_info(
&route_hop.dial_info,
&mut builder.reborrow().init_dial_info(),
)?;
if let Some(rhd) = &route_hop.next_hop {
let mut rhd_builder = builder.reborrow().init_next_hop();
encode_route_hop_data(rhd, &mut rhd_builder)?;
}
Ok(())
}
pub fn encode_private_route(
private_route: &PrivateRoute,
builder: &mut veilid_capnp::private_route::Builder,
) -> Result<(), RPCError> {
encode_public_key(
&private_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
builder.set_hop_count(private_route.hop_count);
if let Some(rh) = &private_route.hops {
let mut rh_builder = builder.reborrow().init_first_hop();
encode_route_hop(rh, &mut rh_builder)?;
};
Ok(())
}
pub fn encode_safety_route(
safety_route: &SafetyRoute,
builder: &mut veilid_capnp::safety_route::Builder,
) -> Result<(), RPCError> {
encode_public_key(
&safety_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
builder.set_hop_count(safety_route.hop_count);
let h_builder = builder.reborrow().init_hops();
match &safety_route.hops {
SafetyRouteHops::Data(rhd) => {
let mut rhd_builder = h_builder.init_data();
encode_route_hop_data(rhd, &mut rhd_builder)?;
}
SafetyRouteHops::Private(pr) => {
let mut pr_builder = h_builder.init_private();
encode_private_route(pr, &mut pr_builder)?;
}
};
Ok(())
}
pub fn decode_route_hop_data(
reader: &veilid_capnp::route_hop_data::Reader,
) -> Result<RouteHopData, RPCError> {
@ -173,13 +43,45 @@ pub fn decode_route_hop_data(
Ok(RouteHopData { nonce, blob })
}
////////////////////////////////////////////////////////////////////////////////////////////////////
pub fn encode_route_hop(
route_hop: &RouteHop,
builder: &mut veilid_capnp::route_hop::Builder,
) -> Result<(), RPCError> {
let node_builder = builder.reborrow().init_node();
match &route_hop.node {
RouteNode::NodeId(ni) => {
let mut ni_builder = node_builder.init_node_id();
encode_dht_key(&ni.key, &mut ni_builder)?;
}
RouteNode::PeerInfo(pi) => {
let mut pi_builder = node_builder.init_peer_info();
encode_peer_info(&pi, &mut pi_builder)?;
}
}
if let Some(rhd) = &route_hop.next_hop {
let mut rhd_builder = builder.reborrow().init_next_hop();
encode_route_hop_data(rhd, &mut rhd_builder)?;
}
Ok(())
}
pub fn decode_route_hop(reader: &veilid_capnp::route_hop::Reader) -> Result<RouteHop, RPCError> {
let dial_info = decode_node_dial_info(
&reader
.reborrow()
.get_dial_info()
.map_err(RPCError::map_protocol("invalid dial info in route hop"))?,
)?;
let n_reader = reader.reborrow().get_node();
let node = match n_reader.which().map_err(RPCError::protocol)? {
veilid_capnp::route_hop::node::Which::NodeId(ni) => {
let ni_reader = ni.map_err(RPCError::protocol)?;
RouteNode::NodeId(NodeId::new(decode_dht_key(&ni_reader)))
}
veilid_capnp::route_hop::node::Which::PeerInfo(pi) => {
let pi_reader = pi.map_err(RPCError::protocol)?;
RouteNode::PeerInfo(
decode_peer_info(&pi_reader)
.map_err(RPCError::map_protocol("invalid peer info in route hop"))?,
)
}
};
let next_hop = if reader.has_next_hop() {
let rhd_reader = reader
@ -190,26 +92,55 @@ pub fn decode_route_hop(reader: &veilid_capnp::route_hop::Reader) -> Result<Rout
None
};
Ok(RouteHop {
dial_info,
next_hop,
})
Ok(RouteHop { node, next_hop })
}
////////////////////////////////////////////////////////////////////////////////////////////////////
pub fn encode_private_route(
private_route: &PrivateRoute,
builder: &mut veilid_capnp::private_route::Builder,
) -> Result<(), RPCError> {
encode_dht_key(
&private_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
builder.set_hop_count(private_route.hop_count);
let mut h_builder = builder.reborrow().init_hops();
match &private_route.hops {
PrivateRouteHops::FirstHop(first_hop) => {
let mut rh_builder = h_builder.init_first_hop();
encode_route_hop(first_hop, &mut rh_builder)?;
}
PrivateRouteHops::Data(data) => {
let mut rhd_builder = h_builder.init_data();
encode_route_hop_data(data, &mut rhd_builder)?;
}
PrivateRouteHops::Empty => {
h_builder.set_empty(());
}
};
Ok(())
}
pub fn decode_private_route(
reader: &veilid_capnp::private_route::Reader,
) -> Result<PrivateRoute, RPCError> {
let public_key = decode_public_key(&reader.get_public_key().map_err(
RPCError::map_protocol("invalid public key in private route"),
)?);
let public_key = decode_dht_key(&reader.get_public_key().map_err(RPCError::map_protocol(
"invalid public key in private route",
))?);
let hop_count = reader.get_hop_count();
let hops = if reader.has_first_hop() {
let rh_reader = reader
.get_first_hop()
.map_err(RPCError::map_protocol("invalid first hop in private route"))?;
Some(decode_route_hop(&rh_reader)?)
} else {
None
let hops = match reader.get_hops().which().map_err(RPCError::protocol)? {
veilid_capnp::private_route::hops::Which::FirstHop(rh_reader) => {
let rh_reader = rh_reader.map_err(RPCError::protocol)?;
PrivateRouteHops::FirstHop(decode_route_hop(&rh_reader)?)
}
veilid_capnp::private_route::hops::Which::Data(rhd_reader) => {
let rhd_reader = rhd_reader.map_err(RPCError::protocol)?;
PrivateRouteHops::Data(decode_route_hop_data(&rhd_reader)?)
}
veilid_capnp::private_route::hops::Which::Empty(_) => PrivateRouteHops::Empty,
};
Ok(PrivateRoute {
@ -219,10 +150,36 @@ pub fn decode_private_route(
})
}
////////////////////////////////////////////////////////////////////////////////////////////////////
pub fn encode_safety_route(
safety_route: &SafetyRoute,
builder: &mut veilid_capnp::safety_route::Builder,
) -> Result<(), RPCError> {
encode_dht_key(
&safety_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
builder.set_hop_count(safety_route.hop_count);
let h_builder = builder.reborrow().init_hops();
match &safety_route.hops {
SafetyRouteHops::Data(rhd) => {
let mut rhd_builder = h_builder.init_data();
encode_route_hop_data(rhd, &mut rhd_builder)?;
}
SafetyRouteHops::Private(pr) => {
let mut pr_builder = h_builder.init_private();
encode_private_route(pr, &mut pr_builder)?;
}
};
Ok(())
}
pub fn decode_safety_route(
reader: &veilid_capnp::safety_route::Reader,
) -> Result<SafetyRoute, RPCError> {
let public_key = decode_public_key(
let public_key = decode_dht_key(
&reader
.get_public_key()
.map_err(RPCError::map_protocol("invalid public key in safety route"))?,

View File

@ -5,30 +5,21 @@ pub fn encode_sender_info(
sender_info: &SenderInfo,
builder: &mut veilid_capnp::sender_info::Builder,
) -> Result<(), RPCError> {
if let Some(socket_address) = &sender_info.socket_address {
let mut sab = builder.reborrow().init_socket_address();
encode_socket_address(socket_address, &mut sab)?;
}
let mut sab = builder.reborrow().init_socket_address();
encode_socket_address(&sender_info.socket_address, &mut sab)?;
Ok(())
}
pub fn decode_sender_info(
reader: &veilid_capnp::sender_info::Reader,
) -> Result<SenderInfo, RPCError> {
if !reader.has_socket_address() {
return Err(RPCError::internal("invalid socket address type"));
}
let socket_address = if reader.has_socket_address() {
Some(decode_socket_address(
&reader
.reborrow()
.get_socket_address()
.map_err(RPCError::map_internal(
"invalid socket address in sender_info",
))?,
)?)
} else {
None
};
let sa_reader = reader
.reborrow()
.get_socket_address()
.map_err(RPCError::map_internal(
"invalid socket address in sender_info",
))?;
let socket_address = decode_socket_address(&sa_reader)?;
Ok(SenderInfo { socket_address })
}

View File

@ -53,7 +53,7 @@ pub fn decode_signal_info(
let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol(
"invalid peer info in hole punch signal info",
))?;
let peer_info = decode_peer_info(&pi_reader, true)?;
let peer_info = decode_peer_info(&pi_reader)?;
SignalInfo::HolePunch { receipt, peer_info }
}
@ -69,7 +69,7 @@ pub fn decode_signal_info(
let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol(
"invalid peer info in reverse connect signal info",
))?;
let peer_info = decode_peer_info(&pi_reader, true)?;
let peer_info = decode_peer_info(&pi_reader)?;
SignalInfo::ReverseConnect { receipt, peer_info }
}

View File

@ -0,0 +1,46 @@
use crate::*;
use rpc_processor::*;
pub fn encode_signed_direct_node_info(
signed_direct_node_info: &SignedDirectNodeInfo,
builder: &mut veilid_capnp::signed_direct_node_info::Builder,
) -> Result<(), RPCError> {
//
let mut ni_builder = builder.reborrow().init_node_info();
encode_node_info(&signed_direct_node_info.node_info, &mut ni_builder)?;
builder
.reborrow()
.set_timestamp(signed_direct_node_info.timestamp);
let mut sig_builder = builder.reborrow().init_signature();
let Some(signature) = &signed_direct_node_info.signature else {
return Err(RPCError::internal("Should not encode SignedDirectNodeInfo without signature!"));
};
encode_signature(signature, &mut sig_builder);
Ok(())
}
pub fn decode_signed_direct_node_info(
reader: &veilid_capnp::signed_direct_node_info::Reader,
node_id: &DHTKey,
) -> Result<SignedDirectNodeInfo, RPCError> {
let ni_reader = reader
.reborrow()
.get_node_info()
.map_err(RPCError::protocol)?;
let node_info = decode_node_info(&ni_reader)?;
let sig_reader = reader
.reborrow()
.get_signature()
.map_err(RPCError::protocol)?;
let timestamp = reader.reborrow().get_timestamp();
let signature = decode_signature(&sig_reader);
SignedDirectNodeInfo::new(NodeId::new(*node_id), node_info, timestamp, signature)
.map_err(RPCError::protocol)
}

View File

@ -5,14 +5,16 @@ pub fn encode_signed_node_info(
signed_node_info: &SignedNodeInfo,
builder: &mut veilid_capnp::signed_node_info::Builder,
) -> Result<(), RPCError> {
//
let mut ni_builder = builder.reborrow().init_node_info();
encode_node_info(&signed_node_info.node_info, &mut ni_builder)?;
let mut sig_builder = builder.reborrow().init_signature();
encode_signature(&signed_node_info.signature, &mut sig_builder);
builder.reborrow().set_timestamp(signed_node_info.timestamp);
match signed_node_info {
SignedNodeInfo::Direct(d) => {
let mut d_builder = builder.reborrow().init_direct();
encode_signed_direct_node_info(d, &mut d_builder)?;
}
SignedNodeInfo::Relayed(r) => {
let mut r_builder = builder.reborrow().init_relayed();
encode_signed_relayed_node_info(r, &mut r_builder)?;
}
}
Ok(())
}
@ -20,22 +22,20 @@ pub fn encode_signed_node_info(
pub fn decode_signed_node_info(
reader: &veilid_capnp::signed_node_info::Reader,
node_id: &DHTKey,
allow_relay_peer_info: bool,
) -> Result<SignedNodeInfo, RPCError> {
let ni_reader = reader
.reborrow()
.get_node_info()
.map_err(RPCError::protocol)?;
let node_info = decode_node_info(&ni_reader, allow_relay_peer_info)?;
let sig_reader = reader
.reborrow()
.get_signature()
.map_err(RPCError::protocol)?;
let signature = decode_signature(&sig_reader);
let timestamp = reader.reborrow().get_timestamp();
SignedNodeInfo::new(node_info, NodeId::new(*node_id), signature, timestamp)
.map_err(RPCError::protocol)
match reader
.which()
.map_err(RPCError::map_internal("invalid signal operation"))?
{
veilid_capnp::signed_node_info::Direct(d) => {
let d_reader = d.map_err(RPCError::protocol)?;
let sdni = decode_signed_direct_node_info(&d_reader, node_id)?;
Ok(SignedNodeInfo::Direct(sdni))
}
veilid_capnp::signed_node_info::Relayed(r) => {
let r_reader = r.map_err(RPCError::protocol)?;
let srni = decode_signed_relayed_node_info(&r_reader, node_id)?;
Ok(SignedNodeInfo::Relayed(srni))
}
}
}

Some files were not shown because too many files have changed in this diff Show More