Skip to content

Commit

Permalink
Fix per-server outbound options not taking effect
Browse files Browse the repository at this point in the history
1. Add outbound_bind_addr and outbound_bind_interface into ServerInstanceConfig
2. Add new interfaces for specifying ConnectOpts in AutoProxyClientStream
  • Loading branch information
stormynoct committed Apr 14, 2024
1 parent f8410cc commit c475cc3
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 13 deletions.
50 changes: 49 additions & 1 deletion crates/shadowsocks-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ struct SSServerExtConfig {
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: Option<u32>,

#[serde(skip_serializing_if = "Option::is_none")]
outbound_bind_addr: Option<IpAddr>,

#[serde(skip_serializing_if = "Option::is_none")]
outbound_bind_interface: Option<String>,
}

/// Server config type
Expand Down Expand Up @@ -1178,9 +1184,11 @@ pub struct ServerInstanceConfig {
pub config: ServerConfig,
/// Server's private ACL, set to `None` will use the global `AccessControl`
pub acl: Option<AccessControl>,
/// Server's outbound fwmark to support split tunnel
/// Server's outbound fwmark / address / interface to support split tunnel
#[cfg(any(target_os = "linux", target_os = "android"))]
pub outbound_fwmark: Option<u32>,
pub outbound_bind_addr: Option<IpAddr>,
pub outbound_bind_interface: Option<String>,
}

impl ServerInstanceConfig {
Expand All @@ -1191,6 +1199,8 @@ impl ServerInstanceConfig {
acl: None,
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: None,
outbound_bind_addr: None,
outbound_bind_interface: None,
}
}
}
Expand Down Expand Up @@ -1861,11 +1871,25 @@ impl Config {
nsvr.set_timeout(timeout);
}

let mut outbound_bind_addr: Option<IpAddr> = None;

if let Some(ref bind_addr) = config.outbound_bind_addr {
match bind_addr.parse::<IpAddr>() {
Ok(b) => outbound_bind_addr = Some(b),
Err(..) => {
let err = Error::new(ErrorKind::Invalid, "invalid outbound_bind_addr", None);
return Err(err);
}
}
}

let server_instance = ServerInstanceConfig {
config: nsvr,
acl: None,
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: config.outbound_fwmark,
outbound_bind_addr: outbound_bind_addr,

Check warning on line 1891 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (ubuntu-latest)

redundant field names in struct initialization

warning: redundant field names in struct initialization --> crates/shadowsocks-service/src/config.rs:1891:21 | 1891 | outbound_bind_addr: outbound_bind_addr, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names = note: `#[warn(clippy::redundant_field_names)]` on by default

Check warning on line 1891 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (macos-latest)

redundant field names in struct initialization

warning: redundant field names in struct initialization --> crates/shadowsocks-service/src/config.rs:1891:21 | 1891 | outbound_bind_addr: outbound_bind_addr, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names = note: `#[warn(clippy::redundant_field_names)]` on by default
outbound_bind_interface: config.outbound_bind_interface.clone(),
};

nconfig.server.push(server_instance);
Expand Down Expand Up @@ -2029,11 +2053,25 @@ impl Config {
nsvr.set_weight(weight);
}

let mut outbound_bind_addr: Option<IpAddr> = None;

if let Some(ref bind_addr) = config.outbound_bind_addr {
match bind_addr.parse::<IpAddr>() {
Ok(b) => outbound_bind_addr = Some(b),
Err(..) => {
let err = Error::new(ErrorKind::Invalid, "invalid outbound_bind_addr", None);
return Err(err);
}
}
}

let mut server_instance = ServerInstanceConfig {
config: nsvr,
acl: None,
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: config.outbound_fwmark,
outbound_bind_addr: outbound_bind_addr,

Check warning on line 2073 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (ubuntu-latest)

redundant field names in struct initialization

warning: redundant field names in struct initialization --> crates/shadowsocks-service/src/config.rs:2073:21 | 2073 | outbound_bind_addr: outbound_bind_addr, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names

Check warning on line 2073 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (macos-latest)

redundant field names in struct initialization

warning: redundant field names in struct initialization --> crates/shadowsocks-service/src/config.rs:2073:21 | 2073 | outbound_bind_addr: outbound_bind_addr, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
outbound_bind_interface: config.outbound_bind_interface.clone(),
};

if let Some(acl_path) = svr.acl {
Expand All @@ -2056,6 +2094,14 @@ impl Config {
server_instance.outbound_fwmark = Some(outbound_fwmark);
}

if let Some(outbound_bind_addr) = svr.outbound_bind_addr {
server_instance.outbound_bind_addr = Some(outbound_bind_addr);
}

if let Some(ref outbound_bind_interface) = svr.outbound_bind_interface {
server_instance.outbound_bind_interface = Some(outbound_bind_interface.clone());
}

nconfig.server.push(server_instance);
}
}
Expand Down Expand Up @@ -2830,6 +2876,8 @@ impl fmt::Display for Config {
.and_then(|a| a.file_path().to_str().map(ToOwned::to_owned)),
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: inst.outbound_fwmark.clone(),

Check warning on line 2878 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (ubuntu-latest)

using `clone` on type `Option<u32>` which implements the `Copy` trait

warning: using `clone` on type `Option<u32>` which implements the `Copy` trait --> crates/shadowsocks-service/src/config.rs:2878:42 | 2878 | outbound_fwmark: inst.outbound_fwmark.clone(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `inst.outbound_fwmark` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy = note: `#[warn(clippy::clone_on_copy)]` on by default
outbound_bind_addr: inst.outbound_bind_addr.clone(),

Check warning on line 2879 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (ubuntu-latest)

using `clone` on type `Option<IpAddr>` which implements the `Copy` trait

warning: using `clone` on type `Option<IpAddr>` which implements the `Copy` trait --> crates/shadowsocks-service/src/config.rs:2879:45 | 2879 | outbound_bind_addr: inst.outbound_bind_addr.clone(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `inst.outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy

Check warning on line 2879 in crates/shadowsocks-service/src/config.rs

View workflow job for this annotation

GitHub Actions / clippy-check (macos-latest)

using `clone` on type `Option<IpAddr>` which implements the `Copy` trait

warning: using `clone` on type `Option<IpAddr>` which implements the `Copy` trait --> crates/shadowsocks-service/src/config.rs:2879:45 | 2879 | outbound_bind_addr: inst.outbound_bind_addr.clone(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the `clone` call: `inst.outbound_bind_addr` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy = note: `#[warn(clippy::clone_on_copy)]` on by default
outbound_bind_interface: inst.outbound_bind_interface.clone(),
});
}

Expand Down
7 changes: 6 additions & 1 deletion crates/shadowsocks-service/src/local/http/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ pub async fn connect_host(
} else {
let server = balancer.best_tcp_server();

match AutoProxyClientStream::connect(context, server.as_ref(), host).await {
match AutoProxyClientStream::connect_with_opts(
context,
server.as_ref(),
host,
server.connect_opts_ref()
).await {
Ok(s) => Ok((s, Some(server))),
Err(err) => {
error!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ impl ServerIdent {
connect_opts.fwmark = Some(fwmark);
}

if let Some(bind_local_addr) = svr_cfg.outbound_bind_addr {
connect_opts.bind_local_addr = Some(bind_local_addr);
}

if let Some(ref bind_interface) = svr_cfg.outbound_bind_interface {
connect_opts.bind_interface = Some(bind_interface.clone());
}

ServerIdent {
tcp_score: ServerScore::new(svr_cfg.config.weight().tcp_weight(), max_server_rtt, check_window),
udp_score: ServerScore::new(svr_cfg.config.weight().udp_weight(), max_server_rtt, check_window),
Expand Down
54 changes: 49 additions & 5 deletions crates/shadowsocks-service/src/local/net/tcp/auto_proxy_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{

use pin_project::pin_project;
use shadowsocks::{
net::TcpStream,
net::{ConnectOpts, TcpStream},
relay::{socks5::Address, tcprelay::proxy_stream::ProxyClientStream},
};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
Expand All @@ -37,19 +37,44 @@ impl AutoProxyClientStream {
server: &ServerIdent,
addr: A,
) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
AutoProxyClientStream::connect_with_opts(context.clone(), server, addr, context.connect_opts_ref()).await
}

/// Connect to target `addr` via shadowsocks' server configured by `svr_cfg`
pub async fn connect_with_opts<A>(
context: Arc<ServiceContext>,
server: &ServerIdent,
addr: A,
opts: &ConnectOpts,
) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
let addr = addr.into();
if context.check_target_bypassed(&addr).await {
AutoProxyClientStream::connect_bypassed(context, addr).await
AutoProxyClientStream::connect_bypassed_with_opts(context, addr, opts).await
} else {
AutoProxyClientStream::connect_proxied(context, server, addr).await
AutoProxyClientStream::connect_proxied_with_opts(context, server, addr, opts).await
}
}

/// Connect directly to target `addr`
pub async fn connect_bypassed<A>(context: Arc<ServiceContext>, addr: A) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
AutoProxyClientStream::connect_bypassed_with_opts(context.clone(), addr, context.connect_opts_ref()).await
}

/// Connect directly to target `addr`
pub async fn connect_bypassed_with_opts<A>(
context: Arc<ServiceContext>,
addr: A,
connect_opts: &ConnectOpts,
) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
Expand All @@ -61,7 +86,7 @@ impl AutoProxyClientStream {
addr = mapped_addr;
}
let stream =
TcpStream::connect_remote_with_opts(context.context_ref(), &addr, context.connect_opts_ref()).await?;
TcpStream::connect_remote_with_opts(context.context_ref(), &addr, connect_opts).await?;
Ok(AutoProxyClientStream::Bypassed(stream))
}

Expand All @@ -71,6 +96,25 @@ impl AutoProxyClientStream {
server: &ServerIdent,
addr: A,
) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
AutoProxyClientStream::connect_proxied_with_opts(
context.clone(),
server,
addr,
context.connect_opts_ref()
)
.await
}

/// Connect to target `addr` via shadowsocks' server configured by `svr_cfg`
pub async fn connect_proxied_with_opts<A>(
context: Arc<ServiceContext>,
server: &ServerIdent,
addr: A,
connect_opts: &ConnectOpts,
) -> io::Result<AutoProxyClientStream>
where
A: Into<Address>,
{
Expand All @@ -85,7 +129,7 @@ impl AutoProxyClientStream {
context.context(),
server.server_config(),
addr,
context.connect_opts_ref(),
connect_opts,
|stream| MonProxyStream::from_stream(stream, flow_stat),
)
.await
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ where
let svr_cfg = server.server_config();

let socket =
ProxySocket::connect_with_opts(self.context.context(), svr_cfg, self.context.connect_opts_ref())
ProxySocket::connect_with_opts(self.context.context(), svr_cfg, server.connect_opts_ref())
.await?;
let socket = MonProxySocket::from_socket(socket, self.context.flow_stat());

Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async fn establish_client_tcp_redir<'a>(
let server = balancer.best_tcp_server();
let svr_cfg = server.server_config();

let mut remote = AutoProxyClientStream::connect(context, &server, addr).await?;
let mut remote = AutoProxyClientStream::connect_with_opts(context, &server, addr, server.connect_opts_ref()).await?;

establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, addr).await
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,13 @@ impl Socks4TcpHandler {
} else {
let server = self.balancer.best_tcp_server();

let r = AutoProxyClientStream::connect(self.context, &server, &target_addr).await;
let r = AutoProxyClientStream::connect_with_opts(
self.context,
&server,
&target_addr,
server.connect_opts_ref()
)
.await;
server_opt = Some(server);

r
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,13 @@ impl Socks5TcpHandler {
} else {
let server = self.balancer.best_tcp_server();

let r = AutoProxyClientStream::connect(self.context.clone(), &server, &target_addr).await;
let r = AutoProxyClientStream::connect_with_opts(
self.context,
&server,
&target_addr,
server.connect_opts_ref()
)
.await;
server_opt = Some(server);

r
Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/src/local/tun/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ async fn establish_client_tcp_redir<'a>(
let server = balancer.best_tcp_server();
let svr_cfg = server.server_config();

let mut remote = AutoProxyClientStream::connect(context, &server, addr).await?;
let mut remote = AutoProxyClientStream::connect_with_opts(context, &server, addr, server.connect_opts_ref()).await?;
establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, addr).await
}

Expand Down
8 changes: 7 additions & 1 deletion crates/shadowsocks-service/src/local/tunnel/tcprelay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ async fn handle_tcp_client(
svr_cfg.addr(),
);

let mut remote = AutoProxyClientStream::connect_proxied(context, &server, forward_addr).await?;
let mut remote = AutoProxyClientStream::connect_proxied_with_opts(
context,
&server,
forward_addr,
server.connect_opts_ref()
)
.await?;
establish_tcp_tunnel(svr_cfg, &mut stream, &mut remote, peer_addr, forward_addr).await
}
2 changes: 2 additions & 0 deletions crates/shadowsocks-service/src/manager/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ impl Manager {
acl: None, // Set with --acl command line argument
#[cfg(any(target_os = "linux", target_os = "android"))]
outbound_fwmark: None,
outbound_bind_addr: None,
outbound_bind_interface: None,
};

let mut config = Config::new(ConfigType::Server);
Expand Down
8 changes: 8 additions & 0 deletions crates/shadowsocks-service/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ pub async fn run(config: Config) -> io::Result<()> {
connect_opts.fwmark = Some(fwmark);
}

if let Some(bind_local_addr) = inst.outbound_bind_addr {
connect_opts.bind_local_addr = Some(bind_local_addr);
}

if let Some(bind_interface) = inst.outbound_bind_interface {
connect_opts.bind_interface = Some(bind_interface);
}

server_builder.set_connect_opts(connect_opts.clone());
server_builder.set_accept_opts(accept_opts.clone());

Expand Down

0 comments on commit c475cc3

Please sign in to comment.