mime type handling and setuid/setgid support
This commit is contained in:
parent
4a9c6c11ac
commit
872197c558
6 changed files with 98 additions and 5 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -68,6 +68,19 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.29"
|
version = "0.10.29"
|
||||||
|
@ -111,6 +124,7 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||||
name = "sheldond"
|
name = "sheldond"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"nix",
|
||||||
"openssl",
|
"openssl",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
@ -155,3 +169,9 @@ name = "vcpkg"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
|
@ -7,4 +7,5 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
openssl = { version = "0.10" }
|
openssl = { version = "0.10" }
|
||||||
url = { version = "2.1" }
|
url = { version = "2.1" }
|
||||||
|
nix = { version = "0.17" }
|
||||||
|
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
mod server;
|
mod server;
|
||||||
|
mod mime;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
fn help() {
|
fn help() {
|
||||||
|
@ -12,12 +13,16 @@ fn help() {
|
||||||
println!(" -l, --listen\t\tadd a listening address (you can define multiple)");
|
println!(" -l, --listen\t\tadd a listening address (you can define multiple)");
|
||||||
println!(" -d, --default-host\tdefault hostname to listen for");
|
println!(" -d, --default-host\tdefault hostname to listen for");
|
||||||
println!(" -g, --gem-root\tpath to the gemini root, aka the folder to serve files from");
|
println!(" -g, --gem-root\tpath to the gemini root, aka the folder to serve files from");
|
||||||
|
println!(" --user\tuser to drop to after opening TLS socket");
|
||||||
|
println!(" --group\tgroup to drop to after opening TLS socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_args() -> Option<server::ServerConfig> {
|
fn parse_args() -> Option<server::ServerConfig> {
|
||||||
let mut has_addr = false;
|
let mut has_addr = false;
|
||||||
let mut has_host = false;
|
let mut has_host = false;
|
||||||
let mut has_root = false;
|
let mut has_root = false;
|
||||||
|
let mut has_user = false;
|
||||||
|
let mut has_group = false;
|
||||||
|
|
||||||
let mut config = server::ServerConfig::new();
|
let mut config = server::ServerConfig::new();
|
||||||
let mut args = env::args();
|
let mut args = env::args();
|
||||||
|
@ -42,12 +47,22 @@ fn parse_args() -> Option<server::ServerConfig> {
|
||||||
config.set_gem_root(gem_root);
|
config.set_gem_root(gem_root);
|
||||||
has_root = true;
|
has_root = true;
|
||||||
}
|
}
|
||||||
|
if arg == "--user" {
|
||||||
|
let user = args.next().unwrap();
|
||||||
|
config.set_user(user);
|
||||||
|
has_user = true;
|
||||||
|
}
|
||||||
|
if arg == "--group" {
|
||||||
|
let group = args.next().unwrap();
|
||||||
|
config.set_group(group);
|
||||||
|
has_group = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_addr || !has_host || !has_root {
|
if !has_addr || !has_host || !has_root || !has_user || !has_group {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/mime/mod.rs
Normal file
20
src/mime/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
pub fn default_mime_type() -> &'static str {
|
||||||
|
"application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mime_type(extension: &OsStr) -> &'static str {
|
||||||
|
let ext_str = match extension.to_str() {
|
||||||
|
Some(ext_str) => ext_str,
|
||||||
|
None => {
|
||||||
|
return default_mime_type();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match ext_str {
|
||||||
|
"gmi" => "text/gemini",
|
||||||
|
"txt" => "text/plain",
|
||||||
|
_ => default_mime_type(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::server::response;
|
use crate::server::response;
|
||||||
use crate::server::ServerConfig;
|
use crate::server::ServerConfig;
|
||||||
|
use crate::mime;
|
||||||
use openssl::ssl::SslStream;
|
use openssl::ssl::SslStream;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{copy, BufReader, BufWriter};
|
use std::io::{copy, BufReader, BufWriter};
|
||||||
|
@ -77,7 +78,11 @@ fn handle_response(config: &ServerConfig, url: Url, mut stream: &mut SslStream<T
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let header = response::Header::new(response::Status::Success, "text/gemini");
|
let mime_type = match path.extension() {
|
||||||
|
Some(ext) => mime::get_mime_type(ext),
|
||||||
|
None => mime::default_mime_type(),
|
||||||
|
};
|
||||||
|
let header = response::Header::new(response::Status::Success, mime_type);
|
||||||
send_header(&mut stream, &header);
|
send_header(&mut stream, &header);
|
||||||
|
|
||||||
let mut buf_file = BufReader::new(file);
|
let mut buf_file = BufReader::new(file);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use nix::unistd;
|
||||||
|
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
@ -14,6 +15,8 @@ pub struct ServerConfig {
|
||||||
default_host: Url,
|
default_host: Url,
|
||||||
gem_root: PathBuf,
|
gem_root: PathBuf,
|
||||||
addrs: Vec<SocketAddr>,
|
addrs: Vec<SocketAddr>,
|
||||||
|
user: unistd::Uid,
|
||||||
|
group: unistd::Gid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerConfig {
|
impl ServerConfig {
|
||||||
|
@ -22,6 +25,8 @@ impl ServerConfig {
|
||||||
default_host: Url::parse("gemini://localhost").unwrap(),
|
default_host: Url::parse("gemini://localhost").unwrap(),
|
||||||
gem_root: PathBuf::from(""),
|
gem_root: PathBuf::from(""),
|
||||||
addrs: Vec::new(),
|
addrs: Vec::new(),
|
||||||
|
user: unistd::getuid(),
|
||||||
|
group: unistd::getgid(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,17 +50,29 @@ impl ServerConfig {
|
||||||
Err(e) => panic!(e),
|
Err(e) => panic!(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_user(&mut self, uname: String) {
|
||||||
|
self.user = match unistd::User::from_name(&uname) {
|
||||||
|
Ok(user) => user.unwrap().uid,
|
||||||
|
Err(e) => panic!(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_group(&mut self, gname: String) {
|
||||||
|
self.group = match unistd::Group::from_name(&gname) {
|
||||||
|
Ok(group) => group.unwrap().gid,
|
||||||
|
Err(e) => panic!(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
acceptor: std::sync::Arc<SslAcceptor>,
|
|
||||||
config: ServerConfig,
|
config: ServerConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(config: &ServerConfig) -> Server {
|
pub fn new(config: &ServerConfig) -> Server {
|
||||||
Server {
|
Server {
|
||||||
acceptor: Server::build_acceptor(),
|
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,12 +88,27 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve(&self) {
|
pub fn serve(&self) {
|
||||||
|
let acceptor = Server::build_acceptor();
|
||||||
let listener = TcpListener::bind(&self.config.addrs[..]).unwrap();
|
let listener = TcpListener::bind(&self.config.addrs[..]).unwrap();
|
||||||
|
|
||||||
|
if self.config.user.is_root() {
|
||||||
|
panic!("refusing to run as root");
|
||||||
|
}
|
||||||
|
|
||||||
|
match unistd::setgid(self.config.group) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => {panic!(e);},
|
||||||
|
};
|
||||||
|
|
||||||
|
match unistd::setuid(self.config.user) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => {panic!(e);},
|
||||||
|
};
|
||||||
|
|
||||||
for stream in listener.incoming() {
|
for stream in listener.incoming() {
|
||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
let acceptor = self.acceptor.clone();
|
let acceptor = acceptor.clone();
|
||||||
let config = self.config.clone();
|
let config = self.config.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let stream = acceptor.accept(stream).unwrap();
|
let stream = acceptor.accept(stream).unwrap();
|
||||||
|
|
Loading…
Reference in a new issue