diff options
Diffstat (limited to 'src/cmd/server.rs')
-rw-r--r-- | src/cmd/server.rs | 131 |
1 files changed, 114 insertions, 17 deletions
diff --git a/src/cmd/server.rs b/src/cmd/server.rs index db3ae96..0633a77 100644 --- a/src/cmd/server.rs +++ b/src/cmd/server.rs @@ -1,6 +1,9 @@ use futures::future::Future as _; use futures::stream::Stream as _; +use snafu::futures01::stream::StreamExt as _; +use snafu::futures01::FutureExt as _; use snafu::ResultExt as _; +use std::io::Read as _; #[derive(Debug, snafu::Snafu)] pub enum Error { @@ -19,6 +22,38 @@ pub enum Error { input: String, source: std::num::ParseIntError, }, + + #[snafu(display("failed to accept: {}", source))] + Acceptor { source: tokio::io::Error }, + + #[snafu(display( + "failed to send accepted socket to server thread: {}", + source + ))] + SocketChannel { + source: tokio::sync::mpsc::error::TrySendError<tokio::net::TcpStream>, + }, + + #[snafu(display("failed to send accepted socket to server thread"))] + TlsSocketChannel { + // XXX tokio_tls::Accept doesn't implement Debug or Display + // source: tokio::sync::mpsc::error::TrySendError<tokio_tls::Accept<tokio::net::TcpStream>>, + }, + + #[snafu(display("failed to run server: {}", source))] + Server { source: crate::server::Error }, + + #[snafu(display("failed to open identity file: {}", source))] + OpenIdentityFile { source: std::io::Error }, + + #[snafu(display("failed to read identity file: {}", source))] + ReadIdentityFile { source: std::io::Error }, + + #[snafu(display("failed to parse identity file: {}", source))] + ParseIdentity { source: native_tls::Error }, + + #[snafu(display("failed to create tls acceptor: {}", source))] + CreateAcceptor { source: native_tls::Error }, } pub type Result<T> = std::result::Result<T, Error>; @@ -40,6 +75,11 @@ pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { .long("read-timeout") .takes_value(true), ) + .arg( + clap::Arg::with_name("tls-identity-file") + .long("tls-identity-file") + .takes_value(true), + ) } pub fn run<'a>(matches: &clap::ArgMatches<'a>) -> super::Result<()> { @@ -70,34 +110,91 @@ pub fn run<'a>(matches: &clap::ArgMatches<'a>) -> super::Result<()> { .context(super::Server) }, )?; - run_impl(address, buffer_size, read_timeout).context(super::Server) + let tls_identity_file = matches.value_of("tls-identity-file"); + run_impl(address, buffer_size, read_timeout, tls_identity_file) + .context(super::Server) } fn run_impl( address: std::net::SocketAddr, buffer_size: usize, read_timeout: std::time::Duration, + tls_identity_file: Option<&str>, ) -> Result<()> { + let (acceptor, server) = + if let Some(tls_identity_file) = tls_identity_file { + create_server_tls( + address, + buffer_size, + read_timeout, + tls_identity_file, + )? + } else { + create_server(address, buffer_size, read_timeout)? + }; + tokio::run(futures::future::lazy(move || { + tokio::spawn(server.map_err(|e| { + eprintln!("{}", e); + })); + + acceptor.map_err(|e| { + eprintln!("{}", e); + }) + })); + Ok(()) +} + +fn create_server( + address: std::net::SocketAddr, + buffer_size: usize, + read_timeout: std::time::Duration, +) -> Result<( + Box<dyn futures::future::Future<Item = (), Error = Error> + Send>, + Box<dyn futures::future::Future<Item = (), Error = Error> + Send>, +)> { let (mut sock_w, sock_r) = tokio::sync::mpsc::channel(100); let listener = tokio::net::TcpListener::bind(&address).context(Bind)?; let acceptor = listener .incoming() - .map_err(|e| { - eprintln!("accept failed: {}", e); - }) - .for_each(move |sock| { - sock_w.try_send(sock).map_err(|e| { - eprintln!("sending socket to manager thread failed: {}", e); - }) - }); + .context(Acceptor) + .for_each(move |sock| sock_w.try_send(sock).context(SocketChannel)); + let server = + crate::server::Server::new(buffer_size, read_timeout, sock_r) + .context(Server); + Ok((Box::new(acceptor), Box::new(server))) +} - tokio::run(futures::future::lazy(move || { - let server = - crate::server::Server::new(buffer_size, read_timeout, sock_r) - .map_err(|e| eprintln!("{}", e)); - tokio::spawn(server); +fn create_server_tls( + address: std::net::SocketAddr, + buffer_size: usize, + read_timeout: std::time::Duration, + tls_identity_file: &str, +) -> Result<( + Box<dyn futures::future::Future<Item = (), Error = Error> + Send>, + Box<dyn futures::future::Future<Item = (), Error = Error> + Send>, +)> { + let (mut sock_w, sock_r) = tokio::sync::mpsc::channel(100); + let listener = tokio::net::TcpListener::bind(&address).context(Bind)?; - acceptor - })); - Ok(()) + let mut file = + std::fs::File::open(tls_identity_file).context(OpenIdentityFile)?; + let mut identity = vec![]; + file.read_to_end(&mut identity).context(ReadIdentityFile)?; + let identity = native_tls::Identity::from_pkcs12(&identity, "") + .context(ParseIdentity)?; + let acceptor = + native_tls::TlsAcceptor::new(identity).context(CreateAcceptor)?; + let acceptor = tokio_tls::TlsAcceptor::from(acceptor); + + let acceptor = + listener.incoming().context(Acceptor).for_each(move |sock| { + let sock = acceptor.accept(sock); + sock_w + .try_send(sock) + .map_err(|_| Error::TlsSocketChannel {}) + }); + let server = + crate::server::TlsServer::new(buffer_size, read_timeout, sock_r) + .context(Server); + Ok((Box::new(acceptor), Box::new(server))) } |