2017-02-22 03:42:32 +00:00
|
|
|
extern crate clap;
|
2017-02-22 05:30:01 +00:00
|
|
|
extern crate rustls;
|
2018-03-22 11:47:27 +00:00
|
|
|
extern crate tokio;
|
2017-09-03 22:58:55 +00:00
|
|
|
extern crate webpki;
|
2017-02-22 03:42:32 +00:00
|
|
|
extern crate webpki_roots;
|
|
|
|
extern crate tokio_rustls;
|
|
|
|
|
2018-05-03 10:07:31 +00:00
|
|
|
#[cfg(unix)] extern crate tokio_file_unix;
|
|
|
|
#[cfg(not(unix))] extern crate tokio_fs;
|
2017-03-30 20:09:35 +00:00
|
|
|
|
2017-02-22 03:42:32 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
use std::net::ToSocketAddrs;
|
2018-05-03 10:07:31 +00:00
|
|
|
use std::io::BufReader;
|
2017-02-22 03:42:32 +00:00
|
|
|
use std::fs;
|
2018-03-22 11:47:27 +00:00
|
|
|
use tokio::io;
|
2018-05-03 10:07:31 +00:00
|
|
|
use tokio::net::TcpStream;
|
2018-03-22 11:47:27 +00:00
|
|
|
use tokio::prelude::*;
|
2017-02-22 03:42:32 +00:00
|
|
|
use clap::{ App, Arg };
|
2017-02-22 05:30:01 +00:00
|
|
|
use rustls::ClientConfig;
|
|
|
|
use tokio_rustls::ClientConfigExt;
|
2017-02-22 03:42:32 +00:00
|
|
|
|
|
|
|
fn app() -> App<'static, 'static> {
|
|
|
|
App::new("client")
|
|
|
|
.about("tokio-rustls client example")
|
|
|
|
.arg(Arg::with_name("host").value_name("HOST").required(true))
|
|
|
|
.arg(Arg::with_name("port").short("p").long("port").value_name("PORT").help("port, default `443`"))
|
|
|
|
.arg(Arg::with_name("domain").short("d").long("domain").value_name("DOMAIN").help("domain"))
|
|
|
|
.arg(Arg::with_name("cafile").short("c").long("cafile").value_name("FILE").help("CA certificate chain"))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let matches = app().get_matches();
|
|
|
|
|
|
|
|
let host = matches.value_of("host").unwrap();
|
2018-03-23 17:22:45 +00:00
|
|
|
let port = matches.value_of("port")
|
|
|
|
.map(|port| port.parse().unwrap())
|
|
|
|
.unwrap_or(443);
|
2018-05-03 10:07:31 +00:00
|
|
|
let domain = matches.value_of("domain").unwrap_or(host).to_owned();
|
2017-02-22 03:42:32 +00:00
|
|
|
let cafile = matches.value_of("cafile");
|
|
|
|
let text = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain);
|
|
|
|
|
|
|
|
let addr = (host, port)
|
|
|
|
.to_socket_addrs().unwrap()
|
|
|
|
.next().unwrap();
|
|
|
|
|
|
|
|
let mut config = ClientConfig::new();
|
|
|
|
if let Some(cafile) = cafile {
|
|
|
|
let mut pem = BufReader::new(fs::File::open(cafile).unwrap());
|
|
|
|
config.root_store.add_pem_file(&mut pem).unwrap();
|
|
|
|
} else {
|
2017-08-28 04:53:47 +00:00
|
|
|
config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
2017-02-22 03:42:32 +00:00
|
|
|
}
|
|
|
|
let arc_config = Arc::new(config);
|
2017-09-03 22:58:55 +00:00
|
|
|
|
2018-05-03 10:07:31 +00:00
|
|
|
let socket = TcpStream::connect(&addr);
|
|
|
|
|
2017-03-30 20:09:35 +00:00
|
|
|
#[cfg(unix)]
|
2018-05-03 10:07:31 +00:00
|
|
|
let resp = {
|
|
|
|
use tokio::reactor::Handle;
|
|
|
|
use tokio_file_unix::{ raw_stdin, raw_stdout, File };
|
|
|
|
|
|
|
|
let stdin = raw_stdin()
|
|
|
|
.and_then(File::new_nb)
|
|
|
|
.and_then(|fd| fd.into_reader(&Handle::current()))
|
|
|
|
.unwrap();
|
|
|
|
let stdout = raw_stdout()
|
|
|
|
.and_then(File::new_nb)
|
|
|
|
.and_then(|fd| fd.into_io(&Handle::current()))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
socket
|
|
|
|
.and_then(move |stream| {
|
|
|
|
let domain = webpki::DNSNameRef::try_from_ascii_str(&domain).unwrap();
|
|
|
|
arc_config.connect_async(domain, stream)
|
|
|
|
})
|
|
|
|
.and_then(move |stream| io::write_all(stream, text))
|
|
|
|
.and_then(move |(stream, _)| {
|
2018-03-23 17:22:45 +00:00
|
|
|
let (r, w) = stream.split();
|
|
|
|
io::copy(r, stdout)
|
2018-05-03 10:07:31 +00:00
|
|
|
.map(drop)
|
|
|
|
.select2(io::copy(stdin, w).map(drop))
|
|
|
|
.map_err(|res| res.split().0)
|
|
|
|
})
|
|
|
|
.map(drop)
|
|
|
|
.map_err(|err| eprintln!("{:?}", err))
|
|
|
|
};
|
2017-02-22 03:42:32 +00:00
|
|
|
|
2017-03-30 20:09:35 +00:00
|
|
|
#[cfg(not(unix))]
|
2018-05-03 10:07:31 +00:00
|
|
|
let resp = {
|
|
|
|
use tokio_fs::{ stdin as tokio_stdin, stdout as tokio_stdout };
|
|
|
|
|
|
|
|
let (stdin, stdout) = (tokio_stdin(), tokio_stdout());
|
|
|
|
|
|
|
|
socket
|
|
|
|
.and_then(move |stream| {
|
|
|
|
let domain = webpki::DNSNameRef::try_from_ascii_str(&domain).unwrap();
|
|
|
|
arc_config.connect_async(domain, stream)
|
|
|
|
})
|
|
|
|
.and_then(move |stream| io::write_all(stream, text))
|
|
|
|
.and_then(move |(stream, _)| {
|
|
|
|
let (r, w) = stream.split();
|
|
|
|
io::copy(r, stdout)
|
|
|
|
.map(drop)
|
|
|
|
.join(io::copy(stdin, w).map(drop))
|
|
|
|
})
|
|
|
|
.map(drop)
|
|
|
|
.map_err(|err| eprintln!("{:?}", err))
|
|
|
|
};
|
|
|
|
|
|
|
|
tokio::run(resp);
|
2017-02-22 03:42:32 +00:00
|
|
|
}
|