163 lines
4.6 KiB
Rust
163 lines
4.6 KiB
Rust
// Copyright (c) 2019 Reyk Floeter <contact@reykfloeter.com>
|
|
//
|
|
// Permission to use, copy, modify, and distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
mod cert;
|
|
mod client;
|
|
mod server;
|
|
|
|
use bubblebabble::stablebabble;
|
|
use cert::KeyPair;
|
|
use getopts::Options;
|
|
use log::LevelFilter;
|
|
use std::{
|
|
env,
|
|
io::{Error, ErrorKind, Result},
|
|
net::SocketAddr,
|
|
path::{Path, PathBuf},
|
|
process,
|
|
time::Duration,
|
|
};
|
|
use tokio_libtls::prelude::*;
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub(crate) struct Config {
|
|
keypair: Option<KeyPair>,
|
|
ca: Option<PathBuf>,
|
|
timeout: Option<Duration>,
|
|
servername: Option<String>,
|
|
address: Option<SocketAddr>,
|
|
size_limit: usize,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
address: "[::1]:8023".parse().ok(),
|
|
servername: Some(env::var("USER").unwrap_or("localhost".to_string())),
|
|
timeout: Some(Duration::from_secs(10)),
|
|
size_limit: 1_073_741_824,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn load_keys(&self) -> Result<(&Path, &Path, &Path)> {
|
|
let keypair = self
|
|
.keypair
|
|
.as_ref()
|
|
.ok_or(Error::new(ErrorKind::Other, "keypair"))?;
|
|
let key = keypair
|
|
.key
|
|
.as_ref()
|
|
.ok_or(Error::new(ErrorKind::Other, "key"))?;
|
|
let ca = self.ca.as_ref().ok_or(Error::new(ErrorKind::Other, "CA"))?;
|
|
Ok((&keypair.cert, key, ca))
|
|
}
|
|
|
|
pub fn load_server_options(&self) -> AsyncTlsOptions {
|
|
let mut options = AsyncTlsOptions::new();
|
|
if let Some(timeout) = self.timeout {
|
|
options.timeout(timeout);
|
|
}
|
|
if let Some(ref servername) = self.servername {
|
|
options.servername(servername);
|
|
} else {
|
|
options.servername("localhost");
|
|
}
|
|
options
|
|
}
|
|
|
|
pub fn load_client_options(&self) -> AsyncTlsOptions {
|
|
self.load_server_options()
|
|
}
|
|
}
|
|
|
|
fn usage(program: &str, opts: Options) -> ! {
|
|
let brief = format!("Usage: {} [options]", program);
|
|
print!("{}", opts.usage(&brief));
|
|
process::exit(1)
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let args: Vec<String> = env::args().collect();
|
|
let program = args[0].clone();
|
|
let mut config = Config::new();
|
|
|
|
let mut opts = Options::new();
|
|
opts.optopt(
|
|
"a",
|
|
"address",
|
|
"client/server address",
|
|
&config.address.unwrap().to_string(),
|
|
);
|
|
opts.optopt(
|
|
"n",
|
|
"name",
|
|
"server name",
|
|
&config.servername.as_ref().unwrap(),
|
|
);
|
|
opts.optopt("f", "file", "send file as client", "filename");
|
|
opts.optflag("s", "server", "run server");
|
|
opts.optflag("h", "help", "print this help menu");
|
|
let matches = match opts.parse(&args[1..]) {
|
|
Ok(m) => m,
|
|
Err(f) => panic!(f.to_string()),
|
|
};
|
|
if matches.opt_present("h") || (matches.opt_present("f") && matches.opt_present("s")) {
|
|
usage(&program, opts);
|
|
}
|
|
if let Some(address) = matches.opt_str("a") {
|
|
let addr: SocketAddr = address.parse().unwrap();
|
|
config.address = Some(addr);
|
|
}
|
|
|
|
let addr = match config.address {
|
|
Some(SocketAddr::V6(addr)) => addr.clone(),
|
|
_ => panic!("invalid address: {:?}", config.address),
|
|
};
|
|
|
|
env_logger::builder()
|
|
.filter_level(LevelFilter::Debug)
|
|
.init();
|
|
|
|
let file = if let Some(file) = matches.opt_str("f") {
|
|
let file = PathBuf::from(file);
|
|
if !file.exists() {
|
|
panic!("invalid file: {}", file.display())
|
|
}
|
|
Some(file)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let keypair = KeyPair::new(&config);
|
|
config.ca = Some(keypair.cert.clone());
|
|
config.keypair = Some(keypair);
|
|
|
|
debug!(
|
|
"{}:{} started",
|
|
stablebabble(&addr.ip().octets()),
|
|
addr.port()
|
|
);
|
|
|
|
if let Some(file) = file {
|
|
client::run(config, file).await.expect("client");
|
|
} else {
|
|
server::run(config).await.expect("server");
|
|
}
|
|
}
|