diff options
-rw-r--r-- | src/bin/server.rs | 48 | ||||
-rw-r--r-- | src/config/environment.rs | 126 | ||||
-rw-r--r-- | src/config/file.rs (renamed from src/config.rs) | 82 | ||||
-rw-r--r-- | src/config/mod.rs | 90 | ||||
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 1 |
6 files changed, 246 insertions, 103 deletions
diff --git a/src/bin/server.rs b/src/bin/server.rs index 0a1bb21..9b2b01a 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -45,7 +45,6 @@ use std::io::ErrorKind; use std::process; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; -use std::thread; use std::time::Duration; use mio::net::UdpSocket; @@ -54,7 +53,7 @@ use mio_extras::timer::Timer; use byteorder::{LittleEndian, WriteBytesExt}; -use roughenough::config::{FileConfig, ServerConfig}; +use roughenough::config::{EnvironmentConfig, FileConfig, ServerConfig}; use roughenough::keys::{LongTermKey, OnlineKey}; use roughenough::merkle::MerkleTree; use roughenough::{Error, RtMessage, Tag}; @@ -104,18 +103,18 @@ fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> { } fn polling_loop( - config: &ServerConfig, + config: Box<ServerConfig>, online_key: &mut OnlineKey, cert_bytes: &[u8], - response_counter: Arc<AtomicUsize>, ) { + let response_counter = AtomicUsize::new(0); let keep_running = Arc::new(AtomicBool::new(true)); let kr = keep_running.clone(); ctrlc::set_handler(move || kr.store(false, Ordering::Release)) .expect("failed setting Ctrl-C handler"); - let sock_addr = config.socket_addr(); + let sock_addr = config.socket_addr().expect(""); let socket = UdpSocket::bind(&sock_addr).expect("failed to bind to socket"); let poll_duration = Some(Duration::from_millis(100)); @@ -251,11 +250,16 @@ pub fn main() { let mut args = env::args(); if args.len() != 2 { - error!("Usage: server /path/to/config.file"); + error!("Usage: server /path/to/config.file|ENV"); process::exit(1); } - let config = FileConfig::from_file(&args.nth(1).unwrap()).expect("Unable to load config"); + let arg1 = args.nth(1).unwrap(); + + let config: Box<ServerConfig> = match arg1.as_ref() { + "ENV" => Box::new(EnvironmentConfig::new()), + _ => Box::new(FileConfig::from_file(&arg1).unwrap()), + }; let mut online_key = OnlineKey::new(); let mut long_term_key = LongTermKey::new(config.seed()); @@ -267,35 +271,9 @@ pub fn main() { info!("Status updates every : {} seconds", config.status_interval().as_secs()); info!("Server listening on : {}:{}", config.interface(), config.port()); - let response_counter = Arc::new(AtomicUsize::new(0)); - - if env::var("BENCH").is_ok() { - log::set_max_level(log::LevelFilter::Warn); - let response_counter = response_counter.clone(); - - thread::spawn(move || loop { - let old = time::get_time().sec; - let old_reqs = response_counter.load(Ordering::SeqCst); - - thread::sleep(Duration::from_secs(1)); - - let new = time::get_time().sec; - let new_reqs = response_counter.load(Ordering::SeqCst); - - warn!( - "Processing at {:?} reqs/sec", - (new_reqs - old_reqs) / (new - old) as usize - ); - }); - } - - polling_loop( - &config, - &mut online_key, - &cert_bytes, - response_counter.clone(), - ); + polling_loop(config, &mut online_key, &cert_bytes); info!("Done."); process::exit(0); } + diff --git a/src/config/environment.rs b/src/config/environment.rs new file mode 100644 index 0000000..c4d2c06 --- /dev/null +++ b/src/config/environment.rs @@ -0,0 +1,126 @@ +// Copyright 2017-2018 int08h LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate hex; + +use std::env; +use std::net::SocketAddr; +use std::time::Duration; + +use Error; +use config::ServerConfig; +use config::{DEFAULT_BATCH_SIZE, DEFAULT_STATUS_INTERVAL}; + +/// +/// Obtain a Roughenough server configuration ([ServerConfig](trait.ServerConfig.html)) +/// from environment variables. +/// +/// Config parameter | Environment Variable +/// ---------------- | -------------------- +/// port | `ROUGHENOUGH_PORT` +/// interface | `ROUGHENOUGH_INTERFACE` +/// seed | `ROUGHENOUGH_SEED` +/// batch_size | `ROUGHENOUGH_BATCH_SIZE` +/// status_interval | `ROUGHENOUGH_STATUS_INTERVAL` +/// +pub struct EnvironmentConfig { + port: u16, + interface: String, + seed: Vec<u8>, + batch_size: u8, + status_interval: Duration, +} + +const ROUGHENOUGH_PORT: &str = "ROUGHENOUGH_PORT"; +const ROUGHENOUGH_INTERFACE: &str = "ROUGHENOUGH_INTERFACE"; +const ROUGHENOUGH_SEED: &str = "ROUGHENOUGH_SEED"; +const ROUGHENOUGH_BATCH_SIZE: &str = "ROUGHENOUGH_BATCH_SIZE"; +const ROUGHENOUGH_STATUS_INTERVAL: &str = "ROUGHENOUGH_STATUS_INTERVAL"; + +impl EnvironmentConfig { + pub fn new() -> Self { + let mut cfg = EnvironmentConfig { + port: 0, + interface: "".to_string(), + seed: Vec::new(), + batch_size: DEFAULT_BATCH_SIZE, + status_interval: DEFAULT_STATUS_INTERVAL, + }; + + if let Ok(port) = env::var(ROUGHENOUGH_PORT) { + cfg.port = port + .parse() + .expect(format!("invalid port: {}", port).as_ref()); + }; + + if let Ok(interface) = env::var(ROUGHENOUGH_INTERFACE) { + cfg.interface = interface.to_string(); + }; + + if let Ok(seed) = env::var(ROUGHENOUGH_SEED) { + cfg.seed = hex::decode(&seed).expect( + format!( + "invalid seed value: {}\n'seed' should be 32 byte hex value", + seed + ).as_ref(), + ); + }; + + if let Ok(batch_size) = env::var(ROUGHENOUGH_BATCH_SIZE) { + cfg.batch_size = batch_size + .parse() + .expect(format!("invalid batch_size: {}", batch_size).as_ref()); + }; + + if let Ok(status_interval) = env::var(ROUGHENOUGH_STATUS_INTERVAL) { + let val: u16 = status_interval + .parse() + .expect(format!("invalid status_interval: {}", status_interval).as_ref()); + + cfg.status_interval = Duration::from_secs(val as u64); + }; + + cfg + } +} + +impl ServerConfig for EnvironmentConfig { + fn interface(&self) -> &str { + self.interface.as_ref() + } + + fn port(&self) -> u16 { + self.port + } + + fn seed(&self) -> &[u8] { + &self.seed + } + + fn batch_size(&self) -> u8 { + self.batch_size + } + + fn status_interval(&self) -> Duration { + self.status_interval + } + + fn socket_addr(&self) -> Result<SocketAddr, Error> { + let addr = format!("{}:{}", self.interface, self.port); + match addr.parse() { + Ok(v) => Ok(v), + Err(_) => Err(Error::InvalidConfiguration(addr)), + } + } +} diff --git a/src/config.rs b/src/config/file.rs index 669df1e..24011bb 100644 --- a/src/config.rs +++ b/src/config/file.rs @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! -//! Ways to configure the Roughenough server. -//! -//! The [ServerConfig](trait.ServerConfig.html) trait specifies the required and optional -//! parameters available for configuring a Roughenoguh server instance. -//! -//! Implementations of `ServerConfig` obtain configurations from different back-end sources. -//! - extern crate hex; use std::fs::File; @@ -28,64 +19,16 @@ use std::io::Read; use std::net::SocketAddr; use std::time::Duration; use yaml_rust::YamlLoader; -use Error; - -const DEFAULT_BATCH_SIZE: u8 = 64; -const DEFAULT_STATUS_INTERVAL: Duration = Duration::from_secs(600); -/// -/// Specifies parameters needed to configure a Roughenough server. -/// -/// Parameters labeled "**Required**" must are always be provided and have no default value. -/// Parameters labeled "**Optional**" provide default values that can be overridden. -/// -/// * **`interface`** - [Required] IP address or interface name for listening to client requests -/// * **`port`** - [Required] UDP port to listen for requests -/// * **`seed`** - [Required] A 32-byte hexadecimal value used to generate the server's long-term -/// key pair. **This is a secret value and must be un-guessable**, -/// treat it with care. -/// * **`batch_size`** - [Optional] The maximum number of requests to process in one batch. All -/// nonces in a batch are used to build a Merkle tree, the root of which -/// is signed. Default is 64 requests per batch. -/// * **`status_interval`** - [Optional] Amount of time between each logged status update. -/// Default is 600 seconds (10 minutes). -/// -/// Implementations of this trait obtain a valid configuration from different back- -/// end sources. -/// -/// See: -/// * [FileConfig](struct.FileConfig.html) -/// -pub trait ServerConfig { - /// [Required] IP address or interface name to listen for client requests - fn interface(&self) -> &str; - - /// [Required] UDP port to listen for requests - fn port(&self) -> u16; - - /// [Required] A 32-byte hexadecimal value used to generate the server's - /// long-term key pair. **This is a secret value and must be un-guessable**, - /// treat it with care. - fn seed(&self) -> &[u8]; - - /// [Optional] The maximum number of requests to process in one batch. All - /// nonces in a batch are used to build a Merkle tree, the root of which is signed. - /// Default is 64 requests per batch. - fn batch_size(&self) -> u8; - - /// [Optional] Amount of time between each logged status update. - /// Default is 600 seconds (10 minutes). - fn status_interval(&self) -> Duration; - - /// Convenience function to create a `SocketAddr` from the provided `interface` and `port` - fn socket_addr(&self) -> SocketAddr; -} +use Error; +use config::ServerConfig; +use config::{DEFAULT_BATCH_SIZE, DEFAULT_STATUS_INTERVAL}; /// -/// Read the configuration from a YAML file +/// Read a Roughenough server configuration ([ServerConfig](trait.ServerConfig.html)) +/// from a YAML file. /// -/// Example minimal config file with only the required parameters from -/// [ServerConfig](trait.ServerConfig.html): +/// Example config: /// /// ```yaml /// interface: 127.0.0.1 @@ -120,7 +63,7 @@ impl FileConfig { let mut config = FileConfig { port: 0, - interface: "unknown".to_string(), + interface: "".to_string(), seed: Vec::new(), batch_size: DEFAULT_BATCH_SIZE, status_interval: DEFAULT_STATUS_INTERVAL, @@ -142,7 +85,8 @@ impl FileConfig { } unknown => { return Err(Error::InvalidConfiguration(format!( - "unknown config key: {}", unknown + "unknown config key: {}", + unknown ))); } } @@ -173,9 +117,11 @@ impl ServerConfig for FileConfig { self.status_interval } - fn socket_addr(&self) -> SocketAddr { + fn socket_addr(&self) -> Result<SocketAddr, Error> { let addr = format!("{}:{}", self.interface, self.port); - addr.parse() - .expect(&format!("could not create socket address from {}", addr)) + match addr.parse() { + Ok(v) => Ok(v), + Err(_) => Err(Error::InvalidConfiguration(addr)), + } } } diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..0cd8742 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,90 @@ +// Copyright 2017-2018 int08h LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! +//! Ways to configure the Roughenough server. +//! +//! The [ServerConfig](trait.ServerConfig.html) trait specifies the required and optional +//! parameters available for configuring a Roughenoguh server instance. +//! +//! Implementations of `ServerConfig` obtain configurations from different back-end sources. +//! + +extern crate hex; + +use std::net::SocketAddr; +use std::time::Duration; + +mod file; +pub use self::file::FileConfig; + +mod environment; +pub use self::environment::EnvironmentConfig; + +use Error; + +/// Maximum number of requests to process in one batch and include the the Merkle tree. +pub const DEFAULT_BATCH_SIZE: u8 = 64; + +/// Amount of time between each logged status update. +pub const DEFAULT_STATUS_INTERVAL: Duration = Duration::from_secs(600); + +/// +/// Specifies parameters needed to configure a Roughenough server. +/// +/// Parameters labeled "**Required**" must are always be provided and have no default value. +/// Parameters labeled "**Optional**" provide default values that can be overridden. +/// +/// * **`interface`** - [Required] IP address or interface name for listening to client requests +/// * **`port`** - [Required] UDP port to listen for requests +/// * **`seed`** - [Required] A 32-byte hexadecimal value used to generate the server's long-term +/// key pair. **This is a secret value and must be un-guessable**, +/// treat it with care. +/// * **`batch_size`** - [Optional] The maximum number of requests to process in one batch. All +/// nonces in a batch are used to build a Merkle tree, the root of which +/// is signed. +/// Defaults to [DEFAULT_BATCH_SIZE](constant.DEFAULT_BATCH_SIZE.html) +/// * **`status_interval`** - [Optional] Amount of time between each logged status update. +/// Defaults to [DEFAULT_STATUS_INTERVAL](constant.DEFAULT_STATUS_INTERVAL.html) +/// +/// Implementations of this trait obtain a valid configuration from different back- +/// end sources. +/// +/// See: +/// * [FileConfig](struct.FileConfig.html) +/// +pub trait ServerConfig { + /// [Required] IP address or interface name to listen for client requests + fn interface(&self) -> &str; + + /// [Required] UDP port to listen for requests + fn port(&self) -> u16; + + /// [Required] A 32-byte hexadecimal value used to generate the server's + /// long-term key pair. **This is a secret value and must be un-guessable**, + /// treat it with care. + fn seed(&self) -> &[u8]; + + /// [Optional] The maximum number of requests to process in one batch. All + /// nonces in a batch are used to build a Merkle tree, the root of which is signed. + /// Defaults to [DEFAULT_BATCH_SIZE](constant.DEFAULT_BATCH_SIZE.html) + fn batch_size(&self) -> u8; + + /// [Optional] Amount of time between each logged status update. + /// Defaults to [DEFAULT_STATUS_INTERVAL](constant.DEFAULT_STATUS_INTERVAL.html) + fn status_interval(&self) -> Duration; + + /// Convenience function to create a `SocketAddr` from the provided `interface` and `port` + fn socket_addr(&self) -> Result<SocketAddr, Error>; +} diff --git a/src/error.rs b/src/error.rs index b681f33..49102a4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -58,3 +58,5 @@ impl From<std::io::Error> for Error { Error::EncodingFailure(err) } } + + @@ -58,6 +58,7 @@ mod message; mod tag; pub mod config; + pub mod keys; pub mod merkle; pub mod sign; |