refactor ngclie and ngd error handling, and refactor server_broker and its storage

pull/19/head
Niko PLP 7 months ago
parent dcc9bbcf52
commit a4d96aa92c
  1. 13
      ng-broker/src/broker_storage/mod.rs
  2. 10
      ng-broker/src/lib.rs
  3. 44
      ng-broker/src/rocksdb_server_storage.rs
  4. 171
      ng-broker/src/server_broker.rs
  5. 12
      ng-broker/src/server_storage/admin/account.rs
  6. 12
      ng-broker/src/server_storage/admin/invitation.rs
  7. 5
      ng-broker/src/server_storage/admin/mod.rs
  8. 0
      ng-broker/src/server_storage/admin/wallet.rs
  9. 0
      ng-broker/src/server_storage/config.rs
  10. 5
      ng-broker/src/server_storage/core/mod.rs
  11. 0
      ng-broker/src/server_storage/core/overlay.rs
  12. 0
      ng-broker/src/server_storage/core/peer.rs
  13. 0
      ng-broker/src/server_storage/core/topic.rs
  14. 5
      ng-broker/src/server_storage/mod.rs
  15. 50
      ng-broker/src/server_ws.rs
  16. 2
      ng-net/src/actors/add_invitation.rs
  17. 2
      ng-net/src/actors/add_user.rs
  18. 2
      ng-net/src/actors/client/commit_get.rs
  19. 2
      ng-net/src/actors/client/pin_repo.rs
  20. 2
      ng-net/src/actors/client/repo_pin_status.rs
  21. 2
      ng-net/src/actors/client/topic_sub.rs
  22. 2
      ng-net/src/actors/del_user.rs
  23. 2
      ng-net/src/actors/list_invitations.rs
  24. 2
      ng-net/src/actors/list_users.rs
  25. 28
      ng-net/src/broker.rs
  26. 2
      ng-net/src/lib.rs
  27. 4
      ng-net/src/server_broker.rs
  28. 46
      ng-repo/src/errors.rs
  29. 4
      ng-repo/src/types.rs
  30. 8
      ng-repo/src/utils.rs
  31. 3
      ng-storage-rocksdb/src/block_storage.rs
  32. 2
      ng-storage-rocksdb/src/kcv_storage.rs
  33. 6
      ng-verifier/src/rocksdb_user_storage.rs
  34. 2
      ng-verifier/src/types.rs
  35. 99
      ng-verifier/src/user_storage/storage.rs
  36. 4
      ng-verifier/src/verifier.rs
  37. 264
      ngcli/src/main.rs
  38. 483
      ngd/src/main.rs

@ -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;

@ -1,11 +1,13 @@
pub mod broker_storage;
pub mod server_ws;
pub mod types; pub mod types;
pub mod utils; pub mod utils;
pub mod interfaces; pub mod interfaces;
pub mod server_broker;
pub mod server_storage; pub mod server_storage;
pub mod rocksdb_server_storage;
pub mod server_ws;

@ -15,11 +15,11 @@ use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Mutex; use std::sync::Mutex;
use crate::broker_storage::account::Account; use crate::server_storage::admin::account::Account;
use crate::broker_storage::invitation::Invitation; use crate::server_storage::admin::invitation::Invitation;
use crate::broker_storage::wallet::Wallet; use crate::server_storage::admin::wallet::Wallet;
use crate::types::*; use crate::types::*;
use ng_net::server_storage::*; use ng_net::server_broker::*;
use ng_net::types::*; use ng_net::types::*;
use ng_repo::errors::{ProtocolError, ServerError, StorageError}; use ng_repo::errors::{ProtocolError, ServerError, StorageError};
use ng_repo::kcv_storage::KCVStorage; 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::block_storage::RocksDbBlockStorage;
use ng_storage_rocksdb::kcv_storage::RocksDbKCVStorage; use ng_storage_rocksdb::kcv_storage::RocksDbKCVStorage;
pub struct RocksDbServerStorage { pub(crate) struct RocksDbServerStorage {
wallet_storage: RocksDbKCVStorage, wallet_storage: RocksDbKCVStorage,
accounts_storage: RocksDbKCVStorage, accounts_storage: RocksDbKCVStorage,
//peers_storage: RocksDbKCVStorage, //peers_storage: RocksDbKCVStorage,
@ -39,7 +39,7 @@ pub struct RocksDbServerStorage {
} }
impl RocksDbServerStorage { impl RocksDbServerStorage {
pub fn open( pub(crate) fn open(
path: &mut PathBuf, path: &mut PathBuf,
master_key: SymKey, master_key: SymKey,
admin_invite: Option<BootstrapContentV0>, admin_invite: Option<BootstrapContentV0>,
@ -132,10 +132,8 @@ impl RocksDbServerStorage {
core_storage, core_storage,
}) })
} }
}
impl ServerStorage for RocksDbServerStorage { pub(crate) fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> {
fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> {
// for now we don't use the hashmap. // for now we don't use the hashmap.
// TODO: let's see if the lock is even needed // TODO: let's see if the lock is even needed
let _ = self.peers_last_seq.lock(); let _ = self.peers_last_seq.lock();
@ -165,26 +163,26 @@ impl ServerStorage for RocksDbServerStorage {
Ok(()) Ok(())
} }
fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError> { pub(crate) fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError> {
log_debug!("get_user {user_id}"); log_debug!("get_user {user_id}");
Ok(Account::open(&user_id, &self.accounts_storage)?.is_admin()?) 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}"); log_debug!("add_user {user_id} is admin {is_admin}");
Account::create(&user_id, is_admin, &self.accounts_storage)?; Account::create(&user_id, is_admin, &self.accounts_storage)?;
Ok(()) 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}"); log_debug!("del_user {user_id}");
let acc = Account::open(&user_id, &self.accounts_storage)?; let acc = Account::open(&user_id, &self.accounts_storage)?;
acc.del()?; acc.del()?;
Ok(()) Ok(())
} }
fn list_users(&self, admins: bool) -> Result<Vec<PubKey>, ProtocolError> { pub(crate) fn list_users(&self, admins: bool) -> Result<Vec<PubKey>, ProtocolError> {
log_debug!("list_users that are admin == {admins}"); log_debug!("list_users that are admin == {admins}");
Ok(Account::get_all_users(admins, &self.accounts_storage)?) Ok(Account::get_all_users(admins, &self.accounts_storage)?)
} }
fn list_invitations( pub(crate) fn list_invitations(
&self, &self,
admin: bool, admin: bool,
unique: bool, unique: bool,
@ -198,7 +196,7 @@ impl ServerStorage for RocksDbServerStorage {
multi, multi,
)?) )?)
} }
fn add_invitation( pub(crate) fn add_invitation(
&self, &self,
invite_code: &InvitationCode, invite_code: &InvitationCode,
expiry: u32, expiry: u32,
@ -208,18 +206,18 @@ impl ServerStorage for RocksDbServerStorage {
Invitation::create(invite_code, expiry, memo, &self.accounts_storage)?; Invitation::create(invite_code, expiry, memo, &self.accounts_storage)?;
Ok(()) Ok(())
} }
fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result<u8, ProtocolError> { pub(crate) fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result<u8, ProtocolError> {
log_debug!("get_invitation_type {:?}", invite_code); log_debug!("get_invitation_type {:?}", invite_code);
let inv = Invitation::open(&invite_code, &self.accounts_storage)?; let inv = Invitation::open(&invite_code, &self.accounts_storage)?;
inv.get_type() 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); log_debug!("remove_invitation {:?}", invite_code);
let inv = Invitation::open(&invite_code, &self.accounts_storage)?; let inv = Invitation::open(&invite_code, &self.accounts_storage)?;
inv.del()?; inv.del()?;
Ok(()) Ok(())
} }
fn get_repo_pin_status( pub(crate) fn get_repo_pin_status(
&self, &self,
overlay: &OverlayId, overlay: &OverlayId,
repo: &RepoHash, repo: &RepoHash,
@ -237,7 +235,7 @@ impl ServerStorage for RocksDbServerStorage {
// })) // }))
} }
fn pin_repo( pub(crate) fn pin_repo(
&self, &self,
overlay: &OverlayId, overlay: &OverlayId,
repo: &RepoHash, repo: &RepoHash,
@ -255,7 +253,7 @@ impl ServerStorage for RocksDbServerStorage {
Ok(opened) Ok(opened)
} }
fn topic_sub( pub(crate) fn topic_sub(
&self, &self,
overlay: &OverlayId, overlay: &OverlayId,
repo: &RepoHash, repo: &RepoHash,
@ -270,7 +268,11 @@ impl ServerStorage for RocksDbServerStorage {
})) }))
} }
fn get_commit(&self, overlay: &OverlayId, id: &ObjectId) -> Result<Vec<Block>, ServerError> { pub(crate) fn get_commit(
&self,
overlay: &OverlayId,
id: &ObjectId,
) -> Result<Vec<Block>, ServerError> {
//TODO: implement correctly ! //TODO: implement correctly !
Ok(vec![Block::dummy()]) Ok(vec![Block::dummy()])
} }

@ -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
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* 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<RepoHash>,
publisher_advert: Option<PublisherAdvert>,
current_heads: Vec<ObjectId>,
expose_outer: bool,
/// indicates which users have subscribed to topic (boolean says if as publisher or not)
users: HashMap<UserId, bool>,
}
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<UserId>,
/// set of topics of this repo
topics: HashSet<TopicId>,
}
struct OverlayInfo {
inner: Option<OverlayId>,
topics: HashMap<TopicId, TopicInfo>,
repos: HashMap<RepoHash, RepoInfo>,
}
pub struct ServerBroker {
storage: RocksDbServerStorage,
overlays: HashMap<OverlayId, OverlayInfo>,
inner_overlays: HashMap<OverlayId, Option<OverlayId>>,
}
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<bool, ProtocolError> {
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<Vec<PubKey>, ProtocolError> {
self.storage.list_users(admins)
}
fn list_invitations(
&self,
admin: bool,
unique: bool,
multi: bool,
) -> Result<Vec<(InvitationCode, u32, Option<String>)>, ProtocolError> {
self.storage.list_invitations(admin, unique, multi)
}
fn add_invitation(
&self,
invite_code: &InvitationCode,
expiry: u32,
memo: &Option<String>,
) -> Result<(), ProtocolError> {
self.storage.add_invitation(invite_code, expiry, memo)
}
fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result<u8, ProtocolError> {
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<RepoPinStatus, ServerError> {
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<TopicId>,
rw_topics: &Vec<PublisherAdvert>,
) -> Result<RepoOpened, ServerError> {
//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<TopicSubRes, ServerError> {
//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<Vec<Block>, ServerError> {
//TODO: implement correctly !
Ok(vec![Block::dummy()])
}
}

@ -28,13 +28,13 @@ pub struct Account<'a> {
} }
impl<'a> Account<'a> { impl<'a> Account<'a> {
const PREFIX_ACCOUNT: u8 = b"a"[0]; const PREFIX_ACCOUNT: u8 = b'a';
const PREFIX_CLIENT: u8 = b"c"[0]; const PREFIX_CLIENT: u8 = b'c';
const PREFIX_CLIENT_PROPERTY: u8 = b"d"[0]; const PREFIX_CLIENT_PROPERTY: u8 = b'd';
// propertie's client suffixes // propertie's client suffixes
const INFO: u8 = b"i"[0]; const INFO: u8 = b'i';
const LAST_SEEN: u8 = b"l"[0]; const LAST_SEEN: u8 = b'l';
const ALL_CLIENT_PROPERTIES: [u8; 2] = [Self::INFO, Self::LAST_SEEN]; const ALL_CLIENT_PROPERTIES: [u8; 2] = [Self::INFO, Self::LAST_SEEN];
@ -252,7 +252,7 @@ mod test {
use std::fs; use std::fs;
use tempfile::Builder; use tempfile::Builder;
use crate::broker_storage::account::Account; use crate::server_storage::admin::account::Account;
#[test] #[test]
pub fn test_account() { pub fn test_account() {

@ -31,15 +31,15 @@ pub struct Invitation<'a> {
} }
impl<'a> Invitation<'a> { impl<'a> Invitation<'a> {
const PREFIX: u8 = b"i"[0]; const PREFIX: u8 = b'i';
// propertie's invitation suffixes // propertie's invitation suffixes
const TYPE: u8 = b"t"[0]; const TYPE: u8 = b't';
//const EXPIRE: u8 = b"e"[0]; //const EXPIRE: u8 = b'e';
const PREFIX_EXPIRE: u8 = b"e"[0]; const PREFIX_EXPIRE: u8 = b'e';
// propertie's expiry suffixes // propertie's expiry suffixes
const INVITATION: u8 = b"i"[0]; const INVITATION: u8 = b'i';
const ALL_PROPERTIES: [u8; 1] = [Self::TYPE]; const ALL_PROPERTIES: [u8; 1] = [Self::TYPE];
@ -196,7 +196,7 @@ mod test {
use std::fs; use std::fs;
use tempfile::Builder; use tempfile::Builder;
use crate::broker_storage::account::Account; use crate::server_storage::admin::account::Account;
#[test] #[test]
pub fn test_invitation() {} pub fn test_invitation() {}

@ -0,0 +1,5 @@
pub mod invitation;
pub mod wallet;
pub mod account;

@ -0,0 +1,5 @@
pub mod overlay;
pub mod peer;
pub mod topic;

@ -0,0 +1,5 @@
pub mod admin;
pub mod core;
//pub mod config;

@ -12,7 +12,8 @@
//! WebSocket implementation of the Broker //! WebSocket implementation of the Broker
use crate::interfaces::*; use crate::interfaces::*;
use crate::server_storage::RocksDbServerStorage; use crate::rocksdb_server_storage::RocksDbServerStorage;
use crate::server_broker::ServerBroker;
use crate::types::*; use crate::types::*;
use async_std::io::ReadExt; use async_std::io::ReadExt;
use async_std::net::{TcpListener, TcpStream}; 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_private_ip;
use ng_net::utils::is_public_ip; use ng_net::utils::is_public_ip;
use ng_net::NG_BOOTSTRAP_LOCAL_PATH; use ng_net::NG_BOOTSTRAP_LOCAL_PATH;
use ng_repo::errors::NgError;
use ng_repo::log::*; use ng_repo::log::*;
use ng_repo::types::SymKey; use ng_repo::types::SymKey;
use ng_repo::types::{PrivKey, PubKey}; use ng_repo::types::{PrivKey, PubKey};
@ -610,7 +612,7 @@ pub async fn run_server_v0(
config: DaemonConfigV0, config: DaemonConfigV0,
mut path: PathBuf, mut path: PathBuf,
admin_invite: bool, admin_invite: bool,
) -> Result<(), ()> { ) -> Result<(), NgError> {
// check config // check config
let mut run_core = false; let mut run_core = false;
@ -624,8 +626,9 @@ pub async fn run_server_v0(
} }
} }
if !run_core && !run_server { 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(NgError::BrokerConfigErrorStr(
return Err(()); "There isn't any overlay_config that should run as core or server. Check your config.",
));
} }
if run_core && !run_server { if run_core && !run_server {
@ -636,11 +639,10 @@ pub async fn run_server_v0(
for listener in &config.listeners { for listener in &config.listeners {
let id: String = listener.to_string(); let id: String = listener.to_string();
if !listeners.insert(id.clone()) { if !listeners.insert(id.clone()) {
log_err!( return Err(NgError::BrokerConfigError(format!(
"The listener {} is defined twice. Check your config file. cannot start", "The listener {} is defined twice. Check your config file.",
id id
); )));
return Err(());
} }
} }
@ -670,11 +672,10 @@ pub async fn run_server_v0(
match find_name(&interfaces, &listener.interface_name) { match find_name(&interfaces, &listener.interface_name) {
None => { None => {
log_err!( return Err(NgError::BrokerConfigError(format!(
"The interface {} does not exist on your host. Check your config file. cannot start", "The interface {} does not exist on your host. Check your config file.",
listener.interface_name listener.interface_name
); )));
return Err(());
} }
Some(interface) => { Some(interface) => {
let mut addrs: Vec<SocketAddr> = interface let mut addrs: Vec<SocketAddr> = interface
@ -689,11 +690,10 @@ pub async fn run_server_v0(
}) })
.collect(); .collect();
if addrs.len() == 0 { if addrs.len() == 0 {
log_err!( return Err(NgError::BrokerConfigError(format!(
"The interface {} does not have any IPv4 address. cannot start", "The interface {} does not have any IPv4 address.",
listener.interface_name listener.interface_name
); )));
return Err(());
} }
if listener.ipv6 { if listener.ipv6 {
let mut ipv6s: Vec<SocketAddr> = interface let mut ipv6s: Vec<SocketAddr> = interface
@ -753,8 +753,7 @@ pub async fn run_server_v0(
} }
if listeners_addrs.len() == 0 { if listeners_addrs.len() == 0 {
log_err!("No listener configured. cannot start",); return Err(NgError::BrokerConfigErrorStr("No listener configured."));
return Err(());
} }
if !accept_clients { if !accept_clients {
@ -777,7 +776,7 @@ pub async fn run_server_v0(
std::fs::create_dir_all(path.clone()).unwrap(); std::fs::create_dir_all(path.clone()).unwrap();
// opening the server storage (that contains the encryption keys for each store/overlay ) // 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, &mut path,
wallet_master_key, wallet_master_key,
if admin_invite { if admin_invite {
@ -786,10 +785,15 @@ pub async fn run_server_v0(
None 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; let mut broker = BROKER.write().await;
broker.set_server_storage(broker_storage); broker.set_server_broker(server_broker);
LISTENERS_INFO LISTENERS_INFO
.set(broker.set_listeners(listener_infos)) .set(broker.set_listeners(listener_infos))
.unwrap(); .unwrap();
@ -815,12 +819,12 @@ pub async fn run_server_v0(
for addr in addrs.0 { for addr in addrs.0 {
let tcp_listener = TcpListener::bind(addr).await.map_err(|e| { let tcp_listener = TcpListener::bind(addr).await.map_err(|e| {
log_err!( NgError::BrokerConfigError(format!(
"cannot bind to {} with addresses {} : {}", "cannot bind to {} with addresses {} : {}",
addrs.1, addrs.1,
addrs_string, addrs_string,
e.to_string() e.to_string()
) ))
})?; })?;
listeners.push(tcp_listener); listeners.push(tcp_listener);
} }

@ -102,7 +102,7 @@ impl EActor for Actor<'_, AddInvitation, AdminResponse> {
let req = AddInvitation::try_from(msg)?; let req = AddInvitation::try_from(msg)?;
let broker = BROKER.read().await; let broker = BROKER.read().await;
broker broker
.get_server_storage()? .get_server_broker()?
.add_invitation(req.code(), req.expiry(), req.memo())?; .add_invitation(req.code(), req.expiry(), req.memo())?;
let invitation = crate::types::Invitation::V0(InvitationV0::new( let invitation = crate::types::Invitation::V0(InvitationV0::new(

@ -101,7 +101,7 @@ impl EActor for Actor<'_, AddUser, AdminResponse> {
is_admin = true; 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(); let response: AdminResponseV0 = res.into();
fsm.lock().await.send(response.into()).await?; fsm.lock().await.send(response.into()).await?;
Ok(()) Ok(())

@ -90,7 +90,7 @@ impl EActor for Actor<'_, CommitGet, Block> {
log_info!("GOT CommitGet {:?}", req); log_info!("GOT CommitGet {:?}", req);
let broker = BROKER.read().await; let broker = BROKER.read().await;
let blocks_res = broker let blocks_res = broker
.get_server_storage()? .get_server_broker()?
.get_commit(req.overlay(), req.id()); .get_commit(req.overlay(), req.id());
match blocks_res { match blocks_res {

@ -109,7 +109,7 @@ impl EActor for Actor<'_, PinRepo, RepoOpened> {
//TODO implement all the server side logic //TODO implement all the server side logic
let broker = BROKER.read().await; let broker = BROKER.read().await;
let res = broker.get_server_storage()?.pin_repo( let res = broker.get_server_broker()?.pin_repo(
req.overlay(), req.overlay(),
req.hash(), req.hash(),
req.ro_topics(), req.ro_topics(),

@ -79,7 +79,7 @@ impl EActor for Actor<'_, RepoPinStatusReq, RepoPinStatus> {
let req = RepoPinStatusReq::try_from(msg)?; let req = RepoPinStatusReq::try_from(msg)?;
let broker = BROKER.read().await; let broker = BROKER.read().await;
let res = broker let res = broker
.get_server_storage()? .get_server_broker()?
.get_repo_pin_status(req.overlay(), req.hash()); .get_repo_pin_status(req.overlay(), req.hash());
fsm.lock() fsm.lock()
.await .await

@ -100,7 +100,7 @@ impl EActor for Actor<'_, TopicSub, TopicSubRes> {
//TODO implement all the server side logic //TODO implement all the server side logic
let broker = BROKER.read().await; let broker = BROKER.read().await;
let res = broker.get_server_storage()?.topic_sub( let res = broker.get_server_broker()?.topic_sub(
req.overlay(), req.overlay(),
req.hash(), req.hash(),
req.topic(), req.topic(),

@ -82,7 +82,7 @@ impl EActor for Actor<'_, DelUser, AdminResponse> {
) -> Result<(), ProtocolError> { ) -> Result<(), ProtocolError> {
let req = DelUser::try_from(msg)?; let req = DelUser::try_from(msg)?;
let broker = BROKER.read().await; 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(); let response: AdminResponseV0 = res.into();
fsm.lock().await.send(response.into()).await?; fsm.lock().await.send(response.into()).await?;
Ok(()) Ok(())

@ -97,7 +97,7 @@ impl EActor for Actor<'_, ListInvitations, AdminResponse> {
fsm: Arc<Mutex<NoiseFSM>>, fsm: Arc<Mutex<NoiseFSM>>,
) -> Result<(), ProtocolError> { ) -> Result<(), ProtocolError> {
let req = ListInvitations::try_from(msg)?; 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.admin(),
req.unique(), req.unique(),
req.multi(), req.multi(),

@ -86,7 +86,7 @@ impl EActor for Actor<'_, ListUsers, AdminResponse> {
let res = BROKER let res = BROKER
.read() .read()
.await .await
.get_server_storage()? .get_server_broker()?
.list_users(req.admins()); .list_users(req.admins());
let response: AdminResponseV0 = res.into(); let response: AdminResponseV0 = res.into();
fsm.lock().await.send(response.into()).await?; fsm.lock().await.send(response.into()).await?;

@ -14,7 +14,7 @@
use crate::actor::EActor; use crate::actor::EActor;
use crate::actor::SoS; use crate::actor::SoS;
use crate::connection::*; use crate::connection::*;
use crate::server_storage::ServerStorage; use crate::server_broker::IServerBroker;
use crate::types::*; use crate::types::*;
use crate::utils::spawn_and_log_error; use crate::utils::spawn_and_log_error;
use crate::utils::{Receiver, ResultSend, Sender}; use crate::utils::{Receiver, ResultSend, Sender};
@ -85,7 +85,7 @@ pub struct Broker<'a> {
shutdown: Option<Receiver<ProtocolError>>, shutdown: Option<Receiver<ProtocolError>>,
shutdown_sender: Sender<ProtocolError>, shutdown_sender: Sender<ProtocolError>,
closing: bool, closing: bool,
server_storage: Option<Box<dyn ServerStorage + Send + Sync + 'a>>, server_broker: Option<Box<dyn IServerBroker + Send + Sync + 'a>>,
tauri_streams: HashMap<String, Sender<Commit>>, tauri_streams: HashMap<String, Sender<Commit>>,
disconnections_sender: Sender<String>, disconnections_sender: Sender<String>,
@ -146,9 +146,9 @@ impl<'a> Broker<'a> {
.ok_or(ProtocolError::BrokerError) .ok_or(ProtocolError::BrokerError)
} }
pub fn set_server_storage(&mut self, storage: impl ServerStorage + 'a) { pub fn set_server_broker(&mut self, broker: impl IServerBroker + 'a) {
//log_debug!("set_storage"); //log_debug!("set_server_broker");
self.server_storage = Some(Box::new(storage)); self.server_broker = Some(Box::new(broker));
} }
pub fn set_local_broker(&mut self, broker: Arc<RwLock<dyn ILocalBroker + 'a>>) { pub fn set_local_broker(&mut self, broker: Arc<RwLock<dyn ILocalBroker + 'a>>) {
@ -178,11 +178,11 @@ impl<'a> Broker<'a> {
(copy_listeners, copy_bind_addresses) (copy_listeners, copy_bind_addresses)
} }
pub fn get_server_storage( pub fn get_server_broker(
&self, &self,
) -> Result<&Box<dyn ServerStorage + Send + Sync + 'a>, ProtocolError> { ) -> Result<&Box<dyn IServerBroker + Send + Sync + 'a>, ProtocolError> {
//log_debug!("GET STORAGE {:?}", self.server_storage); //log_debug!("GET STORAGE {:?}", self.server_storage);
self.server_storage self.server_broker
.as_ref() .as_ref()
.ok_or(ProtocolError::BrokerError) .ok_or(ProtocolError::BrokerError)
} }
@ -222,7 +222,7 @@ impl<'a> Broker<'a> {
Authorization::Client(user_and_registration) => { Authorization::Client(user_and_registration) => {
if user_and_registration.1.is_some() { if user_and_registration.1.is_some() {
// user wants to register // 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() { if storage.get_user(user_and_registration.0).is_ok() {
return Ok(()); return Ok(());
} }
@ -266,7 +266,7 @@ impl<'a> Broker<'a> {
storage.remove_invitation(code)?; storage.remove_invitation(code)?;
} }
} }
self.get_server_storage()? self.get_server_broker()?
.add_user(user_and_registration.0, is_admin)?; .add_user(user_and_registration.0, is_admin)?;
Ok(()) Ok(())
} }
@ -290,7 +290,7 @@ impl<'a> Broker<'a> {
return Ok(()); 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() { if found.is_ok() && found.unwrap() {
return Ok(()); return Ok(());
} }
@ -302,11 +302,11 @@ impl<'a> Broker<'a> {
} }
// pub fn add_user(&self, user: PubKey, is_admin: bool) -> Result<(), ProtocolError> { // 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<Vec<PubKey>, ProtocolError> { // pub fn list_users(&self, admins: bool) -> Result<Vec<PubKey>, 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( pub async fn get_block_from_store_with_block_id(
@ -482,7 +482,7 @@ impl<'a> Broker<'a> {
peers: HashMap::new(), peers: HashMap::new(),
tauri_streams: HashMap::new(), tauri_streams: HashMap::new(),
closing: false, closing: false,
server_storage: None, server_broker: None,
disconnections_sender, disconnections_sender,
disconnections_receiver: Some(disconnections_receiver), disconnections_receiver: Some(disconnections_receiver),
local_broker: None, local_broker: None,

@ -17,7 +17,7 @@ pub mod errors;
pub mod broker; pub mod broker;
pub mod server_storage; pub mod server_broker;
pub mod connection; pub mod connection;

@ -9,13 +9,13 @@
* according to those terms. * according to those terms.
*/ */
//! Trait for ServerStorage //! Trait for ServerBroker
use crate::types::*; use crate::types::*;
use ng_repo::errors::*; use ng_repo::errors::*;
use ng_repo::types::*; use ng_repo::types::*;
pub trait ServerStorage: Send + Sync { pub trait IServerBroker: Send + Sync {
fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError>; fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError>;
fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError>; fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError>;
fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError>; fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError>;

@ -66,6 +66,8 @@ pub enum NgError {
NotAServerError, NotAServerError,
VerifierError(VerifierError), VerifierError(VerifierError),
SiteNotFoundOnBroker, SiteNotFoundOnBroker,
BrokerConfigErrorStr(&'static str),
BrokerConfigError(String),
} }
impl Error for NgError {} impl Error for NgError {}
@ -75,6 +77,19 @@ impl fmt::Display for NgError {
match self { match self {
Self::WalletError(string) => write!(f, "WalletError: {}", string), Self::WalletError(string) => write!(f, "WalletError: {}", string),
Self::JsStorageWriteError(string) => write!(f, "JsStorageWriteError: {}", 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), _ => write!(f, "{:?}", self),
} }
} }
@ -85,38 +100,7 @@ impl From<NgError> for std::io::Error {
match err { match err {
NgError::InvalidArgument => std::io::Error::from(std::io::ErrorKind::InvalidInput), NgError::InvalidArgument => std::io::Error::from(std::io::ErrorKind::InvalidInput),
NgError::PermissionDenied => std::io::Error::from(std::io::ErrorKind::PermissionDenied), 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::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()), _ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()),
} }
} }

@ -184,7 +184,7 @@ impl fmt::Display for PubKey {
impl TryFrom<&str> for PubKey { impl TryFrom<&str> for PubKey {
type Error = NgError; type Error = NgError;
fn try_from(str: &str) -> Result<Self, NgError> { fn try_from(str: &str) -> Result<Self, NgError> {
let key = decode_key(str).map_err(|_| NgError::InvalidKey)?; let key = decode_key(str)?;
Ok(PubKey::Ed25519PubKey(key)) Ok(PubKey::Ed25519PubKey(key))
} }
} }
@ -254,7 +254,7 @@ impl TryFrom<&[u8]> for PrivKey {
impl TryFrom<&str> for PrivKey { impl TryFrom<&str> for PrivKey {
type Error = NgError; type Error = NgError;
fn try_from(str: &str) -> Result<Self, NgError> { fn try_from(str: &str) -> Result<Self, NgError> {
let key = decode_key(str).map_err(|_| NgError::InvalidKey)?; let key = decode_key(str)?;
Ok(PrivKey::Ed25519PrivKey(key)) Ok(PrivKey::Ed25519PrivKey(key))
} }
} }

@ -53,11 +53,9 @@ pub fn from_ed_privkey_to_dh_privkey(private: &PrivKey) -> PrivKey {
} }
/// don't forget to zeroize the string later on /// don't forget to zeroize the string later on
pub fn decode_key(key_string: &str) -> Result<[u8; 32], ()> { pub fn decode_key(key_string: &str) -> Result<[u8; 32], NgError> {
let vec = base64_url::decode(key_string).map_err(|_| log_err!("key has invalid content"))?; let vec = base64_url::decode(key_string).map_err(|_| NgError::InvalidKey)?;
Ok(*slice_as_array!(&vec, [u8; 32]) Ok(*slice_as_array!(&vec, [u8; 32]).ok_or(NgError::InvalidKey)?)
.ok_or(())
.map_err(|_| log_err!("key has invalid content array"))?)
} }
pub fn ed_privkey_to_ed_pubkey(privkey: &PrivKey) -> PubKey { pub fn ed_privkey_to_ed_pubkey(privkey: &PrivKey) -> PubKey {

@ -55,6 +55,7 @@ impl RocksDbBlockStorage {
opts.set_max_bytes_for_level_base(16 * 1024 * 1024); opts.set_max_bytes_for_level_base(16 * 1024 * 1024);
opts.set_target_file_size_multiplier(10); opts.set_target_file_size_multiplier(10);
opts.set_level_compaction_dynamic_level_bytes(true); opts.set_level_compaction_dynamic_level_bytes(true);
opts.set_num_levels(7); // the default
opts.create_if_missing(true); opts.create_if_missing(true);
opts.create_missing_column_families(false); opts.create_missing_column_families(false);
@ -67,7 +68,7 @@ impl RocksDbBlockStorage {
opts.set_blob_compression_type(DBCompressionType::None); opts.set_blob_compression_type(DBCompressionType::None);
opts.set_enable_blob_gc(true); opts.set_enable_blob_gc(true);
// the oldest half of blob files will be selected for GC // 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. // 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 // this way we are reducing the space amplification by small decrements of 8MB
opts.set_blob_gc_force_threshold(0.5); opts.set_blob_gc_force_threshold(0.5);

@ -676,6 +676,7 @@ impl RocksDbKCVStorage {
opts.set_max_bytes_for_level_base(256 * 1024 * 1024); opts.set_max_bytes_for_level_base(256 * 1024 * 1024);
opts.set_target_file_size_multiplier(10); opts.set_target_file_size_multiplier(10);
opts.set_level_compaction_dynamic_level_bytes(true); opts.set_level_compaction_dynamic_level_bytes(true);
opts.set_num_levels(7); // the default
opts.create_if_missing(true); opts.create_if_missing(true);
opts.create_missing_column_families(true); opts.create_missing_column_families(true);
@ -685,6 +686,7 @@ impl RocksDbKCVStorage {
let cache = Cache::new_lru_cache(64 * 1024 * 1024); let cache = Cache::new_lru_cache(64 * 1024 * 1024);
block_based_opts.set_block_cache(&cache); block_based_opts.set_block_cache(&cache);
block_based_opts.set_cache_index_and_filter_blocks(true); 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_block_size(16 * 1024);
block_based_opts.set_bloom_filter(10.0, false); block_based_opts.set_bloom_filter(10.0, false);
block_based_opts.set_format_version(6); block_based_opts.set_format_version(6);

@ -39,9 +39,9 @@ impl RocksDbUserStorage {
} }
impl UserStorage for RocksDbUserStorage { impl UserStorage for RocksDbUserStorage {
fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError> { // fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError> { fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError> {
RepoStorage::get_all_store_and_repo_ids(&self.user_storage) RepoStorage::get_all_store_and_repo_ids(&self.user_storage)

@ -15,7 +15,7 @@ use core::fmt;
//use oxigraph::model::GroundQuad; //use oxigraph::model::GroundQuad;
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
use crate::rocksdb_user_storage::RocksDbUserStorage; use crate::rocksdb_user_storage::RocksDbUserStorage;
use crate::user_storage::{InMemoryUserStorage, UserStorage}; use crate::user_storage::UserStorage;
use async_std::sync::Mutex; use async_std::sync::Mutex;
use std::{collections::HashMap, path::PathBuf, sync::Arc}; use std::{collections::HashMap, path::PathBuf, sync::Arc};

@ -26,8 +26,7 @@ use std::{
}; };
pub trait UserStorage: Send + Sync { pub trait UserStorage: Send + Sync {
/// Gets the StoreRepo for a given RepoId //fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError>;
fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError>;
fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError>; fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError>;
@ -46,51 +45,51 @@ pub trait UserStorage: Send + Sync {
fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError>; fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError>;
} }
pub(crate) struct InMemoryUserStorage { // pub(crate) struct InMemoryUserStorage {
repo_id_to_store_overlay: HashMap<RepoId, StoreOverlay>, // repo_id_to_store_overlay: HashMap<RepoId, StoreOverlay>,
} // }
impl InMemoryUserStorage { // impl InMemoryUserStorage {
pub fn new() -> Self { // pub fn new() -> Self {
InMemoryUserStorage { // InMemoryUserStorage {
repo_id_to_store_overlay: HashMap::new(), // repo_id_to_store_overlay: HashMap::new(),
} // }
} // }
} // }
impl UserStorage for InMemoryUserStorage { // impl UserStorage for InMemoryUserStorage {
fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError> { // fn repo_id_to_store_overlay(&self, id: &RepoId) -> Result<StoreOverlay, StorageError> {
Ok(self // Ok(self
.repo_id_to_store_overlay // .repo_id_to_store_overlay
.get(&id) // .get(&id)
.ok_or(StorageError::NotFound)? // .ok_or(StorageError::NotFound)?
.to_owned()) // .to_owned())
} // }
fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError> { // fn get_all_store_and_repo_ids(&self) -> Result<HashMap<StoreRepo, Vec<RepoId>>, StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn load_store( // fn load_store(
&self, // &self,
repo_store: &StoreRepo, // repo_store: &StoreRepo,
block_storage: Arc<RwLock<dyn BlockStorage + Send + Sync>>, // block_storage: Arc<RwLock<dyn BlockStorage + Send + Sync>>,
) -> Result<Repo, StorageError> { // ) -> Result<Repo, StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn load_repo(&self, repo_id: &RepoId, store: Arc<Store>) -> Result<Repo, StorageError> { // fn load_repo(&self, repo_id: &RepoId, store: Arc<Store>) -> Result<Repo, StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn save_repo(&self, repo: &Repo) -> Result<(), StorageError> { // fn save_repo(&self, repo: &Repo) -> Result<(), StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn add_branch(&self, repo_id: &RepoId, branch_info: &BranchInfo) -> Result<(), StorageError> { // fn add_branch(&self, repo_id: &RepoId, branch_info: &BranchInfo) -> Result<(), StorageError> {
unimplemented!(); // unimplemented!();
} // }
fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError> { // fn update_signer_cap(&self, signer_cap: &SignerCap) -> Result<(), StorageError> {
unimplemented!(); // unimplemented!();
} // }
} // }

@ -35,7 +35,7 @@ use core::fmt;
//use oxigraph::model::GroundQuad; //use oxigraph::model::GroundQuad;
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
use crate::rocksdb_user_storage::RocksDbUserStorage; 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 async_std::sync::{Mutex, RwLockReadGuard, RwLockWriteGuard};
use std::{collections::HashMap, path::PathBuf, sync::Arc}; use std::{collections::HashMap, path::PathBuf, sync::Arc};
@ -1114,7 +1114,7 @@ impl Verifier {
let (graph, user, block) = match &config.config_type { let (graph, user, block) = match &config.config_type {
VerifierConfigType::Memory | VerifierConfigType::JsSaveSession(_) => ( VerifierConfigType::Memory | VerifierConfigType::JsSaveSession(_) => (
Some(oxigraph::store::Store::new().unwrap()), Some(oxigraph::store::Store::new().unwrap()),
Some(Box::new(InMemoryUserStorage::new()) as Box<dyn UserStorage>), None, //Some(Box::new(InMemoryUserStorage::new()) as Box<dyn UserStorage>),
Some(block_storage), Some(block_storage),
), ),
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]

@ -9,6 +9,7 @@
use ed25519_dalek::*; use ed25519_dalek::*;
use core::fmt;
use duration_str::parse; use duration_str::parse;
use futures::{future, pin_mut, stream, SinkExt, StreamExt}; use futures::{future, pin_mut, stream, SinkExt, StreamExt};
use ng_net::actors::*; use ng_net::actors::*;
@ -19,6 +20,7 @@ use rand::rngs::OsRng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_string_pretty}; use serde_json::{from_str, to_string_pretty};
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 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<NgcliError> for std::io::Error {
fn from(err: NgcliError) -> std::io::Error {
match err {
NgcliError::NgError(e) => e.into(),
NgcliError::ProtocolError(e) => Into::<NgError>::into(e).into(),
NgcliError::IoError(e) => e,
_ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()),
}
}
}
impl From<NgError> for NgcliError {
fn from(err: NgError) -> NgcliError {
Self::NgError(err)
}
}
impl From<ProtocolError> for NgcliError {
fn from(err: ProtocolError) -> NgcliError {
Self::ProtocolError(err)
}
}
impl From<std::io::Error> 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] { fn gen_client_keys(key: Option<[u8; 32]>) -> [[u8; 32]; 4] {
let key = match key { let key = match key {
None => { None => {
@ -87,7 +149,14 @@ fn gen_client_keys(key: Option<[u8; 32]>) -> [[u8; 32]; 4] {
} }
#[async_std::main] #[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!() let matches = command!()
.arg(arg!( .arg(arg!(
-v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace" -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 // reading key from file, if any
let mut key_path = path.clone(); let mut key_path = path.clone();
key_path.push("key"); key_path.push("key");
let key_from_file: Option<[u8; 32]>; let key_from_file: Option<[u8; 32]> = match read_to_string(key_path.clone()) {
let res = |key_path| -> Result<[u8; 32], &str> { Err(_) => None,
let mut file = read_to_string(key_path).map_err(|_| "")?; Ok(mut file) => {
let first_line = file.lines().nth(0).ok_or("empty file")?; let first_line = file
let res = decode_key(first_line.trim()).map_err(|_| "invalid file"); .lines()
file.zeroize(); .nth(0)
res .ok_or(NgcliError::InvalidKeyFile("empty file".to_string()))?;
}(&key_path); let res = decode_key(first_line.trim())
.map_err(|_| NgcliError::InvalidKeyFile("deserialization error".to_string()))?;
if res.is_err() && res.unwrap_err().len() > 0 { file.zeroize();
log_err!( Some(res)
"provided key file is incorrect. {}. cannot start", }
res.unwrap_err() };
);
return Err(ProtocolError::InvalidValue);
}
key_from_file = res.ok();
let mut keys: [[u8; 32]; 4] = match matches.get_one::<String>("key") { let mut keys: [[u8; 32]; 4] = match matches.get_one::<String>("key") {
Some(key_string) => { Some(key_string) => {
@ -229,15 +294,14 @@ async fn main() -> Result<(), ProtocolError> {
gen_client_keys(key_from_file) gen_client_keys(key_from_file)
} else { } else {
let res = decode_key(key_string.as_str()).map_err(|_| { let res = decode_key(key_string.as_str()).map_err(|_| {
log_err!("provided key is invalid. cannot start"); NgcliError::InvalidKeyFile(
ProtocolError::InvalidValue "check the argument provided in command line".to_string(),
)
})?; })?;
if matches.get_flag("save_key") { if matches.get_flag("save_key") {
let mut master_key = base64_url::encode(&res); let mut master_key = base64_url::encode(&res);
write(key_path.clone(), &master_key).map_err(|e| { write(key_path.clone(), &master_key)
log_err!("cannot save key to file. {}.cannot start", e.to_string()); .map_err(|e| NgcliError::CannotSaveKey(e.to_string()))?;
ProtocolError::InvalidValue
})?;
master_key.zeroize(); master_key.zeroize();
log_info!("The key has been saved to {}", key_path.to_str().unwrap()); 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 res = gen_client_keys(None);
let mut master_key = base64_url::encode(&res[0]); let mut master_key = base64_url::encode(&res[0]);
if matches.get_flag("save_key") { if matches.get_flag("save_key") {
write(key_path.clone(), &master_key).map_err(|e| { write(key_path.clone(), &master_key)
log_err!("cannot save key to file. {}.cannot start", e.to_string()); .map_err(|e| NgcliError::CannotSaveKey(e.to_string()))?;
ProtocolError::InvalidValue
})?;
log_info!("The key has been saved to {}", key_path.to_str().unwrap()); log_info!("The key has been saved to {}", key_path.to_str().unwrap());
} else { } 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 // 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 // reading config from file, if any
let mut config_path = path.clone(); let mut config_path = path.clone();
config_path.push("config.json"); config_path.push("config.json");
let mut config: Option<CliConfig>; let mut config: Option<CliConfig> = match read_to_string(config_path.clone()) {
let res = |config_path| -> Result<CliConfig, String> { Err(_) => None,
let file = read_to_string(config_path).map_err(|_| "".to_string())?; Ok(file) => {
from_str(&file).map_err(|e| e.to_string()) Some(from_str(&file).map_err(|e| NgcliError::InvalidConfigFile(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();
if let Some(server) = matches.get_one::<String>("server") { if let Some(server) = matches.get_one::<String>("server") {
let addr: Vec<&str> = server.split(',').collect(); let addr: Vec<&str> = server.split(',').collect();
if addr.len() != 3 { if addr.len() != 3 {
log_err!( return Err(NgcliError::OtherConfigErrorStr(
"NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. cannot start" "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID.",
); ));
return Err(ProtocolError::InvalidValue);
} }
let ip = IpAddr::from_str(addr[0]).map_err(|_| { 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"); 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.")
ProtocolError::InvalidValue
})?; })?;
let port = match from_str::<u16>(addr[1]) { let port = from_str::<u16>(addr[1]).map_err(|_| {
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.")
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 peer_id: PubKey = addr[2].try_into().map_err(|_| { let peer_id: PubKey = addr[2].try_into().map_err(|_| {
log_err!( NgcliError::OtherConfigErrorStr(
"NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. "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]."
The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]. cannot start" )
);
ProtocolError::InvalidValue
})?; })?;
if config.is_some() { if config.is_some() {
log_warn!("Overriding the config found in file with new server parameters provided on command line!"); 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() { if config.is_none() {
log_err!( return Err(NgcliError::OtherConfigErrorStr(
"No config found for the server to connect to. The config file is missing. "No config found for the server to connect to. The config file is missing. You must provide NG_CLIENT_SERVER or the --server option.",
You must provide NG_CLIENT_SERVER or the --server option. cannot start" ));
);
return Err(ProtocolError::InvalidValue);
} }
if let Some(user) = matches.get_one::<String>("user") { if let Some(user) = matches.get_one::<String>("user") {
let privkey: PrivKey = user.as_str().try_into().map_err(|_| { let privkey: PrivKey = user.as_str().try_into().map_err(|_| {
log_err!( NgcliError::OtherConfigErrorStr(
"NG_CLIENT_USER or the --user option is invalid. It should be a base64-url encoded "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.",
serde serialization of a [u8; 32] of a private key for a user. cannot start" )
);
ProtocolError::InvalidValue
})?; })?;
if config.is_some() { if config.is_some() {
let CliConfig::V0(c) = config.as_mut().unwrap(); 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(); let CliConfig::V0(config_v0) = config.as_ref().unwrap();
if config_v0.user.is_none() { if config_v0.user.is_none() {
log_err!( return Err(NgcliError::OtherConfigErrorStr(
"No config found for the user. The config file is missing. "No config found for the user. The config file is missing. You must provide NG_CLIENT_USER or the --user option.",
You must provide NG_CLIENT_USER or the --user option. cannot start" ));
);
return Err(ProtocolError::InvalidValue);
} }
if matches.get_flag("save_config") { if matches.get_flag("save_config") {
// saves the config to file // 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| { write(config_path.clone(), json_string).map_err(|e| {
log_err!( NgcliError::CannotSaveConfig(format!("cannot save config to file. {}.", e.to_string()))
"cannot save config to file. {}. cannot start",
e.to_string()
);
ProtocolError::InvalidValue
})?; })?;
log_info!( log_info!(
"The config file has been saved to {}", "The config file has been saved to {}",
config_path.to_str().unwrap() config_path.to_str().unwrap()
@ -430,18 +467,15 @@ async fn main() -> Result<(), ProtocolError> {
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| { .map_err(|_| {
log_err!("supplied USER_ID is invalid"); NgcliError::OtherConfigErrorStr("supplied USER_ID is invalid")
ProtocolError::InvalidValue
})?, })?,
is_admin: sub2_matches.get_flag("admin"), is_admin: sub2_matches.get_flag("admin"),
}), }),
) )
.await; .await?;
match &res {
Err(e) => log_err!("An error occurred: {e}"), println!("User added successfully");
Ok(_) => println!("User added successfully"), return Ok(());
}
return res.map(|_| ());
} }
Some(("del-user", sub2_matches)) => { Some(("del-user", sub2_matches)) => {
log_debug!("add-user"); log_debug!("add-user");
@ -455,26 +489,21 @@ async fn main() -> Result<(), ProtocolError> {
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| { .map_err(|_| {
log_err!("supplied USER_ID is invalid"); NgcliError::OtherConfigErrorStr("supplied USER_ID is invalid")
ProtocolError::InvalidValue
})?, })?,
}), }),
) )
.await; .await?;
match &res { println!("User removed successfully");
Err(e) => log_err!("An error occurred: {e}"), return Ok(());
Ok(_) => println!("User removed successfully"),
}
return res.map(|_| ());
} }
Some(("list-users", sub2_matches)) => { Some(("list-users", sub2_matches)) => {
log_debug!("list-users"); log_debug!("list-users");
let admins = sub2_matches.get_flag("admin"); let admins = sub2_matches.get_flag("admin");
let res = let res = do_admin_call(keys[1], config_v0, ListUsers::V0(ListUsersV0 { admins }))
do_admin_call(keys[1], config_v0, ListUsers::V0(ListUsersV0 { admins })).await; .await?;
match &res { match &res {
Err(e) => log_err!("An error occurred: {e}"), AdminResponseContentV0::Users(list) => {
Ok(AdminResponseContentV0::Users(list)) => {
println!( println!(
"Found {} {}users", "Found {} {}users",
list.len(), list.len(),
@ -484,12 +513,9 @@ async fn main() -> Result<(), ProtocolError> {
println!("{user}"); println!("{user}");
} }
} }
_ => { _ => return Err(NgError::InvalidResponse.into()),
log_err!("Invalid response");
return Err(ProtocolError::InvalidValue);
}
} }
return res.map(|_| ()); return Ok(());
} }
Some(("add-invitation", sub2_matches)) => { Some(("add-invitation", sub2_matches)) => {
log_debug!("add-invitation"); log_debug!("add-invitation");
@ -523,10 +549,9 @@ async fn main() -> Result<(), ProtocolError> {
tos_url: !sub2_matches.get_flag("notos"), tos_url: !sub2_matches.get_flag("notos"),
}), }),
) )
.await; .await?;
match res.as_mut() { match &mut res {
Err(e) => log_err!("An error occurred: {e}"), AdminResponseContentV0::Invitation(invitation) => {
Ok(AdminResponseContentV0::Invitation(invitation)) => {
invitation invitation
.set_name(sub2_matches.get_one::<String>("name").map(|s| s.clone())); .set_name(sub2_matches.get_one::<String>("name").map(|s| s.clone()));
@ -536,12 +561,9 @@ async fn main() -> Result<(), ProtocolError> {
println!("The invitation link is: {}", link) println!("The invitation link is: {}", link)
} }
} }
_ => { _ => return Err(NgError::InvalidResponse.into()),
log_err!("Invalid response");
return Err(ProtocolError::InvalidValue);
}
} }
return res.map(|_| ()); return Ok(());
} }
Some(("list-invitations", sub2_matches)) => { Some(("list-invitations", sub2_matches)) => {
log_debug!("invitations"); log_debug!("invitations");
@ -557,10 +579,9 @@ async fn main() -> Result<(), ProtocolError> {
unique, unique,
}), }),
) )
.await; .await?;
match &res { match &res {
Err(e) => log_err!("An error occurred: {e}"), AdminResponseContentV0::Invitations(list) => {
Ok(AdminResponseContentV0::Invitations(list)) => {
println!( println!(
"Found {} {}invitations", "Found {} {}invitations",
list.len(), list.len(),
@ -593,12 +614,9 @@ async fn main() -> Result<(), ProtocolError> {
); );
} }
} }
_ => { _ => return Err(NgError::InvalidResponse.into()),
log_err!("Invalid response");
return Err(ProtocolError::InvalidValue);
}
} }
return res.map(|_| ()); return Ok(());
} }
_ => panic!("shouldn't happen"), _ => panic!("shouldn't happen"),
}, },

@ -14,6 +14,7 @@ mod cli;
use crate::cli::*; use crate::cli::*;
use crate::types::*; use crate::types::*;
use clap::Parser; use clap::Parser;
use core::fmt;
use ng_broker::interfaces::*; use ng_broker::interfaces::*;
use ng_broker::server_ws::run_server_v0; use ng_broker::server_ws::run_server_v0;
use ng_broker::types::*; 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, 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_net::{WS_PORT, WS_PORT_REVERSE_PROXY};
use ng_repo::errors::NgError;
use ng_repo::log::*; use ng_repo::log::*;
use ng_repo::types::Sig; use ng_repo::types::Sig;
use ng_repo::types::SymKey; use ng_repo::types::SymKey;
@ -36,6 +38,7 @@ use ng_repo::{
utils::{decode_key, generate_keypair, sign, verify}, utils::{decode_key, generate_keypair, sign, verify},
}; };
use serde_json::{from_str, to_string_pretty}; use serde_json::{from_str, to_string_pretty};
use std::error::Error;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@ -79,7 +82,7 @@ fn parse_interface_and_port_for(
string: &String, string: &String,
for_option: &str, for_option: &str,
default_port: u16, default_port: u16,
) -> Result<(String, u16), ()> { ) -> Result<(String, u16), NgdError> {
let c = RE_INTERFACE.captures(string); let c = RE_INTERFACE.captures(string);
if c.is_some() && c.as_ref().unwrap().get(1).is_some() { 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)) Ok((interface.to_string(), port))
} else { } else {
log_err!( Err(NgdError::OtherConfigError(format!(
"The <INTERFACE:PORT> 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", "The <INTERFACE:PORT> 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 for_option
); )))
Err(())
} }
} }
fn parse_ipv6_for(string: String, for_option: &str) -> Result<Ipv6Addr, ()> { fn parse_ipv6_for(string: String, for_option: &str) -> Result<Ipv6Addr, NgdError> {
string.parse::<Ipv6Addr>().map_err(|_| { string.parse::<Ipv6Addr>().map_err(|_| {
log_err!( NgdError::OtherConfigError(format!(
"The <IPv6> value submitted for the {} option is invalid. cannot start", "The <IPv6> value submitted for the {} option is invalid.",
for_option for_option
) ))
}) })
} }
@ -125,18 +127,17 @@ fn parse_ipv4_and_port_for(
string: String, string: String,
for_option: &str, for_option: &str,
default_port: u16, default_port: u16,
) -> Result<(Ipv4Addr, u16), ()> { ) -> Result<(Ipv4Addr, u16), NgdError> {
let parts: Vec<&str> = string.split(":").collect(); let parts: Vec<&str> = string.split(":").collect();
let ipv4 = parts[0].parse::<Ipv4Addr>().map_err(|_| { let ipv4 = parts[0].parse::<Ipv4Addr>().map_err(|_| {
log_err!( NgdError::OtherConfigError(format!(
"The <IPv4:PORT> value submitted for the {} option is invalid. cannot start", "The <IPv4:PORT> value submitted for the {} option is invalid.",
for_option for_option
) ))
})?; })?;
let port; let port = if parts.len() > 1 {
if parts.len() > 1 { match from_str::<u16>(parts[1]) {
port = match from_str::<u16>(parts[1]) {
Err(_) => default_port, Err(_) => default_port,
Ok(p) => { Ok(p) => {
if p == 0 { if p == 0 {
@ -145,14 +146,14 @@ fn parse_ipv4_and_port_for(
p p
} }
} }
}; }
} else { } else {
port = default_port; default_port
} };
return Ok((ipv4, 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 c = RE_IPV6_WITH_PORT.captures(&string);
let ipv6; let ipv6;
let port; 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::<Ipv6Addr>().map_err(|_| { let ipv6 = ipv6_str.parse::<Ipv6Addr>().map_err(|_| {
log_err!( NgdError::OtherConfigError(format!(
"The <[IPv6]:PORT> value submitted for the {} option is invalid. cannot start", "The <[IPv6]:PORT> value submitted for the {} option is invalid.",
for_option for_option
) ))
})?; })?;
return Ok((IpAddr::V6(ipv6), port)); Ok((IpAddr::V6(ipv6), port))
} else { } else {
// we try just an IPV6 without port // we try just an IPV6 without port
let ipv6_res = string.parse::<Ipv6Addr>(); let ipv6_res = string.parse::<Ipv6Addr>();
if ipv6_res.is_err() { if ipv6_res.is_err() {
// let's try IPv4 // let's try IPv4
return parse_ipv4_and_port_for(string, for_option, DEFAULT_PORT) parse_ipv4_and_port_for(string, for_option, DEFAULT_PORT)
.map(|ipv4| (IpAddr::V4(ipv4.0), ipv4.1)); .map(|ipv4| (IpAddr::V4(ipv4.0), ipv4.1))
} else { } else {
ipv6 = ipv6_res.unwrap(); ipv6 = ipv6_res.unwrap();
port = DEFAULT_PORT; 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( fn parse_triple_interface_and_port_for(
string: &String, string: &String,
for_option: &str, for_option: &str,
) -> Result<((String, u16), (Option<Ipv6Addr>, (Ipv4Addr, u16))), ()> { ) -> Result<((String, u16), (Option<Ipv6Addr>, (Ipv4Addr, u16))), NgdError> {
let parts: Vec<&str> = string.split(',').collect(); let parts: Vec<&str> = string.split(',').collect();
if parts.len() < 2 { if parts.len() < 2 {
log_err!( return Err(NgdError::OtherConfigError(format!(
"The <PRIVATE_INTERFACE:PORT,[PUBLIC_IPV6,]PUBLIC_IPV4:PORT> value submitted for the {} option is invalid. It should be composed of at least 2 parts separated by a comma. cannot start", "The <PRIVATE_INTERFACE:PORT,[PUBLIC_IPV6,]PUBLIC_IPV4:PORT> value submitted for the {} option is invalid. It should be composed of at least 2 parts separated by a comma.",
for_option for_option
); )));
return Err(());
} }
let first_part = parse_interface_and_port_for( let first_part = parse_interface_and_port_for(
&parts[0].to_string(), &parts[0].to_string(),
&format!("private interface+PORT (left) part of the {}", for_option), &format!("private interface+PORT (left) part of the {}", for_option),
DEFAULT_PORT, DEFAULT_PORT,
); )?;
if first_part.is_err() {
return Err(());
}
let mut middle_part = None; let mut middle_part = None;
if parts.len() == 3 { if parts.len() == 3 {
let middle_part_res = parse_ipv6_for( let middle_part_res = parse_ipv6_for(
parts[1].to_string(), parts[1].to_string(),
&format!("public IPv6 (middle) part of the {}", for_option), &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( let last_part = parse_ipv4_and_port_for(
parts[parts.len() - 1].to_string(), parts[parts.len() - 1].to_string(),
&format!("public IPv4+PORT (right) part of the {}", for_option), &format!("public IPv4+PORT (right) part of the {}", for_option),
DEFAULT_PORT, 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( fn parse_domain_and_port(
domain_string: &String, domain_string: &String,
option: &str, option: &str,
default_port: u16, default_port: u16,
) -> Result<(String, String, u16), ()> { ) -> Result<(String, String, u16), NgdError> {
let parts: Vec<&str> = domain_string.split(':').collect(); let parts: Vec<&str> = domain_string.split(':').collect();
// check validity of domain name // check validity of domain name
let valid_domain = List.parse_dns_name(parts[0]); let valid_domain = List.parse_dns_name(parts[0]);
match valid_domain { match valid_domain {
Err(e) => { Err(e) => {
log_err!( return Err(NgdError::OtherConfigError(format!(
"The domain name provided for option {} is invalid. {}. cannot start", "The domain name provided for option {} is invalid. {}.",
option, option,
e.to_string() e.to_string()
); )));
return Err(());
} }
Ok(name) => { Ok(name) => {
if !name.has_known_suffix() { if !name.has_known_suffix() {
log_err!( return Err(NgdError::OtherConfigError(format!(
"The domain name provided for option {} is invalid. Unknown suffix in public list. cannot start", option "The domain name provided for option {} is invalid. Unknown suffix in public list.", option
); )));
return Err(());
} }
} }
} }
@ -296,7 +284,7 @@ fn parse_domain_and_port(
fn prepare_accept_forward_for_domain( fn prepare_accept_forward_for_domain(
domain: String, domain: String,
args: &mut Cli, args: &mut Cli,
) -> Result<AcceptForwardForV0, ()> { ) -> Result<AcceptForwardForV0, NgError> {
if args.domain_peer.is_some() { if args.domain_peer.is_some() {
let key = decode_key(args.domain_peer.as_ref().unwrap().as_str())?; let key = decode_key(args.domain_peer.as_ref().unwrap().as_str())?;
args.domain_peer.as_mut().unwrap().zeroize(); args.domain_peer.as_mut().unwrap().zeroize();
@ -310,15 +298,84 @@ fn prepare_accept_forward_for_domain(
Ok(AcceptForwardForV0::PublicDomain((domain, "".to_string()))) 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<NgdError> 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<NgError> for NgdError {
fn from(err: NgError) -> NgdError {
Self::NgError(err)
}
}
impl From<std::io::Error> 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_std::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
main_inner() if let Err(err) = main_inner().await {
.await log_err!("Cannot start: {}", err.to_string());
.map_err(|_| ErrorKind::InvalidInput.into()) return Err(err.into());
}
Ok(())
} }
async fn main_inner() -> Result<(), NgdError> {
async fn main_inner() -> Result<(), ()> {
let mut args = Cli::parse(); let mut args = Cli::parse();
if args.list_interfaces { if args.list_interfaces {
@ -340,7 +397,10 @@ async fn main_inner() -> Result<(), ()> {
} }
env_logger::init(); 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); log_debug!("base {:?}", args.base);
@ -357,23 +417,20 @@ async fn main_inner() -> Result<(), ()> {
// reading key from file, if any // reading key from file, if any
let mut key_path = path.clone(); let mut key_path = path.clone();
key_path.push("key"); key_path.push("key");
let key_from_file: Option<[u8; 32]>;
let res = |key_path| -> Result<[u8; 32], &str> { let key_from_file: Option<[u8; 32]> = match read_to_string(key_path.clone()) {
let mut file = read_to_string(key_path).map_err(|_| "")?; Err(_) => None,
let first_line = file.lines().nth(0).ok_or("empty file")?; Ok(mut file) => {
let res = decode_key(first_line.trim()).map_err(|_| "invalid file"); let first_line = file
file.zeroize(); .lines()
res .nth(0)
}(&key_path); .ok_or(NgdError::InvalidKeyFile("empty file".to_string()))?;
let res = decode_key(first_line.trim())
if res.is_err() && res.unwrap_err().len() > 0 { .map_err(|_| NgdError::InvalidKeyFile("deserialization error".to_string()))?;
log_err!( file.zeroize();
"provided key file is incorrect. {}. cannot start", Some(res)
res.unwrap_err() }
); };
return Err(());
}
key_from_file = res.ok();
let mut keys: [[u8; 32]; 4] = match &args.key { let mut keys: [[u8; 32]; 4] = match &args.key {
Some(key_string) => { Some(key_string) => {
@ -382,13 +439,15 @@ async fn main_inner() -> Result<(), ()> {
args.key.as_mut().unwrap().zeroize(); args.key.as_mut().unwrap().zeroize();
gen_broker_keys(key_from_file) gen_broker_keys(key_from_file)
} else { } else {
let res = decode_key(key_string.as_str()) let res = decode_key(key_string.as_str()).map_err(|_| {
.map_err(|_| log_err!("provided key is invalid. cannot start"))?; NgdError::InvalidKeyFile(
"check the argument provided in command line".to_string(),
)
})?;
if args.save_key { if args.save_key {
let mut master_key = base64_url::encode(&res); let mut master_key = base64_url::encode(&res);
write(key_path.clone(), &master_key).map_err(|e| { write(key_path.clone(), &master_key)
log_err!("cannot save key to file. {}.cannot start", e.to_string()) .map_err(|e| NgdError::CannotSaveKey(e.to_string()))?;
})?;
master_key.zeroize(); master_key.zeroize();
log_info!("The key has been saved to {}", key_path.to_str().unwrap()); 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 res = gen_broker_keys(None);
let mut master_key = base64_url::encode(&res[0]); let mut master_key = base64_url::encode(&res[0]);
if args.save_key { if args.save_key {
write(key_path.clone(), &master_key).map_err(|e| { write(key_path.clone(), &master_key)
log_err!("cannot save key to file. {}.cannot start", e.to_string()) .map_err(|e| NgdError::CannotSaveKey(e.to_string()))?;
})?;
log_info!("The key has been saved to {}", key_path.to_str().unwrap()); log_info!("The key has been saved to {}", key_path.to_str().unwrap());
} else { } 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 // 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(); let mut sign_path = path.clone();
sign_path.push("sign"); sign_path.push("sign");
let sign_from_file: Option<[u8; 32]>; let sign_from_file: Option<[u8; 32]>;
let res = |sign_path| -> Result<(), &str> { let privkey: PrivKey = keys[3].into();
let file = std::fs::read(sign_path).map_err(|_| "")?; let pubkey = privkey.to_pub();
let sig: Sig = serde_bare::from_slice(&file).map_err(|_| "invalid serialization")?;
let privkey: PrivKey = keys[3].into(); if match std::fs::read(sign_path.clone()) {
let pubkey = privkey.to_pub(); Err(_) => true,
verify(&vec![110u8, 103u8, 100u8], sig, pubkey).map_err(|_| "invalid signature")?; Ok(file) => {
Ok(()) let sig: Sig = serde_bare::from_slice(&file).map_err(|_| NgdError::InvalidSignature)?;
}(&sign_path); verify(&vec![110u8, 103u8, 100u8], sig, pubkey)
.map_err(|_| NgdError::InvalidSignature)?;
if res.is_err() { false
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(());
}
} }
} {
// 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 // DEALING WITH CONFIG
@ -466,24 +511,13 @@ async fn main_inner() -> Result<(), ()> {
// reading config from file, if any // reading config from file, if any
let mut config_path = path.clone(); let mut config_path = path.clone();
config_path.push("config.json"); config_path.push("config.json");
let mut config: Option<DaemonConfig>; let mut config: Option<DaemonConfig> = match read_to_string(config_path.clone()) {
let res = |config_path| -> Result<DaemonConfig, String> { Err(_) => None,
let file = read_to_string(config_path).map_err(|_| "".to_string())?; Ok(file) => Some(from_str(&file).map_err(|e| NgdError::InvalidConfigFile(e.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();
if config.is_some() && args.save_config { 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(NgdError::ConfigCannotSave);
return Err(());
} }
if args.local.is_some() if args.local.is_some()
@ -498,17 +532,11 @@ async fn main_inner() -> Result<(), ()> {
// QUICK CONFIG // QUICK CONFIG
if config.is_some() && !args.print_config { if config.is_some() && !args.print_config {
log_err!( return Err(NgdError::ConfigFilePresent);
"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(());
} }
if args.domain_peer.is_some() && args.domain_private.is_none() && args.domain.is_none() { if args.domain_peer.is_some() && args.domain_private.is_none() && args.domain.is_none() {
log_err!( return Err(NgdError::ConfigCannotSave);
"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(());
} }
let mut listeners: Vec<ListenerV0> = vec![]; let mut listeners: Vec<ListenerV0> = vec![];
@ -541,17 +569,14 @@ async fn main_inner() -> Result<(), ()> {
match find_first(&interfaces, InterfaceType::Loopback) { match find_first(&interfaces, InterfaceType::Loopback) {
None => { None => {
log_err!( return Err(NgdError::NoLoopback);
"That's pretty unusual, but no loopback interface could be found on your host. --domain option failed for that reason. cannot start"
);
return Err(());
} }
Some(loopback) => { Some(loopback) => {
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
let mut listener = ListenerV0::new_direct(loopback, !args.no_ipv6, local_port); let mut listener = ListenerV0::new_direct(loopback, !args.no_ipv6, local_port);
listener.accept_direct = false; listener.accept_direct = false;
let res = prepare_accept_forward_for_domain(domain, &mut args).map_err(|_| { 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; listener.accept_forward_for = res;
listeners.push(listener); listeners.push(listener);
@ -564,10 +589,7 @@ async fn main_inner() -> Result<(), ()> {
if args.local.is_some() { if args.local.is_some() {
match find_first(&interfaces, InterfaceType::Loopback) { match find_first(&interfaces, InterfaceType::Loopback) {
None => { None => {
log_err!( return Err(NgdError::OtherConfigErrorStr("That's pretty unusual, but no loopback interface could be found on your host."));
"That's pretty unusual, but no loopback interface could be found on your host. cannot start"
);
return Err(());
} }
Some(loopback) => { Some(loopback) => {
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
@ -577,10 +599,7 @@ async fn main_inner() -> Result<(), ()> {
&& listeners.last().unwrap().port == args.local.unwrap() && listeners.last().unwrap().port == args.local.unwrap()
{ {
if args.domain_peer.is_some() { if args.domain_peer.is_some() {
log_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"));
"--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(());
} }
let r = listeners.last_mut().unwrap(); let r = listeners.last_mut().unwrap();
r.accept_direct = true; r.accept_direct = true;
@ -605,18 +624,14 @@ async fn main_inner() -> Result<(), ()> {
let if_name = &arg_value.0; let if_name = &arg_value.0;
match find_first_or_name(&interfaces, InterfaceType::Public, &if_name) { match find_first_or_name(&interfaces, InterfaceType::Public, &if_name) {
None => { None => {
log_err!( 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."));
if if_name == "default" { } else {
"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() return Err(NgdError::OtherConfigError(format!(
} else { "We could not find a public IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.",
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_name if_name
) )));
} }
);
return Err(());
} }
Some(public) => { Some(public) => {
overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; overlays_config.core = BrokerOverlayPermission::AllRegisteredUser;
@ -644,19 +659,14 @@ async fn main_inner() -> Result<(), ()> {
let if_name = &private_part.0; let if_name = &private_part.0;
match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) {
None => { None => {
log_err!( if if_name == "default" {
"{}", return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --public option."));
if if_name == "default" { } else {
"We could not find a private IP interface on your host for --public option. cannot start" return Err(NgdError::OtherConfigError(format!(
.to_string() "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.",
} 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_name if_name
) )));
} }
);
return Err(());
} }
Some(inter) => { Some(inter) => {
private_interface = inter; private_interface = inter;
@ -666,15 +676,15 @@ async fn main_inner() -> Result<(), ()> {
if !is_public_ipv4(&public_part.1 .0) if !is_public_ipv4(&public_part.1 .0)
|| public_part.0.is_some() && !is_public_ipv6(public_part.0.as_ref().unwrap()) || 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(NgdError::OtherConfigErrorStr(
return Err(()); "The provided IPs are not public.",
));
} }
if args.no_ipv6 && public_part.0.is_some() { if args.no_ipv6 && public_part.0.is_some() {
log_err!( return Err(NgdError::OtherConfigErrorStr(
"The public IP is IPv6 but you selected the --no-ipv6 option. cannot start" "The public IP is IPv6 but you selected the --no-ipv6 option.",
); ));
return Err(());
} }
overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; overlays_config.core = BrokerOverlayPermission::AllRegisteredUser;
@ -738,19 +748,14 @@ async fn main_inner() -> Result<(), ()> {
match find_first_or_name(&interfaces, InterfaceType::Private, if_name) { match find_first_or_name(&interfaces, InterfaceType::Private, if_name) {
None => { None => {
log_err!( if if_name == "default" {
"{}", return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --dynamic option."));
if if_name == "default" { } else {
"We could not find a private IP interface on your host for --dynamic option. cannot start" return Err(NgdError::OtherConfigError(format!(
.to_string() "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.",
} 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_name if_name
) )));
} }
);
return Err(());
} }
Some(inter) => { Some(inter) => {
overlays_config.core = BrokerOverlayPermission::AllRegisteredUser; overlays_config.core = BrokerOverlayPermission::AllRegisteredUser;
@ -764,8 +769,7 @@ async fn main_inner() -> Result<(), ()> {
{ {
let r = listeners.last_mut().unwrap(); let r = listeners.last_mut().unwrap();
if r.accept_forward_for != AcceptForwardForV0::No { 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(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."));
return Err(());
} }
panic!("this should never happen. --dynamic created after a --private"); panic!("this should never happen. --dynamic created after a --private");
//r.ipv6 = !args.no_ipv6; //r.ipv6 = !args.no_ipv6;
@ -804,25 +808,20 @@ async fn main_inner() -> Result<(), ()> {
let if_name = &arg_value.0; let if_name = &arg_value.0;
match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) {
None => { None => {
log_err!( if if_name == "default" {
"{}", return Err(NgdError::OtherConfigErrorStr("We could not find a private IP interface on your host for --domain-private option."));
if if_name == "default" { } else {
"We could not find a private IP interface on your host for --domain-private option. cannot start" return Err(NgdError::OtherConfigError(format!(
.to_string() "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host.",
} 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_name if_name
) )));
} }
);
return Err(());
} }
Some(inter) => { Some(inter) => {
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
let res = prepare_accept_forward_for_domain(domain, &mut args).map_err(|_| { 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() if listeners.last().is_some()
&& listeners.last().unwrap().interface_name == inter.name && listeners.last().unwrap().interface_name == inter.name
@ -830,8 +829,7 @@ async fn main_inner() -> Result<(), ()> {
{ {
let r = listeners.last_mut().unwrap(); let r = listeners.last_mut().unwrap();
if r.accept_forward_for != AcceptForwardForV0::No { 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(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."));
return Err(());
} }
panic!( panic!(
"this should never happen. --domain-private created after a --private" "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; let if_name = &arg_value.0;
match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) { match find_first_or_name(&interfaces, InterfaceType::Private, &if_name) {
None => { None => {
log_err!( if if_name == "default" {
"{}", return Err(NgdError::OtherConfigErrorStr(
if if_name == "default" { "We could not find a private IP interface on your host.",
"We could not find a private IP interface on your host. cannot start" ));
.to_string() } else {
} else { return Err(NgdError::OtherConfigError(
format!( 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 if_name
) )));
} }
);
return Err(());
} }
Some(inter) => { Some(inter) => {
overlays_config.server = BrokerOverlayPermission::AllRegisteredUser; overlays_config.server = BrokerOverlayPermission::AllRegisteredUser;
@ -884,10 +880,9 @@ async fn main_inner() -> Result<(), ()> {
&& listeners.last().unwrap().port == arg_value.1 && listeners.last().unwrap().port == arg_value.1
{ {
if args.domain_peer.is_some() { if args.domain_peer.is_some() {
log_err!( 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. cannot start" "--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(); let r = listeners.last_mut().unwrap();
r.accept_direct = true; r.accept_direct = true;
@ -908,13 +903,15 @@ async fn main_inner() -> Result<(), ()> {
let parts: Vec<&str> = forward_string.split('@').collect(); let parts: Vec<&str> = forward_string.split('@').collect();
if parts.len() != 2 { if parts.len() != 2 {
log_err!( return Err(NgdError::OtherConfigErrorStr(
"The option --forward is invalid. It must contain two parts separated by a @ character. cannot start" "The option --forward is invalid. It must contain two parts separated by a @ character."
); ));
return Err(());
} }
let pub_key_array = decode_key(parts[1]) let pub_key_array = decode_key(parts[1]).map_err(|_| {
.map_err(|_| log_err!("The PEER_ID provided in the --forward option is invalid"))?; NgdError::OtherConfigErrorStr(
"The PEER_ID provided in the --forward option is invalid",
)
})?;
let peer_id = PubKey::Ed25519PubKey(pub_key_array); let peer_id = PubKey::Ed25519PubKey(pub_key_array);
let server_type = if parts[0].len() > 0 { let server_type = if parts[0].len() > 0 {
@ -932,8 +929,9 @@ async fn main_inner() -> Result<(), ()> {
} else if is_public_ip(&bind.0) { } else if is_public_ip(&bind.0) {
BrokerServerTypeV0::Public(vec![bind_addr]) BrokerServerTypeV0::Public(vec![bind_addr])
} else { } else {
log_err!("Invalid IP address given for --forward option. cannot start"); return Err(NgdError::OtherConfigErrorStr(
return Err(()); "Invalid IP address given for --forward option.",
));
} }
} else { } else {
// a domain name // a domain name
@ -994,10 +992,10 @@ async fn main_inner() -> Result<(), ()> {
// saves the config to file // saves the config to file
let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap(); let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap();
write(config_path.clone(), json_string).map_err(|e| { write(config_path.clone(), json_string).map_err(|e| {
log_err!( NgdError::CannotSaveConfig(format!(
"cannot save config to file. {}. cannot start", "cannot save config to file. {}.",
e.to_string() e.to_string()
) ))
})?; })?;
log_info!( log_info!(
"The config file has been saved to {}", "The config file has been saved to {}",
@ -1009,10 +1007,9 @@ async fn main_inner() -> Result<(), ()> {
} }
} else { } else {
if config.is_none() { if config.is_none() {
log_err!( return Err(NgdError::OtherConfigErrorStr(
"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" "No Quick config option passed, neither is a config file present. Choose at least one Quick config option. see --help for details"
); ));
return Err(());
} }
if args.print_config { if args.print_config {
let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap(); let json_string = to_string_pretty(config.as_ref().unwrap()).unwrap();

Loading…
Cancel
Save