Skip to content

Commit

Permalink
feat(local): Allow configuring udp associate addr
Browse files Browse the repository at this point in the history
User might want to bind the socket to a different address
than what is advertised in the Socks 5 UDP Associate responses.

This adds a new command line option to control only that but to still
default to udp bind addr and local address.

Example:

    sslocal --local-addr 0.0.0.0:1080 --udp-associate-addr 10.51.1.1:1080 -U

Here the server would without this change respond to UDP Associate request with
address 0.0.0.0:1080. With the new switch, it responds 10.51.1.1:1080.
  • Loading branch information
vvilhonen authored and zonyitoo committed Aug 13, 2024
1 parent 4c2bdae commit 0041f62
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 17 deletions.
4 changes: 4 additions & 0 deletions crates/shadowsocks-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,9 @@ pub struct LocalConfig {
/// Resolving Android's issue: [shadowsocks/shadowsocks-android#2571](https://github.com/shadowsocks/shadowsocks-android/issues/2571)
pub udp_addr: Option<ServerAddr>,

/// UDP Associate address. Uses `udp_addr` if not specified
pub udp_associate_addr: Option<ServerAddr>,

/// Destination address for tunnel
#[cfg(feature = "local-tunnel")]
pub forward_addr: Option<Address>,
Expand Down Expand Up @@ -1027,6 +1030,7 @@ impl LocalConfig {

mode,
udp_addr: None,
udp_associate_addr: None,

#[cfg(feature = "local-tunnel")]
forward_addr: None,
Expand Down
3 changes: 3 additions & 0 deletions crates/shadowsocks-service/src/local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ impl Server {
if let Some(b) = local_config.udp_addr {
server_builder.set_udp_bind_addr(b.clone());
}
if let Some(b) = local_config.udp_associate_addr {
server_builder.set_udp_associate_addr(b.clone());
}

#[cfg(target_os = "macos")]
if let Some(n) = local_config.launchd_tcp_socket_name {
Expand Down
18 changes: 15 additions & 3 deletions crates/shadowsocks-service/src/local/socks/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct SocksBuilder {
udp_expiry_duration: Option<Duration>,
udp_capacity: Option<usize>,
udp_bind_addr: Option<ServerAddr>,
udp_associate_addr: Option<ServerAddr>,
socks5_auth: Socks5AuthConfig,
client_config: ServerAddr,
balancer: PingBalancer,
Expand Down Expand Up @@ -53,6 +54,7 @@ impl SocksBuilder {
udp_expiry_duration: None,
udp_capacity: None,
udp_bind_addr: None,
udp_associate_addr: None,
socks5_auth: Socks5AuthConfig::default(),
client_config,
balancer,
Expand Down Expand Up @@ -86,6 +88,10 @@ impl SocksBuilder {
self.udp_bind_addr = Some(a);
}

pub fn set_udp_associate_addr(&mut self, a: ServerAddr) {
self.udp_associate_addr = Some(a);
}

/// Set SOCKS5 Username/Password Authentication configuration
pub fn set_socks5_auth(&mut self, p: Socks5AuthConfig) {
self.socks5_auth = p;
Expand All @@ -104,14 +110,20 @@ impl SocksBuilder {
}

pub async fn build(self) -> io::Result<Socks> {
let udp_bind_addr = self.udp_bind_addr.unwrap_or_else(|| self.client_config.clone());
let udp_bind_addr = self.udp_bind_addr.clone().unwrap_or_else(|| self.client_config.clone());
let udp_associate_addr: ServerAddr = self
.udp_associate_addr
.as_ref()
.or_else(|| self.udp_bind_addr.as_ref())

Check warning on line 117 in crates/shadowsocks-service/src/local/socks/server/mod.rs

View workflow job for this annotation

GitHub Actions / clippy ubuntu-latest

unnecessary closure used to substitute value for `Option::None`

warning: unnecessary closure used to substitute value for `Option::None` --> crates/shadowsocks-service/src/local/socks/server/mod.rs:114:46 | 114 | let udp_associate_addr: ServerAddr = self | ______________________________________________^ 115 | | .udp_associate_addr 116 | | .as_ref() 117 | | .or_else(|| self.udp_bind_addr.as_ref()) | |______________--------------------------------------^ | | | help: use `or(..)` instead: `or(self.udp_bind_addr.as_ref())` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations

Check warning on line 117 in crates/shadowsocks-service/src/local/socks/server/mod.rs

View workflow job for this annotation

GitHub Actions / clippy macos-latest

unnecessary closure used to substitute value for `Option::None`

warning: unnecessary closure used to substitute value for `Option::None` --> crates/shadowsocks-service/src/local/socks/server/mod.rs:114:46 | 114 | let udp_associate_addr: ServerAddr = self | ______________________________________________^ 115 | | .udp_associate_addr 116 | | .as_ref() 117 | | .or_else(|| self.udp_bind_addr.as_ref()) | |______________--------------------------------------^ | | | help: use `or(..)` instead: `or(self.udp_bind_addr.as_ref())` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
.unwrap_or_else(|| &self.client_config)

Check warning on line 118 in crates/shadowsocks-service/src/local/socks/server/mod.rs

View workflow job for this annotation

GitHub Actions / clippy ubuntu-latest

unnecessary closure used to substitute value for `Option::None`

warning: unnecessary closure used to substitute value for `Option::None` --> crates/shadowsocks-service/src/local/socks/server/mod.rs:114:46 | 114 | let udp_associate_addr: ServerAddr = self | ______________________________________________^ 115 | | .udp_associate_addr 116 | | .as_ref() 117 | | .or_else(|| self.udp_bind_addr.as_ref()) 118 | | .unwrap_or_else(|| &self.client_config) | |______________-------------------------------------^ | | | help: use `unwrap_or(..)` instead: `unwrap_or(&self.client_config)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations = note: `#[warn(clippy::unnecessary_lazy_evaluations)]` on by default

Check warning on line 118 in crates/shadowsocks-service/src/local/socks/server/mod.rs

View workflow job for this annotation

GitHub Actions / clippy macos-latest

unnecessary closure used to substitute value for `Option::None`

warning: unnecessary closure used to substitute value for `Option::None` --> crates/shadowsocks-service/src/local/socks/server/mod.rs:114:46 | 114 | let udp_associate_addr: ServerAddr = self | ______________________________________________^ 115 | | .udp_associate_addr 116 | | .as_ref() 117 | | .or_else(|| self.udp_bind_addr.as_ref()) 118 | | .unwrap_or_else(|| &self.client_config) | |______________-------------------------------------^ | | | help: use `unwrap_or(..)` instead: `unwrap_or(&self.client_config)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations = note: `#[warn(clippy::unnecessary_lazy_evaluations)]` on by default
.clone();

let mut udp_server = None;
if self.mode.enable_udp() {
#[allow(unused_mut)]
let mut builder = Socks5UdpServerBuilder::new(
self.context.clone(),
udp_bind_addr.clone(),
udp_bind_addr,
self.udp_expiry_duration,
self.udp_capacity,
self.balancer.clone(),
Expand All @@ -132,7 +144,7 @@ impl SocksBuilder {
let mut builder = SocksTcpServerBuilder::new(
self.context.clone(),
self.client_config,
udp_bind_addr,
udp_associate_addr,
self.balancer.clone(),
self.mode,
self.socks5_auth,
Expand Down
20 changes: 10 additions & 10 deletions crates/shadowsocks-service/src/local/socks/server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use super::socks5::{Socks5TcpHandler, Socks5UdpServer};
pub struct SocksTcpServerBuilder {
context: Arc<ServiceContext>,
client_config: ServerAddr,
udp_bind_addr: ServerAddr,
udp_associate_addr: ServerAddr,
balancer: PingBalancer,
mode: Mode,
socks5_auth: Arc<Socks5AuthConfig>,
Expand All @@ -30,15 +30,15 @@ impl SocksTcpServerBuilder {
pub(crate) fn new(
context: Arc<ServiceContext>,
client_config: ServerAddr,
udp_bind_addr: ServerAddr,
udp_associate_addr: ServerAddr,
balancer: PingBalancer,
mode: Mode,
socks5_auth: Socks5AuthConfig,
) -> SocksTcpServerBuilder {
SocksTcpServerBuilder {
context,
client_config,
udp_bind_addr,
udp_associate_addr,
balancer,
mode,
socks5_auth: Arc::new(socks5_auth),
Expand Down Expand Up @@ -74,7 +74,7 @@ impl SocksTcpServerBuilder {
Ok(SocksTcpServer {
context: self.context,
listener,
udp_bind_addr: self.udp_bind_addr,
udp_associate_addr: self.udp_associate_addr,
balancer: self.balancer,
mode: self.mode,
socks5_auth: self.socks5_auth,
Expand All @@ -86,7 +86,7 @@ impl SocksTcpServerBuilder {
pub struct SocksTcpServer {
context: Arc<ServiceContext>,
listener: ShadowTcpListener,
udp_bind_addr: ServerAddr,
udp_associate_addr: ServerAddr,
balancer: PingBalancer,
mode: Mode,
socks5_auth: Arc<Socks5AuthConfig>,
Expand All @@ -103,7 +103,7 @@ impl SocksTcpServer {
info!("shadowsocks socks TCP listening on {}", self.listener.local_addr()?);

// 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);
let udp_associate_addr = Arc::new(self.udp_associate_addr);
#[cfg(feature = "local-http")]
let http_handler = HttpConnectionHandler::new(self.context.clone(), self.balancer.clone());

Expand All @@ -119,7 +119,7 @@ impl SocksTcpServer {

let handler = SocksTcpHandler {
context: self.context.clone(),
udp_bind_addr: udp_bind_addr.clone(),
udp_associate_addr: udp_associate_addr.clone(),
stream,
balancer: self.balancer.clone(),
peer_addr,
Expand All @@ -140,7 +140,7 @@ impl SocksTcpServer {

struct SocksTcpHandler {
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
udp_associate_addr: Arc<ServerAddr>,
stream: TcpStream,
balancer: PingBalancer,
peer_addr: SocketAddr,
Expand All @@ -155,7 +155,7 @@ impl SocksTcpHandler {
async fn handle_tcp_client(self) -> io::Result<()> {
let handler = Socks5TcpHandler::new(
self.context,
self.udp_bind_addr,
self.udp_associate_addr,
self.balancer,
self.mode,
self.socks5_auth,
Expand Down Expand Up @@ -183,7 +183,7 @@ impl SocksTcpHandler {
0x05 => {
let handler = Socks5TcpHandler::new(
self.context,
self.udp_bind_addr,
self.udp_associate_addr,
self.balancer,
self.mode,
self.socks5_auth,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{

pub struct Socks5TcpHandler {
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
udp_associate_addr: Arc<ServerAddr>,
balancer: PingBalancer,
mode: Mode,
auth: Arc<Socks5AuthConfig>,
Expand All @@ -40,14 +40,14 @@ pub struct Socks5TcpHandler {
impl Socks5TcpHandler {
pub fn new(
context: Arc<ServiceContext>,
udp_bind_addr: Arc<ServerAddr>,
udp_associate_addr: Arc<ServerAddr>,
balancer: PingBalancer,
mode: Mode,
auth: Arc<Socks5AuthConfig>,
) -> Socks5TcpHandler {
Socks5TcpHandler {
context,
udp_bind_addr,
udp_associate_addr,
balancer,
mode,
auth,
Expand Down Expand Up @@ -304,7 +304,7 @@ impl Socks5TcpHandler {

// shadowsocks accepts both TCP and UDP from the same address

let rh = TcpResponseHeader::new(socks5::Reply::Succeeded, self.udp_bind_addr.as_ref().into());
let rh = TcpResponseHeader::new(socks5::Reply::Succeeded, self.udp_associate_addr.as_ref().into());
rh.write_to(&mut stream).await?;

// Hold connection until EOF.
Expand Down
12 changes: 12 additions & 0 deletions src/service/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.value_parser(vparser::parse_server_addr)
.help("UDP relay's bind address, default is the same as local-addr"),
)
.arg(
Arg::new("UDP_ASSOCIATE_ADDR")
.long("udp-associate-addr")
.num_args(1)
.action(ArgAction::Set)
.value_parser(vparser::parse_server_addr)
.help("UDP relay's externally visible address return in UDP Associate responses"),
)
.arg(
Arg::new("SERVER_ADDR")
.short('s')
Expand Down Expand Up @@ -731,6 +739,10 @@ pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future<Output = Exi
local_config.udp_addr = Some(udp_bind_addr);
}

if let Some(udp_associate_addr) = matches.get_one::<ServerAddr>("UDP_ASSOCIATE_ADDR").cloned() {
local_config.udp_associate_addr = Some(udp_associate_addr);
}

#[cfg(feature = "local-tunnel")]
if let Some(addr) = matches.get_one::<Address>("FORWARD_ADDR").cloned() {
local_config.forward_addr = Some(addr);
Expand Down

0 comments on commit 0041f62

Please sign in to comment.