diff --git a/Cargo.lock b/Cargo.lock index 892010938..c2dd0eb5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2182,14 +2182,15 @@ dependencies = [ "async-executor", "async-std", "async-trait", - "clap 3.1.8", + "clap 3.1.12", "ctrlc-async", "darkfi", "easy-parallel", "futures", "fxhash", "log", - "rand 0.8.5", + "rand", + "serde", "serde_json", "simplelog", "smol", diff --git a/bin/ircd2/Cargo.toml b/bin/ircd2/Cargo.toml index 48af3e623..280f30b60 100644 --- a/bin/ircd2/Cargo.toml +++ b/bin/ircd2/Cargo.toml @@ -27,7 +27,8 @@ clap = {version = "3.1.8", features = ["derive"]} log = "0.4.16" simplelog = "0.12.0-alpha1" fxhash = "0.2.1" -ctrlc-async = {version= "3.2.2", features = ["termination"]} +ctrlc-async = {version= "3.2.2", default-features = false, features = ["async-std", "termination"]} # Encoding and parsing serde_json = "1.0.79" +serde = {version = "1.0.136", features = ["derive"]} diff --git a/bin/ircd2/ircd_config.toml b/bin/ircd2/ircd_config.toml new file mode 100644 index 000000000..06dcae3e8 --- /dev/null +++ b/bin/ircd2/ircd_config.toml @@ -0,0 +1,36 @@ +## taud configuration file +## +## Please make sure you go through all the settings so you can configure +## your daemon properly. + +# Path to the datastore +datastore_raft = "~/.config/tau/tau.db" + +# Path to DER-formatted PKCS#12 archive. (used only with tls url) +# This can be created using openssl: +# openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem +tls_identity_path = "" + +### Number of outbound connections +#outbound_connections = 5 + +# The address where taud should bind its RPC socket +[rpc_listener_url] +url="127.0.0.1:6667" +password = "FOOBAR" + +# The address where taud should bind its RPC socket +[irc_listener_url] +url="127.0.0.1:8000" +password = "FOOBAR" + +### The accept address +#[accept_address] +#url="127.0.0.1:8822" +#password = "FOOBAR" + +### Seed node addresses +#[[seeds]] +#url="127.0.0.1:8811" +#password = "FOOBAR" + diff --git a/bin/ircd2/src/main.rs b/bin/ircd2/src/main.rs index 9b5ba3d83..a9cd0eebf 100644 --- a/bin/ircd2/src/main.rs +++ b/bin/ircd2/src/main.rs @@ -10,54 +10,27 @@ use log::{debug, error, info, warn}; use simplelog::{ColorChoice, TermLogger, TerminalMode}; use darkfi::{ - cli_desc, net, + net, raft::Raft, rpc::rpcserver::{listen_and_serve, RpcServerConfig}, - util::cli::log_config, + util::{ + cli::{log_config, spawn_config, Config}, + path::get_config_path, + }, Error, Result, }; -pub(crate) mod privmsg; -pub(crate) mod rpc; -pub(crate) mod server; +pub mod privmsg; +pub mod rpc; +pub mod server; +pub mod settings; -use crate::{privmsg::Privmsg, rpc::JsonRpcInterface, server::IrcServerConnection}; - -#[derive(Parser)] -#[clap(name = "ircd", about = cli_desc!(), version)] -struct Args { - /// Accept address - #[clap(short, long)] - accept: Option, - - /// Seed node (repeatable) - #[clap(short, long)] - seed: Vec, - - /// Manual connection (repeatable) - #[clap(short, long)] - connect: Vec, - - /// Connection slots - #[clap(long, default_value_t = 0)] - slots: u32, - - /// External address - #[clap(short, long)] - external: Option, - - /// IRC listen address - #[clap(short = 'r', long, default_value = "127.0.0.1:6667")] - irc: SocketAddr, - - /// RPC listen address - #[clap(long, default_value = "127.0.0.1:8000")] - rpc: SocketAddr, - - /// Verbosity level - #[clap(short, parse(from_occurrences))] - verbose: u8, -} +use crate::{ + privmsg::Privmsg, + rpc::JsonRpcInterface, + server::IrcServerConnection, + settings::{CliArgs, IrcdConfig, Settings, CONFIG_FILE_CONTENTS}, +}; async fn process_user_input( mut line: String, @@ -100,7 +73,7 @@ async fn process( let mut line = String::new(); futures::select! { privmsg = receiver.recv().fuse() => { - let msg = privmsg.expect("internal message queue error"); + let msg = privmsg?; debug!("ABOUT TO SEND: {:?}", msg); let irc_msg = format!(":{}!anon@dark.fi PRIVMSG {} :{}\r\n", msg.nickname, @@ -123,31 +96,32 @@ async fn process( } } -async fn start(executor: Arc>, args: Args, net_settings: net::Settings) -> Result<()> { - let listener = TcpListener::bind(args.irc).await?; +async fn start(executor: Arc>, settings: Settings) -> Result<()> { + let listener = TcpListener::bind(settings.irc_listener_url).await?; let local_addr = listener.local_addr()?; info!("Listening on {}", local_addr); // // Raft // - let mut raft = Raft::::new(net_settings.inbound, std::path::PathBuf::from("msgs.db"))?; + let mut raft = + Raft::::new(settings.accept_address, std::path::PathBuf::from("msgs.db"))?; let raft_sender = raft.get_broadcast(); let commits = raft.get_commits(); // // RPC interface - + // let rpc_config = RpcServerConfig { - socket_addr: args.rpc, + socket_addr: settings.rpc_listener_url, // TODO: Use net/transport: use_tls: false, identity_path: Default::default(), identity_pass: Default::default(), }; let executor_cloned = executor.clone(); - let rpc_interface = Arc::new(JsonRpcInterface { addr: args.rpc }); + let rpc_interface = Arc::new(JsonRpcInterface { addr: settings.rpc_listener_url }); let rpc_task = executor.spawn(async move { listen_and_serve(rpc_config, rpc_interface, executor_cloned.clone()).await }); @@ -176,6 +150,15 @@ async fn start(executor: Arc>, args: Args, net_settings: net::Setti let stop_signal = async_channel::bounded::<()>(10); + let net_settings = net::Settings { + inbound: settings.accept_address, + outbound_connections: settings.outbound_connections, + external_addr: settings.accept_address, + peers: settings.connect.clone(), + seeds: settings.seeds.clone(), + ..Default::default() + }; + ctrlc_async::set_async_handler(async move { warn!(target: "ircd", "ircd start() Exit Signal"); // cleaning up tasks running in the background @@ -192,19 +175,17 @@ async fn start(executor: Arc>, args: Args, net_settings: net::Setti } fn main() -> Result<()> { - let args = Args::parse(); + let args = CliArgs::parse(); let (lvl, conf) = log_config(args.verbose.into())?; TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?; - let net_settings = net::Settings { - inbound: args.accept, - outbound_connections: args.slots, - external_addr: args.external, - peers: args.connect.clone(), - seeds: args.seed.clone(), - ..Default::default() - }; + let config_path = get_config_path(args.config.clone(), "ircd_config.toml")?; + spawn_config(&config_path, CONFIG_FILE_CONTENTS)?; + + let config: IrcdConfig = Config::::load(config_path)?; + + let settings = Settings::load(args, config)?; let ex = Arc::new(Executor::new()); let ex_clone = ex.clone(); @@ -214,7 +195,7 @@ fn main() -> Result<()> { // Run the main future on the current thread. .finish(|| { smol::future::block_on(async move { - start(ex_clone.clone(), args, net_settings).await?; + start(ex_clone.clone(), settings).await?; drop(signal); Ok::<(), darkfi::Error>(()) }) diff --git a/bin/ircd2/src/settings.rs b/bin/ircd2/src/settings.rs new file mode 100644 index 000000000..c25f50679 --- /dev/null +++ b/bin/ircd2/src/settings.rs @@ -0,0 +1,134 @@ +use std::{net::SocketAddr, path::PathBuf}; + +use clap::Parser; +use serde::{Deserialize, Serialize}; + +use darkfi::{ + cli_desc, + util::{cli::UrlConfig, path::expand_path}, + Error, Result, +}; + +pub const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../ircd_config.toml"); + +#[derive(Clone, Debug)] +pub struct Settings { + pub datastore_raft: PathBuf, + pub rpc_listener_url: SocketAddr, + pub irc_listener_url: SocketAddr, + pub accept_address: Option, + pub outbound_connections: u32, + pub connect: Vec, + pub seeds: Vec, +} + +impl Settings { + pub fn load(args: CliArgs, config: IrcdConfig) -> Result { + if config.datastore_raft.is_empty() { + return Err(Error::ParseFailed("Failed to parse datastore_raft path")) + } + + let datastore_raft = expand_path(&config.datastore_raft)?; + + let rpc_listener_url = if args.rpc.is_none() { + SocketAddr::try_from(config.rpc_listener_url)? + } else { + args.rpc.unwrap() + }; + + let irc_listener_url = if args.irc.is_none() { + SocketAddr::try_from(config.irc_listener_url)? + } else { + args.irc.unwrap() + }; + + let accept_address = if args.accept.is_none() { + match config.accept_address { + Some(addr) => { + let socket_addr = SocketAddr::try_from(addr)?; + Some(socket_addr) + } + None => None, + } + } else { + args.accept + }; + + let outbound_connections = if args.slots == 0 { + config.outbound_connections.unwrap_or_default() + } else { + args.slots + }; + + let connect = args.connect; + + let config_seeds = config + .seeds + .map(|addrs| { + addrs.iter().filter_map(|addr| SocketAddr::try_from(addr.clone()).ok()).collect() + }) + .unwrap_or_default(); + + let seeds = if args.seeds.is_empty() { config_seeds } else { args.seeds }; + + Ok(Settings { + datastore_raft, + rpc_listener_url, + irc_listener_url, + accept_address, + outbound_connections, + connect, + seeds, + }) + } +} + +#[derive(Parser)] +#[clap(name = "ircd", about = cli_desc!(), version)] +pub struct CliArgs { + /// Sets a custom config file + #[clap(long)] + pub config: Option, + /// Accept address + #[clap(short, long)] + pub accept: Option, + /// Seed node (repeatable) + #[clap(short, long)] + pub seeds: Vec, + /// Manual connection (repeatable) + #[clap(short, long)] + pub connect: Vec, + /// Connection slots + #[clap(long, default_value = "0")] + pub slots: u32, + /// External address + #[clap(short, long)] + pub external: Option, + /// IRC listen address + #[clap(short = 'r', long)] + pub irc: Option, + /// RPC listen address + #[clap(long)] + pub rpc: Option, + /// Verbosity level + #[clap(short, parse(from_occurrences))] + pub verbose: u8, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct IrcdConfig { + /// path to datastore for raft + pub datastore_raft: String, + /// Path to DER-formatted PKCS#12 archive. (used only with tls listener url) + pub tls_identity_path: String, + /// The address where taud should bind its RPC socket + pub rpc_listener_url: UrlConfig, + /// IRC listen address + pub irc_listener_url: UrlConfig, + /// Accept address for p2p network + pub accept_address: Option, + /// Number of outbound connections for p2p + pub outbound_connections: Option, + /// The seeds for receiving ip addresses from the p2p network + pub seeds: Option>, +}