@ -14,9 +14,12 @@ 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 , keys_from_bytes , Dual25519Keys , Sensitive , U8Array } ;
use p2p_net ::WS_PORT ;
use p2p_repo ::log ::* ;
@ -24,10 +27,12 @@ use p2p_repo::{
types ::{ PrivKey , PubKey } ,
utils ::{ generate_keypair , keypair_from_ed , sign , verify } ,
} ;
use serde_json ::{ from_str , json , to_string_pretty } ;
use std ::fs ::{ read_to_string , write } ;
use std ::io ::Read ;
use std ::io ::Write ;
use std ::io ::{ BufReader , ErrorKind } ;
use std ::net ::{ IpAddr , Ipv4Addr , Ipv6Addr } ;
use std ::path ::{ Path , PathBuf } ;
#[ derive(Clone, Debug, PartialEq, Eq) ]
@ -46,6 +51,7 @@ 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 ,
@ -56,6 +62,28 @@ pub struct 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
}
pub fn get_interface ( ) -> Vec < Interface > {
let mut res : Vec < Interface > = vec! [ ] ;
let interfaces = default_net ::get_interfaces ( ) ;
@ -127,6 +155,172 @@ fn decode_key(key_string: String) -> Result<[u8; 32], ()> {
. map_err ( | _ | log_err ! ( "key has invalid content array" ) ) ? )
}
use lazy_static ::lazy_static ;
use regex ::Regex ;
//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 = 80 ;
fn parse_interface_and_port_for ( string : String , for_option : & str ) -> 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 ) = > 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. Stopping here" ,
for_option
) ;
Err ( ( ) )
}
}
fn parse_ipv6_for ( string : String , for_option : & str ) -> Result < Ipv6Addr , ( ) > {
string . parse ::< Ipv6Addr > ( ) . map_err ( | _ | ( ) )
}
fn parse_ipv4_and_port_for ( string : String , for_option : & str ) -> Result < ( Ipv4Addr , u16 ) , ( ) > {
let parts : Vec < & str > = string . split ( ":" ) . collect ( ) ;
let ipv4_res = parts [ 0 ] . parse ::< Ipv4Addr > ( ) ;
if ipv4_res . is_err ( ) {
log_err ! (
"The <IPv4:PORT> value submitted for the {} option is invalid. Stopping here" ,
for_option
) ;
return Err ( ( ) ) ;
}
let port ;
let ipv4 = ipv4_res . unwrap ( ) ;
if parts . len ( ) > 1 {
port = match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > DEFAULT_PORT ,
Ok ( p ) = > 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 ) = > p ,
}
}
} ;
let ipv6_res = ipv6_str . parse ::< Ipv6Addr > ( ) ;
if ipv6_res . is_err ( ) {
log_err ! (
"The <[IPv6]:PORT> value submitted for the {} option is invalid. Stopping here" ,
for_option
) ;
return Err ( ( ) ) ;
}
ipv6 = ipv6_res . unwrap ( ) ;
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 )
. 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. Stopping here" ,
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 ) ,
) ;
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 ) ,
) ;
if last_part . is_err ( ) {
return Err ( ( ) ) ;
}
Ok ( ( first_part . unwrap ( ) , ( middle_part , last_part . unwrap ( ) ) ) )
}
#[ async_std::main ]
async fn main ( ) -> std ::io ::Result < ( ) > {
let args = Cli ::parse ( ) ;
@ -188,10 +382,7 @@ async fn main() -> std::io::Result<()> {
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
key_from_file = match res {
Err ( _ ) = > None ,
Ok ( k ) = > Some ( k ) ,
} ;
key_from_file = res . ok ( ) ;
let keys : [ [ u8 ; 32 ] ; 4 ] = match args . key {
Some ( key_string ) = > {
@ -228,7 +419,7 @@ async fn main() -> std::io::Result<()> {
}
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 stdout, as it should 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
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" ) ;
@ -240,6 +431,312 @@ async fn main() -> std::io::Result<()> {
println! ( "{:?}" , keys ) ;
// 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. {}. aborting start" ,
res . unwrap_err ( )
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
config = res . ok ( ) ;
println! ( "CONFIG {:?}" , config ) ;
if config . is_some ( ) & & args . save_config {
log_err ! ( "A config file is present. We cannot override it with Quick config options." ) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
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 ( ) {
log_err ! (
"A config file is present. You can use the Quick config options of the command-line. In order to use them, delete your config file first."
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
if args . domain_peer . is_some ( ) & & args . domain . is_none ( ) {
log_err ! (
"The --domain-peer option can only be set when the --domain option is also present on the command line"
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
let mut listeners : Vec < ListenerV0 > = vec! [ ] ;
let mut overlays_config : BrokerOverlayConfigV0 = BrokerOverlayConfigV0 ::new ( ) ;
let interfaces = get_interface ( ) ;
//// --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"
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
Some ( loopback ) = > {
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
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 . unwrap ( ) , "--core" ) ;
if arg_value . is_err ( ) {
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
let if_name = & arg_value . as_ref ( ) . unwrap ( ) . 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" . 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" ,
if_name
)
}
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
Some ( public ) = > {
overlays_config . core = BrokerOverlayPermission ::AllRegisteredUser ;
overlays_config . server = BrokerOverlayPermission ::AllRegisteredUser ;
listeners . push ( ListenerV0 ::new_direct (
public . name ,
! args . no_ipv6 ,
arg_value . unwrap ( ) . 1 ,
) ) ;
}
}
}
//// --public
if args . public . is_some ( ) {
let arg_value = parse_triple_interface_and_port_for ( args . public . unwrap ( ) , "--public" ) ;
if arg_value . is_err ( ) {
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
let public_part = & arg_value . as_ref ( ) . unwrap ( ) . 1 ;
let private_part = & arg_value . as_ref ( ) . unwrap ( ) . 0 ;
let private_interface ;
let if_name = & private_part . 0 ;
match find_first_or_name ( & interfaces , InterfaceType ::Private , & if_name ) {
None = > {
log_err ! ( "We could not find a private IP interface named {} on your host. use --list-interfaces to find the available interfaces on your host" ,
if_name
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
Some ( inter ) = > {
private_interface = inter ;
}
}
if args . no_ipv6 & & public_part . 0. is_some ( ) {
log_err ! ( "The public IP is IPv6 but you selected the --no-ipv6 option" ) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
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 ( ) ,
) ) ,
} ) ;
}
//// --private
if args . private . is_some ( ) {
let arg_value = parse_interface_and_port_for ( args . private . unwrap ( ) , "--private" ) ;
if arg_value . is_err ( ) {
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
let if_name = & arg_value . as_ref ( ) . unwrap ( ) . 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." . 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" ,
if_name
)
}
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
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 . as_ref ( ) . unwrap ( ) . 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 . unwrap ( ) . 1 ,
) ) ;
}
}
}
}
//// --dynamic
if args . dynamic . is_some ( ) {
let dynamic_string = args . dynamic . unwrap ( ) ;
let parts : Vec < & str > = dynamic_string . split ( ',' ) . collect ( ) ;
let arg_value = parse_interface_and_port_for ( parts [ 0 ] . to_string ( ) , "--dynamic" ) ;
if arg_value . is_err ( ) {
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
let public_port = if parts . len ( ) = = 2 {
match from_str ::< u16 > ( parts [ 1 ] ) {
Err ( _ ) = > DEFAULT_PORT ,
Ok ( p ) = > p ,
}
} else {
DEFAULT_PORT
} ;
let if_name = & arg_value . as_ref ( ) . unwrap ( ) . 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." . 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" ,
if_name
)
}
) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
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 . as_ref ( ) . unwrap ( ) . 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. Aborting" ) ;
return Err ( ErrorKind ::InvalidInput . into ( ) ) ;
}
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 . unwrap ( ) . 1 ) ;
listener . accept_direct = false ;
listener . accept_forward_for =
AcceptForwardForV0 ::PublicDyn ( ( public_port , 60 , "" . to_string ( ) ) ) ;
listeners . push ( listener ) ;
}
}
}
}
config = Some ( DaemonConfig ::V0 ( DaemonConfigV0 {
listeners ,
overlays_config ,
} ) ) ;
if args . save_config {
// saves the config to file
let json_string = to_string_pretty ( config . as_ref ( ) . unwrap ( ) ) . unwrap ( ) ;
if let Err ( e ) = write ( config_path . clone ( ) , json_string ) {
log_err ! ( "cannot save config to file. aborting start" ) ;
return Err ( e ) ;
}
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 ( ErrorKind ::InvalidInput . into ( ) ) ;
}
}
// let keys = gen_keys();
// let pub_key = PubKey::Ed25519PubKey(keys.1);
// let (ed_priv_key, ed_pub_key) = generate_keypair();