refactoring, more config, packaging

This commit is contained in:
John Smith 2022-05-16 11:52:48 -04:00
parent 444f65d76d
commit ef1f5d7b52
42 changed files with 1329 additions and 368 deletions

95
Cargo.lock generated
View File

@ -1259,6 +1259,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [ dependencies = [
"block-buffer 0.10.2", "block-buffer 0.10.2",
"crypto-common", "crypto-common",
"subtle",
] ]
[[package]] [[package]]
@ -1308,7 +1309,7 @@ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"ed25519", "ed25519",
"rand 0.7.3", "rand 0.7.3",
"sha2", "sha2 0.9.9",
"zeroize", "zeroize",
] ]
@ -1813,7 +1814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
dependencies = [ dependencies = [
"digest 0.9.0", "digest 0.9.0",
"hmac", "hmac 0.10.1",
] ]
[[package]] [[package]]
@ -1826,6 +1827,15 @@ dependencies = [
"digest 0.9.0", "digest 0.9.0",
] ]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest 0.10.3",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.6" version = "0.2.6"
@ -2046,7 +2056,7 @@ dependencies = [
"log", "log",
"ndk", "ndk",
"ndk-glue", "ndk-glue",
"rpassword", "rpassword 5.0.1",
"secret-service", "secret-service",
"security-framework", "security-framework",
"security-framework-sys", "security-framework-sys",
@ -2167,6 +2177,24 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "libsystemd"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8144587c71c16756b1055d3dcb0c75cb605a10ecd6523cc33702d5f90902bf6d"
dependencies = [
"hmac 0.12.1",
"libc",
"log",
"nix 0.23.1",
"nom 7.1.0",
"once_cell",
"serde 1.0.136",
"sha2 0.10.2",
"thiserror",
"uuid",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.3.0" version = "0.3.0"
@ -3280,6 +3308,18 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rpassword"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956"
dependencies = [
"libc",
"serde 1.0.136",
"serde_json",
"winapi",
]
[[package]] [[package]]
name = "rtnetlink" name = "rtnetlink"
version = "0.9.0" version = "0.9.0"
@ -3451,7 +3491,7 @@ dependencies = [
"num 0.3.1", "num 0.3.1",
"rand 0.8.5", "rand 0.8.5",
"serde 1.0.136", "serde 1.0.136",
"sha2", "sha2 0.9.9",
"zbus", "zbus",
"zbus_macros", "zbus_macros",
"zvariant", "zvariant",
@ -3699,6 +3739,17 @@ dependencies = [
"opaque-debug 0.3.0", "opaque-debug 0.3.0",
] ]
[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures 0.2.1",
"digest 0.10.3",
]
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.13" version = "0.3.13"
@ -3709,6 +3760,18 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
] ]
[[package]]
name = "signal-hook-async-std"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556"
dependencies = [
"async-io",
"futures-lite",
"libc",
"signal-hook",
]
[[package]] [[package]]
name = "signal-hook-mio" name = "signal-hook-mio"
version = "0.2.1" version = "0.2.1"
@ -3861,6 +3924,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "systemd-journal-logger"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2b2b2ff370208ad472629786a66dc252933843755a1d620a54a8fdd0fccb31f"
dependencies = [
"libsystemd",
"log",
]
[[package]] [[package]]
name = "tap" name = "tap"
version = "1.0.1" version = "1.0.1"
@ -4124,6 +4197,15 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"serde 1.0.136",
]
[[package]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.0.0-alpha.8" version = "1.0.0-alpha.8"
@ -4294,12 +4376,17 @@ dependencies = [
"futures", "futures",
"lazy_static", "lazy_static",
"log", "log",
"nix 0.23.1",
"parking_lot 0.12.0", "parking_lot 0.12.0",
"rpassword 6.0.1",
"serde 1.0.136", "serde 1.0.136",
"serde_derive", "serde_derive",
"serde_yaml", "serde_yaml",
"serial_test 0.6.0", "serial_test 0.6.0",
"signal-hook",
"signal-hook-async-std",
"simplelog", "simplelog",
"systemd-journal-logger",
"url", "url",
"veilid-core", "veilid-core",
"windows-service", "windows-service",

View File

@ -72,7 +72,7 @@ deps:
code: code:
FROM +deps FROM +deps
COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-wasm Cargo.lock Cargo.toml /veilid COPY --dir .cargo external files scripts veilid-cli veilid-core veilid-server veilid-flutter veilid-wasm Cargo.lock Cargo.toml /veilid
WORKDIR /veilid WORKDIR /veilid
# Clippy only # Clippy only
@ -113,3 +113,36 @@ unit-tests-linux-amd64:
unit-tests-linux-arm64: unit-tests-linux-arm64:
FROM +code FROM +code
RUN cargo test --target aarch64-unknown-linux-gnu --release RUN cargo test --target aarch64-unknown-linux-gnu --release
# Package
package-linux-amd64:
FROM +build-linux-amd64
#################################
### DEBIAN DPKG .DEB FILES
#################################
COPY --dir package /veilid
# veilid-server
RUN /veilid/package/debian/earthly_make_veilid_server_deb.sh amd64 x86_64-unknown-linux-gnu
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
# veilid-cli
RUN /veilid/package/debian/earthly_make_veilid_cli_deb.sh amd64 x86_64-unknown-linux-gnu
# save artifacts
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
package-linux-arm64:
FROM +build-linux-arm64
#################################
### DEBIAN DPKG .DEB FILES
#################################
COPY --dir package /veilid
# veilid-server
RUN /veilid/package/debian/earthly_make_veilid_server_deb.sh arm64 aarch64-unknown-linux-gnu
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
# veilid-cli
RUN /veilid/package/debian/earthly_make_veilid_cli_deb.sh arm64 aarch64-unknown-linux-gnu
# save artifacts
SAVE ARTIFACT --keep-ts /dpkg/out/*.deb AS LOCAL ./target/packages/
package-linux:
BUILD +package-linux-amd64
BUILD +package-linux-arm64

4
package/cargo_version.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
INPUTFILE=$1
cat $1 | grep version | head -n 1 | cut -d\" -f 2

View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
ARCH=$1
CARGO_ARCH=$2
CARGO_VERSION="$(/veilid/package/cargo_version.sh /veilid/veilid-cli/Cargo.toml)"
rm -rf /dpkg
mkdir -p /dpkg/out
# veilid-cli dpkg control
cp -rf /veilid/package/debian/veilid-cli /dpkg
/veilid/package/replace_variable.sh /dpkg/veilid-cli/DEBIAN/control CARGO_VERSION $CARGO_VERSION
/veilid/package/replace_variable.sh /dpkg/veilid-cli/DEBIAN/control ARCH $ARCH
# veilid-cli executable
mkdir -p /dpkg/veilid-cli/usr/bin
cp -f /veilid/target/$CARGO_ARCH/release/veilid-cli /dpkg/veilid-cli/usr/bin
# pack it up
dpkg-deb -b /dpkg/veilid-cli/
mv /dpkg/veilid-cli.deb /dpkg/out/veilid-cli-$CARGO_VERSION-$ARCH.deb

View File

@ -0,0 +1,25 @@
#!/bin/bash
set -e
ARCH=$1
CARGO_ARCH=$2
CARGO_VERSION="$(/veilid/package/cargo_version.sh /veilid/veilid-server/Cargo.toml)"
rm -rf /dpkg
mkdir -p /dpkg/out
# veilid-server dpkg control
cp -rf /veilid/package/debian/veilid-server /dpkg
/veilid/package/replace_variable.sh /dpkg/veilid-server/DEBIAN/control CARGO_VERSION $CARGO_VERSION
/veilid/package/replace_variable.sh /dpkg/veilid-server/DEBIAN/control ARCH $ARCH
# veilid-server configuration
mkdir -p /dpkg/veilid-server/etc/veilid-server
cp -f /veilid/package/linux/veilid-server.conf /dpkg/veilid-server/etc/veilid-server/veilid-server.conf
# veilid-server systemd unit file
mkdir -p /dpkg/veilid-server/etc/systemd/system
cp -f /veilid/package/systemd/veilid-server.service /dpkg/veilid-server/etc/systemd/system
# veilid-server executable
mkdir -p /dpkg/veilid-server/usr/bin
cp -f /veilid/target/$CARGO_ARCH/release/veilid-server /dpkg/veilid-server/usr/bin
# pack it up
dpkg-deb -b /dpkg/veilid-server/
mv /dpkg/veilid-server.deb /dpkg/out/veilid-server-$CARGO_VERSION-$ARCH.deb

View File

@ -0,0 +1,10 @@
Package: veilid-cli
Version: $CARGO_VERSION
Section: network
Priority: optional
Architecture: $ARCH
Depends: libc6 (>= 2.23)
Maintainer: jsmith@veilid.org
Description: Veilid Server Command Line Interface
The Veilid peer-to-peer network server command line interface

View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
# Set permissions
chmod 755 /usr/bin/veilid-cli

View File

@ -0,0 +1,9 @@
Package: veilid-server
Version: $CARGO_VERSION
Section: network
Priority: optional
Architecture: $ARCH
Depends: libc6 (>= 2.23)
Maintainer: jsmith@veilid.org
Description: Veilid Server
The Veilid peer-to-peer network server

View File

@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Add veilid user and group
adduser --system --group veilid &>/dev/null || true
# Make db folders
mkdir -p /var/db/veilid-server/protected_store
mkdir -p /var/db/veilid-server/table_store
mkdir -p /var/db/veilid-server/block_store
# Set permissions
chown -R veilid:veilid /var/db/veilid-server
chmod 0750 /var/db/veilid-server/protected_store
chmod 0750 /var/db/veilid-server/table_store
chmod 0750 /var/db/veilid-server/block_store
chmod 0750 /var/db/veilid-server
chmod 755 /usr/bin/veilid-server
# Add and start systemd unit
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
# This will only remove masks created by d-s-h on package removal.
deb-systemd-helper unmask 'veilid-server.service' >/dev/null || true
# was-enabled defaults to true, so new installations run enable.
if deb-systemd-helper --quiet was-enabled 'veilid-server.service'; then
# Enables the unit on first installation, creates new
# symlinks on upgrades if the unit file has changed.
deb-systemd-helper enable 'veilid-server.service' >/dev/null || true
else
# Update the statefile to add new symlinks (if any), which need to be
# cleaned up on purge. Also remove old symlinks.
deb-systemd-helper update-state 'veilid-server.service' >/dev/null || true
fi
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
if [ -n "$2" ]; then
_dh_action=restart
else
_dh_action=start
fi
deb-systemd-invoke $_dh_action 'veilid-server.service' >/dev/null || true
fi
fi

View File

@ -0,0 +1,18 @@
#!/bin/sh
set -e
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
fi
if [ "$1" = "remove" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper mask 'veilid-server.service' >/dev/null || true
fi
elif [ "$1" = "purge" ]; then
if [ -x "/usr/bin/deb-systemd-helper" ]; then
deb-systemd-helper purge 'veilid-server.service' >/dev/null || true
deb-systemd-helper unmask 'veilid-server.service' >/dev/null || true
fi
rm -rf /var/db/veilid-server
fi

View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
if [ -d /run/systemd/system ] && [ "$1" = remove ]; then
deb-systemd-invoke stop 'veilid-server.service' >/dev/null || true
fi

View File

@ -0,0 +1,20 @@
# Veilid Server
# =============
#
# Default Server Configuration
#
# -----------------------------------------------------------
---
daemon:
enabled: true
pid_file: '/run/veilid-server.pid'
working_directory: '/'
user: veilid
group: veilid
logging:
system:
enabled: true
level: info
terminal:
enabled: false

7
package/replace_variable.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
INPUTFILE=$1
VARIABLE_NAME=$2
VARIABLE_VALUE=$3
sed -i "s/\$$VARIABLE_NAME/$VARIABLE_VALUE/g" "$INPUTFILE"

View File

@ -0,0 +1,20 @@
# /etc/systemd/system/veilid-server.service
[Unit]
Description=The Veilid peer-to-peer network server
Requires=network-online.target
After=network-online.target
[Service]
Type=forking
PIDFile=/run/veilid-server.pid
ExecStartPre=/usr/bin/rm -f /run/veilid-server.pid
ExecStart=/usr/bin/veilid-server
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target

View File

@ -146,8 +146,8 @@ def main():
time.sleep(1) time.sleep(1)
sub_args = base_args.copy() sub_args = base_args.copy()
sub_args.append("--subnode_index={}".format(n)) sub_args.append("--subnode-index={}".format(n))
sub_args.append("--bootstrap={}".format(main_di)) sub_args.append("--bootstrap-nodes={}".format(main_di))
if args.wait_for_debug and (str(n) in args.wait_for_debug): if args.wait_for_debug and (str(n) in args.wait_for_debug):
sub_args.append("--wait-for-debug") sub_args.append("--wait-for-debug")

View File

@ -88,14 +88,29 @@ impl ClientApiConnection {
} }
async fn process_veilid_state<'a>( async fn process_veilid_state<'a>(
&'a mut self, &'a mut self,
state: veilid_state::Reader<'a>, veilid_state: veilid_state::Reader<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
// Process attachment state // Process attachment state
let attachment = state.reborrow().get_attachment().map_err(map_to_string)?; let attachment = veilid_state
let state = attachment.get_state().map_err(map_to_string)?; .reborrow()
inner.comproc.update_attachment(state); .get_attachment()
.map_err(map_to_string)?;
let attachment_state = attachment.get_state().map_err(map_to_string)?;
let network = veilid_state
.reborrow()
.get_network()
.map_err(map_to_string)?;
let started = network.get_started();
let bps_down = network.get_bps_down();
let bps_up = network.get_bps_up();
inner.comproc.update_attachment(attachment_state);
inner
.comproc
.update_network_status(started, bps_down, bps_up);
Ok(()) Ok(())
} }

View File

@ -277,6 +277,12 @@ debug - send a debugging command to the Veilid server
self.inner_mut().ui.set_attachment_state(state); self.inner_mut().ui.set_attachment_state(state);
} }
pub fn update_network_status(&mut self, started: bool, bps_down: u64, bps_up: u64) {
self.inner_mut()
.ui
.set_network_status(started, bps_down, bps_up);
}
pub fn add_log_message(&mut self, message: &str) { pub fn add_log_message(&mut self, message: &str) {
self.inner().ui.add_node_event(message); self.inner().ui.add_node_event(message);
} }

View File

@ -49,6 +49,8 @@ pub type UICallback = Box<dyn Fn(&mut Cursive) + Send>;
struct UIState { struct UIState {
attachment_state: Dirty<AttachmentState>, attachment_state: Dirty<AttachmentState>,
network_started: Dirty<bool>,
network_down_up: Dirty<(f32, f32)>,
connection_state: Dirty<ConnectionState>, connection_state: Dirty<ConnectionState>,
} }
@ -56,6 +58,8 @@ impl UIState {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
attachment_state: Dirty::new(AttachmentState::Detached), attachment_state: Dirty::new(AttachmentState::Detached),
network_started: Dirty::new(false),
network_down_up: Dirty::new((0.0, 0.0)),
connection_state: Dirty::new(ConnectionState::Disconnected), connection_state: Dirty::new(ConnectionState::Disconnected),
} }
} }
@ -221,6 +225,15 @@ impl UI {
AttachmentState::Detaching => "Detaching [////]", AttachmentState::Detaching => "Detaching [////]",
} }
} }
fn render_network_status(inner: &mut UIInner) -> String {
match inner.ui_state.network_started.get() {
false => "Down: ----KB/s Up: ----KB/s".to_owned(),
true => {
let (d, u) = inner.ui_state.network_down_up.get();
format!("Down: {:.2}KB/s Up: {:.2}KB/s", d, u)
}
}
}
fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) { fn render_button_attach<'a>(inner: &mut UIInner) -> (&'a str, bool) {
if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() { if let ConnectionState::Connected(_, _) = inner.ui_state.connection_state.get() {
match inner.ui_state.attachment_state.get() { match inner.ui_state.attachment_state.get() {
@ -576,7 +589,7 @@ impl UI {
status.append_styled("|", ColorStyle::highlight_inactive()); status.append_styled("|", ColorStyle::highlight_inactive());
// Add bandwidth status // Add bandwidth status
status.append_styled( status.append_styled(
" Down: 0.0KB/s Up: 0.0KB/s ", format!(" {} ", UI::render_network_status(&mut inner)),
ColorStyle::highlight_inactive(), ColorStyle::highlight_inactive(),
); );
status.append_styled("|", ColorStyle::highlight_inactive()); status.append_styled("|", ColorStyle::highlight_inactive());
@ -599,6 +612,12 @@ impl UI {
refresh_statusbar = true; refresh_statusbar = true;
refresh_button_attach = true; refresh_button_attach = true;
} }
if inner.ui_state.network_started.take_dirty() {
refresh_statusbar = true;
}
if inner.ui_state.network_down_up.take_dirty() {
refresh_statusbar = true;
}
if inner.ui_state.connection_state.take_dirty() { if inner.ui_state.connection_state.take_dirty() {
refresh_statusbar = true; refresh_statusbar = true;
refresh_button_attach = true; refresh_button_attach = true;
@ -757,6 +776,15 @@ impl UI {
inner.ui_state.attachment_state.set(state); inner.ui_state.attachment_state.set(state);
let _ = inner.cb_sink.send(Box::new(UI::update_cb)); let _ = inner.cb_sink.send(Box::new(UI::update_cb));
} }
pub fn set_network_status(&mut self, started: bool, bps_down: u64, bps_up: u64) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.network_started.set(started);
inner.ui_state.network_down_up.set((
((bps_down as f64) / 1000.0f64) as f32,
((bps_up as f64) / 1000.0f64) as f32,
));
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
}
pub fn set_connection_state(&mut self, state: ConnectionState) { pub fn set_connection_state(&mut self, state: ConnectionState) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
inner.ui_state.connection_state.set(state); inner.ui_state.connection_state.set(state);

View File

@ -136,10 +136,10 @@ impl Log for ApiLogger {
let s = format!("{}{}{}", tgt, loc, record.args()); let s = format!("{}{}{}", tgt, loc, record.args());
(inner.update_callback)(VeilidUpdate::Log { (inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
log_level: ll, log_level: ll,
message: s, message: s,
}) }))
} }
} }
} }

View File

@ -271,15 +271,18 @@ impl AttachmentManager {
let network_manager = { let network_manager = {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.update_callback = Some(update_callback.clone()); inner.update_callback = Some(update_callback.clone());
let update_callback2 = update_callback.clone();
inner.attachment_machine.set_state_change_callback(Arc::new( inner.attachment_machine.set_state_change_callback(Arc::new(
move |_old_state: AttachmentState, new_state: AttachmentState| { move |_old_state: AttachmentState, new_state: AttachmentState| {
update_callback(VeilidUpdate::Attachment { state: new_state }) update_callback2(VeilidUpdate::Attachment(VeilidStateAttachment {
state: new_state,
}))
}, },
)); ));
inner.network_manager.clone() inner.network_manager.clone()
}; };
network_manager.init().await?; network_manager.init(update_callback).await?;
Ok(()) Ok(())
} }
@ -356,4 +359,10 @@ impl AttachmentManager {
let attachment_machine = self.inner.lock().attachment_machine.clone(); let attachment_machine = self.inner.lock().attachment_machine.clone();
attachment_machine.state() attachment_machine.state()
} }
pub fn get_veilid_state(&self) -> VeilidStateAttachment {
VeilidStateAttachment {
state: self.get_state(),
}
}
} }

View File

@ -214,7 +214,14 @@ impl Network {
.interfaces .interfaces
.best_addresses() .best_addresses()
.iter() .iter()
.map(|a| SocketAddr::new(*a, from.port())) .filter_map(|a| {
// We create sockets that are only ipv6 or ipv6 (not dual, so only translate matching unspecified address)
if (a.is_ipv4() && from.is_ipv4()) || (a.is_ipv6() && from.is_ipv6()) {
Some(SocketAddr::new(*a, from.port()))
} else {
None
}
})
.collect() .collect()
} }
} }

View File

@ -445,6 +445,7 @@ impl Network {
log_net!("updating network class"); log_net!("updating network class");
let protocol_config = self.inner.lock().protocol_config.unwrap_or_default(); let protocol_config = self.inner.lock().protocol_config.unwrap_or_default();
let old_network_class = self.inner.lock().network_class;
let context = DiscoveryContext::new(self.routing_table(), self.clone()); let context = DiscoveryContext::new(self.routing_table(), self.clone());
@ -470,9 +471,10 @@ impl Network {
} }
let network_class = context.inner.lock().network_class; let network_class = context.inner.lock().network_class;
self.inner.lock().network_class = network_class; if network_class != old_network_class {
self.inner.lock().network_class = network_class;
log_net!(debug "network class set to {:?}", network_class); log_net!(debug "network class changed to {:?}", network_class);
}
// send updates to everyone // send updates to everyone
self.routing_table().send_node_info_updates(); self.routing_table().send_node_info_updates();

View File

@ -291,6 +291,8 @@ impl Network {
let local_dial_info_list = self.create_udp_inbound_sockets(ip_addrs, udp_port).await?; let local_dial_info_list = self.create_udp_inbound_sockets(ip_addrs, udp_port).await?;
let mut static_public = false; let mut static_public = false;
trace!("UDP: listener started on {:#?}", local_dial_info_list);
// Register local dial info // Register local dial info
for di in &local_dial_info_list { for di in &local_dial_info_list {
// If the local interface address is global, or we are enabling local peer scope // If the local interface address is global, or we are enabling local peer scope
@ -397,7 +399,7 @@ impl Network {
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))), Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
) )
.await?; .await?;
trace!("WS: listener started"); trace!("WS: listener started on {:#?}", socket_addresses);
let mut static_public = false; let mut static_public = false;
let mut registered_addresses: HashSet<IpAddr> = HashSet::new(); let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
@ -515,7 +517,7 @@ impl Network {
wss_port, wss_port,
ip_addrs ip_addrs
); );
let _socket_addresses = self let socket_addresses = self
.start_tcp_listener( .start_tcp_listener(
ip_addrs, ip_addrs,
wss_port, wss_port,
@ -523,7 +525,7 @@ impl Network {
Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))), Box::new(|c, t, a| Box::new(WebsocketProtocolHandler::new(c, t, a))),
) )
.await?; .await?;
trace!("WSS: listener started"); trace!("WSS: listener started on {:#?}", socket_addresses);
// NOTE: No interface dial info for WSS, as there is no way to connect to a local dialinfo via TLS // NOTE: No interface dial info for WSS, as there is no way to connect to a local dialinfo via TLS
// If the hostname is specified, it is the public dialinfo via the URL. If no hostname // If the hostname is specified, it is the public dialinfo via the URL. If no hostname
@ -629,7 +631,7 @@ impl Network {
Box::new(|_, _, a| Box::new(RawTcpProtocolHandler::new(a))), Box::new(|_, _, a| Box::new(RawTcpProtocolHandler::new(a))),
) )
.await?; .await?;
trace!("TCP: listener started"); trace!("TCP: listener started on {:#?}", socket_addresses);
let mut static_public = false; let mut static_public = false;
let mut registered_addresses: HashSet<IpAddr> = HashSet::new(); let mut registered_addresses: HashSet<IpAddr> = HashSet::new();

View File

@ -90,6 +90,7 @@ pub enum SendDataKind {
struct NetworkManagerInner { struct NetworkManagerInner {
routing_table: Option<RoutingTable>, routing_table: Option<RoutingTable>,
components: Option<NetworkComponents>, components: Option<NetworkComponents>,
update_callback: Option<UpdateCallback>,
stats: NetworkManagerStats, stats: NetworkManagerStats,
client_whitelist: LruCache<key::DHTKey, ClientWhitelistEntry>, client_whitelist: LruCache<key::DHTKey, ClientWhitelistEntry>,
relay_node: Option<NodeRef>, relay_node: Option<NodeRef>,
@ -116,6 +117,7 @@ impl NetworkManager {
NetworkManagerInner { NetworkManagerInner {
routing_table: None, routing_table: None,
components: None, components: None,
update_callback: None,
stats: NetworkManagerStats::default(), stats: NetworkManagerStats::default(),
client_whitelist: LruCache::new_unbounded(), client_whitelist: LruCache::new_unbounded(),
relay_node: None, relay_node: None,
@ -205,10 +207,11 @@ impl NetworkManager {
self.inner.lock().relay_node.clone() self.inner.lock().relay_node.clone()
} }
pub async fn init(&self) -> Result<(), String> { pub async fn init(&self, update_callback: UpdateCallback) -> Result<(), String> {
let routing_table = RoutingTable::new(self.clone()); let routing_table = RoutingTable::new(self.clone());
routing_table.init().await?; routing_table.init().await?;
self.inner.lock().routing_table = Some(routing_table.clone()); self.inner.lock().routing_table = Some(routing_table.clone());
self.inner.lock().update_callback = Some(update_callback);
Ok(()) Ok(())
} }
pub async fn terminate(&self) { pub async fn terminate(&self) {
@ -219,6 +222,7 @@ impl NetworkManager {
if let Some(routing_table) = routing_table { if let Some(routing_table) = routing_table {
routing_table.terminate().await; routing_table.terminate().await;
} }
self.inner.lock().update_callback = None;
} }
pub async fn internal_startup(&self) -> Result<(), String> { pub async fn internal_startup(&self) -> Result<(), String> {
@ -256,9 +260,16 @@ impl NetworkManager {
self.shutdown().await; self.shutdown().await;
return Err(e); return Err(e);
} }
self.send_network_update();
Ok(()) Ok(())
} }
pub fn is_started(&self) -> bool {
self.inner.lock().components.is_some()
}
pub async fn shutdown(&self) { pub async fn shutdown(&self) {
trace!("NetworkManager::shutdown begin"); trace!("NetworkManager::shutdown begin");
@ -272,8 +283,13 @@ impl NetworkManager {
} }
// reset the state // reset the state
let mut inner = self.inner.lock(); {
inner.components = None; let mut inner = self.inner.lock();
inner.components = None;
}
// send update
self.send_network_update();
trace!("NetworkManager::shutdown end"); trace!("NetworkManager::shutdown end");
} }
@ -334,7 +350,9 @@ impl NetworkManager {
// if things can't restart, then we fail out of the attachment manager // if things can't restart, then we fail out of the attachment manager
if net.needs_restart() { if net.needs_restart() {
net.shutdown().await; net.shutdown().await;
self.send_network_update();
net.startup().await?; net.startup().await?;
self.send_network_update();
} }
// Run the routing table tick // Run the routing table tick
@ -1155,35 +1173,41 @@ impl NetworkManager {
// Compute transfer statistics for the low level network // Compute transfer statistics for the low level network
async fn rolling_transfers_task_routine(self, last_ts: u64, cur_ts: u64) -> Result<(), String> { async fn rolling_transfers_task_routine(self, last_ts: u64, cur_ts: u64) -> Result<(), String> {
log_net!("--- network manager rolling_transfers task"); log_net!("--- network manager rolling_transfers task");
let inner = &mut *self.inner.lock(); {
let inner = &mut *self.inner.lock();
// Roll the low level network transfer stats for our address // Roll the low level network transfer stats for our address
inner inner
.stats .stats
.self_stats .self_stats
.transfer_stats_accounting .transfer_stats_accounting
.roll_transfers(last_ts, cur_ts, &mut inner.stats.self_stats.transfer_stats); .roll_transfers(last_ts, cur_ts, &mut inner.stats.self_stats.transfer_stats);
// Roll all per-address transfers // Roll all per-address transfers
let mut dead_addrs: HashSet<PerAddressStatsKey> = HashSet::new(); let mut dead_addrs: HashSet<PerAddressStatsKey> = HashSet::new();
for (addr, stats) in &mut inner.stats.per_address_stats { for (addr, stats) in &mut inner.stats.per_address_stats {
stats.transfer_stats_accounting.roll_transfers( stats.transfer_stats_accounting.roll_transfers(
last_ts, last_ts,
cur_ts, cur_ts,
&mut stats.transfer_stats, &mut stats.transfer_stats,
); );
// While we're here, lets see if this address has timed out // While we're here, lets see if this address has timed out
if cur_ts - stats.last_seen_ts >= IPADDR_MAX_INACTIVE_DURATION_US { if cur_ts - stats.last_seen_ts >= IPADDR_MAX_INACTIVE_DURATION_US {
// it's dead, put it in the dead list // it's dead, put it in the dead list
dead_addrs.insert(*addr); dead_addrs.insert(*addr);
}
}
// Remove the dead addresses from our tables
for da in &dead_addrs {
inner.stats.per_address_stats.remove(da);
} }
} }
// Remove the dead addresses from our tables // Send update
for da in &dead_addrs { self.send_network_update();
inner.stats.per_address_stats.remove(da);
}
Ok(()) Ok(())
} }
@ -1220,6 +1244,45 @@ impl NetworkManager {
.add_down(bytes); .add_down(bytes);
} }
// Get stats
pub fn get_stats(&self) -> NetworkManagerStats {
let inner = self.inner.lock();
inner.stats.clone()
}
fn get_veilid_state_inner(inner: &NetworkManagerInner) -> VeilidStateNetwork {
if inner.components.is_some() {
VeilidStateNetwork {
started: true,
bps_down: inner.stats.self_stats.transfer_stats.down.average,
bps_up: inner.stats.self_stats.transfer_stats.up.average,
}
} else {
VeilidStateNetwork {
started: false,
bps_down: 0,
bps_up: 0,
}
}
}
pub fn get_veilid_state(&self) -> VeilidStateNetwork {
let inner = self.inner.lock();
Self::get_veilid_state_inner(&*inner)
}
fn send_network_update(&self) {
let (update_cb, state) = {
let inner = self.inner.lock();
let update_cb = inner.update_callback.clone();
if update_cb.is_none() {
return;
}
let state = Self::get_veilid_state_inner(&*inner);
(update_cb.unwrap(), state)
};
update_cb(VeilidUpdate::Network(state));
}
// Determine if a local IP address has changed // Determine if a local IP address has changed
// this means we should restart the low level network and and recreate all of our dial info // this means we should restart the low level network and and recreate all of our dial info
// Wait until we have received confirmation from N different peers // Wait until we have received confirmation from N different peers

View File

@ -205,10 +205,6 @@ impl ReceiptManager {
let config = self.core().config(); let config = self.core().config();
let c = config.get(); let c = config.get();
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.max_server_signal_leases = c.network.leases.max_server_signal_leases as usize;
inner.max_server_relay_leases = c.network.leases.max_server_relay_leases as usize;
inner.max_client_signal_leases = c.network.leases.max_client_signal_leases as usize;
inner.max_client_relay_leases = c.network.leases.max_client_relay_leases as usize;
} }
*/ */
Ok(()) Ok(())

View File

@ -22,20 +22,36 @@ impl RoutingTable {
out out
} }
pub fn debug_info_dialinfo(&self) -> String { pub fn debug_info_dialinfo(&self, txt_format: bool) -> String {
let ldis = self.dial_info_details(RoutingDomain::LocalNetwork); if txt_format {
let gdis = self.dial_info_details(RoutingDomain::PublicInternet); let mut out = String::new();
let mut out = String::new();
out += "Local Network Dial Info Details:\n"; let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
for (n, ldi) in ldis.iter().enumerate() { if gdis.is_empty() {
out += &format!(" {:>2}: {:?}\n", n, ldi); out += "No TXT Record DialInfo\n";
} else {
out += "TXT Record DialInfo:\n";
out += &format!("{}\n", self.node_id().encode());
for gdi in gdis {
out += &format!("{}\n", gdi.dial_info);
}
}
out
} else {
let ldis = self.dial_info_details(RoutingDomain::LocalNetwork);
let gdis = self.dial_info_details(RoutingDomain::PublicInternet);
let mut out = String::new();
out += "Local Network Dial Info Details:\n";
for (n, ldi) in ldis.iter().enumerate() {
out += &format!(" {:>2}: {:?}\n", n, ldi);
}
out += "Public Internet Dial Info Details:\n";
for (n, gdi) in gdis.iter().enumerate() {
out += &format!(" {:>2}: {:?}\n", n, gdi);
}
out
} }
out += "Public Internet Dial Info Details:\n";
for (n, gdi) in gdis.iter().enumerate() {
out += &format!(" {:>2}: {:?}\n", n, gdi);
}
out
} }
pub fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String { pub fn debug_info_entries(&self, limit: usize, min_state: BucketEntryState) -> String {
let inner = self.inner.lock(); let inner = self.inner.lock();

View File

@ -771,17 +771,36 @@ impl RoutingTable {
} }
} }
async fn resolve_bootstrap(&self, bootstrap: Vec<String>) -> Result<Vec<String>, String> {
let mut out = Vec::<String>::new();
for bh in bootstrap {
//
}
Ok(out)
}
async fn bootstrap_task_routine(self) -> Result<(), String> { async fn bootstrap_task_routine(self) -> Result<(), String> {
let bootstrap = { let (bootstrap, bootstrap_nodes) = {
let c = self.config.get(); let c = self.config.get();
c.network.bootstrap.clone() (
c.network.bootstrap.clone(),
c.network.bootstrap_nodes.clone(),
)
}; };
log_rtab!("--- bootstrap_task"); log_rtab!("--- bootstrap_task");
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
let bootstrap_nodes = if !bootstrap_nodes.is_empty() {
bootstrap_nodes
} else {
// Resolve bootstrap servers and recurse their TXT entries
self.resolve_bootstrap(bootstrap).await?
};
// Map all bootstrap entries to a single key with multiple dialinfo // Map all bootstrap entries to a single key with multiple dialinfo
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new(); let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new();
for b in bootstrap { for b in bootstrap_nodes {
let ndis = NodeDialInfo::from_str(b.as_str()) let ndis = NodeDialInfo::from_str(b.as_str())
.map_err(map_to_string) .map_err(map_to_string)
.map_err(logthru_rtab!("Invalid dial info in bootstrap entry: {}", b))?; .map_err(logthru_rtab!("Invalid dial info in bootstrap entry: {}", b))?;
@ -794,7 +813,7 @@ impl RoutingTable {
class: DialInfoClass::Direct, // Bootstraps are always directly reachable class: DialInfoClass::Direct, // Bootstraps are always directly reachable
}); });
} }
log_rtab!(" bootstrap list: {:?}", bsmap); log_rtab!(" bootstrap node dialinfo: {:?}", bsmap);
// Run all bootstrap operations concurrently // Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::new(); let mut unord = FuturesUnordered::new();

View File

@ -192,9 +192,12 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.max_connections_per_ip6_prefix_size" => Ok(Box::new(56u32)), "network.max_connections_per_ip6_prefix_size" => Ok(Box::new(56u32)),
"network.max_connection_frequency_per_min" => Ok(Box::new(8u32)), "network.max_connection_frequency_per_min" => Ok(Box::new(8u32)),
"network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)), "network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)),
"network.reverse_connection_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.hole_punch_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.node_id" => Ok(Box::new(dht::key::DHTKey::default())), "network.node_id" => Ok(Box::new(dht::key::DHTKey::default())),
"network.node_id_secret" => Ok(Box::new(dht::key::DHTKeySecret::default())), "network.node_id_secret" => Ok(Box::new(dht::key::DHTKeySecret::default())),
"network.bootstrap" => Ok(Box::new(Vec::<String>::new())), "network.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.bootstrap_nodes" => Ok(Box::new(Vec::<String>::new())),
"network.routing_table.limit_over_attached" => Ok(Box::new(64u32)), "network.routing_table.limit_over_attached" => Ok(Box::new(64u32)),
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)), "network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
"network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)), "network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)),
@ -255,10 +258,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.protocol.wss.listen_address" => Ok(Box::new("".to_owned())), "network.protocol.wss.listen_address" => Ok(Box::new("".to_owned())),
"network.protocol.wss.path" => Ok(Box::new(String::from("ws"))), "network.protocol.wss.path" => Ok(Box::new(String::from("ws"))),
"network.protocol.wss.url" => Ok(Box::new(Option::<String>::None)), "network.protocol.wss.url" => Ok(Box::new(Option::<String>::None)),
"network.leases.max_server_signal_leases" => Ok(Box::new(256u32)),
"network.leases.max_server_relay_leases" => Ok(Box::new(8u32)),
"network.leases.max_client_signal_leases" => Ok(Box::new(2u32)),
"network.leases.max_client_relay_leases" => Ok(Box::new(2u32)),
_ => { _ => {
let err = format!("config key '{}' doesn't exist", key); let err = format!("config key '{}' doesn't exist", key);
debug!("{}", err); debug!("{}", err);
@ -318,9 +317,12 @@ pub async fn test_config() {
assert_eq!(inner.network.max_connections_per_ip6_prefix_size, 56u32); assert_eq!(inner.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(inner.network.max_connection_frequency_per_min, 8u32); assert_eq!(inner.network.max_connection_frequency_per_min, 8u32);
assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32); assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(inner.network.reverse_connection_receipt_time_ms, 5_000u32);
assert_eq!(inner.network.hole_punch_receipt_time_ms, 5_000u32);
assert!(!inner.network.node_id.valid); assert!(!inner.network.node_id.valid);
assert!(!inner.network.node_id_secret.valid); assert!(!inner.network.node_id_secret.valid);
assert_eq!(inner.network.bootstrap, Vec::<String>::new()); assert_eq!(inner.network.bootstrap, Vec::<String>::new());
assert_eq!(inner.network.bootstrap_nodes, Vec::<String>::new());
assert_eq!(inner.network.rpc.concurrency, 2u32); assert_eq!(inner.network.rpc.concurrency, 2u32);
assert_eq!(inner.network.rpc.queue_size, 128u32); assert_eq!(inner.network.rpc.queue_size, 128u32);
assert_eq!(inner.network.rpc.timeout_ms, 10_000u32); assert_eq!(inner.network.rpc.timeout_ms, 10_000u32);

View File

@ -79,10 +79,16 @@ impl VeilidAPI {
Ok(routing_table.debug_info_buckets(min_state)) Ok(routing_table.debug_info_buckets(min_state))
} }
async fn debug_dialinfo(&self, _args: String) -> Result<String, VeilidAPIError> { async fn debug_dialinfo(&self, args: String) -> Result<String, VeilidAPIError> {
let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect();
let is_txt = if args.len() == 1 {
args[0] == "txt"
} else {
false
};
// Dump routing table dialinfo // Dump routing table dialinfo
let routing_table = self.network_manager()?.routing_table(); let routing_table = self.network_manager()?.routing_table();
Ok(routing_table.debug_info_dialinfo()) Ok(routing_table.debug_info_dialinfo(is_txt))
} }
async fn debug_entries(&self, args: String) -> Result<String, VeilidAPIError> { async fn debug_entries(&self, args: String) -> Result<String, VeilidAPIError> {
@ -147,7 +153,7 @@ impl VeilidAPI {
// Must be detached // Must be detached
if !matches!( if !matches!(
self.get_state().await?.attachment, self.get_state().await?.attachment.state,
AttachmentState::Detached AttachmentState::Detached
) { ) {
return Err(VeilidAPIError::Internal { return Err(VeilidAPIError::Internal {
@ -168,7 +174,7 @@ impl VeilidAPI {
if args[0] == "buckets" { if args[0] == "buckets" {
// Must be detached // Must be detached
if matches!( if matches!(
self.get_state().await?.attachment, self.get_state().await?.attachment.state,
AttachmentState::Detached | AttachmentState::Detaching AttachmentState::Detached | AttachmentState::Detaching
) { ) {
return Err(VeilidAPIError::Internal { return Err(VeilidAPIError::Internal {
@ -194,7 +200,7 @@ impl VeilidAPI {
async fn debug_attach(&self, _args: String) -> Result<String, VeilidAPIError> { async fn debug_attach(&self, _args: String) -> Result<String, VeilidAPIError> {
if !matches!( if !matches!(
self.get_state().await?.attachment, self.get_state().await?.attachment.state,
AttachmentState::Detached AttachmentState::Detached
) { ) {
return Err(VeilidAPIError::Internal { return Err(VeilidAPIError::Internal {
@ -209,7 +215,7 @@ impl VeilidAPI {
async fn debug_detach(&self, _args: String) -> Result<String, VeilidAPIError> { async fn debug_detach(&self, _args: String) -> Result<String, VeilidAPIError> {
if matches!( if matches!(
self.get_state().await?.attachment, self.get_state().await?.attachment.state,
AttachmentState::Detaching AttachmentState::Detaching
) { ) {
return Err(VeilidAPIError::Internal { return Err(VeilidAPIError::Internal {

View File

@ -167,22 +167,37 @@ impl VeilidLogLevel {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateLog {
pub log_level: VeilidLogLevel,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateAttachment {
pub state: AttachmentState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidStateNetwork {
pub started: bool,
pub bps_down: u64,
pub bps_up: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")] #[serde(tag = "kind")]
pub enum VeilidUpdate { pub enum VeilidUpdate {
Log { Log(VeilidStateLog),
log_level: VeilidLogLevel, Attachment(VeilidStateAttachment),
message: String, Network(VeilidStateNetwork),
},
Attachment {
state: AttachmentState,
},
Shutdown, Shutdown,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VeilidState { pub struct VeilidState {
pub attachment: AttachmentState, pub attachment: VeilidStateAttachment,
pub network: VeilidStateNetwork,
} }
///////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////
@ -745,8 +760,37 @@ impl fmt::Display for DialInfo {
match self { match self {
DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address), DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address),
DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address), DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address),
DialInfo::WS(di) => write!(f, "ws|{}|{}", di.socket_address, di.request), DialInfo::WS(di) => {
DialInfo::WSS(di) => write!(f, "wss|{}|{}", di.socket_address, di.request), let url = format!("ws://{}", di.request);
let split_url = SplitUrl::from_str(&url).unwrap();
match split_url.host {
SplitUrlHost::Hostname(_) => {
write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request)
}
SplitUrlHost::IpAddr(a) => {
if di.socket_address.to_ip_addr() == a {
write!(f, "ws|{}", di.request)
} else {
panic!("resolved address does not match url: {}", di.request);
}
}
}
}
DialInfo::WSS(di) => {
let url = format!("wss://{}", di.request);
let split_url = SplitUrl::from_str(&url).unwrap();
match split_url.host {
SplitUrlHost::Hostname(_) => {
write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request)
}
SplitUrlHost::IpAddr(_) => {
panic!(
"secure websockets can not use ip address in request: {}",
di.request
);
}
}
}
} }
} }
} }
@ -767,18 +811,50 @@ impl FromStr for DialInfo {
Ok(DialInfo::tcp(socket_address)) Ok(DialInfo::tcp(socket_address))
} }
"ws" => { "ws" => {
let (sa, rest) = rest.split_once('|').ok_or_else(|| { let url = format!("ws://{}", rest);
parse_error!("DialInfo::from_str missing socket address '|' separator", s) let split_url = SplitUrl::from_str(&url)
})?; .map_err(|e| parse_error!(format!("unable to split WS url: {}", e), url))?;
let socket_address = SocketAddress::from_str(sa)?; if split_url.scheme != "ws" || !url.starts_with("ws://") {
DialInfo::try_ws(socket_address, format!("ws://{}", rest)) return Err(parse_error!("incorrect scheme for WS dialinfo", url));
}
let url_port = split_url.port.unwrap_or(80u16);
match rest.split_once('|') {
Some((sa, rest)) => {
let address = Address::from_str(sa)?;
DialInfo::try_ws(
SocketAddress::new(address, url_port),
format!("ws://{}", rest),
)
}
None => {
let address = Address::from_str(&split_url.host.to_string())?;
DialInfo::try_ws(
SocketAddress::new(address, url_port),
format!("ws://{}", rest),
)
}
}
} }
"wss" => { "wss" => {
let (sa, rest) = rest.split_once('|').ok_or_else(|| { let url = format!("wss://{}", rest);
let split_url = SplitUrl::from_str(&url)
.map_err(|e| parse_error!(format!("unable to split WSS url: {}", e), url))?;
if split_url.scheme != "wss" || !url.starts_with("wss://") {
return Err(parse_error!("incorrect scheme for WSS dialinfo", url));
}
let url_port = split_url.port.unwrap_or(443u16);
let (a, rest) = rest.split_once('|').ok_or_else(|| {
parse_error!("DialInfo::from_str missing socket address '|' separator", s) parse_error!("DialInfo::from_str missing socket address '|' separator", s)
})?; })?;
let socket_address = SocketAddress::from_str(sa)?;
DialInfo::try_wss(socket_address, format!("wss://{}", rest)) let address = Address::from_str(a)?;
DialInfo::try_wss(
SocketAddress::new(address, url_port),
format!("wss://{}", rest),
)
} }
_ => Err(parse_error!("DialInfo::from_str has invalid scheme", s)), _ => Err(parse_error!("DialInfo::from_str has invalid scheme", s)),
} }
@ -819,6 +895,14 @@ impl DialInfo {
url url
)); ));
} }
if let SplitUrlHost::IpAddr(a) = split_url.host {
if socket_address.to_ip_addr() != a {
return Err(parse_error!(
format!("request address does not match socket address: {}", a),
socket_address
));
}
}
Ok(Self::WS(DialInfoWS { Ok(Self::WS(DialInfoWS {
socket_address: socket_address.to_canonical(), socket_address: socket_address.to_canonical(),
request: url[5..].to_string(), request: url[5..].to_string(),
@ -1497,11 +1581,19 @@ impl VeilidAPI {
// get a full copy of the current state // get a full copy of the current state
pub async fn get_state(&self) -> Result<VeilidState, VeilidAPIError> { pub async fn get_state(&self) -> Result<VeilidState, VeilidAPIError> {
let attachment_manager = self.attachment_manager()?; let attachment_manager = self.attachment_manager()?;
let network_manager = attachment_manager.network_manager();
let attachment = attachment_manager.get_veilid_state();
let network = network_manager.get_veilid_state();
Ok(VeilidState { Ok(VeilidState {
attachment: attachment_manager.get_state(), attachment,
network,
}) })
} }
// get network connectedness
// connect to the network // connect to the network
pub async fn attach(&self) -> Result<(), VeilidAPIError> { pub async fn attach(&self) -> Result<(), VeilidAPIError> {
let attachment_manager = self.attachment_manager()?; let attachment_manager = self.attachment_manager()?;

View File

@ -104,7 +104,6 @@ pub struct VeilidConfigDHT {
pub min_peer_count: u32, pub min_peer_count: u32,
pub min_peer_refresh_time_ms: u32, pub min_peer_refresh_time_ms: u32,
pub validate_dial_info_receipt_time_ms: u32, pub validate_dial_info_receipt_time_ms: u32,
pub nearby_node_percentage: u32,
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
@ -117,13 +116,6 @@ pub struct VeilidConfigRPC {
pub max_route_hop_count: u8, pub max_route_hop_count: u8,
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct VeilidConfigLeases {
pub max_server_signal_leases: u32,
pub max_server_relay_leases: u32,
pub max_client_signal_leases: u32,
pub max_client_relay_leases: u32,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct VeilidConfigRoutingTable { pub struct VeilidConfigRoutingTable {
pub limit_over_attached: u32, pub limit_over_attached: u32,
@ -147,6 +139,7 @@ pub struct VeilidConfigNetwork {
pub node_id: key::DHTKey, pub node_id: key::DHTKey,
pub node_id_secret: key::DHTKeySecret, pub node_id_secret: key::DHTKeySecret,
pub bootstrap: Vec<String>, pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<String>,
pub routing_table: VeilidConfigRoutingTable, pub routing_table: VeilidConfigRoutingTable,
pub rpc: VeilidConfigRPC, pub rpc: VeilidConfigRPC,
pub dht: VeilidConfigDHT, pub dht: VeilidConfigDHT,
@ -157,7 +150,6 @@ pub struct VeilidConfigNetwork {
pub tls: VeilidConfigTLS, pub tls: VeilidConfigTLS,
pub application: VeilidConfigApplication, pub application: VeilidConfigApplication,
pub protocol: VeilidConfigProtocol, pub protocol: VeilidConfigProtocol,
pub leases: VeilidConfigLeases,
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
@ -305,6 +297,7 @@ impl VeilidConfig {
get_config!(inner.network.max_connection_frequency_per_min); get_config!(inner.network.max_connection_frequency_per_min);
get_config!(inner.network.client_whitelist_timeout_ms); get_config!(inner.network.client_whitelist_timeout_ms);
get_config!(inner.network.bootstrap); get_config!(inner.network.bootstrap);
get_config!(inner.network.bootstrap_nodes);
get_config!(inner.network.routing_table.limit_over_attached); get_config!(inner.network.routing_table.limit_over_attached);
get_config!(inner.network.routing_table.limit_fully_attached); get_config!(inner.network.routing_table.limit_fully_attached);
get_config!(inner.network.routing_table.limit_attached_strong); get_config!(inner.network.routing_table.limit_attached_strong);
@ -365,10 +358,6 @@ impl VeilidConfig {
get_config!(inner.network.protocol.wss.listen_address); get_config!(inner.network.protocol.wss.listen_address);
get_config!(inner.network.protocol.wss.path); get_config!(inner.network.protocol.wss.path);
get_config!(inner.network.protocol.wss.url); get_config!(inner.network.protocol.wss.url);
get_config!(inner.network.leases.max_server_signal_leases);
get_config!(inner.network.leases.max_server_relay_leases);
get_config!(inner.network.leases.max_client_signal_leases);
get_config!(inner.network.leases.max_client_relay_leases);
} }
// Validate settings // Validate settings
self.validate()?; self.validate()?;

View File

@ -38,104 +38,102 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
delete: false, delete: false,
), ),
network: VeilidConfigNetwork( network: VeilidConfigNetwork(
connectionInitialTimeoutMs: 2000,
connectionInactivityTimeoutMs: 60000,
maxConnectionsPerIp4: 8,
maxConnectionsPerIp6Prefix: 8,
maxConnectionsPerIp6PrefixSize: 56,
maxConnectionFrequencyPerMin: 8,
clientWhitelistTimeoutMs: 300000,
reverseConnectionReceiptTimeMs: 5000,
holePunchReceiptTimeMs: 5000,
nodeId: "",
nodeIdSecret: "",
bootstrap: [],
bootstrapNodes: [],
routingTable: VeilidConfigRoutingTable(
limitOverAttached: 64,
limitFullyAttached: 32,
limitAttachedStrong: 16,
limitAttachedGood: 8,
limitAttachedWeak: 4,
),
rpc: VeilidConfigRPC(
concurrency: 0,
queueSize: 1024,
maxTimestampBehindMs: 10000,
maxTimestampAheadMs: 10000,
timeoutMs: 10000,
maxRouteHopCount: 7,
),
dht: VeilidConfigDHT(
resolveNodeTimeoutMs: null,
resolveNodeCount: 20,
resolveNodeFanout: 3,
maxFindNodeCount: 20,
getValueTimeoutMs: null,
getValueCount: 20,
getValueFanout: 3,
setValueTimeoutMs: null,
setValueCount: 20,
setValueFanout: 5,
minPeerCount: 20,
minPeerRefreshTimeMs: 2000,
validateDialInfoReceiptTimeMs: 5000,
),
upnp: true,
natpmp: true,
enableLocalPeerScope: false,
restrictedNatRetries: 3,
tls: VeilidConfigTLS(
certificatePath: "",
privateKeyPath: "",
connectionInitialTimeoutMs: 2000, connectionInitialTimeoutMs: 2000,
connectionInactivityTimeoutMs: 60000, ),
maxConnectionsPerIp4: 8, application: VeilidConfigApplication(
maxConnectionsPerIp6Prefix: 8, https: VeilidConfigHTTPS(
maxConnectionsPerIp6PrefixSize: 56, enabled: false,
maxConnectionFrequencyPerMin: 8,
clientWhitelistTimeoutMs: 300000,
nodeId: "",
nodeIdSecret: "",
bootstrap: [],
routingTable: VeilidConfigRoutingTable(
limitOverAttached: 64,
limitFullyAttached: 32,
limitAttachedStrong: 16,
limitAttachedGood: 8,
limitAttachedWeak: 4,
),
rpc: VeilidConfigRPC(
concurrency: 0,
queueSize: 1024,
maxTimestampBehindMs: 10000,
maxTimestampAheadMs: 10000,
timeoutMs: 10000,
maxRouteHopCount: 7,
),
dht: VeilidConfigDHT(
resolveNodeTimeoutMs: null,
resolveNodeCount: 20,
resolveNodeFanout: 3,
maxFindNodeCount: 20,
getValueTimeoutMs: null,
getValueCount: 20,
getValueFanout: 3,
setValueTimeoutMs: null,
setValueCount: 20,
setValueFanout: 5,
minPeerCount: 20,
minPeerRefreshTimeMs: 2000,
validateDialInfoReceiptTimeMs: 5000,
),
upnp: true,
natpmp: true,
enableLocalPeerScope: false,
restrictedNatRetries: 3,
tls: VeilidConfigTLS(
certificatePath: "",
privateKeyPath: "",
connectionInitialTimeoutMs: 2000,
),
application: VeilidConfigApplication(
https: VeilidConfigHTTPS(
enabled: false,
listenAddress: "",
path: "",
url: null,
),
http: VeilidConfigHTTP(
enabled: false,
listenAddress: "",
path: "",
url: null,
)),
protocol: VeilidConfigProtocol(
udp: VeilidConfigUDP(
enabled: !kIsWeb,
socketPoolSize: 0,
listenAddress: "", listenAddress: "",
publicAddress: null, path: "",
),
tcp: VeilidConfigTCP(
connect: !kIsWeb,
listen: !kIsWeb,
maxConnections: 32,
listenAddress: "",
publicAddress: null,
),
ws: VeilidConfigWS(
connect: true,
listen: !kIsWeb,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null, url: null,
), ),
wss: VeilidConfigWSS( http: VeilidConfigHTTP(
connect: true, enabled: false,
listen: false,
maxConnections: 16,
listenAddress: "", listenAddress: "",
path: "ws", path: "",
url: null, url: null,
), )),
protocol: VeilidConfigProtocol(
udp: VeilidConfigUDP(
enabled: !kIsWeb,
socketPoolSize: 0,
listenAddress: "",
publicAddress: null,
), ),
leases: VeilidConfigLeases( tcp: VeilidConfigTCP(
maxServerSignalLeases: 256, connect: !kIsWeb,
maxServerRelayLeases: 8, listen: !kIsWeb,
maxClientSignalLeases: 2, maxConnections: 32,
maxClientRelayLeases: 2, listenAddress: "",
)), publicAddress: null,
),
ws: VeilidConfigWS(
connect: true,
listen: !kIsWeb,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
),
wss: VeilidConfigWSS(
connect: true,
listen: false,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
),
),
),
); );
} }

View File

@ -518,36 +518,6 @@ class VeilidConfigRoutingTable {
//////////// ////////////
class VeilidConfigLeases {
int maxServerSignalLeases;
int maxServerRelayLeases;
int maxClientSignalLeases;
int maxClientRelayLeases;
VeilidConfigLeases(
{required this.maxServerSignalLeases,
required this.maxServerRelayLeases,
required this.maxClientSignalLeases,
required this.maxClientRelayLeases});
Map<String, dynamic> get json {
return {
'max_server_signal_leases': maxServerSignalLeases,
'max_server_relay_leases': maxServerRelayLeases,
'max_client_signal_leases': maxClientSignalLeases,
'max_client_relay_leases': maxClientRelayLeases
};
}
VeilidConfigLeases.fromJson(Map<String, dynamic> json)
: maxServerSignalLeases = json['max_server_signal_leases'],
maxServerRelayLeases = json['max_server_relay_leases'],
maxClientSignalLeases = json['max_client_signal_leases'],
maxClientRelayLeases = json['max_client_relay_leases'];
}
////////////
class VeilidConfigNetwork { class VeilidConfigNetwork {
int connectionInitialTimeoutMs; int connectionInitialTimeoutMs;
int connectionInactivityTimeoutMs; int connectionInactivityTimeoutMs;
@ -556,9 +526,12 @@ class VeilidConfigNetwork {
int maxConnectionsPerIp6PrefixSize; int maxConnectionsPerIp6PrefixSize;
int maxConnectionFrequencyPerMin; int maxConnectionFrequencyPerMin;
int clientWhitelistTimeoutMs; int clientWhitelistTimeoutMs;
int reverseConnectionReceiptTimeMs;
int holePunchReceiptTimeMs;
String nodeId; String nodeId;
String nodeIdSecret; String nodeIdSecret;
List<String> bootstrap; List<String> bootstrap;
List<String> bootstrapNodes;
VeilidConfigRoutingTable routingTable; VeilidConfigRoutingTable routingTable;
VeilidConfigRPC rpc; VeilidConfigRPC rpc;
VeilidConfigDHT dht; VeilidConfigDHT dht;
@ -569,7 +542,6 @@ class VeilidConfigNetwork {
VeilidConfigTLS tls; VeilidConfigTLS tls;
VeilidConfigApplication application; VeilidConfigApplication application;
VeilidConfigProtocol protocol; VeilidConfigProtocol protocol;
VeilidConfigLeases leases;
VeilidConfigNetwork({ VeilidConfigNetwork({
required this.connectionInitialTimeoutMs, required this.connectionInitialTimeoutMs,
@ -579,9 +551,12 @@ class VeilidConfigNetwork {
required this.maxConnectionsPerIp6PrefixSize, required this.maxConnectionsPerIp6PrefixSize,
required this.maxConnectionFrequencyPerMin, required this.maxConnectionFrequencyPerMin,
required this.clientWhitelistTimeoutMs, required this.clientWhitelistTimeoutMs,
required this.reverseConnectionReceiptTimeMs,
required this.holePunchReceiptTimeMs,
required this.nodeId, required this.nodeId,
required this.nodeIdSecret, required this.nodeIdSecret,
required this.bootstrap, required this.bootstrap,
required this.bootstrapNodes,
required this.routingTable, required this.routingTable,
required this.rpc, required this.rpc,
required this.dht, required this.dht,
@ -592,7 +567,6 @@ class VeilidConfigNetwork {
required this.tls, required this.tls,
required this.application, required this.application,
required this.protocol, required this.protocol,
required this.leases,
}); });
Map<String, dynamic> get json { Map<String, dynamic> get json {
@ -604,9 +578,12 @@ class VeilidConfigNetwork {
'max_connections_per_ip6_prefix_size': maxConnectionsPerIp6PrefixSize, 'max_connections_per_ip6_prefix_size': maxConnectionsPerIp6PrefixSize,
'max_connection_frequency_per_min': maxConnectionFrequencyPerMin, 'max_connection_frequency_per_min': maxConnectionFrequencyPerMin,
'client_whitelist_timeout_ms': clientWhitelistTimeoutMs, 'client_whitelist_timeout_ms': clientWhitelistTimeoutMs,
'reverse_connection_receipt_time_ms': reverseConnectionReceiptTimeMs,
'hole_punch_receipt_time_ms': holePunchReceiptTimeMs,
'node_id': nodeId, 'node_id': nodeId,
'node_id_secret': nodeIdSecret, 'node_id_secret': nodeIdSecret,
'bootstrap': bootstrap, 'bootstrap': bootstrap,
'bootstrap_nodes': bootstrapNodes,
'routing_table': routingTable.json, 'routing_table': routingTable.json,
'rpc': rpc.json, 'rpc': rpc.json,
'dht': dht.json, 'dht': dht.json,
@ -617,7 +594,6 @@ class VeilidConfigNetwork {
'tls': tls.json, 'tls': tls.json,
'application': application.json, 'application': application.json,
'protocol': protocol.json, 'protocol': protocol.json,
'leases': leases.json,
}; };
} }
@ -631,9 +607,13 @@ class VeilidConfigNetwork {
json['max_connections_per_ip6_prefix_size'], json['max_connections_per_ip6_prefix_size'],
maxConnectionFrequencyPerMin = json['max_connection_frequency_per_min'], maxConnectionFrequencyPerMin = json['max_connection_frequency_per_min'],
clientWhitelistTimeoutMs = json['client_whitelist_timeout_ms'], clientWhitelistTimeoutMs = json['client_whitelist_timeout_ms'],
reverseConnectionReceiptTimeMs =
json['reverse_connection_receipt_time_ms'],
holePunchReceiptTimeMs = json['hole_punch_receipt_time_ms'],
nodeId = json['node_id'], nodeId = json['node_id'],
nodeIdSecret = json['node_id_secret'], nodeIdSecret = json['node_id_secret'],
bootstrap = json['bootstrap'], bootstrap = json['bootstrap'],
bootstrapNodes = json['bootstrap_nodes'],
routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']), routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']),
rpc = VeilidConfigRPC.fromJson(json['rpc']), rpc = VeilidConfigRPC.fromJson(json['rpc']),
dht = VeilidConfigDHT.fromJson(json['dht']), dht = VeilidConfigDHT.fromJson(json['dht']),
@ -643,8 +623,7 @@ class VeilidConfigNetwork {
restrictedNatRetries = json['restricted_nat_retries'], restrictedNatRetries = json['restricted_nat_retries'],
tls = VeilidConfigTLS.fromJson(json['tls']), tls = VeilidConfigTLS.fromJson(json['tls']),
application = VeilidConfigApplication.fromJson(json['application']), application = VeilidConfigApplication.fromJson(json['application']),
protocol = VeilidConfigProtocol.fromJson(json['protocol']), protocol = VeilidConfigProtocol.fromJson(json['protocol']);
leases = VeilidConfigLeases.fromJson(json['leases']);
} }
//////////// ////////////
@ -824,6 +803,11 @@ abstract class VeilidUpdate {
{ {
return VeilidUpdateAttachment(attachmentStateFromJson(json["state"])); return VeilidUpdateAttachment(attachmentStateFromJson(json["state"]));
} }
case "Network":
{
return VeilidUpdateNetwork(
json["started"], json["bps_up"], json["bps_down"]);
}
default: default:
{ {
throw VeilidAPIExceptionInternal( throw VeilidAPIExceptionInternal(
@ -846,16 +830,50 @@ class VeilidUpdateAttachment implements VeilidUpdate {
VeilidUpdateAttachment(this.state); VeilidUpdateAttachment(this.state);
} }
class VeilidUpdateNetwork implements VeilidUpdate {
final bool started;
final int bpsDown;
final int bpsUp;
//
VeilidUpdateNetwork(this.started, this.bpsDown, this.bpsUp);
}
//////////////////////////////////////
/// VeilidStateAttachment
class VeilidStateAttachment {
final AttachmentState state;
VeilidStateAttachment(this.state);
VeilidStateAttachment.fromJson(Map<String, dynamic> json)
: state = attachmentStateFromJson(json['state']);
}
//////////////////////////////////////
/// VeilidStateNetwork
class VeilidStateNetwork {
final bool started;
VeilidStateNetwork(this.started);
VeilidStateNetwork.fromJson(Map<String, dynamic> json)
: started = json['started'];
}
////////////////////////////////////// //////////////////////////////////////
/// VeilidState /// VeilidState
class VeilidState { class VeilidState {
final AttachmentState attachment; final VeilidStateAttachment attachment;
final VeilidStateNetwork network;
VeilidState(this.attachment); VeilidState(this.attachment, this.network);
VeilidState.fromJson(Map<String, dynamic> json) VeilidState.fromJson(Map<String, dynamic> json)
: attachment = attachmentStateFromJson(json['attachment']); : attachment = VeilidStateAttachment.fromJson(json['attachment']),
network = VeilidStateNetwork.fromJson(json['network']);
} }
////////////////////////////////////// //////////////////////////////////////

View File

@ -33,12 +33,19 @@ ctrlc = "^3"
lazy_static = "^1" lazy_static = "^1"
bugsalot = "^0" bugsalot = "^0"
flume = { version = "^0", features = ["async"] } flume = { version = "^0", features = ["async"] }
rpassword = "^6"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows-service = "^0" windows-service = "^0"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
daemonize = "^0" daemonize = "^0"
signal-hook = "^0"
signal-hook-async-std = "^0"
nix = "^0"
[target.'cfg(target_os = "linux")'.dependencies]
systemd-journal-logger = "^0"
[dev-dependencies] [dev-dependencies]
serial_test = "^0" serial_test = "^0"

View File

@ -11,19 +11,27 @@ enum AttachmentState {
detaching @7; detaching @7;
} }
struct Attachment { struct VeilidStateAttachment {
state @0 :AttachmentState; state @0 :AttachmentState;
}
struct VeilidStateNetwork {
started @0 :Bool;
bpsDown @1 :UInt64;
bpsUp @2 :UInt64;
} }
struct VeilidUpdate { struct VeilidUpdate {
union { union {
attachment @0 :Attachment; attachment @0 :VeilidStateAttachment;
shutdown @1 :Void; network @1 :VeilidStateNetwork;
shutdown @2 :Void;
} }
} }
struct VeilidState { struct VeilidState {
attachment @0 :Attachment; attachment @0 :VeilidStateAttachment;
network @1 :VeilidStateNetwork;
} }
interface Registration {} interface Registration {}

View File

@ -36,16 +36,26 @@ fn convert_update(
mut rpc_update: crate::veilid_client_capnp::veilid_update::Builder, mut rpc_update: crate::veilid_client_capnp::veilid_update::Builder,
) { ) {
match update { match update {
veilid_core::VeilidUpdate::Log { veilid_core::VeilidUpdate::Log(veilid_core::VeilidStateLog {
log_level: _, log_level: _,
message: _, message: _,
} => { }) => {
panic!("Should not be logging to api in server!"); panic!("Should not be logging to api in server!");
} }
veilid_core::VeilidUpdate::Attachment { state } => { veilid_core::VeilidUpdate::Attachment(veilid_core::VeilidStateAttachment { state }) => {
let mut att = rpc_update.init_attachment(); let mut att = rpc_update.init_attachment();
att.set_state(convert_attachment_state(state)); att.set_state(convert_attachment_state(state));
} }
veilid_core::VeilidUpdate::Network(veilid_core::VeilidStateNetwork {
started,
bps_down,
bps_up,
}) => {
let mut nb = rpc_update.init_network();
nb.set_started(*started);
nb.set_bps_down(*bps_down);
nb.set_bps_up(*bps_up);
}
veilid_core::VeilidUpdate::Shutdown => { veilid_core::VeilidUpdate::Shutdown => {
rpc_update.set_shutdown(()); rpc_update.set_shutdown(());
} }
@ -54,11 +64,15 @@ fn convert_update(
fn convert_state( fn convert_state(
state: &veilid_core::VeilidState, state: &veilid_core::VeilidState,
rpc_state: crate::veilid_client_capnp::veilid_state::Builder, mut rpc_state: crate::veilid_client_capnp::veilid_state::Builder,
) { ) {
rpc_state let mut ab = rpc_state.reborrow().init_attachment();
.init_attachment() ab.set_state(convert_attachment_state(&state.attachment.state));
.set_state(convert_attachment_state(&state.attachment));
let mut nb = rpc_state.reborrow().init_network();
nb.set_started(state.network.started);
nb.set_bps_down(state.network.bps_down);
nb.set_bps_up(state.network.bps_up);
} }
// --- interface Registration --------------------------------- // --- interface Registration ---------------------------------

View File

@ -2,6 +2,7 @@ use crate::settings::*;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::str::FromStr; use std::str::FromStr;
use veilid_core::{DHTKey, DHTKeySecret};
fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap::Error> { fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap::Error> {
let matches = Command::new("veilid-server") let matches = Command::new("veilid-server")
@ -44,8 +45,8 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.help("Turn on trace logging on the terminal"), .help("Turn on trace logging on the terminal"),
) )
.arg( .arg(
Arg::new("subnode_index") Arg::new("subnode-index")
.long("subnode_index") .long("subnode-index")
.takes_value(true) .takes_value(true)
.help("Run as an extra daemon on the same machine for testing purposes, specify a number greater than zero to offset the listening ports"), .help("Run as an extra daemon on the same machine for testing purposes, specify a number greater than zero to offset the listening ports"),
) )
@ -54,6 +55,14 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.long("generate-dht-key") .long("generate-dht-key")
.help("Only generate a new dht key and print it"), .help("Only generate a new dht key and print it"),
) )
.arg(
Arg::new("set-node-id")
.long("set-node-id")
.takes_value(true)
.value_name("ID")
.help("Set the node id and secret key")
.long_help("To specify both node id and secret key on the command line, use a ID:SECRET syntax with a colon, like:\n zsVXz5aTU98vZxwTcDmvpcnO5g1B2jRO3wpdNiDrRgw:gJzQLmzuBvA-dFvEmLcYvLoO5bh7hzCWFzfpJHapZKg\nIf no colon is used, the node id is specified, and a prompt appears to enter the secret key interactively.")
)
.arg( .arg(
Arg::new("delete-protected-store") Arg::new("delete-protected-store")
.long("delete-protected-store") .long("delete-protected-store")
@ -74,12 +83,25 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
.long("dump-config") .long("dump-config")
.help("Instead of running the server, print the configuration it would use to the console"), .help("Instead of running the server, print the configuration it would use to the console"),
) )
.arg(
Arg::new("dump-txt-record")
.long("dump-txt-record")
.help("Prints the bootstrap TXT record for this node and then quits")
)
.arg( .arg(
Arg::new("bootstrap") Arg::new("bootstrap")
.long("bootstrap") .long("bootstrap")
.takes_value(true) .takes_value(true)
.value_name("BOOTSTRAP_LIST") .value_name("BOOTSTRAP_LIST")
.help("Specify a list of bootstrap servers to use"), .help("Specify a list of bootstrap hostnames to use")
)
.arg(
Arg::new("bootstrap-nodes")
.conflicts_with("bootstrap")
.long("bootstrap-nodes")
.takes_value(true)
.value_name("BOOTSTRAP_NODE_LIST")
.help("Specify a list of bootstrap node dialinfos to use"),
) )
.arg( .arg(
Arg::new("local") Arg::new("local")
@ -123,16 +145,16 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
// Set config from command line // Set config from command line
if matches.occurrences_of("daemon") != 0 { if matches.occurrences_of("daemon") != 0 {
settingsrw.daemon = true; settingsrw.daemon.enabled = true;
settingsrw.logging.terminal.enabled = false; settingsrw.logging.terminal.enabled = false;
} }
if matches.occurrences_of("subnode_index") != 0 { if matches.occurrences_of("subnode-index") != 0 {
let subnode_index = match matches.value_of("subnode_index") { let subnode_index = match matches.value_of("subnode-index") {
Some(x) => x Some(x) => x
.parse() .parse()
.map_err(|e| format!("couldn't parse subnode index: {}", e))?, .map_err(|e| format!("couldn't parse subnode index: {}", e))?,
None => { None => {
return Err("value not specified for subnode_index".to_owned()); return Err("value not specified for subnode-index".to_owned());
} }
}; };
if subnode_index == 0 { if subnode_index == 0 {
@ -164,16 +186,61 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
if matches.occurrences_of("delete-table-store") != 0 { if matches.occurrences_of("delete-table-store") != 0 {
settingsrw.core.table_store.delete = true; settingsrw.core.table_store.delete = true;
} }
if matches.occurrences_of("dump-txt-record") != 0 {
// Turn off terminal logging so we can be interactive
settingsrw.logging.terminal.enabled = false;
}
if let Some(v) = matches.value_of("set-node-id") {
// Turn off terminal logging so we can be interactive
settingsrw.logging.terminal.enabled = false;
// Split or get secret
let (k, s) = if let Some((k, s)) = v.split_once(':') {
let k = DHTKey::try_decode(k)?;
let s = DHTKeySecret::try_decode(s)?;
(k, s)
} else {
let k = DHTKey::try_decode(v)?;
let buffer = rpassword::prompt_password("Enter secret key (will not echo): ")
.map_err(|e| e.to_string())?;
let buffer = buffer.trim().to_string();
let s = DHTKeySecret::try_decode(&buffer)?;
(k, s)
};
settingsrw.core.network.node_id = k;
settingsrw.core.network.node_id_secret = s;
}
if matches.occurrences_of("bootstrap") != 0 { if matches.occurrences_of("bootstrap") != 0 {
let bootstrap = match matches.value_of("bootstrap") { let bootstrap_list = match matches.value_of("bootstrap-list") {
Some(x) => { Some(x) => {
println!("Overriding bootstrap with: "); println!("Overriding bootstrap list with: ");
let mut out: Vec<String> = Vec::new();
for x in x.split(',') {
let x = x.trim().to_string();
println!(" {}", x);
out.push(x);
}
out
}
None => {
return Err("value not specified for bootstrap list".to_owned());
}
};
settingsrw.core.network.bootstrap = bootstrap_list;
}
if matches.occurrences_of("bootstrap-nodes") != 0 {
let bootstrap_list = match matches.value_of("bootstrap-list") {
Some(x) => {
println!("Overriding bootstrap node list with: ");
let mut out: Vec<ParsedNodeDialInfo> = Vec::new(); let mut out: Vec<ParsedNodeDialInfo> = Vec::new();
for x in x.split(',') { for x in x.split(',') {
let x = x.trim();
println!(" {}", x); println!(" {}", x);
out.push(ParsedNodeDialInfo::from_str(x).map_err(|e| { out.push(ParsedNodeDialInfo::from_str(x).map_err(|e| {
format!( format!(
"unable to parse dial info in bootstrap list: {} for {}", "unable to parse dial info in bootstrap node list: {} for {}",
e, x e, x
) )
})?); })?);
@ -181,10 +248,10 @@ pub fn process_command_line() -> Result<(Settings, ArgMatches), String> {
out out
} }
None => { None => {
return Err("value not specified for bootstrap".to_owned()); return Err("value not specified for bootstrap node list".to_owned());
} }
}; };
settingsrw.core.network.bootstrap = bootstrap; settingsrw.core.network.bootstrap_nodes = bootstrap_list;
} }
// Apply subnode index if we're testing // Apply subnode index if we're testing

View File

@ -28,7 +28,6 @@ fn main() -> Result<(), String> {
// --- Dump Config --- // --- Dump Config ---
if matches.occurrences_of("dump-config") != 0 { if matches.occurrences_of("dump-config") != 0 {
//let cfg = config::Config::try_from(&*settingsr);
return serde_yaml::to_writer(std::io::stdout(), &*settings.read()) return serde_yaml::to_writer(std::io::stdout(), &*settings.read())
.map_err(|e| e.to_string()); .map_err(|e| e.to_string());
} }
@ -40,8 +39,37 @@ fn main() -> Result<(), String> {
return Ok(()); return Ok(());
} }
// See if we're just running a quick command
let (server_mode, success, failure) = if matches.occurrences_of("set-node-id") != 0 {
(
ServerMode::ShutdownImmediate,
"Node Id and Secret set successfully",
"Failed to set Node Id and Secret",
)
} else if matches.occurrences_of("dump-txt-record") != 0 {
(ServerMode::DumpTXTRecord, "", "Failed to dump txt record")
} else {
(ServerMode::Normal, "", "")
};
// Handle non-normal server modes
if !matches!(server_mode, ServerMode::Normal) {
// Init combined console/file logger
let logs = VeilidLogs::setup_normal_logs(settings.clone())?;
// run the server to set the node id and quit
return task::block_on(async { run_veilid_server(settings, logs, server_mode).await })
.map(|v| {
println!("{}", success);
v
})
.map_err(|e| {
println!("{}", failure);
e
});
}
// --- Daemon Mode ---- // --- Daemon Mode ----
if settings.read().daemon { if settings.read().daemon.enabled {
cfg_if! { cfg_if! {
if #[cfg(windows)] { if #[cfg(windows)] {
return windows::run_service(settings, matches).map_err(|e| format!("{}", e)); return windows::run_service(settings, matches).map_err(|e| format!("{}", e));
@ -61,5 +89,5 @@ fn main() -> Result<(), String> {
.expect("Error setting Ctrl-C handler"); .expect("Error setting Ctrl-C handler");
// Run the server loop // Run the server loop
task::block_on(async { run_veilid_server(settings, logs).await }) task::block_on(async { run_veilid_server(settings, logs, server_mode).await })
} }

View File

@ -9,6 +9,13 @@ use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use veilid_core::xx::SingleShotEventual; use veilid_core::xx::SingleShotEventual;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ServerMode {
Normal,
ShutdownImmediate,
DumpTXTRecord,
}
lazy_static! { lazy_static! {
static ref SHUTDOWN_SWITCH: Mutex<Option<SingleShotEventual<()>>> = static ref SHUTDOWN_SWITCH: Mutex<Option<SingleShotEventual<()>>> =
Mutex::new(Some(SingleShotEventual::new(()))); Mutex::new(Some(SingleShotEventual::new(())));
@ -21,7 +28,11 @@ pub fn shutdown() {
} }
} }
pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(), String> { pub async fn run_veilid_server(
settings: Settings,
logs: VeilidLogs,
server_mode: ServerMode,
) -> Result<(), String> {
let settingsr = settings.read(); let settingsr = settings.read();
// Create client api state change pipe // Create client api state change pipe
@ -44,7 +55,7 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
.map_err(|e| format!("VeilidCore startup failed: {}", e))?; .map_err(|e| format!("VeilidCore startup failed: {}", e))?;
// Start client api if one is requested // Start client api if one is requested
let mut capi = if settingsr.client_api.enabled { let mut capi = if settingsr.client_api.enabled && matches!(server_mode, ServerMode::Normal) {
let some_capi = client_api::ClientApi::new(veilid_api.clone()); let some_capi = client_api::ClientApi::new(veilid_api.clone());
some_capi some_capi
.clone() .clone()
@ -55,16 +66,18 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
}; };
// Drop rwlock on settings // Drop rwlock on settings
let auto_attach = settingsr.auto_attach; let auto_attach = settingsr.auto_attach || !matches!(server_mode, ServerMode::Normal);
drop(settingsr); drop(settingsr);
// Handle state changes on main thread for capnproto rpc // Process all updates
let update_receiver_jh = capi.clone().map(|capi| { let capi2 = capi.clone();
async_std::task::spawn_local(async move { let update_receiver_jh = async_std::task::spawn_local(async move {
while let Ok(change) = receiver.recv_async().await { while let Ok(change) = receiver.recv_async().await {
if let Some(capi) = &capi2 {
// Handle state changes on main thread for capnproto rpc
capi.clone().handle_update(change); capi.clone().handle_update(change);
} }
}) }
}); });
// Handle log messages on main thread for capnproto rpc // Handle log messages on main thread for capnproto rpc
let client_log_receiver_jh = capi.clone().and_then(|capi| { let client_log_receiver_jh = capi.clone().and_then(|capi| {
@ -103,14 +116,49 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
}); });
// Auto-attach if desired // Auto-attach if desired
let mut out = Ok(());
if auto_attach { if auto_attach {
info!("Auto-attach to the Veilid network"); info!("Auto-attach to the Veilid network");
if let Err(e) = veilid_api.attach().await { if let Err(e) = veilid_api.attach().await {
error!("Auto-attaching to the Veilid network failed: {:?}", e); let outerr = format!("Auto-attaching to the Veilid network failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
shutdown(); shutdown();
} }
} }
// Process dump-txt-record
if matches!(server_mode, ServerMode::DumpTXTRecord) {
let start_time = Instant::now();
while Instant::now().duration_since(start_time) < Duration::from_secs(10) {
match veilid_api.get_state().await {
Ok(vs) => {
if vs.network.started {
break;
}
}
Err(e) => {
let outerr = format!("Getting state failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
break;
}
}
async_std::task::sleep(Duration::from_millis(100)).await;
}
match veilid_api.debug("dialinfo txt".to_string()).await {
Ok(v) => {
print!("{}", v);
}
Err(e) => {
let outerr = format!("Getting dial info failed: {:?}", e);
error!("{}", outerr);
out = Err(outerr);
}
};
shutdown();
}
// Idle while waiting to exit // Idle while waiting to exit
let shutdown_switch = { let shutdown_switch = {
let shutdown_switch_locked = SHUTDOWN_SWITCH.lock(); let shutdown_switch_locked = SHUTDOWN_SWITCH.lock();
@ -134,14 +182,12 @@ pub async fn run_veilid_server(settings: Settings, logs: VeilidLogs) -> Result<(
} }
// Wait for update receiver to exit // Wait for update receiver to exit
if let Some(update_receiver_jh) = update_receiver_jh { update_receiver_jh.await;
update_receiver_jh.await;
}
// Wait for client api log receiver to exit // Wait for client api log receiver to exit
if let Some(client_log_receiver_jh) = client_log_receiver_jh { if let Some(client_log_receiver_jh) = client_log_receiver_jh {
client_log_receiver_jh.await; client_log_receiver_jh.await;
} }
Ok(()) out
} }

View File

@ -16,12 +16,16 @@ use veilid_core::xx::*;
pub fn load_default_config() -> Result<config::Config, config::ConfigError> { pub fn load_default_config() -> Result<config::Config, config::ConfigError> {
let default_config = String::from( let default_config = String::from(
r#"--- r#"---
daemon: false daemon:
enabled: false
client_api: client_api:
enabled: true enabled: true
listen_address: 'localhost:5959' listen_address: 'localhost:5959'
auto_attach: true auto_attach: true
logging: logging:
system:
enabled: false
level: 'info'
terminal: terminal:
enabled: true enabled: true
level: 'info' level: 'info'
@ -31,14 +35,14 @@ logging:
append: true append: true
level: 'info' level: 'info'
client: client:
enabled: true enabled: false
level: 'info' level: 'info'
testing: testing:
subnode_index: 0 subnode_index: 0
core: core:
protected_store: protected_store:
allow_insecure_fallback: true allow_insecure_fallback: true
always_use_insecure_storage: false always_use_insecure_storage: true
insecure_fallback_directory: '%INSECURE_FALLBACK_DIRECTORY%' insecure_fallback_directory: '%INSECURE_FALLBACK_DIRECTORY%'
delete: false delete: false
table_store: table_store:
@ -55,9 +59,12 @@ core:
max_connections_per_ip6_prefix_size: 56 max_connections_per_ip6_prefix_size: 56
max_connection_frequency_per_min: 8 max_connection_frequency_per_min: 8
client_whitelist_timeout_ms: 300000 client_whitelist_timeout_ms: 300000
reverse_connection_receipt_time_ms: 5000
hole_punch_receipt_time_ms: 5000
node_id: '' node_id: ''
node_id_secret: '' node_id_secret: ''
bootstrap: [] bootstrap: ['bootstrap.veilid.net']
bootstrap_nodes: []
routing_table: routing_table:
limit_over_attached: 64 limit_over_attached: 64
limit_fully_attached: 32 limit_fully_attached: 32
@ -90,8 +97,8 @@ core:
enable_local_peer_scope: false enable_local_peer_scope: false
restricted_nat_retries: 3 restricted_nat_retries: 3
tls: tls:
certificate_path: '/etc/veilid/server.crt' certificate_path: '/etc/veilid-server/server.crt'
private_key_path: '/etc/veilid/private/server.key' private_key_path: '/etc/veilid-server/private/server.key'
connection_initial_timeout_ms: 2000 connection_initial_timeout_ms: 2000
application: application:
https: https:
@ -130,11 +137,6 @@ core:
listen_address: ':5150' listen_address: ':5150'
path: 'ws' path: 'ws'
# url: '' # url: ''
leases:
max_server_signal_leases: 256
max_server_relay_leases: 8
max_client_signal_leases: 2
max_client_relay_leases: 2
"#, "#,
) )
.replace( .replace(
@ -402,6 +404,12 @@ pub struct File {
pub level: LogLevel, pub level: LogLevel,
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct System {
pub enabled: bool,
pub level: LogLevel,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Client { pub struct Client {
pub enabled: bool, pub enabled: bool,
@ -416,6 +424,7 @@ pub struct ClientApi {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Logging { pub struct Logging {
pub system: System,
pub terminal: Terminal, pub terminal: Terminal,
pub file: File, pub file: File,
pub client: Client, pub client: Client,
@ -522,14 +531,6 @@ pub struct Dht {
pub validate_dial_info_receipt_time_ms: u32, pub validate_dial_info_receipt_time_ms: u32,
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct Leases {
pub max_server_signal_leases: u32,
pub max_server_relay_leases: u32,
pub max_client_signal_leases: u32,
pub max_client_relay_leases: u32,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct RoutingTable { pub struct RoutingTable {
pub limit_over_attached: u32, pub limit_over_attached: u32,
@ -548,9 +549,12 @@ pub struct Network {
pub max_connections_per_ip6_prefix_size: u32, pub max_connections_per_ip6_prefix_size: u32,
pub max_connection_frequency_per_min: u32, pub max_connection_frequency_per_min: u32,
pub client_whitelist_timeout_ms: u32, pub client_whitelist_timeout_ms: u32,
pub reverse_connection_receipt_time_ms: u32,
pub hole_punch_receipt_time_ms: u32,
pub node_id: veilid_core::DHTKey, pub node_id: veilid_core::DHTKey,
pub node_id_secret: veilid_core::DHTKeySecret, pub node_id_secret: veilid_core::DHTKeySecret,
pub bootstrap: Vec<ParsedNodeDialInfo>, pub bootstrap: Vec<String>,
pub bootstrap_nodes: Vec<ParsedNodeDialInfo>,
pub routing_table: RoutingTable, pub routing_table: RoutingTable,
pub rpc: Rpc, pub rpc: Rpc,
pub dht: Dht, pub dht: Dht,
@ -561,7 +565,6 @@ pub struct Network {
pub tls: Tls, pub tls: Tls,
pub application: Application, pub application: Application,
pub protocol: Protocol, pub protocol: Protocol,
pub leases: Leases,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
@ -597,9 +600,21 @@ pub struct Core {
pub network: Network, pub network: Network,
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct Daemon {
pub enabled: bool,
pub pid_file: Option<String>,
pub chroot: Option<String>,
pub working_directory: Option<String>,
pub user: Option<String>,
pub group: Option<String>,
pub stdout_file: Option<String>,
pub stderr_file: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct SettingsInner { pub struct SettingsInner {
pub daemon: bool, pub daemon: Daemon,
pub client_api: ClientApi, pub client_api: ClientApi,
pub auto_attach: bool, pub auto_attach: bool,
pub logging: Logging, pub logging: Logging,
@ -708,56 +723,68 @@ impl Settings {
Ok(()) Ok(())
} }
pub fn get_default_config_path() -> PathBuf { fn is_root() -> bool {
// Get default configuration file location cfg_if::cfg_if! {
let mut default_config_path = if #[cfg(unix)] {
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") { use nix::unistd::Uid;
PathBuf::from(my_proj_dirs.config_dir()) Uid::effective().is_root()
} else { } else {
PathBuf::from("./") false
}; }
}
}
pub fn get_default_config_path() -> PathBuf {
let mut default_config_path = if Self::is_root() {
PathBuf::from("/etc/veilid-server")
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.config_dir())
} else {
PathBuf::from("./")
};
default_config_path.push("veilid-server.conf"); default_config_path.push("veilid-server.conf");
default_config_path default_config_path
} }
pub fn get_default_table_store_path() -> PathBuf { pub fn get_default_table_store_path() -> PathBuf {
// Get default configuration file location let mut default_db_path = if Self::is_root() {
let mut default_config_path = PathBuf::from("/var/db/veilid-server")
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") { } else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir()) PathBuf::from(my_proj_dirs.data_local_dir())
} else { } else {
PathBuf::from("./") PathBuf::from("./")
}; };
default_config_path.push("table_store"); default_db_path.push("table_store");
default_config_path default_db_path
} }
pub fn get_default_block_store_path() -> PathBuf { pub fn get_default_block_store_path() -> PathBuf {
// Get default configuration file location let mut default_db_path = if Self::is_root() {
let mut default_config_path = PathBuf::from("/var/db/veilid-server")
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") { } else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir()) PathBuf::from(my_proj_dirs.data_local_dir())
} else { } else {
PathBuf::from("./") PathBuf::from("./")
}; };
default_config_path.push("block_store"); default_db_path.push("block_store");
default_config_path default_db_path
} }
pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf { pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf {
// Get default configuration file location let mut default_db_path = if Self::is_root() {
let mut default_config_path = PathBuf::from("/var/db/veilid-server")
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") { } else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
PathBuf::from(my_proj_dirs.data_local_dir()) PathBuf::from(my_proj_dirs.data_local_dir())
} else { } else {
PathBuf::from("./") PathBuf::from("./")
}; };
default_config_path.push("protected_store"); default_db_path.push("protected_store");
default_config_path default_db_path
} }
pub fn get_core_config_callback(&self) -> veilid_core::ConfigCallback { pub fn get_core_config_callback(&self) -> veilid_core::ConfigCallback {
@ -834,17 +861,23 @@ impl Settings {
"network.max_connection_frequency_per_min" => Ok(Box::new( "network.max_connection_frequency_per_min" => Ok(Box::new(
inner.core.network.max_connection_frequency_per_min, inner.core.network.max_connection_frequency_per_min,
)), )),
"network.client_whitelist_timeout_ms" => { "network.client_whitelist_timeout_ms" => {
Ok(Box::new(inner.core.network.client_whitelist_timeout_ms)) Ok(Box::new(inner.core.network.client_whitelist_timeout_ms))
} }
"network.reverse_connection_receipt_time_ms" => Ok(Box::new(
inner.core.network.reverse_connection_receipt_time_ms,
)),
"network.hole_punch_receipt_time_ms" => {
Ok(Box::new(inner.core.network.hole_punch_receipt_time_ms))
}
"network.node_id" => Ok(Box::new(inner.core.network.node_id)), "network.node_id" => Ok(Box::new(inner.core.network.node_id)),
"network.node_id_secret" => Ok(Box::new(inner.core.network.node_id_secret)), "network.node_id_secret" => Ok(Box::new(inner.core.network.node_id_secret)),
"network.bootstrap" => Ok(Box::new( "network.bootstrap" => Ok(Box::new(inner.core.network.bootstrap.clone())),
"network.bootstrap_nodes" => Ok(Box::new(
inner inner
.core .core
.network .network
.bootstrap .bootstrap_nodes
.clone() .clone()
.into_iter() .into_iter()
.map(|e| e.node_dial_info_string) .map(|e| e.node_dial_info_string)
@ -1112,18 +1145,6 @@ impl Settings {
.as_ref() .as_ref()
.map(|a| a.urlstring.clone()), .map(|a| a.urlstring.clone()),
)), )),
"network.leases.max_server_signal_leases" => {
Ok(Box::new(inner.core.network.leases.max_server_signal_leases))
}
"network.leases.max_server_relay_leases" => {
Ok(Box::new(inner.core.network.leases.max_server_relay_leases))
}
"network.leases.max_client_signal_leases" => {
Ok(Box::new(inner.core.network.leases.max_client_signal_leases))
}
"network.leases.max_client_relay_leases" => {
Ok(Box::new(inner.core.network.leases.max_client_relay_leases))
}
_ => Err(format!("config key '{}' doesn't exist", key)), _ => Err(format!("config key '{}' doesn't exist", key)),
}; };
out out
@ -1150,7 +1171,14 @@ mod tests {
let settings = Settings::new(None).unwrap(); let settings = Settings::new(None).unwrap();
let s = settings.read(); let s = settings.read();
assert_eq!(s.daemon, false); assert_eq!(s.daemon.enabled, false);
assert_eq!(s.daemon.pid_file, None);
assert_eq!(s.daemon.chroot, None);
assert_eq!(s.daemon.working_directory, None);
assert_eq!(s.daemon.user, None);
assert_eq!(s.daemon.group, None);
assert_eq!(s.daemon.stdout_file, None);
assert_eq!(s.daemon.stderr_file, None);
assert_eq!(s.client_api.enabled, true); assert_eq!(s.client_api.enabled, true);
assert_eq!(s.client_api.listen_address.name, "localhost:5959"); assert_eq!(s.client_api.listen_address.name, "localhost:5959");
assert_eq!( assert_eq!(
@ -1164,7 +1192,7 @@ mod tests {
assert_eq!(s.logging.file.path, ""); assert_eq!(s.logging.file.path, "");
assert_eq!(s.logging.file.append, true); assert_eq!(s.logging.file.append, true);
assert_eq!(s.logging.file.level, LogLevel::Info); assert_eq!(s.logging.file.level, LogLevel::Info);
assert_eq!(s.logging.client.enabled, true); assert_eq!(s.logging.client.enabled, false);
assert_eq!(s.logging.client.level, LogLevel::Info); assert_eq!(s.logging.client.level, LogLevel::Info);
assert_eq!(s.testing.subnode_index, 0); assert_eq!(s.testing.subnode_index, 0);
@ -1181,7 +1209,7 @@ mod tests {
assert_eq!(s.core.block_store.delete, false); assert_eq!(s.core.block_store.delete, false);
assert_eq!(s.core.protected_store.allow_insecure_fallback, true); assert_eq!(s.core.protected_store.allow_insecure_fallback, true);
assert_eq!(s.core.protected_store.always_use_insecure_storage, false); assert_eq!(s.core.protected_store.always_use_insecure_storage, true);
assert_eq!( assert_eq!(
s.core.protected_store.insecure_fallback_directory, s.core.protected_store.insecure_fallback_directory,
Settings::get_default_protected_store_insecure_fallback_directory() Settings::get_default_protected_store_insecure_fallback_directory()
@ -1195,13 +1223,19 @@ mod tests {
assert_eq!(s.core.network.max_connections_per_ip6_prefix_size, 56u32); assert_eq!(s.core.network.max_connections_per_ip6_prefix_size, 56u32);
assert_eq!(s.core.network.max_connection_frequency_per_min, 8u32); assert_eq!(s.core.network.max_connection_frequency_per_min, 8u32);
assert_eq!(s.core.network.client_whitelist_timeout_ms, 300_000u32); assert_eq!(s.core.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(s.core.network.reverse_connection_receipt_time_ms, 5_000u32);
assert_eq!(s.core.network.hole_punch_receipt_time_ms, 5_000u32);
assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default()); assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default());
assert_eq!( assert_eq!(
s.core.network.node_id_secret, s.core.network.node_id_secret,
veilid_core::DHTKeySecret::default() veilid_core::DHTKeySecret::default()
); );
// //
assert!(s.core.network.bootstrap.is_empty()); assert_eq!(
s.core.network.bootstrap,
vec!["bootstrap.veilid.net".to_owned()]
);
assert_eq!(s.core.network.bootstrap_nodes, vec![]);
// //
assert_eq!(s.core.network.rpc.concurrency, 0); assert_eq!(s.core.network.rpc.concurrency, 0);
assert_eq!(s.core.network.rpc.queue_size, 1024); assert_eq!(s.core.network.rpc.queue_size, 1024);
@ -1234,11 +1268,11 @@ mod tests {
// //
assert_eq!( assert_eq!(
s.core.network.tls.certificate_path, s.core.network.tls.certificate_path,
std::path::PathBuf::from("/etc/veilid/server.crt") std::path::PathBuf::from("/etc/veilid-server/server.crt")
); );
assert_eq!( assert_eq!(
s.core.network.tls.private_key_path, s.core.network.tls.private_key_path,
std::path::PathBuf::from("/etc/veilid/private/server.key") std::path::PathBuf::from("/etc/veilid-server/private/server.key")
); );
assert_eq!(s.core.network.tls.connection_initial_timeout_ms, 2_000u32); assert_eq!(s.core.network.tls.connection_initial_timeout_ms, 2_000u32);
// //
@ -1317,9 +1351,5 @@ mod tests {
); );
assert_eq!(s.core.network.protocol.wss.url, None); assert_eq!(s.core.network.protocol.wss.url, None);
// //
assert_eq!(s.core.network.leases.max_server_signal_leases, 256);
assert_eq!(s.core.network.leases.max_server_relay_leases, 8);
assert_eq!(s.core.network.leases.max_client_signal_leases, 2);
assert_eq!(s.core.network.leases.max_client_relay_leases, 2);
} }
} }

View File

@ -1,8 +1,102 @@
use crate::server::*;
use crate::settings::Settings; use crate::settings::Settings;
use crate::veilid_logs::*;
use async_std::stream::StreamExt;
use async_std::task;
use clap::ArgMatches; use clap::ArgMatches;
// use log::*; use signal_hook::consts::signal::*;
use signal_hook_async_std::Signals;
pub fn run_daemon(_settings: Settings, _matches: ArgMatches) -> Result<(), String> { async fn handle_signals(mut signals: Signals) {
eprintln!("Windows Service mode not implemented yet."); while let Some(signal) = signals.next().await {
Ok(()) match signal {
SIGHUP => {
// XXX: reload configuration?
}
SIGTERM | SIGINT | SIGQUIT => {
// Shutdown the system;
shutdown();
}
_ => unreachable!(),
}
}
}
pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> Result<(), String> {
let daemon = {
let mut daemon = daemonize::Daemonize::new();
let s = settings.read();
if let Some(pid_file) = &s.daemon.pid_file {
daemon = daemon.pid_file(pid_file).chown_pid_file(true);
}
if let Some(chroot) = &s.daemon.chroot {
daemon = daemon.chroot(chroot);
}
if let Some(working_directory) = &s.daemon.working_directory {
daemon = daemon.working_directory(working_directory);
}
if let Some(user) = &s.daemon.user {
daemon = daemon.user(user.as_str());
}
if let Some(group) = &s.daemon.group {
daemon = daemon.group(group.as_str());
}
let stdout_file = if let Some(stdout_file) = &s.daemon.stdout_file {
Some(
std::fs::File::create(stdout_file)
.map_err(|e| format!("Failed to create stdio file: {}", e))?,
)
} else {
None
};
if let Some(stderr_file) = &s.daemon.stderr_file {
if Some(stderr_file) == s.daemon.stdout_file.as_ref() {
// same output file for stderr and stdout
daemon = daemon.stderr(
stdout_file
.as_ref()
.unwrap()
.try_clone()
.map_err(|e| format!("Failed to clone stdout file: {}", e))?,
);
} else {
daemon = daemon.stderr(
std::fs::File::create(stderr_file)
.map_err(|e| format!("Failed to create stderr file: {}", e))?,
);
}
}
if let Some(stdout_file) = stdout_file {
daemon = daemon.stdout(stdout_file);
}
daemon
};
// Init combined console/file logger
let logs = VeilidLogs::setup_normal_logs(settings.clone())?;
// Daemonize
daemon
.start()
.map_err(|e| format!("Failed to daemonize: {}", e))?;
// Now, run the server
task::block_on(async {
// Catch signals
let signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])
.map_err(|e| format!("failed to init signals: {}", e))?;
let handle = signals.handle();
let signals_task = async_std::task::spawn(handle_signals(signals));
let res = run_veilid_server(settings, logs, ServerMode::Normal).await;
// Terminate the signal stream.
handle.close();
signals_task.await;
res
})
} }

View File

@ -1,5 +1,7 @@
use crate::log_safe_channel::*; use crate::log_safe_channel::*;
use crate::settings::*; use crate::settings::*;
use cfg_if::*;
use log::*;
use simplelog::*; use simplelog::*;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::path::Path; use std::path::Path;
@ -9,6 +11,84 @@ pub struct VeilidLogs {
pub client_log_channel_closer: Option<LogSafeChannelCloser>, pub client_log_channel_closer: Option<LogSafeChannelCloser>,
} }
cfg_if! {
if #[cfg(target_os = "linux")] {
use systemd_journal_logger::JournalLog;
pub struct SystemLogger {
level_filter: LevelFilter,
config: Config,
journal_log: JournalLog<String,String>,
}
impl SystemLogger {
pub fn new(level_filter: LevelFilter, config: Config) -> Box<Self> {
Box::new(Self {
level_filter,
config,
journal_log: JournalLog::with_extra_fields(Vec::new())
})
}
pub fn should_skip(record: &Record<'_>) -> bool {
// // If a module path and allowed list are available
// match (record.target(), &*config.filter_allow) {
// (path, allowed) if !allowed.is_empty() => {
// // Check that the module path matches at least one allow filter
// if !allowed.iter().any(|v| path.starts_with(&**v)) {
// // If not, skip any further writing
// return true;
// }
// }
// _ => {}
// }
// If a module path and ignore list are available
match (record.target(), &veilid_core::DEFAULT_LOG_IGNORE_LIST) {
(path, ignore) if !ignore.is_empty() => {
// Check that the module path does not match any ignore filters
if ignore.iter().any(|v| path.starts_with(&**v)) {
// If not, skip any further writing
return true;
}
}
_ => {}
}
false
}
}
impl Log for SystemLogger {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() <= self.level_filter
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) && ! Self::should_skip(record) {
self.journal_log.log(record);
}
}
fn flush(&self) {
self.journal_log.flush();
}
}
impl SharedLogger for SystemLogger {
fn level(&self) -> LevelFilter {
self.level_filter
}
fn config(&self) -> Option<&Config> {
Some(&self.config)
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
}
}
impl VeilidLogs { impl VeilidLogs {
pub fn setup_normal_logs(settings: Settings) -> Result<VeilidLogs, String> { pub fn setup_normal_logs(settings: Settings) -> Result<VeilidLogs, String> {
let settingsr = settings.read(); let settingsr = settings.read();
@ -64,6 +144,14 @@ impl VeilidLogs {
)) ))
} }
cfg_if! {
if #[cfg(target_os = "linux")] {
if settingsr.logging.system.enabled {
logs.push(SystemLogger::new(convert_loglevel(settingsr.logging.system.level), cb.build()))
}
}
}
CombinedLogger::init(logs).map_err(|e| format!("failed to init logs: {}", e))?; CombinedLogger::init(logs).map_err(|e| format!("failed to init logs: {}", e))?;
Ok(VeilidLogs { Ok(VeilidLogs {