summaryrefslogtreecommitdiff
path: root/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/config')
-rw-r--r--src/config/environment.rs126
-rw-r--r--src/config/file.rs127
-rw-r--r--src/config/mod.rs90
3 files changed, 343 insertions, 0 deletions
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/file.rs b/src/config/file.rs
new file mode 100644
index 0000000..24011bb
--- /dev/null
+++ b/src/config/file.rs
@@ -0,0 +1,127 @@
+// 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::fs::File;
+use std::io::Read;
+use std::net::SocketAddr;
+use std::time::Duration;
+use yaml_rust::YamlLoader;
+
+use Error;
+use config::ServerConfig;
+use config::{DEFAULT_BATCH_SIZE, DEFAULT_STATUS_INTERVAL};
+
+///
+/// Read a Roughenough server configuration ([ServerConfig](trait.ServerConfig.html))
+/// from a YAML file.
+///
+/// Example config:
+///
+/// ```yaml
+/// interface: 127.0.0.1
+/// port: 8686
+/// seed: f61075c988feb9cb700a4a6a3291bfbc9cab11b9c9eca8c802468eb38a43d7d3
+/// ```
+///
+pub struct FileConfig {
+ port: u16,
+ interface: String,
+ seed: Vec<u8>,
+ batch_size: u8,
+ status_interval: Duration,
+}
+
+impl FileConfig {
+ pub fn from_file(config_file: &str) -> Result<Self, Error> {
+ let mut infile = File::open(config_file).expect("failed to open config file");
+
+ let mut contents = String::new();
+ infile
+ .read_to_string(&mut contents)
+ .expect("could not read config file");
+
+ let cfg = YamlLoader::load_from_str(&contents).expect("could not parse config file");
+
+ if cfg.len() != 1 {
+ return Err(Error::InvalidConfiguration(
+ "Empty or malformed config file".to_string(),
+ ));
+ }
+
+ let mut config = FileConfig {
+ port: 0,
+ interface: "".to_string(),
+ seed: Vec::new(),
+ batch_size: DEFAULT_BATCH_SIZE,
+ status_interval: DEFAULT_STATUS_INTERVAL,
+ };
+
+ for (key, value) in cfg[0].as_hash().unwrap() {
+ match key.as_str().unwrap() {
+ "port" => config.port = value.as_i64().unwrap() as u16,
+ "interface" => config.interface = value.as_str().unwrap().to_string(),
+ "batch_size" => config.batch_size = value.as_i64().unwrap() as u8,
+ "seed" => {
+ let val = value.as_str().unwrap().to_string();
+ config.seed = hex::decode(val)
+ .expect("seed value invalid; 'seed' should be 32 byte hex value");
+ }
+ "status_interval" => {
+ let val = value.as_i64().expect("status_interval value invalid");
+ config.status_interval = Duration::from_secs(val as u64)
+ }
+ unknown => {
+ return Err(Error::InvalidConfiguration(format!(
+ "unknown config key: {}",
+ unknown
+ )));
+ }
+ }
+ }
+
+ Ok(config)
+ }
+}
+
+impl ServerConfig for FileConfig {
+ 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/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>;
+}