Add virtual hosts and user configuration.

This commit is contained in:
abbie 2023-07-11 18:33:46 +01:00
parent 65297831c8
commit 14eb58669c
Signed by: threeoh6000
GPG key ID: 801FE4AD456E922C
4 changed files with 168 additions and 27 deletions

93
Cargo.lock generated
View file

@ -42,6 +42,12 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.7.0" version = "1.7.0"
@ -66,6 +72,22 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.12" version = "0.1.12"
@ -83,9 +105,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.119" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]] [[package]]
name = "log" name = "log"
@ -96,6 +118,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.8" version = "0.2.8"
@ -216,12 +244,28 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "serde"
version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
[[package]]
name = "serde_spanned"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "starfig" name = "starfig"
version = "0.1.2" version = "0.2.0"
dependencies = [ dependencies = [
"native-tls", "native-tls",
"packeteer", "packeteer",
"toml",
] ]
[[package]] [[package]]
@ -238,6 +282,40 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "toml"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@ -265,3 +343,12 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winnow"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529"
dependencies = [
"memchr",
]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "starfig" name = "starfig"
version = "0.1.2" version = "0.2.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -8,3 +8,4 @@ edition = "2021"
[dependencies] [dependencies]
packeteer = { version = "0.2.2", features = ["gemini"] } packeteer = { version = "0.2.2", features = ["gemini"] }
native-tls = "0.2.8" native-tls = "0.2.8"
toml = "0.7.6"

11
config.example.toml Normal file
View file

@ -0,0 +1,11 @@
[starfig]
pfx = "identity.pfx"
bind_address = "0.0.0.0:1965"
[hosts]
redirect_invalid_hosts = false
active = [ "default" ]
[hosts.default]
domain = "example.org"
root = "/srv/gemini/content"

View file

@ -9,17 +9,18 @@ use std::sync::Arc;
use std::fs::File; use std::fs::File;
use std::fs; use std::fs;
use std::thread; use std::thread;
use toml::Table;
fn load_page(segments: Vec<String>) -> String { fn load_page(segments: Vec<String>, vhost_root: String) -> String {
let empty: Vec<String> = vec![]; let empty: Vec<String> = vec![];
if segments == empty { if segments == empty {
let page = fs::read_to_string("content/index.gmi"); let page = fs::read_to_string(format!("{}/index.gmi", vhost_root));
match page { match page {
Ok(i) => i.to_string(), Ok(i) => i.to_string(),
Err(_e) => "# 51 Not Found\nThe resource you were looking for couldn't be found :(\nStarfig, gemini server by threeoh6000".to_string(), Err(_e) => "# 51 Not Found\nThe resource you were looking for couldn't be found :(\nStarfig, gemini server by threeoh6000".to_string(),
} }
} else { } else {
let mut m = "content".to_string(); let mut m = vhost_root.to_string();
for v in segments { for v in segments {
m = format!("{}/{}", m, v); m = format!("{}/{}", m, v);
} }
@ -34,51 +35,92 @@ fn load_page(segments: Vec<String>) -> String {
} }
} }
fn serve(mut stream: TlsStream<TcpStream>) { fn serve(mut stream: TlsStream<TcpStream>, vhosts: Vec<String>, vhost_roots: Vec<String>, redirect: bool) {
let mut request = Vec::new(); let mut request = Vec::new();
let mut reader = BufReader::new(&mut stream); let mut reader = BufReader::new(&mut stream);
reader.read_until(b'\n', &mut request).expect("Stream failed to be read."); reader.read_until(b'\n', &mut request).expect("Stream failed to be read.");
let req = construct_request(&String::from_utf8_lossy(&request).to_string()); let req = construct_request(&String::from_utf8_lossy(&request).to_string());
let file = load_page(req.url.segments);
let mut res = generate_response(20, &file); let mut loc = usize::MAX;
if file == "# 51 Not Found\nThe resource you were looking for couldn't be found :(\nStarfig, gemini server by threeoh6000" { let mut i = 0;
res.code = 51; for vhost in &vhosts {
if vhost == &req.url.domain {
loc = i;
} else {
i = i + 1;
}
} }
if res.code == 20 {
res.meta = "text/gemini".to_string(); if loc != usize::MAX {
} else if res.code == 51 { let file = load_page(req.url.segments, vhost_roots[loc].to_string());
res.meta = "Not Found".to_string(); let mut res = generate_response(20, &file);
if file == "# 51 Not Found\nThe resource you were looking for couldn't be found :(\nStarfig, gemini server by threeoh6000" {
res.code = 51;
}
if res.code == 20 {
res.meta = "text/gemini".to_string();
} else if res.code == 51 {
res.meta = "Not Found".to_string();
} else {
res.meta = "Err".to_string();
}
let (res_head, res_bod) = unpack_response(res);
stream.write(res_head.as_bytes()).unwrap();
stream.flush().unwrap();
stream.write(res_bod.as_bytes()).unwrap();
stream.flush().unwrap();
} else { } else {
res.meta = "Err".to_string(); let mut res = generate_response(20, "");
if redirect {
res.code = 30;
res.meta = format!("gemini://{}", vhosts[0].to_string());
} else {
res.code = 40;
}
let (res_head, res_bod) = unpack_response(res);
stream.write(res_head.as_bytes()).unwrap();
stream.flush().unwrap();
stream.write(res_bod.as_bytes()).unwrap();
stream.flush().unwrap();
} }
let (res_head, res_bod) = unpack_response(res);
stream.write(res_head.as_bytes()).unwrap(); let _ = stream.shutdown();
stream.flush().unwrap();
stream.write(res_bod.as_bytes()).unwrap();
stream.flush().unwrap();
stream.shutdown();
} }
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let mut file = File::open("identity.pfx").unwrap(); let configfile = fs::read_to_string("config.toml").unwrap();
let config = configfile.parse::<Table>().unwrap();
let mut file = File::open(config["starfig"]["pfx"].as_str().unwrap()).unwrap();
let mut identity = vec![]; let mut identity = vec![];
file.read_to_end(&mut identity).unwrap(); file.read_to_end(&mut identity).unwrap();
let identity = Identity::from_pkcs12(&identity, "").unwrap(); let identity = Identity::from_pkcs12(&identity, "").unwrap();
let listener = TcpListener::bind("0.0.0.0:1965").unwrap(); let mut vhosts: Vec<String> = vec![];
let mut vhost_roots: Vec<String> = vec![];
for host in config["hosts"]["active"].clone().try_into::<Vec<String>>().unwrap() {
vhosts.push(config["hosts"][host.clone()]["domain"].as_str().unwrap().to_string());
vhost_roots.push(config["hosts"][host.clone()]["root"].as_str().unwrap().to_string());
}
let listener = TcpListener::bind(format!("{}", config["starfig"]["bind_address"].as_str().unwrap())).unwrap();
let acceptor = TlsAcceptor::new(identity).unwrap(); let acceptor = TlsAcceptor::new(identity).unwrap();
let acceptor = Arc::new(acceptor); let acceptor = Arc::new(acceptor);
for stream in listener.incoming() { for stream in listener.incoming() {
match stream { match stream {
Ok(stream) => { Ok(stream) => {
let vhc = vhosts.to_vec();
let vrc = vhost_roots.to_vec();
let redirect = config["hosts"]["redirect_invalid_hosts"].as_bool().unwrap();
let acceptor = acceptor.clone(); let acceptor = acceptor.clone();
thread::spawn(move || { thread::spawn(move || {
let stream = acceptor.accept(stream).unwrap(); let stream = acceptor.accept(stream).unwrap();
serve(stream); serve(stream, vhc, vrc, redirect);
}); });
} }
Err(e) => { /* connection failed */ } Err(_) => { /* connection failed */ }
} }
} }
Ok(()) Ok(())