diff --git a/doc/sheldond.conf b/doc/sheldond.conf new file mode 100644 index 0000000..87610d1 --- /dev/null +++ b/doc/sheldond.conf @@ -0,0 +1,17 @@ +# no virtual host support yet, so this is actually the only host +default_host = localhost + +# should be self explanatory +gem_root = /var/gemini/ + +# you can define as many of these as you like +listen = [::1]:1965 +listen = 127.0.0.1:1965 + +# privilige level for the server to drop to after initializing +user = gem-data +group = gem-data + +# certificate data MUST be in PEM format right now +cert_key = /etc/ssl/private/gemini-key.pem +cert_chain = /etc/ssl/certs/gemini-chain.pem diff --git a/src/main.rs b/src/main.rs index 7f64e33..5639158 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ mod mime; mod server; use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; fn help() { let version = match option_env!("CARGO_PKG_VERSION") { @@ -9,69 +12,73 @@ fn help() { }; println!("usage: sheldond {}", version); - println!(" -h, --help\t\tdisplay this message"); - println!(" -l, --listen\t\tadd a listening address (you can define multiple)"); - println!(" -d, --default-host\tdefault hostname to listen for"); - println!(" -g, --gem-root\tpath to the gemini root, aka the folder to serve files from"); - println!(" --user\tuser to drop to after opening TLS socket"); - println!(" --group\tgroup to drop to after opening TLS socket"); + println!(" -c, --config\t\tpath to the configuration file"); } -fn parse_args() -> Option { - let mut has_addr = false; - let mut has_host = false; - let mut has_root = false; - let mut has_user = false; - let mut has_group = false; - - let mut config = server::ServerConfig::new(); +fn parse_args() -> Option { let mut args = env::args(); loop { match args.next() { Some(arg) => { - if arg == "-h" || arg == "--help" { - return None; - } - if arg == "-l" || arg == "--listen" { - let addr = args.next().unwrap(); - config.add_addr(addr); - has_addr = true; - } - if arg == "-d" || arg == "--default-host" { - let host = args.next().unwrap(); - config.set_default_host(host); - has_host = true; - } - if arg == "-g" || arg == "--gem-root" { - let gem_root = args.next().unwrap(); - config.set_gem_root(gem_root); - has_root = true; - } - if arg == "--user" { - let user = args.next().unwrap(); - config.set_user(user); - has_user = true; - } - if arg == "--group" { - let group = args.next().unwrap(); - config.set_group(group); - has_group = true; + if arg == "-c" || arg == "--config" { + let config_fname = args.next().unwrap(); + return Some(config_fname); } } None => break, } } - if !has_addr || !has_host || !has_root || !has_user || !has_group { - return None; + None +} + +fn parse_config(fname: String) -> server::ServerConfig { + let path = Path::new(&fname); + let mut config = server::ServerConfig::new(); + + let file = match File::open(path) { + Ok(file) => BufReader::new(file), + Err(e) => { + panic!(e); + } + }; + + for rline in file.lines() { + let line = rline.unwrap(); + if line == "" || line.starts_with("#") { + continue; + } + + let mut parts = line.split_whitespace(); + + let key = parts.next().unwrap(); + + if parts.next().unwrap() != "=" { + panic!("malformatted line in config: {}", line); + } + + let val = parts.next().unwrap().to_string(); + + match key { + "default_host" => config.set_default_host(val), + "gem_root" => config.set_gem_root(val), + "listen" => config.add_addr(val), + "user" => config.set_user(val), + "group" => config.set_group(val), + "cert_key" => config.set_cert_key(val), + "cert_chain" => config.set_cert_chain(val), + _ => { + panic!("unknown key in config: {}", key); + } + } } - Some(config) + return config; } fn main() { let config = match parse_args() { - Some(config) => config, + Some(config_fname) => parse_config(config_fname), None => { help(); return; diff --git a/src/server/mod.rs b/src/server/mod.rs index 643c15b..8359c32 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -17,6 +17,8 @@ pub struct ServerConfig { addrs: Vec, user: unistd::Uid, group: unistd::Gid, + cert_key: String, + cert_chain: String, } impl ServerConfig { @@ -27,6 +29,8 @@ impl ServerConfig { addrs: Vec::new(), user: unistd::getuid(), group: unistd::getgid(), + cert_key: "".to_string(), + cert_chain: "".to_string(), } } @@ -53,17 +57,31 @@ impl ServerConfig { pub fn set_user(&mut self, uname: String) { self.user = match unistd::User::from_name(&uname) { - Ok(user) => user.unwrap().uid, + Ok(user) => match user { + Some(user) => user.uid, + None => panic!("unknown user {}", uname), + }, Err(e) => panic!(e), }; } pub fn set_group(&mut self, gname: String) { self.group = match unistd::Group::from_name(&gname) { - Ok(group) => group.unwrap().gid, + Ok(group) => match group { + Some(group) => group.gid, + None => panic!("unknown group {}", gname), + }, Err(e) => panic!(e), }; } + + pub fn set_cert_key(&mut self, fname: String) { + self.cert_key = fname; + } + + pub fn set_cert_chain(&mut self, fname: String) { + self.cert_chain = fname; + } } pub struct Server { @@ -77,18 +95,20 @@ impl Server { } } - fn build_acceptor() -> std::sync::Arc { + fn build_acceptor(config: &ServerConfig) -> std::sync::Arc { let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap(); acceptor - .set_private_key_file("doc/key.pem", SslFiletype::PEM) + .set_private_key_file(config.cert_key.as_str(), SslFiletype::PEM) + .unwrap(); + acceptor + .set_certificate_chain_file(config.cert_chain.as_str()) .unwrap(); - acceptor.set_certificate_chain_file("doc/cert.pem").unwrap(); acceptor.check_private_key().unwrap(); return Arc::new(acceptor.build()); } pub fn serve(&self) { - let acceptor = Server::build_acceptor(); + let acceptor = Server::build_acceptor(&self.config); let listener = TcpListener::bind(&self.config.addrs[..]).unwrap(); if self.config.user.is_root() {