#[forbid(unsafe_code)]
#[macro_use]
extern crate log;
use fast_socks5::{
server::{Config, SimpleUserPassword, Socks5Server, Socks5Socket},
Result, SocksError,
};
use std::future::Future;
use structopt::StructOpt;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::task;
use tokio_stream::StreamExt;
#[derive(Debug, StructOpt)]
#[structopt(
name = "socks5-server",
about = "A simple implementation of a socks5-server."
)]
struct Opt {
#[structopt(short, long)]
pub listen_addr: String,
#[structopt(short = "t", long, default_value = "10")]
pub request_timeout: u64,
#[structopt(subcommand, name = "auth")] pub auth: AuthMode,
#[structopt(short = "k", long)]
pub skip_auth: bool,
}
#[derive(StructOpt, Debug)]
enum AuthMode {
NoAuth,
Password {
#[structopt(short, long)]
username: String,
#[structopt(short, long)]
password: String,
},
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
spawn_socks_server().await
}
async fn spawn_socks_server() -> Result<()> {
let opt: Opt = Opt::from_args();
let mut config = Config::default();
config.set_request_timeout(opt.request_timeout);
config.set_skip_auth(opt.skip_auth);
match opt.auth {
AuthMode::NoAuth => warn!("No authentication has been set!"),
AuthMode::Password { username, password } => {
if opt.skip_auth {
return Err(SocksError::ArgumentInputError(
"Can't use skip-auth flag and authentication altogether.",
));
}
config.set_authentication(SimpleUserPassword { username, password });
info!("Simple auth system has been set.");
}
}
let mut listener = Socks5Server::bind(&opt.listen_addr).await?;
listener.set_config(config);
let mut incoming = listener.incoming();
info!("Listen for socks connections @ {}", &opt.listen_addr);
while let Some(socket_res) = incoming.next().await {
match socket_res {
Ok(socket) => {
spawn_and_log_error(socket.upgrade_to_socks5());
}
Err(err) => {
error!("accept error = {:?}", err);
}
}
}
Ok(())
}
fn spawn_and_log_error<F, T>(fut: F) -> task::JoinHandle<()>
where
F: Future<Output = Result<Socks5Socket<T>>> + Send + 'static,
T: AsyncRead + AsyncWrite + Unpin,
{
task::spawn(async move {
if let Err(e) = fut.await {
error!("{:#}", &e);
}
})
}