diff --git a/Cargo.lock b/Cargo.lock index f355ef0..159f00f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "fastrand" version = "1.7.0" @@ -66,6 +72,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "instant" version = "0.1.12" @@ -83,9 +105,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" @@ -96,6 +118,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "native-tls" version = "0.2.8" @@ -216,12 +244,28 @@ dependencies = [ "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]] name = "starfig" -version = "0.1.2" +version = "0.2.0" dependencies = [ "native-tls", "packeteer", + "toml", ] [[package]] @@ -238,6 +282,40 @@ dependencies = [ "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]] name = "vcpkg" version = "0.2.15" @@ -265,3 +343,12 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winnow" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 66d79aa..502842d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starfig" -version = "0.1.2" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -8,3 +8,4 @@ edition = "2021" [dependencies] packeteer = { version = "0.2.2", features = ["gemini"] } native-tls = "0.2.8" +toml = "0.7.6" diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..0ac03a9 --- /dev/null +++ b/config.example.toml @@ -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" diff --git a/src/main.rs b/src/main.rs index 531ff89..2b2121e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,18 @@ use std::sync::Arc; use std::fs::File; use std::fs; use std::thread; +use toml::Table; -fn load_page(segments: Vec) -> String { +fn load_page(segments: Vec, vhost_root: String) -> String { let empty: Vec = vec![]; 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 { 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(), } } else { - let mut m = "content".to_string(); + let mut m = vhost_root.to_string(); for v in segments { m = format!("{}/{}", m, v); } @@ -34,51 +35,92 @@ fn load_page(segments: Vec) -> String { } } -fn serve(mut stream: TlsStream) { +fn serve(mut stream: TlsStream, vhosts: Vec, vhost_roots: Vec, redirect: bool) { let mut request = Vec::new(); let mut reader = BufReader::new(&mut stream); 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 file = load_page(req.url.segments); - 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; + + let mut loc = usize::MAX; + let mut i = 0; + 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(); - } else if res.code == 51 { - res.meta = "Not Found".to_string(); + + if loc != usize::MAX { + let file = load_page(req.url.segments, vhost_roots[loc].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 { - 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(); - stream.flush().unwrap(); - stream.write(res_bod.as_bytes()).unwrap(); - stream.flush().unwrap(); - stream.shutdown(); + + let _ = stream.shutdown(); } 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::().unwrap(); + let mut file = File::open(config["starfig"]["pfx"].as_str().unwrap()).unwrap(); let mut identity = vec![]; file.read_to_end(&mut identity).unwrap(); let identity = Identity::from_pkcs12(&identity, "").unwrap(); - let listener = TcpListener::bind("0.0.0.0:1965").unwrap(); + let mut vhosts: Vec = vec![]; + let mut vhost_roots: Vec = vec![]; + + for host in config["hosts"]["active"].clone().try_into::>().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 = Arc::new(acceptor); for stream in listener.incoming() { match 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(); thread::spawn(move || { let stream = acceptor.accept(stream).unwrap(); - serve(stream); + serve(stream, vhc, vrc, redirect); }); } - Err(e) => { /* connection failed */ } + Err(_) => { /* connection failed */ } } } Ok(())