Skip to content

Commit

Permalink
feat(local): support HTTP requests in socks local server
Browse files Browse the repository at this point in the history
When local-http is enabled, socks server will check the first byte of
the TCP request and guest the connection type (socks5, socks4a, http).
  • Loading branch information
zonyitoo committed Nov 27, 2023
1 parent 2eff5db commit 964aaf4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 54 deletions.
14 changes: 8 additions & 6 deletions crates/shadowsocks-service/src/local/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,12 @@ impl Http {
};

trace!("HTTP accepted client from {}", peer_addr);
tokio::spawn(handler.clone().serve_connection(stream, peer_addr));
let handler = handler.clone();
tokio::spawn(async move {
if let Err(err) = handler.serve_connection(stream, peer_addr).await {
error!("HTTP connection {} handler failed with error: {}", peer_addr, err);
}
});
}
}
}
Expand All @@ -145,7 +150,7 @@ impl HttpConnectionHandler {
}

/// Handle a TCP HTTP connection
pub async fn serve_connection<S>(self, stream: S, peer_addr: SocketAddr)
pub async fn serve_connection<S>(self, stream: S, peer_addr: SocketAddr) -> hyper::Result<()>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
Expand All @@ -159,7 +164,7 @@ impl HttpConnectionHandler {

// NOTE: Some stupid clients requires HTTP header keys to be case-sensitive.
// For example: Nintendo Switch
if let Err(err) = http1::Builder::new()
http1::Builder::new()
.keep_alive(true)
.title_case_headers(true)
.preserve_header_case(true)
Expand All @@ -172,8 +177,5 @@ impl HttpConnectionHandler {
)
.with_upgrades()
.await
{
error!("failed to serve HTTP connection, error: {}", err);
}
}
}
117 changes: 69 additions & 48 deletions crates/shadowsocks-service/src/local/socks/server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use log::{error, info};
use shadowsocks::{config::Mode, net::TcpListener as ShadowTcpListener, ServerAddr};
use tokio::{net::TcpStream, time};

#[cfg(feature = "local-http")]
use crate::local::http::HttpConnectionHandler;
use crate::local::{
context::ServiceContext,
loadbalancing::PingBalancer,
net::tcp::listener::create_standard_tcp_listener,
context::ServiceContext, loadbalancing::PingBalancer, net::tcp::listener::create_standard_tcp_listener,
socks::config::Socks5AuthConfig,
};

Expand Down Expand Up @@ -104,6 +104,8 @@ impl SocksTcpServer {

// If UDP is enabled, SOCK5 UDP_ASSOCIATE command will let client to send requests to this address
let udp_bind_addr = Arc::new(self.udp_bind_addr);
#[cfg(feature = "local-http")]
let http_handler = HttpConnectionHandler::new(self.context.clone(), self.balancer.clone());

loop {
let (stream, peer_addr) = match self.listener.accept().await {
Expand All @@ -115,57 +117,90 @@ impl SocksTcpServer {
}
};

let balancer = self.balancer.clone();
let context = self.context.clone();
let udp_bind_addr = udp_bind_addr.clone();
let socks5_auth = self.socks5_auth.clone();
let mode = self.mode;
let handler = SocksTcpHandler {
context: self.context.clone(),
udp_bind_addr: udp_bind_addr.clone(),
stream,
balancer: self.balancer.clone(),
peer_addr,
mode: self.mode,
socks5_auth: self.socks5_auth.clone(),
#[cfg(feature = "local-http")]
http_handler: http_handler.clone(),
};

tokio::spawn(async move {
if let Err(err) = SocksTcpServer::handle_tcp_client(
context,
udp_bind_addr,
stream,
balancer,
peer_addr,
mode,
socks5_auth,
)
.await
{
if let Err(err) = handler.handle_tcp_client().await {
error!("socks5 tcp client handler error: {}", err);
}
});
}
}
}

#[cfg(feature = "local-socks4")]
async fn handle_tcp_client(
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
stream: TcpStream,
balancer: PingBalancer,
peer_addr: SocketAddr,
mode: Mode,
socks5_auth: Arc<Socks5AuthConfig>,
) -> io::Result<()> {
struct SocksTcpHandler {
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
stream: TcpStream,
balancer: PingBalancer,
peer_addr: SocketAddr,
mode: Mode,
socks5_auth: Arc<Socks5AuthConfig>,
#[cfg(feature = "local-http")]
http_handler: HttpConnectionHandler,
}

impl SocksTcpHandler {
#[cfg(not(any(feature = "local-socks4", feature = "local-http")))]
async fn handle_tcp_client(self) -> io::Result<()> {
let handler = Socks5TcpHandler::new(
self.context,
self.udp_bind_addr,
self.balancer,
self.mode,
self.socks5_auth,
);
handler.handle_socks5_client(self.stream, self.peer_addr).await
}

#[cfg(any(feature = "local-socks4", feature = "local-http"))]
async fn handle_tcp_client(self) -> io::Result<()> {
use std::io::ErrorKind;

let mut version_buffer = [0u8; 1];
let n = stream.peek(&mut version_buffer).await?;
let n = self.stream.peek(&mut version_buffer).await?;
if n == 0 {
return Err(ErrorKind::UnexpectedEof.into());
}

match version_buffer[0] {
#[cfg(feature = "local-socks4")]
0x04 => {
let handler = Socks4TcpHandler::new(context, balancer, mode);
handler.handle_socks4_client(stream, peer_addr).await
let handler = Socks4TcpHandler::new(self.context, self.balancer, self.mode);
handler.handle_socks4_client(self.stream, self.peer_addr).await
}

0x05 => {
let handler = Socks5TcpHandler::new(context, udp_bind_addr, balancer, mode, socks5_auth);
handler.handle_socks5_client(stream, peer_addr).await
let handler = Socks5TcpHandler::new(
self.context,
self.udp_bind_addr,
self.balancer,
self.mode,
self.socks5_auth,
);
handler.handle_socks5_client(self.stream, self.peer_addr).await
}

#[cfg(feature = "local-http")]
b'G' | b'g' | b'H' | b'h' | b'P' | b'p' | b'D' | b'd' | b'C' | b'c' | b'O' | b'o' | b'T' | b't' => {
// GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
match self.http_handler.serve_connection(self.stream, self.peer_addr).await {
Ok(..) => Ok(()),
Err(err) => {
error!("HTTP connection {} handler failed with error: {}", self.peer_addr, err);
Err(io::Error::new(ErrorKind::Other, err))
}
}
}

version => {
Expand All @@ -175,20 +210,6 @@ impl SocksTcpServer {
}
}
}

#[cfg(not(feature = "local-socks4"))]
async fn handle_tcp_client(
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
stream: TcpStream,
balancer: PingBalancer,
peer_addr: SocketAddr,
mode: Mode,
socks5_auth: Arc<Socks5AuthConfig>,
) -> io::Result<()> {
let handler = Socks5TcpHandler::new(context, udp_bind_addr, balancer, mode, socks5_auth);
handler.handle_socks5_client(stream, peer_addr).await
}
}

/// SOCKS UDP server
Expand Down

0 comments on commit 964aaf4

Please sign in to comment.