2019-05-22 15:57:14 +00:00
|
|
|
#![feature(async_await)]
|
2017-03-30 20:09:35 +00:00
|
|
|
|
2019-05-22 15:57:14 +00:00
|
|
|
use std::io;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::path::PathBuf;
|
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;
|
2019-05-22 15:57:14 +00:00
|
|
|
use structopt::StructOpt;
|
|
|
|
use romio::TcpStream;
|
|
|
|
use futures::prelude::*;
|
|
|
|
use futures::executor;
|
|
|
|
use futures::compat::{ AsyncRead01CompatExt, AsyncWrite01CompatExt };
|
|
|
|
use tokio_rustls::{ TlsConnector, rustls::ClientConfig, webpki::DNSNameRef };
|
2018-08-21 01:57:27 +00:00
|
|
|
use tokio_stdin_stdout::{ stdin as tokio_stdin, stdout as tokio_stdout };
|
2017-02-22 03:42:32 +00:00
|
|
|
|
|
|
|
|
2019-05-22 15:57:14 +00:00
|
|
|
#[derive(StructOpt)]
|
|
|
|
struct Options {
|
|
|
|
host: String,
|
|
|
|
|
|
|
|
/// port
|
|
|
|
#[structopt(short="p", long="port", default_value="443")]
|
|
|
|
port: u16,
|
|
|
|
|
|
|
|
/// domain
|
|
|
|
#[structopt(short="d", long="domain")]
|
|
|
|
domain: Option<String>,
|
|
|
|
|
|
|
|
/// cafile
|
|
|
|
#[structopt(short="c", long="cafile", parse(from_os_str))]
|
|
|
|
cafile: Option<PathBuf>
|
|
|
|
}
|
2017-02-22 03:42:32 +00:00
|
|
|
|
|
|
|
|
2019-05-22 15:57:14 +00:00
|
|
|
fn main() -> io::Result<()> {
|
|
|
|
let options = Options::from_args();
|
2017-02-22 03:42:32 +00:00
|
|
|
|
2019-05-22 15:57:14 +00:00
|
|
|
let addr = (options.host.as_str(), options.port)
|
|
|
|
.to_socket_addrs()?
|
|
|
|
.next()
|
|
|
|
.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
|
|
|
|
let domain = options.domain.unwrap_or(options.host);
|
|
|
|
let content = format!(
|
|
|
|
"GET / HTTP/1.0\r\nHost: {}\r\n\r\n",
|
|
|
|
domain
|
|
|
|
);
|
2017-02-22 03:42:32 +00:00
|
|
|
|
|
|
|
let mut config = ClientConfig::new();
|
2019-05-22 15:57:14 +00:00
|
|
|
if let Some(cafile) = &options.cafile {
|
|
|
|
let mut pem = BufReader::new(File::open(cafile)?);
|
|
|
|
config.root_store.add_pem_file(&mut pem)
|
|
|
|
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?;
|
2017-02-22 03:42:32 +00:00
|
|
|
} 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
|
|
|
}
|
2019-05-22 15:57:14 +00:00
|
|
|
let connector = TlsConnector::from(Arc::new(config));
|
|
|
|
|
|
|
|
let fut = async {
|
|
|
|
let stream = TcpStream::connect(&addr).await?;
|
|
|
|
let (mut stdin, mut stdout) = (tokio_stdin(0).compat(), tokio_stdout(0).compat());
|
|
|
|
|
|
|
|
let domain = DNSNameRef::try_from_ascii_str(&domain)
|
|
|
|
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
|
|
|
|
|
|
|
|
let mut stream = connector.connect(domain, stream).await?;
|
|
|
|
stream.write_all(content.as_bytes()).await?;
|
|
|
|
|
|
|
|
let (mut reader, mut writer) = stream.split();
|
|
|
|
future::try_join(
|
|
|
|
reader.copy_into(&mut stdout),
|
|
|
|
stdin.copy_into(&mut writer)
|
|
|
|
).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
executor::block_on(fut)
|
2017-02-22 03:42:32 +00:00
|
|
|
}
|