summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.rs72
-rw-r--r--src/server/handler.rs73
-rw-r--r--src/server/mod.rs76
-rw-r--r--src/server/response.rs23
4 files changed, 175 insertions, 69 deletions
diff --git a/src/main.rs b/src/main.rs
index 9d1fdcf..a381e28 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,18 +1,68 @@
mod server;
use std::env;
-use std::path::Path;
-
-fn read_config() -> server::ServerConfig {
- let mut config = server::ServerConfig::new(
- "klockenschooster.de".to_string(),
- "/home/jw/code/projects/sheldond/doc".to_string(),
- );
- config.add_addr("127.0.0.1:1965".to_string());
- config.add_addr("[::1]:1965".to_string());
- return config;
+
+fn help() {
+ let version = match option_env!("CARGO_PKG_VERSION") {
+ Some(v) => v,
+ None => "",
+ };
+
+ 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");
+}
+
+fn parse_args() -> Option<server::ServerConfig> {
+ let mut has_addr = false;
+ let mut has_host = false;
+ let mut has_root = false;
+
+ let mut config = server::ServerConfig::new();
+ 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;
+ }
+ }
+ None => break,
+ }
+ }
+
+ if !has_addr || !has_host || !has_root {
+ return None;
+ }
+
+ Some(config)
}
fn main() {
- let server = server::Server::new(&read_config());
+ let config = match parse_args() {
+ Some(config) => config,
+ None => {
+ help();
+ return;
+ }
+ };
+
+ let server = server::Server::new(&config);
server.serve();
}
diff --git a/src/server/handler.rs b/src/server/handler.rs
index 4a593e8..d27d292 100644
--- a/src/server/handler.rs
+++ b/src/server/handler.rs
@@ -1,32 +1,89 @@
use crate::server::response;
use crate::server::ServerConfig;
-use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream};
+use openssl::ssl::SslStream;
use std::fs::File;
use std::io::{copy, BufReader, BufWriter};
use std::net::TcpStream;
use std::path::Path;
use url::Url;
-pub fn handle(config: &ServerConfig, url: Url, stream: &mut SslStream<TcpStream>) {
+fn send_header(stream: &mut SslStream<TcpStream>, header: &response::Header) {
+ match stream.ssl_write(&header.to_vec()) {
+ Ok(_s) => {
+ return;
+ }
+ Err(_e) => {
+ return;
+ }
+ };
+}
+
+pub fn handle_request(config: &ServerConfig, mut stream: SslStream<TcpStream>) {
+ let mut buffer = [0; 1026];
+ match stream.ssl_read(&mut buffer) {
+ Ok(s) => {
+ if s == 0 {
+ send_header(&mut stream, &response::bad_request());
+ return;
+ }
+ }
+ Err(_) => {
+ send_header(&mut stream, &response::bad_request());
+ return;
+ }
+ };
+
+ let request = match String::from_utf8(buffer.to_vec()) {
+ Ok(request) => request,
+ Err(_) => {
+ send_header(&mut stream, &response::bad_request());
+ return;
+ }
+ };
+
+ let location = match Url::parse(&request) {
+ Ok(url) => url,
+ Err(_) => config.default_host.join(&request).unwrap(),
+ };
+
+ handle_response(config, location, &mut stream);
+}
+
+fn handle_response(config: &ServerConfig, url: Url, mut stream: &mut SslStream<TcpStream>) {
if url.scheme() != "gemini" {
- stream.ssl_write(&response::invalid_protocol().to_vec());
+ send_header(&mut stream, &response::permanent_failure());
return;
}
- let rel_path = Path::new(url.path()).strip_prefix("/").unwrap();
- let path = config.www_root.join(rel_path);
+ if url.host() != config.default_host.host() {
+ send_header(&mut stream, &response::proxy_request_refused());
+ return;
+ }
+
+ let rel_path = match Path::new(url.path()).strip_prefix("/") {
+ Ok(path) => path,
+ Err(_) => {
+ send_header(&mut stream, &response::bad_request());
+ return;
+ }
+ };
+ let path = config.gem_root.join(rel_path);
let file = match File::open(&path) {
Ok(file) => file,
Err(_) => {
- stream.ssl_write(&response::not_found().to_vec());
+ send_header(&mut stream, &response::not_found());
return;
}
};
- stream.ssl_write(&response::Header::new(response::Status::Success, "text/gemini").to_vec());
+ let header = response::Header::new(response::Status::Success, "text/gemini");
+ send_header(&mut stream, &header);
let mut buf_file = BufReader::new(file);
let mut buf_stream = BufWriter::new(stream);
- copy(&mut buf_file, &mut buf_stream);
+ match copy(&mut buf_file, &mut buf_stream) {
+ Ok(_s) => {}
+ Err(_e) => {}
+ }
}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 545fbeb..696a3a5 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -1,7 +1,6 @@
-use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream};
-use std::collections::HashMap;
-use std::net::{SocketAddr, TcpListener, TcpStream};
-use std::path::{Path, PathBuf};
+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;
@@ -13,24 +12,38 @@ pub mod response;
#[derive(Clone)]
pub struct ServerConfig {
default_host: Url,
- www_root: PathBuf,
+ gem_root: PathBuf,
addrs: Vec<SocketAddr>,
}
impl ServerConfig {
- pub fn new(default_host: String, www_root: String) -> ServerConfig {
- let mut url = Url::parse("gemini://default").unwrap();
- url.set_host(Some(default_host.as_str()));
-
+ pub fn new() -> ServerConfig {
ServerConfig {
- default_host: url,
- www_root: PathBuf::from(www_root),
+ default_host: Url::parse("gemini://localhost").unwrap(),
+ gem_root: PathBuf::from(""),
addrs: Vec::new(),
}
}
+ 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(addr.parse().unwrap());
+ self.addrs.push(match addr.parse() {
+ Ok(addr) => addr,
+ Err(e) => panic!(e),
+ });
}
}
@@ -42,11 +55,21 @@ pub struct Server {
impl Server {
pub fn new(config: &ServerConfig) -> Server {
Server {
- acceptor: build_acceptor(),
+ acceptor: Server::build_acceptor(),
config: config.clone(),
}
}
+ fn build_acceptor() -> std::sync::Arc<SslAcceptor> {
+ let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap();
+ acceptor
+ .set_private_key_file("doc/key.pem", SslFiletype::PEM)
+ .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 listener = TcpListener::bind(&self.config.addrs[..]).unwrap();
@@ -57,34 +80,11 @@ impl Server {
let config = self.config.clone();
thread::spawn(move || {
let stream = acceptor.accept(stream).unwrap();
- handle_client(&config, stream);
+ handler::handle_request(&config, stream);
});
}
- Err(e) => { /* connection failed */ }
+ Err(_) => { /* connection failed */ }
}
}
}
}
-
-fn handle_client(config: &ServerConfig, mut stream: SslStream<TcpStream>) {
- let mut buffer = [0; 1026];
- stream.ssl_read(&mut buffer);
- let request = String::from_utf8(buffer.to_vec()).unwrap();
-
- let location = match Url::parse(&request) {
- Ok(url) => url,
- Err(e) => config.default_host.join(&request).unwrap(),
- };
-
- handler::handle(config, location, &mut stream);
-}
-
-fn build_acceptor() -> std::sync::Arc<SslAcceptor> {
- let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap();
- acceptor
- .set_private_key_file("doc/key.pem", SslFiletype::PEM)
- .unwrap();
- acceptor.set_certificate_chain_file("doc/cert.pem").unwrap();
- acceptor.check_private_key().unwrap();
- return Arc::new(acceptor.build());
-}
diff --git a/src/server/response.rs b/src/server/response.rs
index 8b087ba..913d881 100644
--- a/src/server/response.rs
+++ b/src/server/response.rs
@@ -2,12 +2,11 @@ use std::vec::Vec;
#[derive(Copy, Clone)]
pub enum Status {
- Input = 10,
Success = 20,
- Redirect = 30,
- TemporaryFailure = 40,
PermanentFailure = 50,
- ClientCertificateRequired = 60,
+ NotFound = 51,
+ ProxyRequestRefused = 53,
+ BadRequest = 59,
}
pub struct Header {
@@ -32,18 +31,18 @@ impl Header {
}
}
-pub fn invalid_protocol() -> Header {
- Header::new(Status::PermanentFailure, "this protocol is not supported")
+pub fn permanent_failure() -> Header {
+ Header::new(Status::PermanentFailure, "permanent failure")
}
-pub fn not_understood() -> Header {
- Header::new(Status::PermanentFailure, "request not understood")
+pub fn not_found() -> Header {
+ Header::new(Status::NotFound, "not found")
}
-pub fn not_found() -> Header {
- Header::new(Status::PermanentFailure, "resource not found")
+pub fn proxy_request_refused() -> Header {
+ Header::new(Status::ProxyRequestRefused, "proxy request refused")
}
-pub fn internal_error() -> Header {
- Header::new(Status::PermanentFailure, "internal server error")
+pub fn bad_request() -> Header {
+ Header::new(Status::BadRequest, "bad request")
}