// Copyright (c) 2022-2023 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.
#[ macro_use ]
extern crate slice_as_array ;
pub mod types ;
mod cli ;
use crate ::cli ::* ;
use crate ::types ::* ;
use clap ::Parser ;
use p2p_broker ::server_ws ::run_server ;
use p2p_broker ::types ::* ;
use p2p_broker ::utils ::* ;
use p2p_net ::types ::* ;
use p2p_net ::utils ::{
gen_keys , is_ipv4_global , is_ipv4_private , is_ipv6_global , is_ipv6_private , keys_from_bytes ,
Dual25519Keys , Sensitive , U8Array ,
} ;
use p2p_net ::{ WS_PORT , WS_PORT_REVERSE_PROXY } ;
use p2p_repo ::log ::* ;
use p2p_repo ::{
types ::{ PrivKey , PubKey } ,
utils ::{ generate_keypair , keypair_from_ed , sign , verify } ,
} ;
use serde_json ::{ from_str , to_string_pretty } ;
use std ::fs ::{ read_to_string , write } ;
use std ::io ::ErrorKind ;
use std ::net ::{ IpAddr , Ipv4Addr , Ipv6Addr } ;
use std ::path ::PathBuf ;
use addr ::parser ::DnsName ;
use addr ::psl ::List ;
use lazy_static ::lazy_static ;
use regex ::Regex ;
#[ derive(Clone, Debug, PartialEq, Eq) ]
pub enum InterfaceType {
Loopback ,
Private ,
Public ,
Invalid ,
}
impl InterfaceType {
pub fn is_ipv4_valid_for_type ( & self , ip : & Ipv4Addr ) -> bool {
match self {
InterfaceType ::Loopback = > ip . is_loopback ( ) ,
InterfaceType ::Public = > is_public_ipv4 ( ip ) ,
// we allow to bind to link-local for IPv4
InterfaceType ::Private = > is_ipv4_private ( ip ) ,
_ = > false ,
}
}
pub fn is_ipv6_valid_for_type ( & self , ip : & Ipv6Addr ) -> bool {
match self {
InterfaceType ::Loopback = > ip . is_loopback ( ) ,
InterfaceType ::Public = > is_public_ipv6 ( ip ) ,
// we do NOT allow to bind to link-local for IPv6
InterfaceType ::Private = > is_ipv6_private ( ip ) ,
_ = > false ,
}
}
}
pub fn print_ipv4 ( ip : & default_net ::ip ::Ipv4Net ) -> String {
format! ( "{}/{}" , ip . addr , ip . prefix_len )
}
pub fn print_ipv6 ( ip : & default_net ::ip ::Ipv6Net ) -> String {
format! ( "{}/{}" , ip . addr , ip . prefix_len )
}
#[ derive(Clone, Debug) ]
pub struct Interface {
pub if_type : InterfaceType ,
pub name : String ,
pub mac_addr : Option < default_net ::interface ::MacAddr > ,
/// List of Ipv4Net for the network interface
pub ipv4 : Vec < default_net ::ip ::Ipv4Net > ,
/// List of Ipv6Net for the network interface
pub ipv6 : Vec < default_net ::ip ::Ipv6Net > ,
}
fn find_first ( list : & Vec < Interface > , iftype : InterfaceType ) -> Option < Interface > {
for inf in list {
if inf . if_type = = iftype {
return Some ( inf . clone ( ) ) ;
}
}
None
}
fn find_first_or_name (
list : & Vec < Interface > ,
iftype : InterfaceType ,
name : & String ,
) -> Option < Interface > {
for inf in list {
if ( name = = "default" | | * name = = inf . name ) & & inf . if_type = = iftype {
return Some ( inf . clone ( ) ) ;
}
}
None
}
fn is_public_ipv4 ( ip : & Ipv4Addr ) -> bool {
// TODO, use core::net::Ipv6Addr.is_global when it will be stable
return is_ipv4_global ( ip ) ;
}
fn is_public_ipv6 ( ip : & Ipv6Addr ) -> bool {
// TODO, use core::net::Ipv6Addr.is_global when it will be stable
return is_ipv6_global ( ip ) ;
}
fn is_public_ip ( ip : & IpAddr ) -> bool {
match ip {
IpAddr ::V4 ( v4 ) = > is_public_ipv4 ( v4 ) ,
IpAddr ::V6 ( v6 ) = > is_public_ipv6 ( v6 ) ,
}
}
fn is_private_ip ( ip : & IpAddr ) -> bool {
match ip {
IpAddr ::V4 ( v4 ) = > is_ipv4_private ( v4 ) ,
IpAddr ::V6 ( v6 ) = > is_ipv6_private ( v6 ) ,
}
}
pub fn get_interface ( ) -> Vec < Interface > {
let mut res : Vec < Interface > = vec! [ ] ;
let interfaces = default_net ::get_interfaces ( ) ;
for interface in interfaces {
if interface . ipv4 . len ( ) > 0 {
let first_v4 = interface . ipv4 [ 0 ] . addr ;
let if_type = if first_v4 . is_loopback ( ) {
InterfaceType ::Loopback
} else if is_ipv4_private ( & first_v4 ) {
InterfaceType ::Private
} else if is_public_ipv4 ( & first_v4 ) {
InterfaceType ::Public
} else {
continue ;
} ;
let interf = Interface {
if_type ,
name : interface . name ,
mac_addr : interface . mac_addr ,
ipv4 : interface . ipv4 ,
ipv6 : interface . ipv6 ,
} ;
res . push ( interf ) ;
}
}
res
}
pub fn print_interfaces ( ) {
let interfaces = get_interface ( ) ;
for interface in interfaces {
println! ( "{} \t{:?}" , interface . name , interface . if_type ) ;
println! (
"\tIPv4: {}" ,
interface
. ipv4
. iter ( )
. map ( | ip | print_ipv4 ( ip ) )
. collect ::< Vec < String > > ( )
. join ( " " )
) ;
println! (
"\tIPv6: {}" ,
interface
. ipv6
. iter ( )
. map ( | ip | print_ipv6 ( ip ) )
. collect ::< Vec < String > > ( )
. join ( " " )
) ;
if let Some ( mac_addr ) = interface . mac_addr {
println! ( "\tMAC: {}" , mac_addr ) ;
}
}
}
fn decode_key ( key_string : & String ) -> Result < [ u8 ; 32 ] , ( ) > {
let vec = base64_url ::decode ( key_string ) . map_err ( | _ | log_err ! ( "key has invalid content" ) ) ? ;
Ok ( * slice_as_array ! ( & vec , [ u8 ; 32 ] )
. ok_or ( ( ) )
. map_err ( | _ | log_err ! ( "key has invalid content array" ) ) ? )
}
//For windows: {846EE342-7039-11DE-9D20-806E6F6E6963}
//For the other OSes: en0 lo ...
#[ cfg(not(target_os = " windows " )) ]
lazy_static ! {
static ref RE_INTERFACE : Regex = Regex ::new ( r"^([0-9a-z]{2,16})(\:\d{1,5})?$" ) . unwrap ( ) ;
}
#[ cfg(target_os = " windows " ) ]
lazy_static ! {
static ref RE_INTERFACE : Regex = Regex ::new (
r"^(\{[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\})(\:\d{1,5})?$"
)
. unwrap ( ) ;
}
lazy_static ! {
static ref RE_IPV6_WITH_PORT : Regex =
Regex ::new ( r"^\[([0-9a-fA-F:]{3,39})\](\:\d{1,5})?$" ) . unwrap ( ) ;
}
pub static DEFAULT_PORT : u16 = WS_PORT ;
pub static DEFAULT_TLS_PORT : u16 = 443 ;
fn parse_interface_and_port_for (
string : & String ,
for_option : & str ,
default_port : u16 ,
) -> Result < ( String , u16 ) , ( ) > {
let c = RE_INTERFACE . captures ( string ) ;
if c . is_some ( ) & & c . as_ref ( ) . unwrap ( ) . get ( 1 ) . is_some ( ) {
let cap = c . unwrap ( ) ;
let interface = cap . get ( 1 ) . unwrap ( ) . as_str ( ) ;
let port = match cap . get ( 2 ) {
None = > default_port ,
Some ( p ) = > {
let mut chars = p . as_str ( ) . chars ( ) ;
chars . next ( ) ;
match from_str ::< u16 > ( chars . as_str ( ) ) {
Err ( _ ) = > default_port ,
Ok ( p ) = > {
if p = = 0 {
default_port
} else {
p
}
}
}
}
} ;
Ok ( ( interface . to_string ( ) , port ) )
} else {
log_err ! (
"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" ,
for_option
) ;
Err ( ( ) )
}
}
fn parse_ipv6_for ( string : String , for_option : & str ) -> Result < Ipv6Addr , ( ) > {
string . parse ::< Ipv6Addr > ( ) . map_err ( | _ | {
log_err ! (
"The <IPv6> value submitted for the {} option is invalid. cannot start" ,
for_option
)
} )
}
fn parse_ipv4_and_port_for (
string : String ,
for_option : & str ,
default_port : u16 ,
) -> Result < ( Ipv4Addr , u16 ) , ( ) > {
let parts : Vec < & str > = string . split ( ":" ) . collect ( ) ;
let ipv4 = parts [ 0 ] . parse ::< Ipv4Addr > ( ) . map_err ( | _ | {
log_err ! (
"The <IPv4:PORT> value submitted for the {} option is invalid. cannot start" ,
for_option
)
} ) ? ;
let port ;
if parts . len ( ) > 1 {
port = match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > default_port ,
Ok ( p ) = > {
if p = = 0 {
default_port
} else {
p
}
}
} ;
} else {
port = default_port ;
}
return Ok ( ( ipv4 , port ) ) ;
}
fn parse_ip_and_port_for ( string : String , for_option : & str ) -> Result < ( IpAddr , u16 ) , ( ) > {
let c = RE_IPV6_WITH_PORT . captures ( & string ) ;
let ipv6 ;
let port ;
if c . is_some ( ) & & c . as_ref ( ) . unwrap ( ) . get ( 1 ) . is_some ( ) {
let cap = c . unwrap ( ) ;
let ipv6_str = cap . get ( 1 ) . unwrap ( ) . as_str ( ) ;
port = match cap . get ( 2 ) {
None = > DEFAULT_PORT ,
Some ( p ) = > {
let mut chars = p . as_str ( ) . chars ( ) ;
chars . next ( ) ;
match from_str ::< u16 > ( chars . as_str ( ) ) {
Err ( _ ) = > DEFAULT_PORT ,
Ok ( p ) = > {
if p = = 0 {
DEFAULT_PORT
} else {
p
}
}
}
}
} ;
let ipv6 = ipv6_str . parse ::< Ipv6Addr > ( ) . map_err ( | _ | {
log_err ! (
"The <[IPv6]:PORT> value submitted for the {} option is invalid. cannot start" ,
for_option
)
} ) ? ;
return Ok ( ( IpAddr ::V6 ( ipv6 ) , port ) ) ;
} else {
// we try just an IPV6 without port
let ipv6_res = string . parse ::< Ipv6Addr > ( ) ;
if ipv6_res . is_err ( ) {
// let's try IPv4
return parse_ipv4_and_port_for ( string , for_option , DEFAULT_PORT )
. map ( | ipv4 | ( IpAddr ::V4 ( ipv4 . 0 ) , ipv4 . 1 ) ) ;
} else {
ipv6 = ipv6_res . unwrap ( ) ;
port = DEFAULT_PORT ;
return Ok ( ( IpAddr ::V6 ( ipv6 ) , port ) ) ;
}
}
}
fn parse_triple_interface_and_port_for (
string : & String ,
for_option : & str ,
) -> Result < ( ( String , u16 ) , ( Option < Ipv6Addr > , ( Ipv4Addr , u16 ) ) ) , ( ) > {
let parts : Vec < & str > = string . split ( ',' ) . collect ( ) ;
if parts . len ( ) < 2 {
log_err ! (
"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" ,
for_option
) ;
return Err ( ( ) ) ;
}
let first_part = parse_interface_and_port_for (
& parts [ 0 ] . to_string ( ) ,
& format! ( "private interface+PORT (left) part of the {}" , for_option ) ,
DEFAULT_PORT ,
) ;
if first_part . is_err ( ) {
return Err ( ( ) ) ;
}
let mut middle_part = None ;
if parts . len ( ) = = 3 {
let middle_part_res = parse_ipv6_for (
parts [ 1 ] . to_string ( ) ,
& format! ( "public IPv6 (middle) part of the {}" , for_option ) ,
) ;
if middle_part_res . is_err ( ) {
return Err ( ( ) ) ;
}
middle_part = middle_part_res . ok ( ) ;
}
let last_part = parse_ipv4_and_port_for (
parts [ parts . len ( ) - 1 ] . to_string ( ) ,
& format! ( "public IPv4+PORT (right) part of the {}" , for_option ) ,
DEFAULT_PORT ,
) ;
if last_part . is_err ( ) {
return Err ( ( ) ) ;
}
Ok ( ( first_part . unwrap ( ) , ( middle_part , last_part . unwrap ( ) ) ) )
}
fn parse_domain_and_port (
domain_string : & String ,
option : & str ,
default_port : u16 ,
) -> Result < ( String , String , u16 ) , ( ) > {
let parts : Vec < & str > = domain_string . split ( ':' ) . collect ( ) ;
// check validity of domain name
let valid_domain = List . parse_dns_name ( parts [ 0 ] ) ;
match valid_domain {
Err ( e ) = > {
log_err ! (
"The domain name provided for option {} is invalid. {}. cannot start" ,
option ,
e . to_string ( )
) ;
return Err ( ( ) ) ;
}
Ok ( name ) = > {
if ! name . has_known_suffix ( ) {
log_err ! (
"The domain name provided for option {} is invalid. Unknown suffix in public list. cannot start" , option
) ;
return Err ( ( ) ) ;
}
}
}
let port = if parts . len ( ) > 1 {
match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > default_port ,
Ok ( p ) = > {
if p = = 0 {
default_port
} else {
p
}
}
}
} else {
default_port
} ;
let mut domain_with_port = parts [ 0 ] . clone ( ) . to_string ( ) ;
if port ! = default_port {
domain_with_port . push_str ( & format! ( ":{}" , port ) ) ;
}
Ok ( ( parts [ 0 ] . to_string ( ) , domain_with_port , port ) )
}
fn prepare_accept_forward_for_domain ( domain : String , args : & Cli ) -> Result < AcceptForwardForV0 , ( ) > {
if args . domain_peer . is_some ( ) {
let key_ser = base64_url ::decode ( args . domain_peer . as_ref ( ) . unwrap ( ) ) . map_err ( | _ | ( ) ) ? ;
let key = serde_bare ::from_slice ::< PrivKey > ( & key_ser ) ;
Ok ( AcceptForwardForV0 ::PublicDomainPeer ( (
domain ,
key . unwrap ( ) ,
"" . to_string ( ) ,
) ) )
} else {
Ok ( AcceptForwardForV0 ::PublicDomain ( ( domain , "" . to_string ( ) ) ) )
}
}
#[ async_std::main ]
async fn main ( ) -> std ::io ::Result < ( ) > {
main_inner ( )
. await
. map_err ( | _ | ErrorKind ::InvalidInput . into ( ) )
}
async fn main_inner ( ) -> Result < ( ) , ( ) > {
let args = Cli ::parse ( ) ;
if args . list_interfaces {
println! ( "list of network interfaces" ) ;
print_interfaces ( ) ;
return Ok ( ( ) ) ;
}
if std ::env ::var ( "RUST_LOG" ) . is_err ( ) {
if args . verbose = = 0 {
std ::env ::set_var ( "RUST_LOG" , "warn" ) ;
} else if args . verbose = = 1 {
std ::env ::set_var ( "RUST_LOG" , "info" ) ;
} else if args . verbose = = 2 {
std ::env ::set_var ( "RUST_LOG" , "debug" ) ;
} else if args . verbose > = 3 {
std ::env ::set_var ( "RUST_LOG" , "trace" ) ;
}
}
env_logger ::init ( ) ;
log_info ! ( "Starting NextGraph daemon (ngd)" ) ;
log_debug ! ( "base {:?}" , args . base ) ;
let mut path = PathBuf ::from ( & args . base ) ;
path . push ( "server" ) ;
if ! path . is_absolute ( ) {
path = std ::env ::current_dir ( ) . unwrap ( ) . join ( path ) ;
}
log_debug ! ( "cur {}" , std ::env ::current_dir ( ) . unwrap ( ) . display ( ) ) ;
log_debug ! ( "home {}" , path . to_str ( ) . unwrap ( ) ) ;
std ::fs ::create_dir_all ( path . clone ( ) ) . unwrap ( ) ;
// reading key from file, if any
let mut key_path = path . clone ( ) ;
key_path . push ( "key" ) ;
let key_from_file : Option < [ u8 ; 32 ] > ;
let res = | key_path | -> Result < [ u8 ; 32 ] , & str > {
let file = read_to_string ( key_path ) . map_err ( | _ | "" ) ? ;
decode_key (
& file
. lines ( )
. nth ( 0 )
. ok_or ( "empty file" ) ?
. to_string ( )
. trim ( )
. to_string ( ) ,
)
. map_err ( | _ | "invalid file" )
} ( & key_path ) ;
if res . is_err ( ) & & res . unwrap_err ( ) . len ( ) > 0 {
log_err ! (
"provided key file is incorrect. {}. cannot start" ,
res . unwrap_err ( )
) ;
return Err ( ( ) ) ;
}
key_from_file = res . ok ( ) ;
let keys : [ [ u8 ; 32 ] ; 4 ] = match & args . key {
Some ( key_string ) = > {
if key_from_file . is_some ( ) {
log_err ! ( "provided --key option will not be used as a key file is already present" ) ;
gen_broker_keys ( Some ( key_from_file . unwrap ( ) ) )
} else {
let res = decode_key ( key_string )
. map_err ( | _ | log_err ! ( "provided key is invalid. cannot start" ) ) ? ;
if args . save_key {
let master_key = base64_url ::encode ( & res ) ;
write ( key_path . clone ( ) , master_key ) . map_err ( | e | {
log_err ! ( "cannot save key to file. {}.cannot start" , e . to_string ( ) )
} ) ? ;
log_info ! ( "The key has been saved to {}" , key_path . to_str ( ) . unwrap ( ) ) ;
}
gen_broker_keys ( Some ( res ) )
}
}
None = > {
if key_from_file . is_some ( ) {
gen_broker_keys ( Some ( key_from_file . unwrap ( ) ) )
} else {
let res = gen_broker_keys ( None ) ;
let master_key = base64_url ::encode ( & res [ 0 ] ) ;
if args . save_key {
write ( key_path . clone ( ) , master_key ) . map_err ( | e | {
log_err ! ( "cannot save key to file. {}.cannot start" , e . to_string ( ) )
} ) ? ;
log_info ! ( "The key has been saved to {}" , key_path . to_str ( ) . unwrap ( ) ) ;
} else {
// on purpose we don't log the key, just print it out to stdout, as it should not be saved in logger's files
println! ( "YOUR GENERATED KEY IS: {}" , master_key ) ;
log_err ! ( "At your request, the key wasn't saved." ) ;
log_err ! ( "provide it again to the next start of ngd with --key option or NG_SERVER_KEY env variable" ) ;
}
res
}
}
} ;
// DEALING WITH CONFIG
// reading config from file, if any
let mut config_path = path . clone ( ) ;
config_path . push ( "config.json" ) ;
let mut config : Option < DaemonConfig > ;
let res = | config_path | -> Result < DaemonConfig , String > {
let file = read_to_string ( config_path ) . map_err ( | _ | "" . to_string ( ) ) ? ;
from_str ( & file ) . map_err ( | e | e . to_string ( ) )
} ( & config_path ) ;
if res . is_err ( ) & & res . as_ref ( ) . unwrap_err ( ) . len ( ) > 0 {
log_err ! (
"provided config file is incorrect. {}. cannot start" ,
res . unwrap_err ( )
) ;
return Err ( ( ) ) ;
}
config = res . ok ( ) ;
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 ( ( ) ) ;
}
if args . local . is_some ( )
| | args . forward . is_some ( )
| | args . core . is_some ( )
| | args . private . is_some ( )
| | args . public . is_some ( )
| | args . dynamic . is_some ( )
| | args . domain . is_some ( )
{
// QUICK CONFIG
if config . is_some ( ) & & ! args . print_config {
log_err ! (
"A config file is present. You can 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 ( ) {
log_err ! (
"The --domain-peer option can only be set when the --domain or --domain-private option is also present on the command line. cannot start"
) ;
return Err ( ( ) ) ;
}
let mut listeners : Vec < ListenerV0 > = vec! [ ] ;
let mut overlays_config : BrokerOverlayConfigV0 = BrokerOverlayConfigV0 ::new ( ) ;
let interfaces = get_interface ( ) ;
//// --domain
if args . domain . is_some ( ) {
let domain_string = args . domain . as_ref ( ) . unwrap ( ) ;
let parts : Vec < & str > = domain_string . split ( ',' ) . collect ( ) ;
let local_port ;
let ( _ , domain , _ ) = if parts . len ( ) = = 1 {
local_port = WS_PORT_REVERSE_PROXY ;
parse_domain_and_port ( domain_string , "--domain" , DEFAULT_TLS_PORT ) ?
} else {
local_port = match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > WS_PORT_REVERSE_PROXY ,
Ok ( p ) = > {
if p = = 0 {
WS_PORT_REVERSE_PROXY
} else {
p
}
}
} ;
parse_domain_and_port ( & parts [ 0 ] . to_string ( ) , "--domain" , DEFAULT_TLS_PORT ) ?
} ;
match find_first ( & interfaces , InterfaceType ::Loopback ) {
None = > {
log_err ! (
"That's pretty unusual, but no loopback interface could be found on your host. --domain option failed for that reason. cannot start"
) ;
return Err ( ( ) ) ;
}
Some ( loopback ) = > {
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
let mut listener =
ListenerV0 ::new_direct ( loopback . name , ! args . no_ipv6 , local_port ) ;
listener . accept_direct = false ;
let res = prepare_accept_forward_for_domain ( domain , & 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" )
} ) ? ;
listener . accept_forward_for = res ;
listeners . push ( listener ) ;
}
}
}
//// --local
if args . local . is_some ( ) {
match find_first ( & interfaces , InterfaceType ::Loopback ) {
None = > {
log_err ! (
"That's pretty unusual, but no loopback interface could be found on your host. cannot start"
) ;
return Err ( ( ) ) ;
}
Some ( loopback ) = > {
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
if listeners . last ( ) . is_some ( )
& & listeners . last ( ) . unwrap ( ) . interface_name = = loopback . name
& & listeners . last ( ) . unwrap ( ) . port = = args . local . unwrap ( )
{
let r = listeners . last_mut ( ) . unwrap ( ) ;
r . accept_direct = true ;
r . ipv6 = ! args . no_ipv6 ;
} else {
listeners . push ( ListenerV0 ::new_direct (
loopback . name ,
! args . no_ipv6 ,
args . local . unwrap ( ) ,
) ) ;
}
}
}
}
//// --core
if args . core . is_some ( ) {
let arg_value =
parse_interface_and_port_for ( args . core . as_ref ( ) . unwrap ( ) , "--core" , DEFAULT_PORT ) ? ;
let if_name = & arg_value . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Public , & if_name ) {
None = > {
log_err ! (
"{}" ,
if if_name = = "default" {
"We could not find a public IP interface on your host. If you are setting up a server behind a reverse proxy, enter the config manually in the config file. cannot start" . to_string ( )
} else {
format! (
"We could not find a public IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start" ,
if_name
)
}
) ;
return Err ( ( ) ) ;
}
Some ( public ) = > {
overlays_config . core = BrokerOverlayPermission ::AllRegisteredUser ;
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
listeners . push ( ListenerV0 ::new_direct (
public . name ,
! args . no_ipv6 ,
arg_value . 1 ,
) ) ;
}
}
}
//// --public
if args . public . is_some ( ) {
let arg_value =
parse_triple_interface_and_port_for ( args . public . as_ref ( ) . unwrap ( ) , "--public" ) ? ;
let public_part = & arg_value . 1 ;
let private_part = & arg_value . 0 ;
let private_interface ;
let if_name = & private_part . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Private , & if_name ) {
None = > {
log_err ! (
"{}" ,
if if_name = = "default" {
"We could not find a private IP interface on your host for --public option. cannot start"
. to_string ( )
} else {
format! (
"We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start" ,
if_name
)
}
) ;
return Err ( ( ) ) ;
}
Some ( inter ) = > {
private_interface = inter ;
}
}
if ! is_public_ipv4 ( & public_part . 1 . 0 )
| | public_part . 0. is_some ( ) & & ! is_public_ipv6 ( public_part . 0. as_ref ( ) . unwrap ( ) )
{
log_err ! ( "The provided IPs are not public. cannot start" ) ;
return Err ( ( ) ) ;
}
if args . no_ipv6 & & public_part . 0. is_some ( ) {
log_err ! (
"The public IP is IPv6 but you selected the --no-ipv6 option. cannot start"
) ;
return Err ( ( ) ) ;
}
overlays_config . core = BrokerOverlayPermission ::AllRegisteredUser ;
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
let ipv6 = public_part . 0. map ( | ipv6 | BindAddress {
port : public_part . 1 . 1 ,
ip : ( & IpAddr ::V6 ( ipv6 ) ) . into ( ) ,
} ) ;
listeners . push ( ListenerV0 {
interface_name : private_interface . name ,
ipv6 : public_part . 0. is_some ( ) ,
interface_refresh : 0 ,
port : private_part . 1 ,
discoverable : false ,
accept_direct : false ,
accept_forward_for : AcceptForwardForV0 ::PublicStatic ( (
BindAddress {
port : public_part . 1 . 1 ,
ip : ( & IpAddr ::V4 ( public_part . 1 . 0 ) ) . into ( ) ,
} ,
ipv6 ,
"" . to_string ( ) ,
) ) ,
} ) ;
}
//// --dynamic
if args . dynamic . is_some ( ) {
let dynamic_string = args . dynamic . as_ref ( ) . unwrap ( ) ;
let parts : Vec < & str > = dynamic_string . split ( ',' ) . collect ( ) ;
let arg_value =
parse_interface_and_port_for ( & parts [ 0 ] . to_string ( ) , "--dynamic" , DEFAULT_PORT ) ? ;
let public_port = if parts . len ( ) = = 2 {
match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > DEFAULT_PORT ,
Ok ( p ) = > {
if p = = 0 {
DEFAULT_PORT
} else {
p
}
}
}
} else {
DEFAULT_PORT
} ;
let if_name = & arg_value . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Private , if_name ) {
None = > {
log_err ! (
"{}" ,
if if_name = = "default" {
"We could not find a private IP interface on your host for --dynamic option. cannot start"
. to_string ( )
} else {
format! (
"We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start" ,
if_name
)
}
) ;
return Err ( ( ) ) ;
}
Some ( inter ) = > {
overlays_config . core = BrokerOverlayPermission ::AllRegisteredUser ;
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
if listeners . last ( ) . is_some ( )
& & listeners . last ( ) . unwrap ( ) . interface_name = = inter . name
& & listeners . last ( ) . unwrap ( ) . port = = arg_value . 1
{
let r = listeners . last_mut ( ) . unwrap ( ) ;
r . ipv6 = ! args . no_ipv6 ;
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 ( ( ) ) ;
}
r . accept_forward_for =
AcceptForwardForV0 ::PublicDyn ( ( public_port , 60 , "" . to_string ( ) ) ) ;
} else {
let mut listener =
ListenerV0 ::new_direct ( inter . name , ! args . no_ipv6 , arg_value . 1 ) ;
listener . accept_direct = false ;
listener . accept_forward_for =
AcceptForwardForV0 ::PublicDyn ( ( public_port , 60 , "" . to_string ( ) ) ) ;
listeners . push ( listener ) ;
}
}
}
}
//// --domain-private
if args . domain_private . is_some ( ) {
let domain_string = args . domain_private . as_ref ( ) . unwrap ( ) ;
let parts : Vec < & str > = domain_string . split ( ',' ) . collect ( ) ;
let ( _ , domain , _ ) =
parse_domain_and_port ( & parts [ 0 ] . to_string ( ) , "--domain-private" , DEFAULT_TLS_PORT ) ? ;
let bind_string = if parts . len ( ) > 1 { parts [ 1 ] } else { "default" } ;
let arg_value = parse_interface_and_port_for (
& bind_string . to_string ( ) ,
"--domain-private" ,
WS_PORT_REVERSE_PROXY ,
) ? ;
let if_name = & arg_value . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Private , & if_name ) {
None = > {
log_err ! (
"{}" ,
if if_name = = "default" {
"We could not find a private IP interface on your host for --domain-private option. cannot start"
. to_string ( )
} else {
format! (
"We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host. cannot start" ,
if_name
)
}
) ;
return Err ( ( ) ) ;
}
Some ( inter ) = > {
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
let res = prepare_accept_forward_for_domain ( domain , & 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" ) } ) ? ;
if listeners . last ( ) . is_some ( )
& & listeners . last ( ) . unwrap ( ) . interface_name = = inter . name
& & listeners . last ( ) . unwrap ( ) . port = = arg_value . 1
{
let r = listeners . last_mut ( ) . unwrap ( ) ;
r . ipv6 = ! args . no_ipv6 ;
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 ( ( ) ) ;
}
r . accept_forward_for = res ;
} else {
let mut listener =
ListenerV0 ::new_direct ( inter . name , ! args . no_ipv6 , arg_value . 1 ) ;
listener . accept_direct = false ;
listener . accept_forward_for = res ;
listeners . push ( listener ) ;
}
}
}
}
//// --private
if args . private . is_some ( ) {
let arg_value = parse_interface_and_port_for (
args . private . as_ref ( ) . unwrap ( ) ,
"--private" ,
DEFAULT_PORT ,
) ? ;
let if_name = & arg_value . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Private , & if_name ) {
None = > {
log_err ! (
"{}" ,
if if_name = = "default" {
"We could not find a private IP interface on your host. cannot start"
. to_string ( )
} else {
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
)
}
) ;
return Err ( ( ) ) ;
}
Some ( inter ) = > {
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
if listeners . last ( ) . is_some ( )
& & listeners . last ( ) . unwrap ( ) . interface_name = = inter . name
& & listeners . last ( ) . unwrap ( ) . port = = arg_value . 1
{
let r = listeners . last_mut ( ) . unwrap ( ) ;
r . accept_direct = true ;
r . ipv6 = ! args . no_ipv6 ;
} else {
listeners . push ( ListenerV0 ::new_direct (
inter . name ,
! args . no_ipv6 ,
arg_value . 1 ,
) ) ;
}
}
}
}
//// --forward
if args . forward . is_some ( ) {
//"[DOMAIN/IP:PORT]@PEERID"
let forward_string = args . forward . as_ref ( ) . unwrap ( ) ;
let parts : Vec < & str > = forward_string . split ( '@' ) . collect ( ) ;
if parts . len ( ) ! = 2 {
log_err ! (
"The option --forward is invalid. It must contain two parts separated by a @ character. cannot start"
) ;
return Err ( ( ) ) ;
}
let vec = base64_url ::decode ( parts [ 1 ] )
. map_err ( | _ | log_err ! ( "The PEERID provided in the --forward option is invalid" ) ) ? ;
let pub_key_array = * slice_as_array ! ( vec . as_slice ( ) , [ u8 ; 32 ] )
. ok_or ( ( ) )
. map_err ( | _ | {
log_err ! ( "PEERID provided in the --forward option, has invalid array" )
} ) ? ;
let peer_id = PubKey ::Ed25519PubKey ( pub_key_array ) ;
let server_type = if parts [ 0 ] . len ( ) > 0 {
let first_char = parts [ 0 ] . chars ( ) . next ( ) . unwrap ( ) ;
if first_char = = '[' | | first_char . is_numeric ( ) {
// an IPv6 or IPv4
let bind = parse_ip_and_port_for ( parts [ 0 ] . to_string ( ) , "--forward" ) ? ;
let bind_addr = BindAddress {
ip : ( & bind . 0 ) . into ( ) ,
port : bind . 1 ,
} ;
if is_private_ip ( & bind . 0 ) {
BrokerServerTypeV0 ::BoxPrivate ( vec! [ bind_addr ] )
} else if is_public_ip ( & bind . 0 ) {
BrokerServerTypeV0 ::BoxPublic ( vec! [ bind_addr ] )
} else {
log_err ! ( "Invalid IP address given for --forward option. cannot start" ) ;
return Err ( ( ) ) ;
}
} else {
// a domain name
let ( _ , domain , _ ) = parse_domain_and_port (
& parts [ 0 ] . to_string ( ) ,
"--forward" ,
DEFAULT_TLS_PORT ,
) ? ;
BrokerServerTypeV0 ::Domain ( domain )
}
} else {
BrokerServerTypeV0 ::BoxPublicDyn ( vec! [ ] )
} ;
overlays_config . forward = vec! [ BrokerServerV0 {
server_type ,
peer_id ,
} ] ;
}
config = Some ( DaemonConfig ::V0 ( DaemonConfigV0 {
listeners ,
overlays_configs : vec ! [ overlays_config ] ,
} ) ) ;
if args . print_config {
let json_string = to_string_pretty ( config . as_ref ( ) . unwrap ( ) ) . unwrap ( ) ;
println! ( "The Quick config would be:\n{}" , json_string ) ;
return Ok ( ( ) ) ;
}
if args . save_config {
// saves the config to file
let json_string = to_string_pretty ( config . as_ref ( ) . unwrap ( ) ) . unwrap ( ) ;
write ( config_path . clone ( ) , json_string ) . map_err ( | e | {
log_err ! (
"cannot save config to file. {}. cannot start" ,
e . to_string ( )
)
} ) ? ;
log_info ! (
"The config file has been saved to {}" ,
config_path . to_str ( ) . unwrap ( )
) ;
log_info ! (
"You cannot use Quick config options anymore on the command line in your next start of the server. But you can go to modify the config file directly, or delete it." ,
) ;
}
} else {
if config . is_none ( ) {
log_err ! (
"No Quick config option passed, neither is a config file present. We cannot start the server. Choose at least one Quick config option. see --help for details"
) ;
return Err ( ( ) ) ;
}
if args . print_config {
let json_string = to_string_pretty ( config . as_ref ( ) . unwrap ( ) ) . unwrap ( ) ;
println! ( "The saved config is:\n{}" , json_string ) ;
return Ok ( ( ) ) ;
}
}
// let keys = gen_keys();
// let pub_key = PubKey::Ed25519PubKey(keys.1);
// let (ed_priv_key, ed_pub_key) = generate_keypair();
// let duals = Dual25519Keys::generate();
// let eds = keypair_from_ed(duals.ed25519_priv, duals.ed25519_pub);
// let test_vector: Vec<u8> = vec![71, 51, 206, 126, 9, 84, 132];
// let sig = sign(eds.0, eds.1, &test_vector).unwrap();
// verify(&test_vector, sig, eds.1).unwrap();
// let privkey = duals.x25519_priv;
// let pubkey = PubKey::Ed25519PubKey(duals.x25519_public);
// log_debug!("Public key of node: {:?}", keys.1);
// log_debug!("Private key of node: {:?}", keys.0.as_slice());
// let pubkey = PubKey::Ed25519PubKey([
// 95, 155, 249, 202, 41, 105, 71, 51, 206, 126, 9, 84, 132, 92, 60, 7, 74, 179, 46, 21, 21,
// 242, 171, 27, 249, 79, 76, 176, 168, 43, 83, 2,
// ]);
// let privkey = Sensitive::<[u8; 32]>::from_slice(&[
// 56, 86, 36, 0, 109, 59, 152, 66, 166, 71, 201, 20, 119, 64, 173, 99, 215, 52, 40, 189, 96,
// 142, 3, 134, 167, 187, 235, 4, 39, 26, 31, 119,
// ]);
let ( privkey , pubkey ) = keys_from_bytes ( keys [ 1 ] ) ;
let priv_key_array = * slice_as_array ! ( privkey . as_slice ( ) , [ u8 ; 32 ] )
. ok_or ( ( ) )
. map_err ( | _ | log_err ! ( "Private key of peer has invalid array" ) ) ? ;
let priv_key = PrivKey ::Ed25519PrivKey ( priv_key_array ) ;
let priv_key_ser = serde_bare ::to_vec ( & priv_key ) . unwrap ( ) ;
let prix_key_encoded = base64_url ::encode ( & priv_key_ser ) ;
log_info ! ( "PeerId of node: {}" , pubkey ) ;
debug_println ! ( "Private key of peer: {}" , prix_key_encoded ) ;
run_server ( "127.0.0.1" , WS_PORT , privkey , pubkey , path ) . await ? ;
Ok ( ( ) )
}