init commit from github.com/dizda/fast-socks5

This commit is contained in:
2022-06-08 00:40:54 +08:00
parent c7c77aaec0
commit c6b81bd13b
14 changed files with 3119 additions and 6 deletions

122
examples/client.rs Normal file
View File

@@ -0,0 +1,122 @@
#[forbid(unsafe_code)]
#[macro_use]
extern crate log;
use anyhow::Context;
use fast_socks5::client::Config;
use fast_socks5::{client::Socks5Stream, Result};
use structopt::StructOpt;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
/// # How to use it:
///
/// GET / of web server by IPv4 address:
/// `$ RUST_LOG=debug cargo run --example client -- --socks-server 127.0.0.1:1337 --username admin --password password -a 208.97.177.124 -p 80`
///
/// GET / of web server by IPv6 address:
/// `$ RUST_LOG=debug cargo run --example client -- --socks-server 127.0.0.1:1337 --username admin --password password -a ::ffff:208.97.177.124 -p 80`
///
/// GET / of web server by domain name:
/// `$ RUST_LOG=debug cargo run --example client -- --socks-server 127.0.0.1:1337 --username admin --password password -a perdu.com -p 80`
///
#[derive(Debug, StructOpt)]
#[structopt(name = "socks5-client", about = "A simple example of a socks5-client.")]
struct Opt {
/// Socks5 server address + port. eg. `127.0.0.1:1080`
#[structopt(short, long)]
pub socks_server: String,
/// Target address server (not the socks server)
#[structopt(short = "a", long)]
pub target_addr: String,
/// Target port server (not the socks server)
#[structopt(short = "p", long)]
pub target_port: u16,
#[structopt(short, long)]
pub username: Option<String>,
#[structopt(long)]
pub password: Option<String>,
/// Don't perform the auth handshake, send directly the command request
#[structopt(short = "k", long)]
pub skip_auth: bool,
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
spawn_socks_client().await
}
async fn spawn_socks_client() -> Result<()> {
let opt: Opt = Opt::from_args();
let domain = opt.target_addr.clone();
let mut socks;
let mut config = Config::default();
config.set_skip_auth(opt.skip_auth);
// Creating a SOCKS stream to the target address thru the socks server
if opt.username.is_some() {
socks = Socks5Stream::connect_with_password(
opt.socks_server,
opt.target_addr,
opt.target_port,
opt.username.unwrap(),
opt.password.expect("Please fill the password"),
config,
)
.await?;
} else {
socks = Socks5Stream::connect(opt.socks_server, opt.target_addr, opt.target_port, config)
.await?;
}
// Once connection is completed, can start to communicate with the server
http_request(&mut socks, domain).await?;
Ok(())
}
/// Simple HTTP request
async fn http_request<T: AsyncRead + AsyncWrite + Unpin>(
stream: &mut T,
domain: String,
) -> Result<()> {
debug!("Requesting body...");
// construct our request, with a dynamic domain
let mut headers = vec![];
headers.extend_from_slice("GET / HTTP/1.1\r\nHost: ".as_bytes());
headers.extend_from_slice(domain.as_bytes());
headers
.extend_from_slice("\r\nUser-Agent: fast-socks5/0.1.0\r\nAccept: */*\r\n\r\n".as_bytes());
// flush headers
stream
.write_all(&headers)
.await
.context("Can't write HTTP Headers")?;
debug!("Reading body response...");
let mut result = [0u8; 1024];
// warning: read_to_end() method sometimes await forever when the web server
// doesn't write EOF char (\r\n\r\n).
// read() seems more appropriate
stream
.read(&mut result)
.await
.context("Can't read HTTP Response")?;
info!("Response: {}", String::from_utf8_lossy(&result));
if result.starts_with(b"HTTP/1.1") {
info!("HTTP/1.1 Response detected!");
}
//assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
Ok(())
}

125
examples/server.rs Normal file
View File

@@ -0,0 +1,125 @@
#[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;
/// # How to use it:
///
/// Listen on a local address, authentication-free:
/// `$ RUST_LOG=debug cargo run --example server -- --listen-addr 127.0.0.1:1337 no-auth`
///
/// Listen on a local address, with basic username/password requirement:
/// `$ RUST_LOG=debug cargo run --example server -- --listen-addr 127.0.0.1:1337 password --username admin --password password`
///
#[derive(Debug, StructOpt)]
#[structopt(
name = "socks5-server",
about = "A simple implementation of a socks5-server."
)]
struct Opt {
/// Bind on address address. eg. `127.0.0.1:1080`
#[structopt(short, long)]
pub listen_addr: String,
/// Request timeout
#[structopt(short = "t", long, default_value = "10")]
pub request_timeout: u64,
/// Choose authentication type
#[structopt(subcommand, name = "auth")] // Note that we mark a field as a subcommand
pub auth: AuthMode,
/// Don't perform the auth handshake, send directly the command request
#[structopt(short = "k", long)]
pub skip_auth: bool,
}
/// Choose the authentication type
#[derive(StructOpt, Debug)]
enum AuthMode {
NoAuth,
Password {
#[structopt(short, long)]
username: String,
#[structopt(short, long)]
password: String,
},
}
/// Useful read 1. https://blog.yoshuawuyts.com/rust-streams/
/// Useful read 2. https://blog.yoshuawuyts.com/futures-concurrency/
/// Useful read 3. https://blog.yoshuawuyts.com/streams-concurrency/
/// error-libs benchmark: https://blog.yoshuawuyts.com/error-handling-survey/
///
/// TODO: Write functional tests: https://github.com/ark0f/async-socks5/blob/master/src/lib.rs#L762
/// TODO: Write functional tests with cURL?
#[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);
// Standard TCP loop
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);
}
})
}

View File

@@ -0,0 +1,118 @@
#[forbid(unsafe_code)]
#[macro_use]
extern crate log;
use fast_socks5::{
server::{Config, SimpleUserPassword, Socks5Socket},
Result,
};
use std::future::Future;
use std::sync::Arc;
use structopt::StructOpt;
use tokio::task;
use tokio::{
io::{AsyncRead, AsyncWrite},
net::TcpListener,
};
/// # How to use it:
///
/// Listen on a local address, authentication-free:
/// `$ RUST_LOG=debug cargo run --example simple_tcp_server -- --listen-addr 127.0.0.1:1337 no-auth`
///
/// Listen on a local address, with basic username/password requirement:
/// `$ RUST_LOG=debug cargo run --example simple_tcp_server -- --listen-addr 127.0.0.1:1337 password --username admin --password password`
///
#[derive(Debug, StructOpt)]
#[structopt(
name = "socks5-server",
about = "A simple implementation of a socks5-server."
)]
struct Opt {
/// Bind on address address. eg. `127.0.0.1:1080`
#[structopt(short, long)]
pub listen_addr: String,
/// Request timeout
#[structopt(short = "t", long, default_value = "10")]
pub request_timeout: u64,
/// Choose authentication type
#[structopt(subcommand, name = "auth")] // Note that we mark a field as a subcommand
pub auth: AuthMode,
}
/// Choose the authentication type
#[derive(StructOpt, Debug)]
enum AuthMode {
NoAuth,
Password {
#[structopt(short, long)]
username: String,
#[structopt(short, long)]
password: String,
},
}
/// Useful read 1. https://blog.yoshuawuyts.com/rust-streams/
/// Useful read 2. https://blog.yoshuawuyts.com/futures-concurrency/
/// Useful read 3. https://blog.yoshuawuyts.com/streams-concurrency/
/// error-libs benchmark: https://blog.yoshuawuyts.com/error-handling-survey/
///
/// TODO: Command to use the socks server with a simple user/password
/// TODO: Write functional tests: https://github.com/ark0f/async-socks5/blob/master/src/lib.rs#L762
/// TODO: Write functional tests with cURL?
/// TODO: Move this to as a standalone library
#[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);
match opt.auth {
AuthMode::NoAuth => warn!("No authentication has been set!"),
AuthMode::Password { username, password } => {
config.set_authentication(SimpleUserPassword { username, password });
info!("Simple auth system has been set.");
}
}
let config = Arc::new(config);
let listener = TcpListener::bind(&opt.listen_addr).await?;
// listener.set_config(config);
info!("Listen for socks connections @ {}", &opt.listen_addr);
// Standard TCP loop
loop {
match listener.accept().await {
Ok((socket, _addr)) => {
info!("Connection from {}", socket.peer_addr()?);
let socket = Socks5Socket::new(socket, config.clone());
spawn_and_log_error(socket.upgrade_to_socks5());
}
Err(err) => error!("accept error = {:?}", err),
}
}
}
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);
}
})
}