#[cfg(unix)] extern crate libc;
#[cfg(unix)] extern crate nix;
#[cfg(unix)] mod unix;
extern crate ctrlc;
#[macro_use]
extern crate crossbeam_channel;
mod fcgi;
mod options;
use options::*;
use std::io;
use std::io::{Read,BufRead,Write,BufWriter};
use std::collections::HashMap;
use std::net::{SocketAddr, IpAddr, TcpStream, TcpListener};
use std::path::PathBuf;
use std::panic::RefUnwindSafe;
#[doc(hidden)]
pub trait Listener : Send {
fn accept_connection(&mut self) -> io::Result<TcpStream>;
}
impl Listener for TcpListener {
fn accept_connection(&mut self) -> io::Result<TcpStream> {
self.accept().map(|(x, _)| x)
}
}
struct ParanoidTcpListener {
listener: TcpListener,
whitelist: Vec<IpAddr>,
}
impl ParanoidTcpListener {
fn make_whitelist(whitelist: &str) -> io::Result<Vec<IpAddr>> {
let mut ret = Vec::new();
for result in whitelist.as_bytes().split(|x| *x == b',')
.map(|mut x| {
while !x.is_empty() && x[0] == b' ' {
x = &x[1..];
}
while !x.is_empty() && x[x.len()-1] == b' ' {
x = &x[..x.len()-1];
}
unsafe{String::from_utf8_unchecked(x.to_vec())}.parse()
}) {
match result {
Ok(addr) => ret.push(addr),
Err(_) => return Err(io::Error::new(io::ErrorKind::Other,
"Invalid address in \
FCGI_WEB_SERVER_ADDRS")),
}
}
Ok(ret)
}
fn new(addr: SocketAddr, whitelist: &str)
-> io::Result<ParanoidTcpListener> {
let whitelist = ParanoidTcpListener::make_whitelist(whitelist)?;
Ok(ParanoidTcpListener {
listener: TcpListener::bind(addr)?,
whitelist,
})
}
#[allow(unused)]
fn with(listener: TcpListener, whitelist: &str)
-> io::Result<ParanoidTcpListener> {
let whitelist = ParanoidTcpListener::make_whitelist(whitelist)?;
Ok(ParanoidTcpListener {
listener,
whitelist,
})
}
}
impl Listener for ParanoidTcpListener {
fn accept_connection(&mut self) -> io::Result<TcpStream> {
loop {
let (sock, addr) = self.listener.accept()?;
let ip = addr.ip();
for white in self.whitelist.iter() {
if ip == *white { return Ok(sock) }
}
}
}
}
pub trait IO : BufRead + Write {
}
struct DualIO<R: BufRead, W: Write> {
i: R,
o: W,
}
impl<R: BufRead, W: Write> Read for DualIO<R, W> {
fn read(&mut self, buf: &mut[u8]) -> io::Result<usize> {
self.i.read(buf)
}
}
impl<R: BufRead, W: Write> BufRead for DualIO<R, W> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.i.fill_buf()
}
fn consume(&mut self, amount: usize) {
self.i.consume(amount)
}
}
impl<R: BufRead, W: Write> Write for DualIO<R, W> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.o.write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
self.o.flush()
}
}
impl<R: BufRead, W: Write> IO for DualIO<R, W> {
}
pub fn main<I, H>(init: I, handler: H) -> !
where I: 'static + Fn(u32),
H: 'static + Fn(&mut dyn IO, HashMap<String, String>) -> io::Result<i32>
+ Sync + Send + Copy + RefUnwindSafe {
use std::process::exit;
match sub_main(init, handler) {
Ok(i) => exit(i),
Err(e) => {
eprintln!("Unexpected error: {}", e);
exit(1)
}
}
}
fn sub_main<I, H>(init: I, handler: H) -> io::Result<i32>
where I: 'static + Fn(u32),
H: 'static + Fn(&mut dyn IO, HashMap<String, String>) -> io::Result<i32>
+ Sync + Send + Copy + RefUnwindSafe {
let args: Vec<String> = std::env::args().collect();
let static_env: HashMap<String, String> = std::env::vars().collect();
if args.len() <= 1 {
if let Some(existing_listener) = fix_fds(&static_env) {
init(1);
Ok(fcgi::listen_loop(existing_listener,
handler,
fcgi::Options { max_connections:1 },
&static_env))
}
else if let Some(_) = static_env.get("GATEWAY_INTERFACE") {
init(1);
if !static_env.get("GATEWAY_INTERFACE").unwrap()
.starts_with("CGI/") {
eprintln!("Unknown GATEWAY_INTERFACE type");
return Ok(1)
}
let stdin = io::stdin();
let stdout = io::stdout();
let mut io = DualIO {
i: stdin.lock(),
o: BufWriter::new(stdout.lock()),
};
handler(&mut io, static_env)
}
else {
print_usage();
Ok(1)
}
}
else {
match args[1].as_str() {
"fcgi-tcp" => {
let mut bind_options: BindOptions<SocketAddr>
= BindOptions::new();
let mut fcgi_options = fcgi::Options::new();
let mut os_options = os_options();
if !handle_command_line(&mut [
&mut bind_options,
&mut fcgi_options,
&mut os_options,
], args[2..].iter()) {
print_usage();
Ok(1)
}
else if bind_options.addr.is_none() {
eprintln!("Please specify an address and port to bind to \
using --bind");
print_usage();
Ok(1)
}
else {
let listener: Box<dyn Listener> =
if let Some(list)=static_env.get("FCGI_WEB_SERVER_ADDRS") {
Box::new(ParanoidTcpListener::new(bind_options.addr
.unwrap(), list)?)
}
else {
Box::new(TcpListener::bind(bind_options.addr
.unwrap())?)
};
os_options.post_setup()?;
init(fcgi_options.max_connections);
Ok(fcgi::listen_loop(listener, handler, fcgi_options,
&static_env))
}
},
#[cfg(unix)] "fcgi-unix" => {
let mut bind_options: BindOptions<PathBuf>
= BindOptions::new();
let mut unix_socket_options = unix::UnixSocketOptions::new();
let mut fcgi_options = fcgi::Options::new();
let mut os_options = os_options();
if !handle_command_line(&mut [
&mut bind_options,
&mut unix_socket_options,
&mut fcgi_options,
&mut os_options,
], args[2..].iter()) {
print_usage();
Ok(1)
}
else if bind_options.addr.is_none() {
eprintln!("Please specify a filesystem path to bind to \
using --bind");
print_usage();
Ok(1)
}
else {
if let Some(_) = static_env.get("FCGI_WEB_SERVER_ADDRS") {
eprintln!("WARNING: Value of FCGI_WEB_SERVER_ADDRS is \
ignored for non-TCP sockets!");
}
let listener
= unix::listen(bind_options.addr.unwrap().as_path(),
unix_socket_options)?;
os_options.post_setup()?;
init(fcgi_options.max_connections);
Ok(fcgi::listen_loop(Box::new(listener), handler,
fcgi_options, &static_env))
}
},
x => {
eprintln!("Unknown mode: {}", x);
print_usage();
Ok(1)
},
}
}
}