init commit from github.com/dizda/fast-socks5
This commit is contained in:
122
examples/client.rs
Normal file
122
examples/client.rs
Normal 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
125
examples/server.rs
Normal 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);
|
||||
}
|
||||
})
|
||||
}
|
||||
118
examples/simple_tcp_server.rs
Normal file
118
examples/simple_tcp_server.rs
Normal 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);
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user