Skip to content

Commit

Permalink
Bump version 0.1.9
Browse files Browse the repository at this point in the history
  • Loading branch information
ssrlive committed Feb 29, 2024
1 parent 1b34be1 commit d9aa2d8
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "socks-hub"
version = "0.1.8"
version = "0.1.9"
license = "MIT"
repository = "https://github.com/ssrlive/socks-hub"
homepage = "https://github.com/ssrlive/socks-hub"
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ cargo install socks-hub
## Usage

```shell
socks-hub -h
socks-hub application.
SOCKS5 hub for HTTP and SOCKS5 downstreams proxying.

Usage: socks-hub [OPTIONS] --local-addr <IP:port> --server-addr <IP:port>

Expand All @@ -42,6 +41,8 @@ Options:
-s, --server-addr <IP:port> Remote SOCKS5 server address
-u, --username <username> Client authentication username, available both for HTTP and SOCKS5, optional
-p, --password <password> Client authentication password, available both for HTTP and SOCKS5, optional
--s5-username <username> SOCKS5 server authentication username, optional
--s5-password <password> SOCKS5 server authentication password, optional
-v, --verbosity <level> Log verbosity level [default: info] [possible values: off, error, warn, info, debug, trace]
-h, --help Print help
-V, --version Print version
Expand Down
20 changes: 19 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use serde_derive::{Deserialize, Serialize};
use socks5_impl::protocol::UserKey;
use std::net::SocketAddr;

/// Proxy tunnel from HTTP or SOCKS5 to SOCKS5
#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize)]
#[command(author, version, about = "socks-hub application.", long_about = None)]
#[command(author, version, about = "SOCKS5 hub for HTTP and SOCKS5 downstreams proxying.", long_about = None)]
pub struct Config {
/// Source type
#[arg(short = 't', long, value_name = "http|socks5", default_value = "http")]
Expand All @@ -25,6 +26,14 @@ pub struct Config {
#[arg(short, long, value_name = "password")]
pub password: Option<String>,

/// SOCKS5 server authentication username, optional
#[arg(long, value_name = "username")]
pub s5_username: Option<String>,

/// SOCKS5 server authentication password, optional
#[arg(long, value_name = "password")]
pub s5_password: Option<String>,

/// Log verbosity level
#[arg(short, long, value_name = "level", default_value = "info")]
pub verbosity: ArgVerbosity,
Expand All @@ -40,6 +49,8 @@ impl Default for Config {
server_addr,
username: None,
password: None,
s5_username: None,
s5_password: None,
verbosity: ArgVerbosity::Info,
}
}
Expand Down Expand Up @@ -95,6 +106,13 @@ impl Config {
password: self.password.clone(),
}
}

pub fn get_socks5_credentials(&self) -> Option<UserKey> {
match (self.s5_username.clone(), self.s5_password.clone()) {
(Some(u), Some(p)) => Some(UserKey::new(u, p)),
_ => None,
}
}
}

#[repr(C)]
Expand Down
11 changes: 6 additions & 5 deletions src/http2socks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use hyper::{
upgrade::Upgraded,
Method, Request, Response,
};
use socks5_impl::protocol::Address;
use socks5_impl::protocol::{Address, UserKey};
use std::net::SocketAddr;
use tokio::{net::TcpListener, sync::mpsc::Receiver};

Expand Down Expand Up @@ -75,6 +75,7 @@ async fn proxy(

let server = config.server_addr;
let credentials = config.get_credentials();
let s5_auth = config.get_socks5_credentials();

fn get_proxy_authorization(req: &Request<hyper::body::Incoming>) -> (Option<HeaderName>, Option<&HeaderValue>) {
if let Some(header) = req.headers().get(AUTHORIZATION) {
Expand Down Expand Up @@ -108,7 +109,7 @@ async fn proxy(
tokio::task::spawn(async move {
match hyper::upgrade::on(req).await {
Ok(upgraded) => {
if let Err(e) = tunnel(upgraded, s5addr, server).await {
if let Err(e) = tunnel(upgraded, s5addr, server, s5_auth).await {
log::error!("server io error: {}", e);
};
}
Expand All @@ -129,7 +130,7 @@ async fn proxy(

log::debug!("destination address {}", s5addr);
log::debug!("connect to SOCKS5 proxy server {:?}", server);
let stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &s5addr, None).await?;
let stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &s5addr, s5_auth).await?;
let io = TokioIo::new(stream);
let (mut sender, conn) = hyper::client::conn::http1::Builder::new()
.preserve_header_case(true)
Expand Down Expand Up @@ -157,9 +158,9 @@ fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {

// Create a TCP connection to host:port, build a tunnel between the connection and
// the upgraded connection
async fn tunnel(upgraded: Upgraded, dst: Address, server: SocketAddr) -> std::io::Result<()> {
async fn tunnel(upgraded: Upgraded, dst: Address, server: SocketAddr, auth: Option<UserKey>) -> std::io::Result<()> {
let mut upgraded = TokioIo::new(upgraded);
let mut server = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, None).await?;
let mut server = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, auth).await?;
let (from_client, from_server) = tokio::io::copy_bidirectional(&mut upgraded, &mut server).await?;
log::debug!("client wrote {} bytes and received {} bytes", from_client, from_server);
Ok(())
Expand Down
34 changes: 23 additions & 11 deletions src/socks2socks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{BoxError, Config, Result, CONNECT_TIMEOUT};
use socks5_impl::{
protocol::{Address, Reply, UdpHeader},
protocol::{Address, Reply, UdpHeader, UserKey},
server::{
auth,
connection::{associate, connect},
Expand All @@ -19,14 +19,15 @@ where
let listen_addr = config.local_addr;
let server_addr = config.server_addr;
let credentials = config.get_credentials();
let s5_auth = config.get_socks5_credentials();
match (credentials.username, credentials.password) {
(Some(username), Some(password)) => {
let auth = Arc::new(auth::UserKeyAuth::new(&username, &password));
main_loop(auth, listen_addr, server_addr, quit, callback).await?;
main_loop(auth, listen_addr, server_addr, s5_auth, quit, callback).await?;
}
_ => {
let auth = Arc::new(auth::NoAuth);
main_loop(auth, listen_addr, server_addr, quit, callback).await?;
main_loop(auth, listen_addr, server_addr, s5_auth, quit, callback).await?;
}
}

Expand All @@ -37,6 +38,7 @@ async fn main_loop<S, F>(
auth: auth::AuthAdaptor<S>,
listen_addr: SocketAddr,
server: SocketAddr,
s5_auth: Option<UserKey>,
mut quit: Receiver<()>,
callback: Option<F>,
) -> Result<()>
Expand All @@ -58,8 +60,9 @@ where
}
result = listener.accept() => {
let (conn, _) = result?;
let s5_auth = s5_auth.clone();
tokio::spawn(async move {
if let Err(err) = handle(conn, server).await {
if let Err(err) = handle(conn, server, s5_auth).await {
log::error!("{err}");
}
});
Expand All @@ -69,7 +72,7 @@ where
Ok(())
}

async fn handle<S>(conn: IncomingConnection<S>, server: SocketAddr) -> Result<()>
async fn handle<S>(conn: IncomingConnection<S>, server: SocketAddr, s5_auth: Option<UserKey>) -> Result<()>
where
S: Send + Sync + 'static,
{
Expand All @@ -86,22 +89,27 @@ where

match conn.wait_request().await? {
ClientConnection::UdpAssociate(associate, _) => {
handle_s5_upd_associate(associate, server).await?;
handle_s5_upd_associate(associate, server, s5_auth).await?;
}
ClientConnection::Bind(bind, _) => {
let mut conn = bind.reply(Reply::CommandNotSupported, Address::unspecified()).await?;
conn.shutdown().await?;
}
ClientConnection::Connect(connect, dst) => {
handle_s5_client_connection(connect, dst, server).await?;
handle_s5_client_connection(connect, dst, server, s5_auth).await?;
}
}

Ok(())
}

async fn handle_s5_client_connection(connect: Connect<connect::NeedReply>, dst: Address, server: SocketAddr) -> Result<()> {
let mut stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, None).await?;
async fn handle_s5_client_connection(
connect: Connect<connect::NeedReply>,
dst: Address,
server: SocketAddr,
s5_auth: Option<UserKey>,
) -> Result<()> {
let mut stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, s5_auth).await?;
let mut conn = connect.reply(Reply::Succeeded, Address::unspecified()).await?;
log::trace!("{} -> {}", conn.peer_addr()?, dst);

Expand All @@ -110,7 +118,11 @@ async fn handle_s5_client_connection(connect: Connect<connect::NeedReply>, dst:
Ok(())
}

pub(crate) async fn handle_s5_upd_associate(associate: UdpAssociate<associate::NeedReply>, server: SocketAddr) -> Result<()> {
pub(crate) async fn handle_s5_upd_associate(
associate: UdpAssociate<associate::NeedReply>,
server: SocketAddr,
s5_auth: Option<UserKey>,
) -> Result<()> {
// listen on a random port
let listen_ip = associate.local_addr()?.ip();
let udp_listener = UdpSocket::bind(SocketAddr::from((listen_ip, 0))).await;
Expand All @@ -133,7 +145,7 @@ pub(crate) async fn handle_s5_upd_associate(associate: UdpAssociate<associate::N
let incoming_addr = std::sync::OnceLock::new();

// TODO: UserKey is always None, this is a bug
let s5_udp_client = socks5_impl::client::create_udp_client(server, None).await?;
let s5_udp_client = socks5_impl::client::create_udp_client(server, s5_auth).await?;

let res = loop {
tokio::select! {
Expand Down

0 comments on commit d9aa2d8

Please sign in to comment.