From a4d96aa92c774ee3c272e35797d4f80c7dcabfad Mon Sep 17 00:00:00 2001 From: Niko PLP Date: Sat, 27 Apr 2024 22:34:16 +0300 Subject: [PATCH] refactor ngclie and ngd error handling, and refactor server_broker and its storage --- ng-broker/src/broker_storage/mod.rs | 13 - ng-broker/src/lib.rs | 10 +- ...r_storage.rs => rocksdb_server_storage.rs} | 44 +- ng-broker/src/server_broker.rs | 171 +++++++ .../admin}/account.rs | 12 +- .../admin}/invitation.rs | 12 +- ng-broker/src/server_storage/admin/mod.rs | 5 + .../admin}/wallet.rs | 0 .../config.rs | 0 ng-broker/src/server_storage/core/mod.rs | 5 + .../core}/overlay.rs | 0 .../core}/peer.rs | 0 .../core}/topic.rs | 0 ng-broker/src/server_storage/mod.rs | 5 + ng-broker/src/server_ws.rs | 50 +- ng-net/src/actors/add_invitation.rs | 2 +- ng-net/src/actors/add_user.rs | 2 +- ng-net/src/actors/client/commit_get.rs | 2 +- ng-net/src/actors/client/pin_repo.rs | 2 +- ng-net/src/actors/client/repo_pin_status.rs | 2 +- ng-net/src/actors/client/topic_sub.rs | 2 +- ng-net/src/actors/del_user.rs | 2 +- ng-net/src/actors/list_invitations.rs | 2 +- ng-net/src/actors/list_users.rs | 2 +- ng-net/src/broker.rs | 28 +- ng-net/src/lib.rs | 2 +- .../{server_storage.rs => server_broker.rs} | 4 +- ng-repo/src/errors.rs | 46 +- ng-repo/src/types.rs | 4 +- ng-repo/src/utils.rs | 8 +- ng-storage-rocksdb/src/block_storage.rs | 3 +- ng-storage-rocksdb/src/kcv_storage.rs | 2 + ng-verifier/src/rocksdb_user_storage.rs | 6 +- ng-verifier/src/types.rs | 2 +- ng-verifier/src/user_storage/storage.rs | 99 ++-- ng-verifier/src/verifier.rs | 4 +- ngcli/src/main.rs | 264 +++++----- ngd/src/main.rs | 483 +++++++++--------- 38 files changed, 740 insertions(+), 560 deletions(-) delete mode 100644 ng-broker/src/broker_storage/mod.rs rename ng-broker/src/{server_storage.rs => rocksdb_server_storage.rs} (88%) create mode 100644 ng-broker/src/server_broker.rs rename ng-broker/src/{broker_storage => server_storage/admin}/account.rs (97%) rename ng-broker/src/{broker_storage => server_storage/admin}/invitation.rs (96%) create mode 100644 ng-broker/src/server_storage/admin/mod.rs rename ng-broker/src/{broker_storage => server_storage/admin}/wallet.rs (100%) rename ng-broker/src/{broker_storage => server_storage}/config.rs (100%) create mode 100644 ng-broker/src/server_storage/core/mod.rs rename ng-broker/src/{broker_storage => server_storage/core}/overlay.rs (100%) rename ng-broker/src/{broker_storage => server_storage/core}/peer.rs (100%) rename ng-broker/src/{broker_storage => server_storage/core}/topic.rs (100%) create mode 100644 ng-broker/src/server_storage/mod.rs rename ng-net/src/{server_storage.rs => server_broker.rs} (96%) diff --git a/ng-broker/src/broker_storage/mod.rs b/ng-broker/src/broker_storage/mod.rs deleted file mode 100644 index 0d2ad4c..0000000 --- a/ng-broker/src/broker_storage/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod account; - -pub mod config; - -pub mod overlay; - -pub mod peer; - -pub mod topic; - -pub mod invitation; - -pub mod wallet; diff --git a/ng-broker/src/lib.rs b/ng-broker/src/lib.rs index 527bfc4..b38f93e 100644 --- a/ng-broker/src/lib.rs +++ b/ng-broker/src/lib.rs @@ -1,11 +1,13 @@ -pub mod broker_storage; - -pub mod server_ws; - pub mod types; pub mod utils; pub mod interfaces; +pub mod server_broker; + pub mod server_storage; + +pub mod rocksdb_server_storage; + +pub mod server_ws; diff --git a/ng-broker/src/server_storage.rs b/ng-broker/src/rocksdb_server_storage.rs similarity index 88% rename from ng-broker/src/server_storage.rs rename to ng-broker/src/rocksdb_server_storage.rs index a15cc2a..d82ad64 100644 --- a/ng-broker/src/server_storage.rs +++ b/ng-broker/src/rocksdb_server_storage.rs @@ -15,11 +15,11 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::Mutex; -use crate::broker_storage::account::Account; -use crate::broker_storage::invitation::Invitation; -use crate::broker_storage::wallet::Wallet; +use crate::server_storage::admin::account::Account; +use crate::server_storage::admin::invitation::Invitation; +use crate::server_storage::admin::wallet::Wallet; use crate::types::*; -use ng_net::server_storage::*; +use ng_net::server_broker::*; use ng_net::types::*; use ng_repo::errors::{ProtocolError, ServerError, StorageError}; use ng_repo::kcv_storage::KCVStorage; @@ -28,7 +28,7 @@ use ng_repo::types::*; use ng_storage_rocksdb::block_storage::RocksDbBlockStorage; use ng_storage_rocksdb::kcv_storage::RocksDbKCVStorage; -pub struct RocksDbServerStorage { +pub(crate) struct RocksDbServerStorage { wallet_storage: RocksDbKCVStorage, accounts_storage: RocksDbKCVStorage, //peers_storage: RocksDbKCVStorage, @@ -39,7 +39,7 @@ pub struct RocksDbServerStorage { } impl RocksDbServerStorage { - pub fn open( + pub(crate) fn open( path: &mut PathBuf, master_key: SymKey, admin_invite: Option, @@ -132,10 +132,8 @@ impl RocksDbServerStorage { core_storage, }) } -} -impl ServerStorage for RocksDbServerStorage { - fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> { + pub(crate) fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> { // for now we don't use the hashmap. // TODO: let's see if the lock is even needed let _ = self.peers_last_seq.lock(); @@ -165,26 +163,26 @@ impl ServerStorage for RocksDbServerStorage { Ok(()) } - fn get_user(&self, user_id: PubKey) -> Result { + pub(crate) fn get_user(&self, user_id: PubKey) -> Result { log_debug!("get_user {user_id}"); Ok(Account::open(&user_id, &self.accounts_storage)?.is_admin()?) } - fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError> { + pub(crate) fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError> { log_debug!("add_user {user_id} is admin {is_admin}"); Account::create(&user_id, is_admin, &self.accounts_storage)?; Ok(()) } - fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError> { + pub(crate) fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError> { log_debug!("del_user {user_id}"); let acc = Account::open(&user_id, &self.accounts_storage)?; acc.del()?; Ok(()) } - fn list_users(&self, admins: bool) -> Result, ProtocolError> { + pub(crate) fn list_users(&self, admins: bool) -> Result, ProtocolError> { log_debug!("list_users that are admin == {admins}"); Ok(Account::get_all_users(admins, &self.accounts_storage)?) } - fn list_invitations( + pub(crate) fn list_invitations( &self, admin: bool, unique: bool, @@ -198,7 +196,7 @@ impl ServerStorage for RocksDbServerStorage { multi, )?) } - fn add_invitation( + pub(crate) fn add_invitation( &self, invite_code: &InvitationCode, expiry: u32, @@ -208,18 +206,18 @@ impl ServerStorage for RocksDbServerStorage { Invitation::create(invite_code, expiry, memo, &self.accounts_storage)?; Ok(()) } - fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result { + pub(crate) fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result { log_debug!("get_invitation_type {:?}", invite_code); let inv = Invitation::open(&invite_code, &self.accounts_storage)?; inv.get_type() } - fn remove_invitation(&self, invite_code: [u8; 32]) -> Result<(), ProtocolError> { + pub(crate) fn remove_invitation(&self, invite_code: [u8; 32]) -> Result<(), ProtocolError> { log_debug!("remove_invitation {:?}", invite_code); let inv = Invitation::open(&invite_code, &self.accounts_storage)?; inv.del()?; Ok(()) } - fn get_repo_pin_status( + pub(crate) fn get_repo_pin_status( &self, overlay: &OverlayId, repo: &RepoHash, @@ -237,7 +235,7 @@ impl ServerStorage for RocksDbServerStorage { // })) } - fn pin_repo( + pub(crate) fn pin_repo( &self, overlay: &OverlayId, repo: &RepoHash, @@ -255,7 +253,7 @@ impl ServerStorage for RocksDbServerStorage { Ok(opened) } - fn topic_sub( + pub(crate) fn topic_sub( &self, overlay: &OverlayId, repo: &RepoHash, @@ -270,7 +268,11 @@ impl ServerStorage for RocksDbServerStorage { })) } - fn get_commit(&self, overlay: &OverlayId, id: &ObjectId) -> Result, ServerError> { + pub(crate) fn get_commit( + &self, + overlay: &OverlayId, + id: &ObjectId, + ) -> Result, ServerError> { //TODO: implement correctly ! Ok(vec![Block::dummy()]) } diff --git a/ng-broker/src/server_broker.rs b/ng-broker/src/server_broker.rs new file mode 100644 index 0000000..2ae79cb --- /dev/null +++ b/ng-broker/src/server_broker.rs @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers + * All rights reserved. + * Licensed under the Apache License, Version 2.0 + * + * or the MIT license , + * at your option. All files in the project carrying such + * notice may not be copied, modified, or distributed except + * according to those terms. + */ + +//! Implementation of the Server Broker + +use std::collections::{HashMap, HashSet}; + +use ng_net::{server_broker::IServerBroker, types::*}; +use ng_repo::{ + errors::{NgError, ProtocolError, ServerError}, + types::*, +}; + +use crate::rocksdb_server_storage::RocksDbServerStorage; + +struct TopicInfo { + /// can be None if the Broker is not currently serving this topic for its clients. + repo: Option, + + publisher_advert: Option, + + current_heads: Vec, + + expose_outer: bool, + + /// indicates which users have subscribed to topic (boolean says if as publisher or not) + users: HashMap, +} + +struct RepoInfo { + /// set of users that requested the repo to be exposed on the outer overlay + /// only possible if the user is a publisher + expose_outer: HashSet, + + /// set of topics of this repo + topics: HashSet, +} + +struct OverlayInfo { + inner: Option, + + topics: HashMap, + + repos: HashMap, +} + +pub struct ServerBroker { + storage: RocksDbServerStorage, + + overlays: HashMap, + + inner_overlays: HashMap>, +} + +impl ServerBroker { + pub fn new(storage: RocksDbServerStorage) -> Self { + ServerBroker { + storage: storage, + overlays: HashMap::new(), + inner_overlays: HashMap::new(), + } + } + + pub fn load(&mut self) -> Result<(), NgError> { + Ok(()) + } +} + +impl IServerBroker for ServerBroker { + fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> { + self.storage.next_seq_for_peer(peer, seq) + } + + fn get_user(&self, user_id: PubKey) -> Result { + self.storage.get_user(user_id) + } + fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError> { + self.storage.add_user(user_id, is_admin) + } + fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError> { + self.storage.del_user(user_id) + } + fn list_users(&self, admins: bool) -> Result, ProtocolError> { + self.storage.list_users(admins) + } + fn list_invitations( + &self, + admin: bool, + unique: bool, + multi: bool, + ) -> Result)>, ProtocolError> { + self.storage.list_invitations(admin, unique, multi) + } + fn add_invitation( + &self, + invite_code: &InvitationCode, + expiry: u32, + memo: &Option, + ) -> Result<(), ProtocolError> { + self.storage.add_invitation(invite_code, expiry, memo) + } + fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result { + self.storage.get_invitation_type(invite_code) + } + fn remove_invitation(&self, invite_code: [u8; 32]) -> Result<(), ProtocolError> { + self.storage.remove_invitation(invite_code) + } + fn get_repo_pin_status( + &self, + overlay: &OverlayId, + repo: &RepoHash, + ) -> Result { + Err(ServerError::False) + //TODO: implement correctly ! + // Ok(RepoPinStatus::V0(RepoPinStatusV0 { + // hash: repo.clone(), + + // // only possible for RW overlays + // expose_outer: false, + + // // list of topics that are subscribed to + // topics: vec![], + // })) + } + + fn pin_repo( + &self, + overlay: &OverlayId, + repo: &RepoHash, + ro_topics: &Vec, + rw_topics: &Vec, + ) -> Result { + //TODO: implement correctly ! + let mut opened = Vec::with_capacity(ro_topics.len() + rw_topics.len()); + for topic in ro_topics { + opened.push((*topic).into()); + } + for topic in rw_topics { + opened.push((*topic).into()); + } + Ok(opened) + } + + fn topic_sub( + &self, + overlay: &OverlayId, + repo: &RepoHash, + topic: &TopicId, + publisher: Option<&PublisherAdvert>, + ) -> Result { + //TODO: implement correctly ! + Ok(TopicSubRes::V0(TopicSubResV0 { + topic: topic.clone(), + known_heads: vec![], + publisher: publisher.is_some(), + })) + } + + fn get_commit(&self, overlay: &OverlayId, id: &ObjectId) -> Result, ServerError> { + //TODO: implement correctly ! + Ok(vec![Block::dummy()]) + } +} diff --git a/ng-broker/src/broker_storage/account.rs b/ng-broker/src/server_storage/admin/account.rs similarity index 97% rename from ng-broker/src/broker_storage/account.rs rename to ng-broker/src/server_storage/admin/account.rs index 96cdef7..4ac40d1 100644 --- a/ng-broker/src/broker_storage/account.rs +++ b/ng-broker/src/server_storage/admin/account.rs @@ -28,13 +28,13 @@ pub struct Account<'a> { } impl<'a> Account<'a> { - const PREFIX_ACCOUNT: u8 = b"a"[0]; - const PREFIX_CLIENT: u8 = b"c"[0]; - const PREFIX_CLIENT_PROPERTY: u8 = b"d"[0]; + const PREFIX_ACCOUNT: u8 = b'a'; + const PREFIX_CLIENT: u8 = b'c'; + const PREFIX_CLIENT_PROPERTY: u8 = b'd'; // propertie's client suffixes - const INFO: u8 = b"i"[0]; - const LAST_SEEN: u8 = b"l"[0]; + const INFO: u8 = b'i'; + const LAST_SEEN: u8 = b'l'; const ALL_CLIENT_PROPERTIES: [u8; 2] = [Self::INFO, Self::LAST_SEEN]; @@ -252,7 +252,7 @@ mod test { use std::fs; use tempfile::Builder; - use crate::broker_storage::account::Account; + use crate::server_storage::admin::account::Account; #[test] pub fn test_account() { diff --git a/ng-broker/src/broker_storage/invitation.rs b/ng-broker/src/server_storage/admin/invitation.rs similarity index 96% rename from ng-broker/src/broker_storage/invitation.rs rename to ng-broker/src/server_storage/admin/invitation.rs index d360501..7f0ea54 100644 --- a/ng-broker/src/broker_storage/invitation.rs +++ b/ng-broker/src/server_storage/admin/invitation.rs @@ -31,15 +31,15 @@ pub struct Invitation<'a> { } impl<'a> Invitation<'a> { - const PREFIX: u8 = b"i"[0]; + const PREFIX: u8 = b'i'; // propertie's invitation suffixes - const TYPE: u8 = b"t"[0]; - //const EXPIRE: u8 = b"e"[0]; + const TYPE: u8 = b't'; + //const EXPIRE: u8 = b'e'; - const PREFIX_EXPIRE: u8 = b"e"[0]; + const PREFIX_EXPIRE: u8 = b'e'; // propertie's expiry suffixes - const INVITATION: u8 = b"i"[0]; + const INVITATION: u8 = b'i'; const ALL_PROPERTIES: [u8; 1] = [Self::TYPE]; @@ -196,7 +196,7 @@ mod test { use std::fs; use tempfile::Builder; - use crate::broker_storage::account::Account; + use crate::server_storage::admin::account::Account; #[test] pub fn test_invitation() {} diff --git a/ng-broker/src/server_storage/admin/mod.rs b/ng-broker/src/server_storage/admin/mod.rs new file mode 100644 index 0000000..802fcdd --- /dev/null +++ b/ng-broker/src/server_storage/admin/mod.rs @@ -0,0 +1,5 @@ +pub mod invitation; + +pub mod wallet; + +pub mod account; diff --git a/ng-broker/src/broker_storage/wallet.rs b/ng-broker/src/server_storage/admin/wallet.rs similarity index 100% rename from ng-broker/src/broker_storage/wallet.rs rename to ng-broker/src/server_storage/admin/wallet.rs diff --git a/ng-broker/src/broker_storage/config.rs b/ng-broker/src/server_storage/config.rs similarity index 100% rename from ng-broker/src/broker_storage/config.rs rename to ng-broker/src/server_storage/config.rs diff --git a/ng-broker/src/server_storage/core/mod.rs b/ng-broker/src/server_storage/core/mod.rs new file mode 100644 index 0000000..dd3301a --- /dev/null +++ b/ng-broker/src/server_storage/core/mod.rs @@ -0,0 +1,5 @@ +pub mod overlay; + +pub mod peer; + +pub mod topic; diff --git a/ng-broker/src/broker_storage/overlay.rs b/ng-broker/src/server_storage/core/overlay.rs similarity index 100% rename from ng-broker/src/broker_storage/overlay.rs rename to ng-broker/src/server_storage/core/overlay.rs diff --git a/ng-broker/src/broker_storage/peer.rs b/ng-broker/src/server_storage/core/peer.rs similarity index 100% rename from ng-broker/src/broker_storage/peer.rs rename to ng-broker/src/server_storage/core/peer.rs diff --git a/ng-broker/src/broker_storage/topic.rs b/ng-broker/src/server_storage/core/topic.rs similarity index 100% rename from ng-broker/src/broker_storage/topic.rs rename to ng-broker/src/server_storage/core/topic.rs diff --git a/ng-broker/src/server_storage/mod.rs b/ng-broker/src/server_storage/mod.rs new file mode 100644 index 0000000..be1eb3e --- /dev/null +++ b/ng-broker/src/server_storage/mod.rs @@ -0,0 +1,5 @@ +pub mod admin; + +pub mod core; + +//pub mod config; diff --git a/ng-broker/src/server_ws.rs b/ng-broker/src/server_ws.rs index 50256d0..1aefefa 100644 --- a/ng-broker/src/server_ws.rs +++ b/ng-broker/src/server_ws.rs @@ -12,7 +12,8 @@ //! WebSocket implementation of the Broker use crate::interfaces::*; -use crate::server_storage::RocksDbServerStorage; +use crate::rocksdb_server_storage::RocksDbServerStorage; +use crate::server_broker::ServerBroker; use crate::types::*; use async_std::io::ReadExt; use async_std::net::{TcpListener, TcpStream}; @@ -38,6 +39,7 @@ use ng_net::utils::get_domain_without_port; use ng_net::utils::is_private_ip; use ng_net::utils::is_public_ip; use ng_net::NG_BOOTSTRAP_LOCAL_PATH; +use ng_repo::errors::NgError; use ng_repo::log::*; use ng_repo::types::SymKey; use ng_repo::types::{PrivKey, PubKey}; @@ -610,7 +612,7 @@ pub async fn run_server_v0( config: DaemonConfigV0, mut path: PathBuf, admin_invite: bool, -) -> Result<(), ()> { +) -> Result<(), NgError> { // check config let mut run_core = false; @@ -624,8 +626,9 @@ pub async fn run_server_v0( } } if !run_core && !run_server { - log_err!("There isn't any overlay_config that should run as core or server. Check your config. cannot start"); - return Err(()); + return Err(NgError::BrokerConfigErrorStr( + "There isn't any overlay_config that should run as core or server. Check your config.", + )); } if run_core && !run_server { @@ -636,11 +639,10 @@ pub async fn run_server_v0( for listener in &config.listeners { let id: String = listener.to_string(); if !listeners.insert(id.clone()) { - log_err!( - "The listener {} is defined twice. Check your config file. cannot start", + return Err(NgError::BrokerConfigError(format!( + "The listener {} is defined twice. Check your config file.", id - ); - return Err(()); + ))); } } @@ -670,11 +672,10 @@ pub async fn run_server_v0( match find_name(&interfaces, &listener.interface_name) { None => { - log_err!( - "The interface {} does not exist on your host. Check your config file. cannot start", + return Err(NgError::BrokerConfigError(format!( + "The interface {} does not exist on your host. Check your config file.", listener.interface_name - ); - return Err(()); + ))); } Some(interface) => { let mut addrs: Vec = interface @@ -689,11 +690,10 @@ pub async fn run_server_v0( }) .collect(); if addrs.len() == 0 { - log_err!( - "The interface {} does not have any IPv4 address. cannot start", + return Err(NgError::BrokerConfigError(format!( + "The interface {} does not have any IPv4 address.", listener.interface_name - ); - return Err(()); + ))); } if listener.ipv6 { let mut ipv6s: Vec = interface @@ -753,8 +753,7 @@ pub async fn run_server_v0( } if listeners_addrs.len() == 0 { - log_err!("No listener configured. cannot start",); - return Err(()); + return Err(NgError::BrokerConfigErrorStr("No listener configured.")); } if !accept_clients { @@ -777,7 +776,7 @@ pub async fn run_server_v0( std::fs::create_dir_all(path.clone()).unwrap(); // opening the server storage (that contains the encryption keys for each store/overlay ) - let broker_storage = RocksDbServerStorage::open( + let server_storage = RocksDbServerStorage::open( &mut path, wallet_master_key, if admin_invite { @@ -786,10 +785,15 @@ pub async fn run_server_v0( None }, ) - .map_err(|e| log_err!("Error while opening server storage: {:?}", e))?; + .map_err(|e| { + NgError::BrokerConfigError(format!("Error while opening server storage: {}", e)) + })?; + + let server_broker = ServerBroker::new(server_storage); let mut broker = BROKER.write().await; - broker.set_server_storage(broker_storage); + broker.set_server_broker(server_broker); + LISTENERS_INFO .set(broker.set_listeners(listener_infos)) .unwrap(); @@ -815,12 +819,12 @@ pub async fn run_server_v0( for addr in addrs.0 { let tcp_listener = TcpListener::bind(addr).await.map_err(|e| { - log_err!( + NgError::BrokerConfigError(format!( "cannot bind to {} with addresses {} : {}", addrs.1, addrs_string, e.to_string() - ) + )) })?; listeners.push(tcp_listener); } diff --git a/ng-net/src/actors/add_invitation.rs b/ng-net/src/actors/add_invitation.rs index 5632936..0b555c4 100644 --- a/ng-net/src/actors/add_invitation.rs +++ b/ng-net/src/actors/add_invitation.rs @@ -102,7 +102,7 @@ impl EActor for Actor<'_, AddInvitation, AdminResponse> { let req = AddInvitation::try_from(msg)?; let broker = BROKER.read().await; broker - .get_server_storage()? + .get_server_broker()? .add_invitation(req.code(), req.expiry(), req.memo())?; let invitation = crate::types::Invitation::V0(InvitationV0::new( diff --git a/ng-net/src/actors/add_user.rs b/ng-net/src/actors/add_user.rs index 405e3ea..b8a0ca2 100644 --- a/ng-net/src/actors/add_user.rs +++ b/ng-net/src/actors/add_user.rs @@ -101,7 +101,7 @@ impl EActor for Actor<'_, AddUser, AdminResponse> { is_admin = true; } } - let res = broker.get_server_storage()?.add_user(req.user(), is_admin); + let res = broker.get_server_broker()?.add_user(req.user(), is_admin); let response: AdminResponseV0 = res.into(); fsm.lock().await.send(response.into()).await?; Ok(()) diff --git a/ng-net/src/actors/client/commit_get.rs b/ng-net/src/actors/client/commit_get.rs index b1aa5d9..7878609 100644 --- a/ng-net/src/actors/client/commit_get.rs +++ b/ng-net/src/actors/client/commit_get.rs @@ -90,7 +90,7 @@ impl EActor for Actor<'_, CommitGet, Block> { log_info!("GOT CommitGet {:?}", req); let broker = BROKER.read().await; let blocks_res = broker - .get_server_storage()? + .get_server_broker()? .get_commit(req.overlay(), req.id()); match blocks_res { diff --git a/ng-net/src/actors/client/pin_repo.rs b/ng-net/src/actors/client/pin_repo.rs index bcb3e14..2a77232 100644 --- a/ng-net/src/actors/client/pin_repo.rs +++ b/ng-net/src/actors/client/pin_repo.rs @@ -109,7 +109,7 @@ impl EActor for Actor<'_, PinRepo, RepoOpened> { //TODO implement all the server side logic let broker = BROKER.read().await; - let res = broker.get_server_storage()?.pin_repo( + let res = broker.get_server_broker()?.pin_repo( req.overlay(), req.hash(), req.ro_topics(), diff --git a/ng-net/src/actors/client/repo_pin_status.rs b/ng-net/src/actors/client/repo_pin_status.rs index ae892b4..9ebfb6f 100644 --- a/ng-net/src/actors/client/repo_pin_status.rs +++ b/ng-net/src/actors/client/repo_pin_status.rs @@ -79,7 +79,7 @@ impl EActor for Actor<'_, RepoPinStatusReq, RepoPinStatus> { let req = RepoPinStatusReq::try_from(msg)?; let broker = BROKER.read().await; let res = broker - .get_server_storage()? + .get_server_broker()? .get_repo_pin_status(req.overlay(), req.hash()); fsm.lock() .await diff --git a/ng-net/src/actors/client/topic_sub.rs b/ng-net/src/actors/client/topic_sub.rs index 27a882c..69f6764 100644 --- a/ng-net/src/actors/client/topic_sub.rs +++ b/ng-net/src/actors/client/topic_sub.rs @@ -100,7 +100,7 @@ impl EActor for Actor<'_, TopicSub, TopicSubRes> { //TODO implement all the server side logic let broker = BROKER.read().await; - let res = broker.get_server_storage()?.topic_sub( + let res = broker.get_server_broker()?.topic_sub( req.overlay(), req.hash(), req.topic(), diff --git a/ng-net/src/actors/del_user.rs b/ng-net/src/actors/del_user.rs index 4b90c4c..695e41e 100644 --- a/ng-net/src/actors/del_user.rs +++ b/ng-net/src/actors/del_user.rs @@ -82,7 +82,7 @@ impl EActor for Actor<'_, DelUser, AdminResponse> { ) -> Result<(), ProtocolError> { let req = DelUser::try_from(msg)?; let broker = BROKER.read().await; - let res = broker.get_server_storage()?.del_user(req.user()); + let res = broker.get_server_broker()?.del_user(req.user()); let response: AdminResponseV0 = res.into(); fsm.lock().await.send(response.into()).await?; Ok(()) diff --git a/ng-net/src/actors/list_invitations.rs b/ng-net/src/actors/list_invitations.rs index 05c2a30..5b9a947 100644 --- a/ng-net/src/actors/list_invitations.rs +++ b/ng-net/src/actors/list_invitations.rs @@ -97,7 +97,7 @@ impl EActor for Actor<'_, ListInvitations, AdminResponse> { fsm: Arc>, ) -> Result<(), ProtocolError> { let req = ListInvitations::try_from(msg)?; - let res = BROKER.read().await.get_server_storage()?.list_invitations( + let res = BROKER.read().await.get_server_broker()?.list_invitations( req.admin(), req.unique(), req.multi(), diff --git a/ng-net/src/actors/list_users.rs b/ng-net/src/actors/list_users.rs index 69a1abc..10ff532 100644 --- a/ng-net/src/actors/list_users.rs +++ b/ng-net/src/actors/list_users.rs @@ -86,7 +86,7 @@ impl EActor for Actor<'_, ListUsers, AdminResponse> { let res = BROKER .read() .await - .get_server_storage()? + .get_server_broker()? .list_users(req.admins()); let response: AdminResponseV0 = res.into(); fsm.lock().await.send(response.into()).await?; diff --git a/ng-net/src/broker.rs b/ng-net/src/broker.rs index d586f4e..63052d9 100644 --- a/ng-net/src/broker.rs +++ b/ng-net/src/broker.rs @@ -14,7 +14,7 @@ use crate::actor::EActor; use crate::actor::SoS; use crate::connection::*; -use crate::server_storage::ServerStorage; +use crate::server_broker::IServerBroker; use crate::types::*; use crate::utils::spawn_and_log_error; use crate::utils::{Receiver, ResultSend, Sender}; @@ -85,7 +85,7 @@ pub struct Broker<'a> { shutdown: Option>, shutdown_sender: Sender, closing: bool, - server_storage: Option>, + server_broker: Option>, tauri_streams: HashMap>, disconnections_sender: Sender, @@ -146,9 +146,9 @@ impl<'a> Broker<'a> { .ok_or(ProtocolError::BrokerError) } - pub fn set_server_storage(&mut self, storage: impl ServerStorage + 'a) { - //log_debug!("set_storage"); - self.server_storage = Some(Box::new(storage)); + pub fn set_server_broker(&mut self, broker: impl IServerBroker + 'a) { + //log_debug!("set_server_broker"); + self.server_broker = Some(Box::new(broker)); } pub fn set_local_broker(&mut self, broker: Arc>) { @@ -178,11 +178,11 @@ impl<'a> Broker<'a> { (copy_listeners, copy_bind_addresses) } - pub fn get_server_storage( + pub fn get_server_broker( &self, - ) -> Result<&Box, ProtocolError> { + ) -> Result<&Box, ProtocolError> { //log_debug!("GET STORAGE {:?}", self.server_storage); - self.server_storage + self.server_broker .as_ref() .ok_or(ProtocolError::BrokerError) } @@ -222,7 +222,7 @@ impl<'a> Broker<'a> { Authorization::Client(user_and_registration) => { if user_and_registration.1.is_some() { // user wants to register - let storage = self.get_server_storage()?; + let storage = self.get_server_broker()?; if storage.get_user(user_and_registration.0).is_ok() { return Ok(()); } @@ -266,7 +266,7 @@ impl<'a> Broker<'a> { storage.remove_invitation(code)?; } } - self.get_server_storage()? + self.get_server_broker()? .add_user(user_and_registration.0, is_admin)?; Ok(()) } @@ -290,7 +290,7 @@ impl<'a> Broker<'a> { return Ok(()); } } - let found = self.get_server_storage()?.get_user(admin_user); + let found = self.get_server_broker()?.get_user(admin_user); if found.is_ok() && found.unwrap() { return Ok(()); } @@ -302,11 +302,11 @@ impl<'a> Broker<'a> { } // pub fn add_user(&self, user: PubKey, is_admin: bool) -> Result<(), ProtocolError> { - // self.get_server_storage()?.add_user(user, is_admin) + // self.get_server_broker()?.add_user(user, is_admin) // } // pub fn list_users(&self, admins: bool) -> Result, ProtocolError> { - // self.get_server_storage()?.list_users(admins) + // self.get_server_broker()?.list_users(admins) // } pub async fn get_block_from_store_with_block_id( @@ -482,7 +482,7 @@ impl<'a> Broker<'a> { peers: HashMap::new(), tauri_streams: HashMap::new(), closing: false, - server_storage: None, + server_broker: None, disconnections_sender, disconnections_receiver: Some(disconnections_receiver), local_broker: None, diff --git a/ng-net/src/lib.rs b/ng-net/src/lib.rs index 39d3417..d468f54 100644 --- a/ng-net/src/lib.rs +++ b/ng-net/src/lib.rs @@ -17,7 +17,7 @@ pub mod errors; pub mod broker; -pub mod server_storage; +pub mod server_broker; pub mod connection; diff --git a/ng-net/src/server_storage.rs b/ng-net/src/server_broker.rs similarity index 96% rename from ng-net/src/server_storage.rs rename to ng-net/src/server_broker.rs index ef14fb7..b3671a8 100644 --- a/ng-net/src/server_storage.rs +++ b/ng-net/src/server_broker.rs @@ -9,13 +9,13 @@ * according to those terms. */ -//! Trait for ServerStorage +//! Trait for ServerBroker use crate::types::*; use ng_repo::errors::*; use ng_repo::types::*; -pub trait ServerStorage: Send + Sync { +pub trait IServerBroker: Send + Sync { fn get_user(&self, user_id: PubKey) -> Result; fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError>; fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError>; diff --git a/ng-repo/src/errors.rs b/ng-repo/src/errors.rs index 535e61c..82b64d2 100644 --- a/ng-repo/src/errors.rs +++ b/ng-repo/src/errors.rs @@ -66,6 +66,8 @@ pub enum NgError { NotAServerError, VerifierError(VerifierError), SiteNotFoundOnBroker, + BrokerConfigErrorStr(&'static str), + BrokerConfigError(String), } impl Error for NgError {} @@ -75,6 +77,19 @@ impl fmt::Display for NgError { match self { Self::WalletError(string) => write!(f, "WalletError: {}", string), Self::JsStorageWriteError(string) => write!(f, "JsStorageWriteError: {}", string), + Self::CommitVerifyError(commit_verify_error) => { + write!(f, "CommitVerifyError: {:?}", commit_verify_error) + } + Self::ProtocolError(error) => write!(f, "ProtocolError: {:?}", error), + Self::ServerError(error) => write!(f, "ServerError: {:?}", error), + Self::VerifierError(error) => write!(f, "VerifierError: {:?}", error), + Self::CommitLoadError(commit_load_error) => { + write!(f, "CommitLoadError: {:?}", commit_load_error) + } + Self::ObjectParseError(error) => write!(f, "ObjectParseError: {:?}", error), + Self::StorageError(storage_error) => write!(f, "StorageError: {:?}", storage_error), + Self::BrokerConfigErrorStr(s) => write!(f, "BrokerConfigError: {s}"), + Self::BrokerConfigError(s) => write!(f, "BrokerConfigError: {s}"), _ => write!(f, "{:?}", self), } } @@ -85,38 +100,7 @@ impl From for std::io::Error { match err { NgError::InvalidArgument => std::io::Error::from(std::io::ErrorKind::InvalidInput), NgError::PermissionDenied => std::io::Error::from(std::io::ErrorKind::PermissionDenied), - NgError::CommitLoadError(commit_load_error) => std::io::Error::new( - std::io::ErrorKind::Other, - format!("CommitLoadError: {:?}", commit_load_error), - ), - NgError::StorageError(storage_error) => std::io::Error::new( - std::io::ErrorKind::Other, - format!("StorageError: {:?}", storage_error), - ), NgError::NotFound => std::io::Error::from(std::io::ErrorKind::NotFound), - NgError::CommitVerifyError(commit_verify_error) => std::io::Error::new( - std::io::ErrorKind::Other, - format!("CommitVerifyError: {:?}", commit_verify_error), - ), - /*NgError::InvalidSignature => , - NgError::IncompleteSignature => - NgError::SerializationError => , - NgError::EncryptionError => , - NgError::InvalidKey => , - NgError::InvalidInvitation => , - NgError::InvalidCreateAccount => , - NgError::InvalidFileFormat => , - NgError::LocalBrokerNotInitialized => , - NgError::JsStorageReadError => , - NgError::JsStorageWriteError(String) => , - NgError::CannotSaveWhenInMemoryConfig => , - NgError::WalletNotFound => , - NgError::WalletAlreadyAdded => , - NgError::WalletAlreadyOpened => , - NgError::WalletError(String) => , - NgError::BrokerError => , - NgError::SessionNotFound, - NgError::IoError => ,*/ _ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()), } } diff --git a/ng-repo/src/types.rs b/ng-repo/src/types.rs index b55f04a..19c839c 100644 --- a/ng-repo/src/types.rs +++ b/ng-repo/src/types.rs @@ -184,7 +184,7 @@ impl fmt::Display for PubKey { impl TryFrom<&str> for PubKey { type Error = NgError; fn try_from(str: &str) -> Result { - let key = decode_key(str).map_err(|_| NgError::InvalidKey)?; + let key = decode_key(str)?; Ok(PubKey::Ed25519PubKey(key)) } } @@ -254,7 +254,7 @@ impl TryFrom<&[u8]> for PrivKey { impl TryFrom<&str> for PrivKey { type Error = NgError; fn try_from(str: &str) -> Result { - let key = decode_key(str).map_err(|_| NgError::InvalidKey)?; + let key = decode_key(str)?; Ok(PrivKey::Ed25519PrivKey(key)) } } diff --git a/ng-repo/src/utils.rs b/ng-repo/src/utils.rs index ac441e6..1741e22 100644 --- a/ng-repo/src/utils.rs +++ b/ng-repo/src/utils.rs @@ -53,11 +53,9 @@ pub fn from_ed_privkey_to_dh_privkey(private: &PrivKey) -> PrivKey { } /// don't forget to zeroize the string later on -pub fn decode_key(key_string: &str) -> Result<[u8; 32], ()> { - let vec = base64_url::decode(key_string).map_err(|_| log_err!("key has invalid content"))?; - Ok(*slice_as_array!(&vec, [u8; 32]) - .ok_or(()) - .map_err(|_| log_err!("key has invalid content array"))?) +pub fn decode_key(key_string: &str) -> Result<[u8; 32], NgError> { + let vec = base64_url::decode(key_string).map_err(|_| NgError::InvalidKey)?; + Ok(*slice_as_array!(&vec, [u8; 32]).ok_or(NgError::InvalidKey)?) } pub fn ed_privkey_to_ed_pubkey(privkey: &PrivKey) -> PubKey { diff --git a/ng-storage-rocksdb/src/block_storage.rs b/ng-storage-rocksdb/src/block_storage.rs index 7db84ea..f0e01f0 100644 --- a/ng-storage-rocksdb/src/block_storage.rs +++ b/ng-storage-rocksdb/src/block_storage.rs @@ -55,6 +55,7 @@ impl RocksDbBlockStorage { opts.set_max_bytes_for_level_base(16 * 1024 * 1024); opts.set_target_file_size_multiplier(10); opts.set_level_compaction_dynamic_level_bytes(true); + opts.set_num_levels(7); // the default opts.create_if_missing(true); opts.create_missing_column_families(false); @@ -67,7 +68,7 @@ impl RocksDbBlockStorage { opts.set_blob_compression_type(DBCompressionType::None); opts.set_enable_blob_gc(true); // the oldest half of blob files will be selected for GC - opts.set_blob_gc_age_cutoff(0.5); + opts.set_blob_gc_age_cutoff(0.75); // in those oldest blob files, if 50% of it (8MB) is garbage, a forced compact will occur. // this way we are reducing the space amplification by small decrements of 8MB opts.set_blob_gc_force_threshold(0.5); diff --git a/ng-storage-rocksdb/src/kcv_storage.rs b/ng-storage-rocksdb/src/kcv_storage.rs index 5570667..12434ea 100644 --- a/ng-storage-rocksdb/src/kcv_storage.rs +++ b/ng-storage-rocksdb/src/kcv_storage.rs @@ -676,6 +676,7 @@ impl RocksDbKCVStorage { opts.set_max_bytes_for_level_base(256 * 1024 * 1024); opts.set_target_file_size_multiplier(10); opts.set_level_compaction_dynamic_level_bytes(true); + opts.set_num_levels(7); // the default opts.create_if_missing(true); opts.create_missing_column_families(true); @@ -685,6 +686,7 @@ impl RocksDbKCVStorage { let cache = Cache::new_lru_cache(64 * 1024 * 1024); block_based_opts.set_block_cache(&cache); block_based_opts.set_cache_index_and_filter_blocks(true); + block_based_opts.set_pin_l0_filter_and_index_blocks_in_cache(true); block_based_opts.set_block_size(16 * 1024); block_based_opts.set_bloom_filter(10.0, false); block_based_opts.set_format_version(6); diff --git a/ng-verifier/src/rocksdb_user_storage.rs b/ng-verifier/src/rocksdb_user_storage.rs index 11fe899..fd1924a 100644 --- a/ng-verifier/src/rocksdb_user_storage.rs +++ b/ng-verifier/src/rocksdb_user_storage.rs @@ -39,9 +39,9 @@ impl RocksDbUserStorage { } impl UserStorage for RocksDbUserStorage { - fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result { - unimplemented!(); - } + // fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result { + // unimplemented!(); + // } fn get_all_store_and_repo_ids(&self) -> Result>, StorageError> { RepoStorage::get_all_store_and_repo_ids(&self.user_storage) diff --git a/ng-verifier/src/types.rs b/ng-verifier/src/types.rs index bbc5f2d..1be5daa 100644 --- a/ng-verifier/src/types.rs +++ b/ng-verifier/src/types.rs @@ -15,7 +15,7 @@ use core::fmt; //use oxigraph::model::GroundQuad; #[cfg(not(target_family = "wasm"))] use crate::rocksdb_user_storage::RocksDbUserStorage; -use crate::user_storage::{InMemoryUserStorage, UserStorage}; +use crate::user_storage::UserStorage; use async_std::sync::Mutex; use std::{collections::HashMap, path::PathBuf, sync::Arc}; diff --git a/ng-verifier/src/user_storage/storage.rs b/ng-verifier/src/user_storage/storage.rs index 54a04e8..702a7e9 100644 --- a/ng-verifier/src/user_storage/storage.rs +++ b/ng-verifier/src/user_storage/storage.rs @@ -26,8 +26,7 @@ use std::{ }; pub trait UserStorage: Send + Sync { - /// Gets the StoreRepo for a given RepoId - fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result; + //fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result; fn get_all_store_and_repo_ids(&self) -> Result>, StorageError>; @@ -46,51 +45,51 @@ pub trait UserStorage: Send + Sync { fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError>; } -pub(crate) struct InMemoryUserStorage { - repo_id_to_store_overlay: HashMap, -} - -impl InMemoryUserStorage { - pub fn new() -> Self { - InMemoryUserStorage { - repo_id_to_store_overlay: HashMap::new(), - } - } -} - -impl UserStorage for InMemoryUserStorage { - fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result { - Ok(self - .repo_id_to_store_overlay - .get(&id) - .ok_or(StorageError::NotFound)? - .to_owned()) - } - - fn get_all_store_and_repo_ids(&self) -> Result>, StorageError> { - unimplemented!(); - } - - fn load_store( - &self, - repo_store: &StoreRepo, - block_storage: Arc>, - ) -> Result { - unimplemented!(); - } - fn load_repo(&self, repo_id: &RepoId, store: Arc) -> Result { - unimplemented!(); - } - - fn save_repo(&self, repo: &Repo) -> Result<(), StorageError> { - unimplemented!(); - } - - fn add_branch(&self, repo_id: &RepoId, branch_info: &BranchInfo) -> Result<(), StorageError> { - unimplemented!(); - } - - fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError> { - unimplemented!(); - } -} +// pub(crate) struct InMemoryUserStorage { +// repo_id_to_store_overlay: HashMap, +// } + +// impl InMemoryUserStorage { +// pub fn new() -> Self { +// InMemoryUserStorage { +// repo_id_to_store_overlay: HashMap::new(), +// } +// } +// } + +// impl UserStorage for InMemoryUserStorage { +// fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result { +// Ok(self +// .repo_id_to_store_overlay +// .get(&id) +// .ok_or(StorageError::NotFound)? +// .to_owned()) +// } + +// fn get_all_store_and_repo_ids(&self) -> Result>, StorageError> { +// unimplemented!(); +// } + +// fn load_store( +// &self, +// repo_store: &StoreRepo, +// block_storage: Arc>, +// ) -> Result { +// unimplemented!(); +// } +// fn load_repo(&self, repo_id: &RepoId, store: Arc) -> Result { +// unimplemented!(); +// } + +// fn save_repo(&self, repo: &Repo) -> Result<(), StorageError> { +// unimplemented!(); +// } + +// fn add_branch(&self, repo_id: &RepoId, branch_info: &BranchInfo) -> Result<(), StorageError> { +// unimplemented!(); +// } + +// fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError> { +// unimplemented!(); +// } +// } diff --git a/ng-verifier/src/verifier.rs b/ng-verifier/src/verifier.rs index 8254f07..179e4a3 100644 --- a/ng-verifier/src/verifier.rs +++ b/ng-verifier/src/verifier.rs @@ -35,7 +35,7 @@ use core::fmt; //use oxigraph::model::GroundQuad; #[cfg(not(target_family = "wasm"))] use crate::rocksdb_user_storage::RocksDbUserStorage; -use crate::user_storage::{InMemoryUserStorage, UserStorage}; +use crate::user_storage::UserStorage; use async_std::sync::{Mutex, RwLockReadGuard, RwLockWriteGuard}; use std::{collections::HashMap, path::PathBuf, sync::Arc}; @@ -1114,7 +1114,7 @@ impl Verifier { let (graph, user, block) = match &config.config_type { VerifierConfigType::Memory | VerifierConfigType::JsSaveSession(_) => ( Some(oxigraph::store::Store::new().unwrap()), - Some(Box::new(InMemoryUserStorage::new()) as Box), + None, //Some(Box::new(InMemoryUserStorage::new()) as Box), Some(block_storage), ), #[cfg(not(target_family = "wasm"))] diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs index 282953a..6b7ae96 100644 --- a/ngcli/src/main.rs +++ b/ngcli/src/main.rs @@ -9,6 +9,7 @@ use ed25519_dalek::*; +use core::fmt; use duration_str::parse; use futures::{future, pin_mut, stream, SinkExt, StreamExt}; use ng_net::actors::*; @@ -19,6 +20,7 @@ use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; use serde_json::{from_str, to_string_pretty}; use std::collections::HashMap; +use std::error::Error; use std::fs::{read_to_string, write}; use std::io::ErrorKind; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -65,6 +67,66 @@ impl CliConfig { } } +#[derive(Debug)] +pub enum NgcliError { + IoError(std::io::Error), + NgError(NgError), + InvalidKeyFile(String), + CannotSaveKey(String), + InvalidConfigFile(String), + ProtocolError(ProtocolError), + OtherConfigError(String), + OtherConfigErrorStr(&'static str), + CannotSaveConfig(String), +} + +impl Error for NgcliError {} + +impl From for std::io::Error { + fn from(err: NgcliError) -> std::io::Error { + match err { + NgcliError::NgError(e) => e.into(), + NgcliError::ProtocolError(e) => Into::::into(e).into(), + NgcliError::IoError(e) => e, + _ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()), + } + } +} + +impl From for NgcliError { + fn from(err: NgError) -> NgcliError { + Self::NgError(err) + } +} + +impl From for NgcliError { + fn from(err: ProtocolError) -> NgcliError { + Self::ProtocolError(err) + } +} + +impl From for NgcliError { + fn from(io: std::io::Error) -> NgcliError { + NgcliError::IoError(io) + } +} + +impl fmt::Display for NgcliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidKeyFile(s) => write!(f, "provided key file is invalid. {}", s), + Self::CannotSaveKey(s) => write!(f, "cannot save key to file. {}", s), + Self::NgError(e) => write!(f, "{}", e.to_string()), + Self::InvalidConfigFile(s) => write!(f, "provided config file is invalid. {}", s), + Self::IoError(e) => write!(f, "IoError : {:?}", e), + Self::ProtocolError(e) => write!(f, "{}", e), + Self::OtherConfigError(s) => write!(f, "{}", s), + Self::OtherConfigErrorStr(s) => write!(f, "{}", s), + _ => write!(f, "{:?}", self), + } + } +} + fn gen_client_keys(key: Option<[u8; 32]>) -> [[u8; 32]; 4] { let key = match key { None => { @@ -87,7 +149,14 @@ fn gen_client_keys(key: Option<[u8; 32]>) -> [[u8; 32]; 4] { } #[async_std::main] -async fn main() -> Result<(), ProtocolError> { +async fn main() -> std::io::Result<()> { + if let Err(err) = main_inner().await { + log_err!("An error occurred: {}", err.to_string()); + return Err(err.into()); + } + Ok(()) +} +async fn main_inner() -> Result<(), NgcliError> { let matches = command!() .arg(arg!( -v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace" @@ -203,23 +272,19 @@ async fn main() -> Result<(), ProtocolError> { // reading key from file, if any let mut key_path = path.clone(); key_path.push("key"); - let key_from_file: Option<[u8; 32]>; - let res = |key_path| -> Result<[u8; 32], &str> { - let mut file = read_to_string(key_path).map_err(|_| "")?; - let first_line = file.lines().nth(0).ok_or("empty file")?; - let res = decode_key(first_line.trim()).map_err(|_| "invalid file"); - file.zeroize(); - res - }(&key_path); - - if res.is_err() && res.unwrap_err().len() > 0 { - log_err!( - "provided key file is incorrect. {}. cannot start", - res.unwrap_err() - ); - return Err(ProtocolError::InvalidValue); - } - key_from_file = res.ok(); + let key_from_file: Option<[u8; 32]> = match read_to_string(key_path.clone()) { + Err(_) => None, + Ok(mut file) => { + let first_line = file + .lines() + .nth(0) + .ok_or(NgcliError::InvalidKeyFile("empty file".to_string()))?; + let res = decode_key(first_line.trim()) + .map_err(|_| NgcliError::InvalidKeyFile("deserialization error".to_string()))?; + file.zeroize(); + Some(res) + } + }; let mut keys: [[u8; 32]; 4] = match matches.get_one::("key") { Some(key_string) => { @@ -229,15 +294,14 @@ async fn main() -> Result<(), ProtocolError> { gen_client_keys(key_from_file) } else { let res = decode_key(key_string.as_str()).map_err(|_| { - log_err!("provided key is invalid. cannot start"); - ProtocolError::InvalidValue + NgcliError::InvalidKeyFile( + "check the argument provided in command line".to_string(), + ) })?; if matches.get_flag("save_key") { let mut master_key = base64_url::encode(&res); - write(key_path.clone(), &master_key).map_err(|e| { - log_err!("cannot save key to file. {}.cannot start", e.to_string()); - ProtocolError::InvalidValue - })?; + write(key_path.clone(), &master_key) + .map_err(|e| NgcliError::CannotSaveKey(e.to_string()))?; master_key.zeroize(); log_info!("The key has been saved to {}", key_path.to_str().unwrap()); } @@ -252,10 +316,8 @@ async fn main() -> Result<(), ProtocolError> { let res = gen_client_keys(None); let mut master_key = base64_url::encode(&res[0]); if matches.get_flag("save_key") { - write(key_path.clone(), &master_key).map_err(|e| { - log_err!("cannot save key to file. {}.cannot start", e.to_string()); - ProtocolError::InvalidValue - })?; + write(key_path.clone(), &master_key) + .map_err(|e| NgcliError::CannotSaveKey(e.to_string()))?; log_info!("The key has been saved to {}", key_path.to_str().unwrap()); } else { // on purpose we don't log the key, just print it out to stdout, as it should not be saved in logger's files @@ -277,47 +339,31 @@ async fn main() -> Result<(), ProtocolError> { // reading config from file, if any let mut config_path = path.clone(); config_path.push("config.json"); - let mut config: Option; - let res = |config_path| -> Result { - let file = read_to_string(config_path).map_err(|_| "".to_string())?; - from_str(&file).map_err(|e| e.to_string()) - }(&config_path); - - if res.is_err() && res.as_ref().unwrap_err().len() > 0 { - log_err!( - "provided config file is incorrect. {}. cannot start", - res.unwrap_err() - ); - return Err(ProtocolError::InvalidValue); - } - config = res.ok(); + let mut config: Option = match read_to_string(config_path.clone()) { + Err(_) => None, + Ok(file) => { + Some(from_str(&file).map_err(|e| NgcliError::InvalidConfigFile(e.to_string()))?) + } + }; if let Some(server) = matches.get_one::("server") { let addr: Vec<&str> = server.split(',').collect(); if addr.len() != 3 { - log_err!( - "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. cannot start" - ); - return Err(ProtocolError::InvalidValue); + return Err(NgcliError::OtherConfigErrorStr( + "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID.", + )); } let ip = IpAddr::from_str(addr[0]).map_err(|_| { - log_err!("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The first part is not an IP address. cannot start"); - ProtocolError::InvalidValue + NgcliError::OtherConfigErrorStr("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The first part is not an IP address.") })?; - let port = match from_str::(addr[1]) { - Err(_) => { - log_err!("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The port is invalid. It should be a number. cannot start"); - return Err(ProtocolError::InvalidValue); - } - Ok(val) => val, - }; + let port = from_str::(addr[1]).map_err(|_| { + NgcliError::OtherConfigErrorStr("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The port is invalid. It should be a number.") + })?; let peer_id: PubKey = addr[2].try_into().map_err(|_| { - log_err!( - "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. - The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]. cannot start" - ); - ProtocolError::InvalidValue + NgcliError::OtherConfigErrorStr( + "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]." + ) })?; if config.is_some() { log_warn!("Overriding the config found in file with new server parameters provided on command line!"); @@ -331,20 +377,16 @@ async fn main() -> Result<(), ProtocolError> { } if config.is_none() { - log_err!( - "No config found for the server to connect to. The config file is missing. - You must provide NG_CLIENT_SERVER or the --server option. cannot start" - ); - return Err(ProtocolError::InvalidValue); + return Err(NgcliError::OtherConfigErrorStr( + "No config found for the server to connect to. The config file is missing. You must provide NG_CLIENT_SERVER or the --server option.", + )); } if let Some(user) = matches.get_one::("user") { let privkey: PrivKey = user.as_str().try_into().map_err(|_| { - log_err!( - "NG_CLIENT_USER or the --user option is invalid. It should be a base64-url encoded - serde serialization of a [u8; 32] of a private key for a user. cannot start" - ); - ProtocolError::InvalidValue + NgcliError::OtherConfigErrorStr( + "NG_CLIENT_USER or the --user option is invalid. It should be a base64-url encoded serde serialization of a [u8; 32] of a private key for a user.", + ) })?; if config.is_some() { let CliConfig::V0(c) = config.as_mut().unwrap(); @@ -359,23 +401,18 @@ async fn main() -> Result<(), ProtocolError> { let CliConfig::V0(config_v0) = config.as_ref().unwrap(); if config_v0.user.is_none() { - log_err!( - "No config found for the user. The config file is missing. - You must provide NG_CLIENT_USER or the --user option. cannot start" - ); - return Err(ProtocolError::InvalidValue); + return Err(NgcliError::OtherConfigErrorStr( + "No config found for the user. The config file is missing. You must provide NG_CLIENT_USER or the --user option.", + )); } if matches.get_flag("save_config") { // saves the config to file - let json_string = to_string_pretty(&config).unwrap(); + let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap(); write(config_path.clone(), json_string).map_err(|e| { - log_err!( - "cannot save config to file. {}. cannot start", - e.to_string() - ); - ProtocolError::InvalidValue + NgcliError::CannotSaveConfig(format!("cannot save config to file. {}.", e.to_string())) })?; + log_info!( "The config file has been saved to {}", config_path.to_str().unwrap() @@ -430,18 +467,15 @@ async fn main() -> Result<(), ProtocolError> { .as_str() .try_into() .map_err(|_| { - log_err!("supplied USER_ID is invalid"); - ProtocolError::InvalidValue + NgcliError::OtherConfigErrorStr("supplied USER_ID is invalid") })?, is_admin: sub2_matches.get_flag("admin"), }), ) - .await; - match &res { - Err(e) => log_err!("An error occurred: {e}"), - Ok(_) => println!("User added successfully"), - } - return res.map(|_| ()); + .await?; + + println!("User added successfully"); + return Ok(()); } Some(("del-user", sub2_matches)) => { log_debug!("add-user"); @@ -455,26 +489,21 @@ async fn main() -> Result<(), ProtocolError> { .as_str() .try_into() .map_err(|_| { - log_err!("supplied USER_ID is invalid"); - ProtocolError::InvalidValue + NgcliError::OtherConfigErrorStr("supplied USER_ID is invalid") })?, }), ) - .await; - match &res { - Err(e) => log_err!("An error occurred: {e}"), - Ok(_) => println!("User removed successfully"), - } - return res.map(|_| ()); + .await?; + println!("User removed successfully"); + return Ok(()); } Some(("list-users", sub2_matches)) => { log_debug!("list-users"); let admins = sub2_matches.get_flag("admin"); - let res = - do_admin_call(keys[1], config_v0, ListUsers::V0(ListUsersV0 { admins })).await; + let res = do_admin_call(keys[1], config_v0, ListUsers::V0(ListUsersV0 { admins })) + .await?; match &res { - Err(e) => log_err!("An error occurred: {e}"), - Ok(AdminResponseContentV0::Users(list)) => { + AdminResponseContentV0::Users(list) => { println!( "Found {} {}users", list.len(), @@ -484,12 +513,9 @@ async fn main() -> Result<(), ProtocolError> { println!("{user}"); } } - _ => { - log_err!("Invalid response"); - return Err(ProtocolError::InvalidValue); - } + _ => return Err(NgError::InvalidResponse.into()), } - return res.map(|_| ()); + return Ok(()); } Some(("add-invitation", sub2_matches)) => { log_debug!("add-invitation"); @@ -523,10 +549,9 @@ async fn main() -> Result<(), ProtocolError> { tos_url: !sub2_matches.get_flag("notos"), }), ) - .await; - match res.as_mut() { - Err(e) => log_err!("An error occurred: {e}"), - Ok(AdminResponseContentV0::Invitation(invitation)) => { + .await?; + match &mut res { + AdminResponseContentV0::Invitation(invitation) => { invitation .set_name(sub2_matches.get_one::("name").map(|s| s.clone())); @@ -536,12 +561,9 @@ async fn main() -> Result<(), ProtocolError> { println!("The invitation link is: {}", link) } } - _ => { - log_err!("Invalid response"); - return Err(ProtocolError::InvalidValue); - } + _ => return Err(NgError::InvalidResponse.into()), } - return res.map(|_| ()); + return Ok(()); } Some(("list-invitations", sub2_matches)) => { log_debug!("invitations"); @@ -557,10 +579,9 @@ async fn main() -> Result<(), ProtocolError> { unique, }), ) - .await; + .await?; match &res { - Err(e) => log_err!("An error occurred: {e}"), - Ok(AdminResponseContentV0::Invitations(list)) => { + AdminResponseContentV0::Invitations(list) => { println!( "Found {} {}invitations", list.len(), @@ -593,12 +614,9 @@ async fn main() -> Result<(), ProtocolError> { ); } } - _ => { - log_err!("Invalid response"); - return Err(ProtocolError::InvalidValue); - } + _ => return Err(NgError::InvalidResponse.into()), } - return res.map(|_| ()); + return Ok(()); } _ => panic!("shouldn't happen"), }, diff --git a/ngd/src/main.rs b/ngd/src/main.rs index f48943c..03c8a10 100644 --- a/ngd/src/main.rs +++ b/ngd/src/main.rs @@ -14,6 +14,7 @@ mod cli; use crate::cli::*; use crate::types::*; use clap::Parser; +use core::fmt; use ng_broker::interfaces::*; use ng_broker::server_ws::run_server_v0; use ng_broker::types::*; @@ -27,6 +28,7 @@ use ng_net::utils::{ gen_dh_keys, is_ipv4_global, is_ipv4_private, is_ipv6_global, is_ipv6_private, }; use ng_net::{WS_PORT, WS_PORT_REVERSE_PROXY}; +use ng_repo::errors::NgError; use ng_repo::log::*; use ng_repo::types::Sig; use ng_repo::types::SymKey; @@ -36,6 +38,7 @@ use ng_repo::{ utils::{decode_key, generate_keypair, sign, verify}, }; use serde_json::{from_str, to_string_pretty}; +use std::error::Error; use std::fs::{read_to_string, write}; use std::io::ErrorKind; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -79,7 +82,7 @@ fn parse_interface_and_port_for( string: &String, for_option: &str, default_port: u16, -) -> Result<(String, u16), ()> { +) -> Result<(String, u16), NgdError> { let c = RE_INTERFACE.captures(string); if c.is_some() && c.as_ref().unwrap().get(1).is_some() { @@ -104,20 +107,19 @@ fn parse_interface_and_port_for( }; Ok((interface.to_string(), port)) } else { - log_err!( - "The value submitted for the {} option is invalid. It should be the name of an interface found with --list-interfaces, with an optional port suffix of the form :123. cannot start", + Err(NgdError::OtherConfigError(format!( + "The value submitted for the {} option is invalid. It should be the name of an interface found with --list-interfaces, with an optional port suffix of the form :123.", for_option - ); - Err(()) + ))) } } -fn parse_ipv6_for(string: String, for_option: &str) -> Result { +fn parse_ipv6_for(string: String, for_option: &str) -> Result { string.parse::().map_err(|_| { - log_err!( - "The value submitted for the {} option is invalid. cannot start", + NgdError::OtherConfigError(format!( + "The value submitted for the {} option is invalid.", for_option - ) + )) }) } @@ -125,18 +127,17 @@ fn parse_ipv4_and_port_for( string: String, for_option: &str, default_port: u16, -) -> Result<(Ipv4Addr, u16), ()> { +) -> Result<(Ipv4Addr, u16), NgdError> { let parts: Vec<&str> = string.split(":").collect(); let ipv4 = parts[0].parse::().map_err(|_| { - log_err!( - "The value submitted for the {} option is invalid. cannot start", + NgdError::OtherConfigError(format!( + "The value submitted for the {} option is invalid.", for_option - ) + )) })?; - let port; - if parts.len() > 1 { - port = match from_str::(parts[1]) { + let port = if parts.len() > 1 { + match from_str::(parts[1]) { Err(_) => default_port, Ok(p) => { if p == 0 { @@ -145,14 +146,14 @@ fn parse_ipv4_and_port_for( p } } - }; + } } else { - port = default_port; - } - return Ok((ipv4, port)); + default_port + }; + Ok((ipv4, port)) } -fn parse_ip_and_port_for(string: String, for_option: &str) -> Result<(IpAddr, u16), ()> { +fn parse_ip_and_port_for(string: String, for_option: &str) -> Result<(IpAddr, u16), NgdError> { let c = RE_IPV6_WITH_PORT.captures(&string); let ipv6; let port; @@ -177,24 +178,24 @@ fn parse_ip_and_port_for(string: String, for_option: &str) -> Result<(IpAddr, u1 } }; let ipv6 = ipv6_str.parse::().map_err(|_| { - log_err!( - "The <[IPv6]:PORT> value submitted for the {} option is invalid. cannot start", + NgdError::OtherConfigError(format!( + "The <[IPv6]:PORT> value submitted for the {} option is invalid.", for_option - ) + )) })?; - return Ok((IpAddr::V6(ipv6), port)); + Ok((IpAddr::V6(ipv6), port)) } else { // we try just an IPV6 without port let ipv6_res = string.parse::(); if ipv6_res.is_err() { // let's try IPv4 - return parse_ipv4_and_port_for(string, for_option, DEFAULT_PORT) - .map(|ipv4| (IpAddr::V4(ipv4.0), ipv4.1)); + parse_ipv4_and_port_for(string, for_option, DEFAULT_PORT) + .map(|ipv4| (IpAddr::V4(ipv4.0), ipv4.1)) } else { ipv6 = ipv6_res.unwrap(); port = DEFAULT_PORT; - return Ok((IpAddr::V6(ipv6), port)); + Ok((IpAddr::V6(ipv6), port)) } } } @@ -202,72 +203,59 @@ fn parse_ip_and_port_for(string: String, for_option: &str) -> Result<(IpAddr, u1 fn parse_triple_interface_and_port_for( string: &String, for_option: &str, -) -> Result<((String, u16), (Option, (Ipv4Addr, u16))), ()> { +) -> Result<((String, u16), (Option, (Ipv4Addr, u16))), NgdError> { let parts: Vec<&str> = string.split(',').collect(); if parts.len() < 2 { - log_err!( - "The value submitted for the {} option is invalid. It should be composed of at least 2 parts separated by a comma. cannot start", + return Err(NgdError::OtherConfigError(format!( + "The value submitted for the {} option is invalid. It should be composed of at least 2 parts separated by a comma.", for_option - ); - return Err(()); + ))); } let first_part = parse_interface_and_port_for( &parts[0].to_string(), &format!("private interface+PORT (left) part of the {}", for_option), DEFAULT_PORT, - ); - if first_part.is_err() { - return Err(()); - } + )?; let mut middle_part = None; if parts.len() == 3 { let middle_part_res = parse_ipv6_for( parts[1].to_string(), &format!("public IPv6 (middle) part of the {}", for_option), - ); - if middle_part_res.is_err() { - return Err(()); - } - middle_part = middle_part_res.ok(); + )?; } let last_part = parse_ipv4_and_port_for( parts[parts.len() - 1].to_string(), &format!("public IPv4+PORT (right) part of the {}", for_option), DEFAULT_PORT, - ); - if last_part.is_err() { - return Err(()); - } + )?; - Ok((first_part.unwrap(), (middle_part, last_part.unwrap()))) + Ok((first_part, (middle_part, last_part))) } fn parse_domain_and_port( domain_string: &String, option: &str, default_port: u16, -) -> Result<(String, String, u16), ()> { +) -> Result<(String, String, u16), NgdError> { let parts: Vec<&str> = domain_string.split(':').collect(); // check validity of domain name let valid_domain = List.parse_dns_name(parts[0]); match valid_domain { Err(e) => { - log_err!( - "The domain name provided for option {} is invalid. {}. cannot start", + return Err(NgdError::OtherConfigError(format!( + "The domain name provided for option {} is invalid. {}.", option, e.to_string() - ); - return Err(()); + ))); } Ok(name) => { if !name.has_known_suffix() { - log_err!( - "The domain name provided for option {} is invalid. Unknown suffix in public list. cannot start", option - ); - return Err(()); + return Err(NgdError::OtherConfigError(format!( + "The domain name provided for option {} is invalid. Unknown suffix in public list.", option + ))); } } } @@ -296,7 +284,7 @@ fn parse_domain_and_port( fn prepare_accept_forward_for_domain( domain: String, args: &mut Cli, -) -> Result { +) -> Result { if args.domain_peer.is_some() { let key = decode_key(args.domain_peer.as_ref().unwrap().as_str())?; args.domain_peer.as_mut().unwrap().zeroize(); @@ -310,15 +298,84 @@ fn prepare_accept_forward_for_domain( Ok(AcceptForwardForV0::PublicDomain((domain, "".to_string()))) } } +#[derive(Debug)] +pub enum NgdError { + IoError(std::io::Error), + NgError(NgError), + InvalidKeyFile(String), + CannotSaveKey(String), + InvalidSignature, + CannotSaveSignature(String), + InvalidConfigFile(String), + ConfigCannotSave, + ConfigFilePresent, + ConfigDomainPeerConflict, + NoLoopback, + OtherConfigError(String), + OtherConfigErrorStr(&'static str), + CannotSaveConfig(String), +} + +impl Error for NgdError {} + +impl From for std::io::Error { + fn from(err: NgdError) -> std::io::Error { + match err { + NgdError::NgError(e) => e.into(), + NgdError::IoError(e) => e, + _ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()), + } + } +} + +impl From for NgdError { + fn from(err: NgError) -> NgdError { + Self::NgError(err) + } +} + +impl From for NgdError { + fn from(io: std::io::Error) -> NgdError { + NgdError::IoError(io) + } +} + +impl fmt::Display for NgdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidKeyFile(s) => write!(f, "provided key file is invalid. {}", s), + Self::CannotSaveKey(s) => write!(f, "cannot save key to file. {}", s), + Self::NgError(e) => write!(f, "{}", e.to_string()), + Self::InvalidConfigFile(s) => write!(f, "provided config file is invalid. {}", s), + Self::IoError(e) => write!(f, "IoError : {:?}", e), + Self::ConfigCannotSave => write!( + f, + "A config file is present. We cannot override it with Quick config options" + ), + Self::ConfigFilePresent => write!( + f, + "A config file is present. You cannot use the Quick config options on the command-line. In order to use them, delete your config file first." + ), + Self::ConfigDomainPeerConflict => write!( + f,"The --domain-peer option can only be set when the --domain or --domain-private option is also present on the command line."), + Self::NoLoopback => write!( + f,"That's pretty unusual, but no loopback interface could be found on your host. --domain option failed for that reason."), + Self::OtherConfigError(s) => write!(f, "{}", s), + Self::OtherConfigErrorStr(s) => write!(f, "{}", s), + _ => write!(f, "{:?}", self), + } + } +} #[async_std::main] async fn main() -> std::io::Result<()> { - main_inner() - .await - .map_err(|_| ErrorKind::InvalidInput.into()) + if let Err(err) = main_inner().await { + log_err!("Cannot start: {}", err.to_string()); + return Err(err.into()); + } + Ok(()) } - -async fn main_inner() -> Result<(), ()> { +async fn main_inner() -> Result<(), NgdError> { let mut args = Cli::parse(); if args.list_interfaces { @@ -340,7 +397,10 @@ async fn main_inner() -> Result<(), ()> { } env_logger::init(); - log_info!("Starting NextGraph daemon (ngd)"); + log_info!( + "Starting NextGraph daemon (ngd) version {}", + env!("CARGO_PKG_VERSION").to_string() + ); log_debug!("base {:?}", args.base); @@ -357,23 +417,20 @@ async fn main_inner() -> Result<(), ()> { // reading key from file, if any let mut key_path = path.clone(); key_path.push("key"); - let key_from_file: Option<[u8; 32]>; - let res = |key_path| -> Result<[u8; 32], &str> { - let mut file = read_to_string(key_path).map_err(|_| "")?; - let first_line = file.lines().nth(0).ok_or("empty file")?; - let res = decode_key(first_line.trim()).map_err(|_| "invalid file"); - file.zeroize(); - res - }(&key_path); - - if res.is_err() && res.unwrap_err().len() > 0 { - log_err!( - "provided key file is incorrect. {}. cannot start", - res.unwrap_err() - ); - return Err(()); - } - key_from_file = res.ok(); + + let key_from_file: Option<[u8; 32]> = match read_to_string(key_path.clone()) { + Err(_) => None, + Ok(mut file) => { + let first_line = file + .lines() + .nth(0) + .ok_or(NgdError::InvalidKeyFile("empty file".to_string()))?; + let res = decode_key(first_line.trim()) + .map_err(|_| NgdError::InvalidKeyFile("deserialization error".to_string()))?; + file.zeroize(); + Some(res) + } + }; let mut keys: [[u8; 32]; 4] = match &args.key { Some(key_string) => { @@ -382,13 +439,15 @@ async fn main_inner() -> Result<(), ()> { args.key.as_mut().unwrap().zeroize(); gen_broker_keys(key_from_file) } else { - let res = decode_key(key_string.as_str()) - .map_err(|_| log_err!("provided key is invalid. cannot start"))?; + let res = decode_key(key_string.as_str()).map_err(|_| { + NgdError::InvalidKeyFile( + "check the argument provided in command line".to_string(), + ) + })?; if args.save_key { let mut master_key = base64_url::encode(&res); - write(key_path.clone(), &master_key).map_err(|e| { - log_err!("cannot save key to file. {}.cannot start", e.to_string()) - })?; + write(key_path.clone(), &master_key) + .map_err(|e| NgdError::CannotSaveKey(e.to_string()))?; master_key.zeroize(); log_info!("The key has been saved to {}", key_path.to_str().unwrap()); } @@ -403,9 +462,8 @@ async fn main_inner() -> Result<(), ()> { let res = gen_broker_keys(None); let mut master_key = base64_url::encode(&res[0]); if args.save_key { - write(key_path.clone(), &master_key).map_err(|e| { - log_err!("cannot save key to file. {}.cannot start", e.to_string()) - })?; + write(key_path.clone(), &master_key) + .map_err(|e| NgdError::CannotSaveKey(e.to_string()))?; log_info!("The key has been saved to {}", key_path.to_str().unwrap()); } else { // on purpose we don't log the key, just print it out to stdout, as it should not be saved in logger's files @@ -427,38 +485,25 @@ async fn main_inner() -> Result<(), ()> { let mut sign_path = path.clone(); sign_path.push("sign"); let sign_from_file: Option<[u8; 32]>; - let res = |sign_path| -> Result<(), &str> { - let file = std::fs::read(sign_path).map_err(|_| "")?; - let sig: Sig = serde_bare::from_slice(&file).map_err(|_| "invalid serialization")?; - let privkey: PrivKey = keys[3].into(); - let pubkey = privkey.to_pub(); - verify(&vec![110u8, 103u8, 100u8], sig, pubkey).map_err(|_| "invalid signature")?; - Ok(()) - }(&sign_path); - - if res.is_err() { - if res.unwrap_err().len() > 0 { - log_err!( - "provided key is invalid. {}. cannot start", - res.unwrap_err() - ); - return Err(()); - } else { - // time to save the signature - let privkey: PrivKey = keys[3].into(); - let pubkey = privkey.to_pub(); - let sig = sign(&privkey, &pubkey, &vec![110u8, 103u8, 100u8]); - if sig.is_err() { - log_err!("cannot save signature. cannot start"); - return Err(()); - } - let sig_ser = serde_bare::to_vec(&sig.unwrap()).unwrap(); - let res = std::fs::write(sign_path, sig_ser); - if res.is_err() { - log_err!("cannot save signature. {}. cannot start", res.unwrap_err()); - return Err(()); - } + let privkey: PrivKey = keys[3].into(); + let pubkey = privkey.to_pub(); + + if match std::fs::read(sign_path.clone()) { + Err(_) => true, + Ok(file) => { + let sig: Sig = serde_bare::from_slice(&file).map_err(|_| NgdError::InvalidSignature)?; + verify(&vec![110u8, 103u8, 100u8], sig, pubkey) + .map_err(|_| NgdError::InvalidSignature)?; + false } + } { + // time to save the signature + let sig = sign(&privkey, &pubkey, &vec![110u8, 103u8, 100u8]) + .map_err(|e| NgdError::CannotSaveSignature(e.to_string()))?; + + let sig_ser = serde_bare::to_vec(&sig).unwrap(); + let res = std::fs::write(sign_path, sig_ser) + .map_err(|e| NgdError::CannotSaveSignature(e.to_string()))?; } // DEALING WITH CONFIG @@ -466,24 +511,13 @@ async fn main_inner() -> Result<(), ()> { // reading config from file, if any let mut config_path = path.clone(); config_path.push("config.json"); - let mut config: Option; - let res = |config_path| -> Result { - let file = read_to_string(config_path).map_err(|_| "".to_string())?; - from_str(&file).map_err(|e| e.to_string()) - }(&config_path); - - if res.is_err() && res.as_ref().unwrap_err().len() > 0 { - log_err!( - "provided config file is incorrect. {}. cannot start", - res.unwrap_err() - ); - return Err(()); - } - config = res.ok(); + let mut config: Option = match read_to_string(config_path.clone()) { + Err(_) => None, + Ok(file) => Some(from_str(&file).map_err(|e| NgdError::InvalidConfigFile(e.to_string()))?), + }; if config.is_some() && args.save_config { - log_err!("A config file is present. We cannot override it with Quick config options. cannot start"); - return Err(()); + return Err(NgdError::ConfigCannotSave); } if args.local.is_some() @@ -498,17 +532,11 @@ async fn main_inner() -> Result<(), ()> { // QUICK CONFIG if config.is_some() && !args.print_config { - log_err!( - "A config file is present. You cannot use the Quick config options on the command-line. In order to use them, delete your config file first. cannot start" - ); - return Err(()); + return Err(NgdError::ConfigFilePresent); } if args.domain_peer.is_some() && args.domain_private.is_none() && args.domain.is_none() { - log_err!( - "The --domain-peer option can only be set when the --domain or --domain-private option is also present on the command line. cannot start" - ); - return Err(()); + return Err(NgdError::ConfigCannotSave); } let mut listeners: Vec = vec![]; @@ -541,17 +569,14 @@ async fn main_inner() -> Result<(), ()> { match find_first(&interfaces, InterfaceType::Loopback) { None => { - log_err!( - "That's pretty unusual, but no loopback interface could be found on your host. --domain option failed for that reason. cannot start" - ); - return Err(()); + return Err(NgdError::NoLoopback); } Some(loopback) => { overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; let mut listener = ListenerV0::new_direct(loopback, !args.no_ipv6, local_port); listener.accept_direct = false; let res = prepare_accept_forward_for_domain(domain, &mut args).map_err(|_| { - log_err!("The --domain-peer option has an invalid key. it must be a base64_url encoded serialization of a PrivKey. cannot start") + NgdError::OtherConfigErrorStr("The --domain-peer option has an invalid key. it must be a base64_url encoded serialization of a PrivKey.") })?; listener.accept_forward_for = res; listeners.push(listener); @@ -564,10 +589,7 @@ async fn main_inner() -> Result<(), ()> { if args.local.is_some() { match find_first(&interfaces, InterfaceType::Loopback) { None => { - log_err!( - "That's pretty unusual, but no loopback interface could be found on your host. cannot start" - ); - return Err(()); + return Err(NgdError::OtherConfigErrorStr("That's pretty unusual, but no loopback interface could be found on your host.")); } Some(loopback) => { overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; @@ -577,10 +599,7 @@ async fn main_inner() -> Result<(), ()> { && listeners.last().unwrap().port == args.local.unwrap() { if args.domain_peer.is_some() { - log_err!( - "--local is not allowed if --domain-peer is selected, as they both use the same port. change the port of one of them. cannot start" - ); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( "--local is not allowed if --domain-peer is selected, as they both use the same port. change the port of one of them")); } let r = listeners.last_mut().unwrap(); r.accept_direct = true; @@ -605,18 +624,14 @@ async fn main_inner() -> Result<(), ()> { let if_name = &arg_value.0; match find_first_or_name(&interfaces, InterfaceType::Public, &if_name) { None => { - log_err!( - "{}", - if if_name == "default" { - "We could not find a public IP interface on your host. If you are setting up a server behind a reverse proxy, enter the config manually in the config file. cannot start".to_string() - } else { - format!( - "We could not find a public IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start", + if if_name == "default" { + return Err(NgdError::OtherConfigErrorStr("We could not find a public IP interface on your host. If you are setting up a server behind a reverse proxy, enter the config manually in the config file.")); + } else { + return Err(NgdError::OtherConfigError(format!( + "We could not find a public IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.", if_name - ) - } - ); - return Err(()); + ))); + } } Some(public) => { overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; @@ -644,19 +659,14 @@ async fn main_inner() -> Result<(), ()> { let if_name = &private_part.0; match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { None => { - log_err!( - "{}", - if if_name == "default" { - "We could not find a private IP interface on your host for --public option. cannot start" - .to_string() - } else { - format!( - "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start", + if if_name == "default" { + return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --public option.")); + } else { + return Err(NgdError::OtherConfigError(format!( + "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.", if_name - ) - } - ); - return Err(()); + ))); + } } Some(inter) => { private_interface = inter; @@ -666,15 +676,15 @@ async fn main_inner() -> Result<(), ()> { if !is_public_ipv4(&public_part.1 .0) || public_part.0.is_some() && !is_public_ipv6(public_part.0.as_ref().unwrap()) { - log_err!("The provided IPs are not public. cannot start"); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( + "The provided IPs are not public.", + )); } if args.no_ipv6 && public_part.0.is_some() { - log_err!( - "The public IP is IPv6 but you selected the --no-ipv6 option. cannot start" - ); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( + "The public IP is IPv6 but you selected the --no-ipv6 option.", + )); } overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; @@ -738,19 +748,14 @@ async fn main_inner() -> Result<(), ()> { match find_first_or_name(&interfaces, InterfaceType::Private, if_name) { None => { - log_err!( - "{}", - if if_name == "default" { - "We could not find a private IP interface on your host for --dynamic option. cannot start" - .to_string() - } else { - format!( - "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start", + if if_name == "default" { + return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --dynamic option.")); + } else { + return Err(NgdError::OtherConfigError(format!( + "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.", if_name - ) - } - ); - return Err(()); + ))); + } } Some(inter) => { overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; @@ -764,8 +769,7 @@ async fn main_inner() -> Result<(), ()> { { let r = listeners.last_mut().unwrap(); if r.accept_forward_for != AcceptForwardForV0::No { - log_err!("The same private interface is already forwarding with a different setting, probably because of a --public option conflicting with a --dynamic option. Changing the port on one of the interfaces can help. cannot start"); - return Err(()); + return Err(NgdError::OtherConfigErrorStr("The same private interface is already forwarding with a different setting, probably because of a --public option conflicting with a --dynamic option. Changing the port on one of the interfaces can help.")); } panic!("this should never happen. --dynamic created after a --private"); //r.ipv6 = !args.no_ipv6; @@ -804,25 +808,20 @@ async fn main_inner() -> Result<(), ()> { let if_name = &arg_value.0; match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { None => { - log_err!( - "{}", - if if_name == "default" { - "We could not find a private IP interface on your host for --domain-private option. cannot start" - .to_string() - } else { - format!( - "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start", + if if_name == "default" { + return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --domain-private option.")); + } else { + return Err(NgdError::OtherConfigError(format!( + "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.", if_name - ) - } - ); - return Err(()); + ))); + } } Some(inter) => { overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; let res = prepare_accept_forward_for_domain(domain, &mut args).map_err(|_| { - log_err!("The --domain-peer option has an invalid key. it must be a base64_url encoded serialization of a PrivKey. cannot start")})?; + NgdError::OtherConfigErrorStr("The --domain-peer option has an invalid key. it must be a base64_url encoded serialization of a PrivKey.")})?; if listeners.last().is_some() && listeners.last().unwrap().interface_name == inter.name @@ -830,8 +829,7 @@ async fn main_inner() -> Result<(), ()> { { let r = listeners.last_mut().unwrap(); if r.accept_forward_for != AcceptForwardForV0::No { - log_err!("The same private interface is already forwarding with a different setting, probably because of a --public or --dynamic option conflicting with the --domain-private option. Changing the port on one of the interfaces can help. cannot start"); - return Err(()); + return Err(NgdError::OtherConfigErrorStr("The same private interface is already forwarding with a different setting, probably because of a --public or --dynamic option conflicting with the --domain-private option. Changing the port on one of the interfaces can help.")); } panic!( "this should never happen. --domain-private created after a --private" @@ -862,19 +860,17 @@ async fn main_inner() -> Result<(), ()> { let if_name = &arg_value.0; match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { None => { - log_err!( - "{}", - if if_name == "default" { - "We could not find a private IP interface on your host. cannot start" - .to_string() - } else { + if if_name == "default" { + return Err(NgdError::OtherConfigErrorStr( + "We could not find a private IP interface on your host.", + )); + } else { + return Err(NgdError::OtherConfigError( format!( - "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start", + "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.", if_name - ) - } - ); - return Err(()); + ))); + } } Some(inter) => { overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; @@ -884,10 +880,9 @@ async fn main_inner() -> Result<(), ()> { && listeners.last().unwrap().port == arg_value.1 { if args.domain_peer.is_some() { - log_err!( - "--private is not allowed if --domain-peer is selected, and they both use the same port. change the port of one of them. cannot start" + return Err(NgdError::OtherConfigErrorStr( + "--private is not allowed if --domain-peer is selected, and they both use the same port. change the port of one of them.") ); - return Err(()); } let r = listeners.last_mut().unwrap(); r.accept_direct = true; @@ -908,13 +903,15 @@ async fn main_inner() -> Result<(), ()> { let parts: Vec<&str> = forward_string.split('@').collect(); if parts.len() != 2 { - log_err!( - "The option --forward is invalid. It must contain two parts separated by a @ character. cannot start" - ); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( + "The option --forward is invalid. It must contain two parts separated by a @ character." + )); } - let pub_key_array = decode_key(parts[1]) - .map_err(|_| log_err!("The PEER_ID provided in the --forward option is invalid"))?; + let pub_key_array = decode_key(parts[1]).map_err(|_| { + NgdError::OtherConfigErrorStr( + "The PEER_ID provided in the --forward option is invalid", + ) + })?; let peer_id = PubKey::Ed25519PubKey(pub_key_array); let server_type = if parts[0].len() > 0 { @@ -932,8 +929,9 @@ async fn main_inner() -> Result<(), ()> { } else if is_public_ip(&bind.0) { BrokerServerTypeV0::Public(vec![bind_addr]) } else { - log_err!("Invalid IP address given for --forward option. cannot start"); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( + "Invalid IP address given for --forward option.", + )); } } else { // a domain name @@ -994,10 +992,10 @@ async fn main_inner() -> Result<(), ()> { // saves the config to file let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap(); write(config_path.clone(), json_string).map_err(|e| { - log_err!( - "cannot save config to file. {}. cannot start", + NgdError::CannotSaveConfig(format!( + "cannot save config to file. {}.", e.to_string() - ) + )) })?; log_info!( "The config file has been saved to {}", @@ -1009,10 +1007,9 @@ async fn main_inner() -> Result<(), ()> { } } else { if config.is_none() { - log_err!( - "No Quick config option passed, neither is a config file present. We cannot start the server. Choose at least one Quick config option. see --help for details" - ); - return Err(()); + return Err(NgdError::OtherConfigErrorStr( + "No Quick config option passed, neither is a config file present. Choose at least one Quick config option. see --help for details" + )); } if args.print_config { let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap();