Url to path doesn't work for dir list

This commit is contained in:
int 80h
2020-04-21 19:40:57 -04:00
parent ef003c3eee
commit 4056cbbc2f
3 changed files with 116 additions and 81 deletions

View File

@@ -1,18 +1,17 @@
extern crate serde_derive; extern crate serde_derive;
extern crate toml; extern crate toml;
use toml::de::Error;
use std::collections::HashMap; use std::collections::HashMap;
use toml::de::Error;
// use serde_derive::Deserialize; // use serde_derive::Deserialize;
#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize)]
pub struct Config { pub struct Config {
pub port: i32, pub port: i32,
pub host: String, pub host: String,
pub server: Vec<Server>, pub server: Vec<Server>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize, Clone)]
pub struct Server { pub struct Server {
pub url: String, pub url: String,
pub dir: String, pub dir: String,
@@ -33,6 +32,4 @@ impl Config {
} }
map map
} }
} }

View File

@@ -2,24 +2,25 @@
#[macro_use] #[macro_use]
extern crate serde_derive; 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 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::prelude::*;
use tokio::runtime; 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::rustls::internal::pemfile::{certs, pkcs8_private_keys};
use tokio_rustls::TlsAcceptor; use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
use tokio_rustls::server::TlsStream; use tokio_rustls::server::TlsStream;
use tokio::net::TcpStream; use tokio_rustls::TlsAcceptor;
use url::Url; use url::Url;
use std::error::Error;
mod config; mod config;
mod tls; mod tls;
@@ -27,54 +28,93 @@ mod tls;
fn get_tls_config(cfg: config::Config) -> rustls::ServerConfig { fn get_tls_config(cfg: config::Config) -> rustls::ServerConfig {
let store = rustls::RootCertStore::empty(); let store = rustls::RootCertStore::empty();
let verifier = rustls::AllowAnyAnonymousOrAuthenticatedClient::new(store); 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.cert_resolver = Arc::new(tls::CertResolver::from_config(cfg).unwrap());
tls_config tls_config
} }
fn get_content(request: String) -> String { fn get_content(mut path: PathBuf, u: url::Url) -> Result<String, io::Error> {
println!("URL: {}", u);
let url = Url::parse(&request).unwrap(); if u.path() == "" || u.path() == "/" {
path.push("index.gemini");
let path = match url.path() { } else {
"/" => String::from("index.gemini"), path.push(u.path().trim_start_matches("/"));
path => str::replace(path, "/", ""),
};
let fd = std::fs::read_to_string(path)
.expect("Unable to read file");
fd
} }
async fn handle_connection(mut stream: TlsStream<TcpStream>) -> Result<(), Box<dyn Error>> { 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<TcpStream>,
cfg: config::Config,
) -> Result<(), io::Error> {
let mut buffer = [0; 512]; let mut buffer = [0; 512];
stream.read(&mut buffer).await?; stream.read(&mut buffer).await?;
let request = String::from_utf8_lossy(&buffer[..]).to_owned(); let request = String::from_utf8_lossy(&buffer[..]).to_owned();
println!("Request: {}", request); println!("Request: {}", request);
/*
let url = Url::parse(&request).unwrap(); let url = Url::parse(&request).unwrap();
for name in cfg.server {
if url.host_str() == Some(&name.url) { if url.scheme() != "gemini" {
println!("serving url: {:?}", url.host_str()); 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.write_all(&b"20\ttext/gemini\r\n"[..]).await?;
stream.flush().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.write_all(content.as_bytes()).await?;
stream.flush().await?; stream.flush().await?;
Ok(()) Ok(())
} }
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
let cfg = config::Config::new("config.toml"); let cfg = config::Config::new("config.toml");
let srv_map = cfg.to_map();
let addr = format!("{}:{}", cfg.host, cfg.port); let addr = format!("{}:{}", cfg.host, cfg.port);
addr.to_socket_addrs()? addr.to_socket_addrs()?
@@ -85,21 +125,22 @@ fn main() -> io::Result<()> {
.threaded_scheduler() .threaded_scheduler()
.enable_io() .enable_io()
.build()?; .build()?;
let handle = runtime.handle().clone(); 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 acceptor = TlsAcceptor::from(Arc::new(config));
let fut = async { let fut = async {
let mut listener = TcpListener::bind(&addr).await?; let mut listener = TcpListener::bind(&addr).await?;
loop { loop {
let (stream, peer_addr) = listener.accept().await?; let (stream, peer_addr) = listener.accept().await?;
let acceptor = acceptor.clone(); let acceptor = acceptor.clone();
let cfg = cfg.clone();
let fut = async move { let fut = async move {
let stream = acceptor.accept(stream).await?; let stream = acceptor.accept(stream).await?;
handle_connection(stream).await; handle_connection(stream, cfg).await?;
println!("Hello: {}", peer_addr); println!("Hello: {}", peer_addr);
Ok(()) as io::Result<()> Ok(()) as io::Result<()>

View File

@@ -1,15 +1,15 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::{self, BufReader}; use std::io::{self, BufReader};
use std::error::Error;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use rustls::{ResolvesServerCert, SignatureScheme};
use rustls::ClientHello;
use rustls::sign::CertifiedKey; use rustls::sign::CertifiedKey;
use rustls::sign::{Signer, SigningKey, RSASigningKey}; use rustls::sign::{RSASigningKey, Signer, SigningKey};
use tokio_rustls::rustls::{ Certificate, NoClientAuth, PrivateKey, ServerConfig }; use rustls::ClientHello;
use rustls::{ResolvesServerCert, SignatureScheme};
use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys};
use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
use crate::config; use crate::config;
@@ -19,16 +19,14 @@ pub fn load_certs(path: &String) -> io::Result<Vec<Certificate>> {
} }
pub fn load_key(path: &String) -> PrivateKey { pub fn load_key(path: &String) -> PrivateKey {
let keyfile = File::open(path) let keyfile = File::open(path).expect("cannot open private key file");
.expect("cannot open private key file");
let mut reader = BufReader::new(keyfile); let mut reader = BufReader::new(keyfile);
let key = pkcs8_private_keys(&mut reader) let key = pkcs8_private_keys(&mut reader).expect("file contains invalid rsa private key");
.expect("file contains invalid rsa private key");
return key[0].clone(); return key[0].clone();
} }
pub struct CertResolver { pub struct CertResolver {
map: HashMap<String, Box<CertifiedKey>> map: HashMap<String, Box<CertifiedKey>>,
} }
impl CertResolver { impl CertResolver {
@@ -45,13 +43,13 @@ impl CertResolver {
CertifiedKey::new(&key).map_err(|_| format!("Failed to create signer for {}", server.url)) CertifiedKey::new(&key).map_err(|_| format!("Failed to create signer for {}", server.url))
)); ));
*/ */
let signing_key = RSASigningKey::new( let signing_key = RSASigningKey::new(&key).unwrap();
&key).unwrap();
let signing_key_boxed: Arc<Box<dyn SigningKey>> = Arc::new( let signing_key_boxed: Arc<Box<dyn SigningKey>> = Arc::new(Box::new(signing_key));
Box::new(signing_key)); map.insert(
map.insert(server.url.clone(), Box::new(rustls::sign::CertifiedKey::new( server.url.clone(),
certs, signing_key_boxed))); Box::new(rustls::sign::CertifiedKey::new(certs, signing_key_boxed)),
);
} }
println!("Successfully loaded {} TLS configurations", map.len()); println!("Successfully loaded {} TLS configurations", map.len());
@@ -64,11 +62,10 @@ impl ResolvesServerCert for CertResolver {
fn resolve(&self, client_hello: ClientHello) -> Option<CertifiedKey> { fn resolve(&self, client_hello: ClientHello) -> Option<CertifiedKey> {
if let Some(url) = client_hello.server_name() { if let Some(url) = client_hello.server_name() {
if let Some(cert) = self.map.get(url.into()) { if let Some(cert) = self.map.get(url.into()) {
return Some(*cert.clone()) return Some(*cert.clone());
} }
} }
None None
} }
} }