From 4056cbbc2f40f3bf98c5b1f1d560998b8df3d30f Mon Sep 17 00:00:00 2001 From: int 80h Date: Tue, 21 Apr 2020 19:40:57 -0400 Subject: [PATCH] Url to path doesn't work for dir list --- src/config.rs | 15 +++---- src/main.rs | 119 +++++++++++++++++++++++++++++++++----------------- src/tls.rs | 63 +++++++++++++------------- 3 files changed, 116 insertions(+), 81 deletions(-) diff --git a/src/config.rs b/src/config.rs index 55b085b..1adab8d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,22 +1,21 @@ extern crate serde_derive; extern crate toml; -use toml::de::Error; use std::collections::HashMap; +use toml::de::Error; // use serde_derive::Deserialize; - -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct Config { pub port: i32, pub host: String, pub server: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct Server { pub url: String, - pub dir: String, - pub key: String, + pub dir: String, + pub key: String, pub cert: String, } @@ -26,13 +25,11 @@ impl Config { let config: Config = toml::from_str(&fd).unwrap(); return config; } - pub fn to_map( &self /*cfg: &config::Config*/) -> HashMap { + pub fn to_map(&self /*cfg: &config::Config*/) -> HashMap { let mut map = HashMap::new(); for srv in &self.server { map.insert(srv.url.clone(), srv.dir.clone()); } map } - } - diff --git a/src/main.rs b/src/main.rs index 6860be2..8d422c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,24 +2,25 @@ #[macro_use] extern crate serde_derive; -use std::fs::File; -use std::sync::Arc; -use std::net::ToSocketAddrs; -use std::io::{ self, BufReader }; -use std::collections::HashMap; use futures_util::future::TryFutureExt; +use std::collections::HashMap; +use std::error::Error; +use std::fs::File; +use std::fs; +use std::io::{self, BufReader}; +use std::net::ToSocketAddrs; +use std::sync::Arc; +use std::path::{Path, PathBuf}; +use tokio::io::AsyncWriteExt; +use tokio::net::TcpListener; +use tokio::net::TcpStream; use tokio::prelude::*; use tokio::runtime; -use tokio::net::TcpListener; -use tokio::io::AsyncWriteExt; -use tokio_rustls::rustls::{ Certificate, NoClientAuth, PrivateKey, ServerConfig }; -use tokio_rustls::rustls::internal::pemfile::{ certs, pkcs8_private_keys }; -use tokio_rustls::TlsAcceptor; +use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; +use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; use tokio_rustls::server::TlsStream; -use tokio::net::TcpStream; +use tokio_rustls::TlsAcceptor; use url::Url; -use std::error::Error; - mod config; mod tls; @@ -27,54 +28,93 @@ mod tls; fn get_tls_config(cfg: config::Config) -> rustls::ServerConfig { let store = rustls::RootCertStore::empty(); let verifier = rustls::AllowAnyAnonymousOrAuthenticatedClient::new(store); - let mut tls_config = rustls::ServerConfig::new( verifier /*NoClientAuth::new()*/); + let mut tls_config = rustls::ServerConfig::new(verifier); tls_config.cert_resolver = Arc::new(tls::CertResolver::from_config(cfg).unwrap()); tls_config } -fn get_content(request: String) -> String { +fn get_content(mut path: PathBuf, u: url::Url) -> Result { + println!("URL: {}", u); + if u.path() == "" || u.path() == "/" { + path.push("index.gemini"); + } else { + path.push(u.path().trim_start_matches("/")); + } - let url = Url::parse(&request).unwrap(); - - let path = match url.path() { - "/" => String::from("index.gemini"), - path => str::replace(path, "/", ""), - }; - - let fd = std::fs::read_to_string(path) - .expect("Unable to read file"); - - fd + if !u.path().ends_with("/") { + u.path().to_string().push('/'); + } + println!("{}", path.to_str().unwrap()); + + if !path.exists() { + return Ok("51 Not found!\r\n".to_string()) + } + let meta = fs::metadata(&path).expect("Unable to read metadata"); + if meta.is_file() { + return Ok(std::fs::read_to_string(path).expect("Unable to read file")); + } + let mut list = String::from("# Directory Listing\n\n"); + if !u.path().ends_with("/") { + path.push(format!("{}/", u.path().trim_start_matches("/"))); + } + for file in fs::read_dir(path)? { + if let Ok(file) = file { + let f = file.file_name().to_str().unwrap().to_owned(); + let p = u.join(&f).unwrap().as_str().to_owned(); + println!("=>\t{} {}\r\n", p, f); + list.push_str(format!("=> {} {}\n", p, f).as_str()); + } + } + return Ok(list); } -async fn handle_connection(mut stream: TlsStream) -> Result<(), Box> { - let mut buffer = [0;512]; +async fn handle_connection( + mut stream: TlsStream, + cfg: config::Config, +) -> Result<(), io::Error> { + + let mut buffer = [0; 512]; stream.read(&mut buffer).await?; let request = String::from_utf8_lossy(&buffer[..]).to_owned(); println!("Request: {}", request); - /* + + let url = Url::parse(&request).unwrap(); - for name in cfg.server { - if url.host_str() == Some(&name.url) { - println!("serving url: {:?}", url.host_str()); + + if url.scheme() != "gemini" { + stream.write_all(&b"53\tnot gemini scheme!\r\n"[..]).await?; + stream.flush().await?; + panic!("Not gemini scheme"); + } + + if url.path().to_string().contains("..") { + stream.write_all(&b"50\tNot in path\r\n"[..]).await?; + stream.flush().await?; + panic!("Contains .."); + } + + let mut dir = String::new(); + for server in cfg.server { + if Some(server.url.as_str()) == url.host_str() { + dir = server.dir; } } - */ + + let p = PathBuf::from(dir); + stream.write_all(&b"20\ttext/gemini\r\n"[..]).await?; stream.flush().await?; - let content = get_content(request.to_string()); + let content = get_content(p, url)?; stream.write_all(content.as_bytes()).await?; stream.flush().await?; Ok(()) } - fn main() -> io::Result<()> { let cfg = config::Config::new("config.toml"); - let srv_map = cfg.to_map(); let addr = format!("{}:{}", cfg.host, cfg.port); addr.to_socket_addrs()? @@ -85,21 +125,22 @@ fn main() -> io::Result<()> { .threaded_scheduler() .enable_io() .build()?; + let handle = runtime.handle().clone(); - let config = get_tls_config(cfg); + let config = get_tls_config(cfg.clone()); let acceptor = TlsAcceptor::from(Arc::new(config)); let fut = async { let mut listener = TcpListener::bind(&addr).await?; - loop { let (stream, peer_addr) = listener.accept().await?; let acceptor = acceptor.clone(); + let cfg = cfg.clone(); let fut = async move { let stream = acceptor.accept(stream).await?; - handle_connection(stream).await; + handle_connection(stream, cfg).await?; println!("Hello: {}", peer_addr); Ok(()) as io::Result<()> diff --git a/src/tls.rs b/src/tls.rs index 7314e2f..814f55f 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,15 +1,15 @@ -use std::fs::File; -use std::io::{ self, BufReader }; -use std::error::Error; use std::collections::HashMap; +use std::error::Error; +use std::fs::File; +use std::io::{self, BufReader}; use std::sync::Arc; -use rustls::{ResolvesServerCert, SignatureScheme}; -use rustls::ClientHello; use rustls::sign::CertifiedKey; -use rustls::sign::{Signer, SigningKey, RSASigningKey}; -use tokio_rustls::rustls::{ Certificate, NoClientAuth, PrivateKey, ServerConfig }; -use tokio_rustls::rustls::internal::pemfile::{ certs, pkcs8_private_keys }; +use rustls::sign::{RSASigningKey, Signer, SigningKey}; +use rustls::ClientHello; +use rustls::{ResolvesServerCert, SignatureScheme}; +use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; +use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; use crate::config; @@ -19,56 +19,53 @@ pub fn load_certs(path: &String) -> io::Result> { } pub fn load_key(path: &String) -> PrivateKey { - let keyfile = File::open(path) - .expect("cannot open private key file"); + let keyfile = File::open(path).expect("cannot open private key file"); let mut reader = BufReader::new(keyfile); - let key = pkcs8_private_keys(&mut reader) - .expect("file contains invalid rsa private key"); + let key = pkcs8_private_keys(&mut reader).expect("file contains invalid rsa private key"); return key[0].clone(); } pub struct CertResolver { - map: HashMap> + map: HashMap>, } impl CertResolver { - pub fn from_config(cfg: config::Config) -> Result { + pub fn from_config(cfg: config::Config) -> Result { let mut map = HashMap::new(); for server in cfg.server.iter() { - let key = load_key(&server.key); - // .chain_err(|| format!("Failed to load private key from {}", https.key_file))?; - let certs = load_certs(&server.cert).unwrap(); - // .chain_err(|| format!("Failed to load certificate from {}", server.cert)); - /* - let signer: Arc> = Arc::new(Box::new( - CertifiedKey::new(&key).map_err(|_| format!("Failed to create signer for {}", server.url)) - )); - */ - let signing_key = RSASigningKey::new( - &key).unwrap(); + let key = load_key(&server.key); + // .chain_err(|| format!("Failed to load private key from {}", https.key_file))?; + let certs = load_certs(&server.cert).unwrap(); + // .chain_err(|| format!("Failed to load certificate from {}", server.cert)); + /* + let signer: Arc> = Arc::new(Box::new( + CertifiedKey::new(&key).map_err(|_| format!("Failed to create signer for {}", server.url)) + )); + */ + let signing_key = RSASigningKey::new(&key).unwrap(); - let signing_key_boxed: Arc> = Arc::new( - Box::new(signing_key)); - map.insert(server.url.clone(), Box::new(rustls::sign::CertifiedKey::new( - certs, signing_key_boxed))); + let signing_key_boxed: Arc> = Arc::new(Box::new(signing_key)); + map.insert( + server.url.clone(), + Box::new(rustls::sign::CertifiedKey::new(certs, signing_key_boxed)), + ); } println!("Successfully loaded {} TLS configurations", map.len()); - Ok(CertResolver{ map }) + Ok(CertResolver { map }) } } impl ResolvesServerCert for CertResolver { - fn resolve( &self, client_hello: ClientHello) -> Option { + fn resolve(&self, client_hello: ClientHello) -> Option { if let Some(url) = client_hello.server_name() { if let Some(cert) = self.map.get(url.into()) { - return Some(*cert.clone()) + return Some(*cert.clone()); } } None } } -