use clap::{Arg, App};
use async_proxy::clients::socks4::general::{
Socks4General, ConnParams
};
use async_proxy::general::ConnectionTimeouts;
use async_proxy::proxy::ProxyStream;
use tokio::net::TcpStream;
use tokio::time::timeout;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::net::SocketAddr;
use std::time::Duration;
use ansi_term::Color;
macro_rules! message {
(Success, $m:expr $(, $a:expr)* $(,)?) => {
print!("{} ", Color::Green.bold().paint("Success:"));
println!($m, $($a), *);
};
(Info, $m:expr $(, $a:expr)* $(,)?) => {
print!("{} ", Color::White.bold().paint("Info:"));
println!($m, $($a), *);
};
(Error, $m:expr $(, $a:expr)* $(,)?) => {
print!("{} ", Color::Red.bold().paint("Error:"));
println!($m, $($a), *);
};
(Fatal, $m:expr $(, $a:expr)* $(,)?) => {
message!(Error, $m, $($a), *);
std::process::exit(1);
};
}
trait Fatal<T> {
fn fatal(self, message: &str) -> T;
}
impl<T, E> Fatal<T> for Result<T, E> {
fn fatal(self, message: &str) -> T {
match self {
Ok(value) => value,
Err(_) => {
message!(Fatal, "{}", message);
}
}
}
}
impl<T> Fatal<T> for Option<T> {
fn fatal(self, message: &str) -> T {
match self {
Some(value) => value,
None => {
message!(Fatal, "{}", message);
}
}
}
}
#[tokio::main]
async fn main() {
let matches = App::new("Example program using async proxies")
.version("0.1.0")
.author("TonyGraim")
.arg(Arg::with_name("proxy-address")
.short("p")
.long("proxy-addr")
.takes_value(true)
.required(true)
.help("The address of the socks4-proxy in format `ip:port`"))
.arg(Arg::with_name("destination")
.short("d")
.long("destination")
.takes_value(true)
.required(true)
.help("The destination point in format `ipv4:port`"))
.arg(Arg::with_name("ident")
.short("i")
.long("ident")
.takes_value(true)
.default_value("")
.help("The ident used for Socks4 connection establishment"))
.get_matches();
let server_addr = matches.value_of("proxy-address").unwrap();
let destination = matches.value_of("destination").unwrap();
let ident = matches.value_of("ident").unwrap();
let timeouts = ConnectionTimeouts::new(
Duration::from_secs(8),
Duration::from_secs(8),
Duration::from_secs(8)
);
let ident = std::borrow::Cow::Owned(ident.to_owned());
let connection_params = ConnParams::new(destination.parse().unwrap(),
ident,
timeouts);
message!(Info, "Starting connection to the Socks4 proxy server `{}`", server_addr);
let server_socket_addr: SocketAddr = server_addr.parse().unwrap();
let future = TcpStream::connect(server_socket_addr);
let future = timeout(Duration::from_secs(8), future);
let stream = future.await
.fatal("Timeout of 8 seconds reached")
.fatal("Unable to connect to the proxy server");
message!(Info, "Starting connection to the destination `{}` throught socks4 proxy `{}`",
destination, server_addr);
let mut stream = match Socks4General::connect(stream, connection_params).await {
Ok(stream) => {
message!(Success, "Successfully connected to the service through the proxy");
stream
},
Err(e) => {
message!(Fatal, "Cannot connect to the service: {}", e);
}
};
println!("Please inter a message to be sent. {}",
Color::White.bold().paint("Message:"));
let mut input = String::new();
std::io::stdin().read_line(&mut input)
.fatal("Unable to read a line from stdin");
let future = stream.write_all(input.as_bytes());
let future = timeout(Duration::from_secs(8), future);
future.await.fatal("Timeout of 8 seconds reached")
.fatal("Unable to send the message");
let future = stream.read_to_string(&mut input);
let future = timeout(Duration::from_secs(8), future);
future.await.fatal("Timeout of 8 seconds reached")
.fatal("Unable to receive a string from the service");
message!(Success, "Received message from the service: {}", input);
}