use nix::unistd; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use std::net::{SocketAddr, TcpListener}; use std::path::PathBuf; use std::sync::Arc; use std::thread; use std::vec::Vec; use url::Url; pub mod handler; pub mod response; #[derive(Clone)] pub struct ServerConfig { default_host: Url, gem_root: PathBuf, addrs: Vec, user: unistd::Uid, group: unistd::Gid, cert_key: String, cert_chain: String, } impl ServerConfig { pub fn new() -> ServerConfig { ServerConfig { default_host: Url::parse("gemini://localhost").unwrap(), gem_root: PathBuf::from(""), addrs: Vec::new(), user: unistd::getuid(), group: unistd::getgid(), cert_key: "".to_string(), cert_chain: "".to_string(), } } pub fn set_default_host(&mut self, default_host: String) { let mut url = Url::parse("gemini://default").unwrap(); match url.set_host(Some(default_host.as_str())) { Ok(_) => {} Err(e) => panic!(e), }; self.default_host = url; } pub fn set_gem_root(&mut self, gem_root: String) { self.gem_root = PathBuf::from(gem_root); } pub fn add_addr(&mut self, addr: String) { self.addrs.push(match addr.parse() { Ok(addr) => addr, Err(e) => panic!(e), }); } pub fn set_user(&mut self, uname: String) { self.user = match unistd::User::from_name(&uname) { 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) => 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 { config: ServerConfig, } impl Server { pub fn new(config: &ServerConfig) -> Server { Server { config: config.clone(), } } fn build_acceptor(config: &ServerConfig) -> std::sync::Arc { let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap(); acceptor .set_private_key_file(config.cert_key.as_str(), SslFiletype::PEM) .unwrap(); acceptor .set_certificate_chain_file(config.cert_chain.as_str()) .unwrap(); acceptor.check_private_key().unwrap(); return Arc::new(acceptor.build()); } pub fn serve(&self) { let acceptor = Server::build_acceptor(&self.config); let listener = TcpListener::bind(&self.config.addrs[..]).unwrap(); if self.config.user.is_root() { panic!("refusing to run as root"); } match unistd::setgid(self.config.group) { Ok(_) => {} Err(e) => { panic!(e); } }; match unistd::setuid(self.config.user) { Ok(_) => {} Err(e) => { panic!(e); } }; for stream in listener.incoming() { match stream { Ok(stream) => { let acceptor = acceptor.clone(); let config = self.config.clone(); thread::spawn(move || { let stream = acceptor.accept(stream).unwrap(); handler::handle_request(&config, stream); }); } Err(_) => { /* connection failed */ } } } } }