From 713cadaaa0db29a2819efb961afa41d67757be8c Mon Sep 17 00:00:00 2001 From: altugbakan Date: Mon, 6 Mar 2023 06:40:06 +0300 Subject: [PATCH] Add server config --- .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 10 +++ src/config.rs | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/main.rs | 14 ++++ 6 files changed, 211 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/config.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e84a412 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "tftpd" +version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7f1a563 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "tftpd" +version = "0.0.0" +authors = ["Altuğ Bakan "] +edition = "2021" +description = "TFTP Server Daemon implemented in Rust" +repository = "https://github.com/altugbakan/rs-tftpd" +license = "MIT" +keywords = ["tftp", "server"] +categories = ["command-line-utilities"] \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..586917d --- /dev/null +++ b/src/config.rs @@ -0,0 +1,177 @@ +use std::error::Error; +use std::net::{AddrParseError, Ipv4Addr}; +use std::num::ParseIntError; +use std::path::{Path, PathBuf}; +use std::{env, fmt, process}; + +pub struct Config { + pub ip_address: Ipv4Addr, + pub port: u16, + pub directory: PathBuf, +} + +impl Config { + pub fn new(mut args: T) -> Result + where + T: Iterator, + { + let mut config = Config { + ip_address: Ipv4Addr::new(127, 0, 0, 1), + port: 69, + directory: env::current_dir().unwrap_or(env::temp_dir()), + }; + + args.next(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "-i" | "--ip-address" => { + if let Some(ip_str) = args.next() { + config.ip_address = ip_str.parse::()?; + } else { + return Err("missing ip address after flag".into()); + } + } + "-p" | "--port" => { + if let Some(port_str) = args.next() { + config.port = port_str.parse::()?; + } else { + return Err("missing port number after flag".into()); + } + } + "-d" | "--directory" => { + if let Some(dir_str) = args.next() { + if !Path::new(&dir_str).exists() { + return Err(format!("{} does not exist", dir_str).into()); + } + config.directory = PathBuf::from(dir_str); + } else { + return Err("missing directory after flag".into()); + } + } + "-h" | "--help" => { + println!("TFTP Server Daemon\n"); + println!("Usage: tftpd [OPTIONS]\n"); + println!("Options:"); + println!(" -i, --ip-address \tSet the ip address of the server (Default: 127.0.0.1)"); + println!( + " -p, --port \t\tSet the listening port of the server (Default: )" + ); + println!(" -d, --directory \tSet the listening port of the server (Default: )"); + println!(" -h, --help\t\t\tPrint help information"); + process::exit(0); + } + invalid => return Err(format!("invalid flag: {}", invalid).into()), + } + } + + Ok(config) + } +} + +#[derive(Debug)] +pub struct ConfigError { + description: String, +} + +impl Error for ConfigError { + fn description(&self) -> &str { + self.description.as_str() + } +} + +impl fmt::Display for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.description) + } +} + +impl From for ConfigError { + fn from(value: AddrParseError) -> Self { + ConfigError { + description: value.to_string(), + } + } +} + +impl From for ConfigError { + fn from(value: ParseIntError) -> Self { + ConfigError { + description: value.to_string(), + } + } +} + +impl From for ConfigError { + fn from(value: String) -> Self { + ConfigError { description: value } + } +} + +impl From<&str> for ConfigError { + fn from(value: &str) -> Self { + ConfigError { + description: value.to_string(), + } + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + + #[test] + fn parses_full_config() { + let config = Config::new( + vec!["/", "-i", "0.0.0.0", "-p", "1234", "-d", "/"] + .iter() + .map(|s| s.to_string()), + ) + .unwrap(); + + assert_eq!(config.ip_address, Ipv4Addr::new(0, 0, 0, 0)); + assert_eq!(config.port, 1234); + assert_eq!(config.directory, PathBuf::from_str("/").unwrap()); + } + + #[test] + fn parses_some_config() { + let config = Config::new( + vec!["/", "-i", "0.0.0.0", "-d", "/"] + .iter() + .map(|s| s.to_string()), + ) + .unwrap(); + + assert_eq!(config.ip_address, Ipv4Addr::new(0, 0, 0, 0)); + assert_eq!(config.port, 69); + assert_eq!(config.directory, PathBuf::from_str("/").unwrap()); + } + + #[test] + fn returns_error_on_invalid_ip() { + assert!(Config::new( + vec!["/", "-i", "1234.5678.9012.3456"] + .iter() + .map(|s| s.to_string()), + ) + .is_err()); + } + + #[test] + fn returns_error_on_invalid_port() { + assert!(Config::new(vec!["/", "-p", "1234567"].iter().map(|s| s.to_string()),).is_err()); + } + + #[test] + fn returns_error_on_invalid_directory() { + assert!(Config::new( + vec!["/", "-d", "/this/does/not/exist"] + .iter() + .map(|s| s.to_string()), + ) + .is_err()); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..86ef2b1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod config; +pub use config::Config; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..533ce17 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,14 @@ +use std::{env, process}; +use tftpd::Config; + +fn main() { + let config = Config::new(env::args()).unwrap_or_else(|err| { + eprintln!("Problem parsing arguments: {}", err); + process::exit(1) + }); + + println!( + "Running TFTP Server on {}:{}", + config.ip_address, config.port + ); +}