Updated README, added ~usrdirs and now checks permissions
This commit is contained in:
30
README
30
README
@@ -1,3 +1,29 @@
|
|||||||
Gemserv is a gemini server written in rust. It supports vhosts and cgi. To run
|
# Gemserv
|
||||||
use "cargo run /path/to/config"
|
|
||||||
|
|
||||||
|
A gemini server written in rust.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Vhosts
|
||||||
|
- CGI
|
||||||
|
- User directories
|
||||||
|
|
||||||
|
## Installation and running
|
||||||
|
|
||||||
|
- Clone the repo
|
||||||
|
- Run 'cargo build --release'
|
||||||
|
- Modify the config.toml to your needs
|
||||||
|
- Run './target/release/gemserv config.toml'
|
||||||
|
|
||||||
|
## Create server key/cert pairs
|
||||||
|
|
||||||
|
|
||||||
|
## CGI
|
||||||
|
- GEMINI_URL
|
||||||
|
- SERVER_NAME
|
||||||
|
- SERVER_PROTOCOL
|
||||||
|
- SCRIPT_NAME
|
||||||
|
- REMOTE_ADDR
|
||||||
|
- REMOTE_HOST
|
||||||
|
- REMOTE_PORT
|
||||||
|
- QUERY_STRING
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ key = "/path/to/key"
|
|||||||
cert = "/path/to/cert"
|
cert = "/path/to/cert"
|
||||||
# cgi dir is optional
|
# cgi dir is optional
|
||||||
cgi = "/path/to/cgi-bin/"
|
cgi = "/path/to/cgi-bin/"
|
||||||
|
# usrdir is optional. it'll look in /home/~usr/public_gemini
|
||||||
|
usrdir = true
|
||||||
|
|
||||||
[[server]]
|
[[server]]
|
||||||
hostname = "example.net"
|
hostname = "example.net"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||||||
use std::io::{self, BufReader};
|
use std::io::{self, BufReader};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_rustls::server::TlsStream;
|
use tokio_rustls::server::TlsStream;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub struct Server {
|
|||||||
pub key: String,
|
pub key: String,
|
||||||
pub cert: String,
|
pub cert: String,
|
||||||
pub cgi: Option<String>,
|
pub cgi: Option<String>,
|
||||||
|
pub usrdir: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -35,7 +36,8 @@ impl Config {
|
|||||||
dir: srv.dir.clone(),
|
dir: srv.dir.clone(),
|
||||||
key: srv.key.clone(),
|
key: srv.key.clone(),
|
||||||
cert: srv.cert.clone(),
|
cert: srv.cert.clone(),
|
||||||
cgi: srv.cgi.clone()
|
cgi: srv.cgi.clone(),
|
||||||
|
usrdir: srv.usrdir.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
map
|
map
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub struct Connection {
|
|||||||
pub hostname: String,
|
pub hostname: String,
|
||||||
pub dir: String,
|
pub dir: String,
|
||||||
pub cgi: String,
|
pub cgi: String,
|
||||||
|
pub usrdir: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
|
|||||||
42
src/main.rs
42
src/main.rs
@@ -82,6 +82,10 @@ fn get_content(path: PathBuf, u: url::Url) -> Result<String, io::Error> {
|
|||||||
for file in fs::read_dir(&path)? {
|
for file in fs::read_dir(&path)? {
|
||||||
if let Ok(file) = file {
|
if let Ok(file) = file {
|
||||||
let m = file.metadata()?;
|
let m = file.metadata()?;
|
||||||
|
let perm = m.permissions();
|
||||||
|
if perm.mode() & 0o0444 != 0o0444 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
let file = file.path();
|
let file = file.path();
|
||||||
let p = file.strip_prefix(&path).unwrap();
|
let p = file.strip_prefix(&path).unwrap();
|
||||||
if m.is_dir() {
|
if m.is_dir() {
|
||||||
@@ -118,17 +122,32 @@ async fn handle_connection(mut con: conn::Connection) -> Result<(), io::Error> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut path = PathBuf::from(&con.dir);
|
let mut path = PathBuf::new();
|
||||||
|
|
||||||
|
if url.path().starts_with("/~") && con.usrdir == true{
|
||||||
|
let usr = url.path().trim_start_matches("/~");
|
||||||
|
let usr: Vec<&str> = usr.splitn(2, "/").collect();
|
||||||
|
path.push("/home/");
|
||||||
|
if usr.len() == 2 {
|
||||||
|
path.push(format!("{}/{}/{}", usr[0], "public_gemini", usr[1]));
|
||||||
|
} else {
|
||||||
|
path.push(format!("{}/{}/",usr[0], "public_gemini"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path.push(&con.dir);
|
||||||
if url.path() != "" || url.path() != "/" {
|
if url.path() != "" || url.path() != "/" {
|
||||||
path.push(url.path().trim_start_matches("/"));
|
path.push(url.path().trim_start_matches("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
con.send_status(status::Status::NotFound, "Not found!\r\n").await?;
|
con.send_status(status::Status::NotFound, "Not found!\r\n").await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let meta = fs::metadata(&path).expect("Unable to read metadata");
|
let mut meta = fs::metadata(&path).expect("Unable to read metadata");
|
||||||
|
let mut perm = meta.permissions();
|
||||||
|
|
||||||
if meta.is_dir() {
|
if meta.is_dir() {
|
||||||
if !url.path().ends_with("/") {
|
if !url.path().ends_with("/") {
|
||||||
@@ -141,13 +160,27 @@ async fn handle_connection(mut con: conn::Connection) -> Result<(), io::Error> {
|
|||||||
}
|
}
|
||||||
if path.join("index.gemini").exists() {
|
if path.join("index.gemini").exists() {
|
||||||
path.push("index.gemini");
|
path.push("index.gemini");
|
||||||
|
meta = fs::metadata(&path).expect("Unable to read metadata");
|
||||||
|
perm = meta.permissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add timeout
|
// add timeout
|
||||||
if con.cgi.trim_end_matches("/") == path.parent().unwrap().to_str().unwrap() {
|
if con.cgi.trim_end_matches("/") == path.parent().unwrap().to_str().unwrap() {
|
||||||
|
if perm.mode() & 0o0111 == 0o0111 {
|
||||||
cgi::cgi(con, path, url).await?;
|
cgi::cgi(con, path, url).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
con.send_status(
|
||||||
|
status::Status::CGIError, "CGI Error!\r\n").await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perm.mode() & 0o0444 != 0o0444 {
|
||||||
|
con.send_status(
|
||||||
|
status::Status::NotFound, "Not Found!\r\n").await?;
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mime = get_mime(&path);
|
let mime = get_mime(&path);
|
||||||
@@ -209,6 +242,7 @@ fn main() -> io::Result<()> {
|
|||||||
let mut dir = String::new();
|
let mut dir = String::new();
|
||||||
let mut cgi = String::new();
|
let mut cgi = String::new();
|
||||||
let mut hostname = String::new();
|
let mut hostname = String::new();
|
||||||
|
let mut usrdir: bool = false;
|
||||||
|
|
||||||
for server in &cfg.server {
|
for server in &cfg.server {
|
||||||
if Some(server.hostname.as_str()) == sni {
|
if Some(server.hostname.as_str()) == sni {
|
||||||
@@ -217,6 +251,9 @@ fn main() -> io::Result<()> {
|
|||||||
if server.cgi.is_some() {
|
if server.cgi.is_some() {
|
||||||
cgi = server.cgi.as_ref().unwrap().to_string();
|
cgi = server.cgi.as_ref().unwrap().to_string();
|
||||||
}
|
}
|
||||||
|
if server.usrdir.is_some() {
|
||||||
|
usrdir = server.usrdir.unwrap();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,6 +264,7 @@ fn main() -> io::Result<()> {
|
|||||||
hostname,
|
hostname,
|
||||||
dir,
|
dir,
|
||||||
cgi,
|
cgi,
|
||||||
|
usrdir,
|
||||||
};
|
};
|
||||||
handle_connection(con).await?;
|
handle_connection(con).await?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user