Skip to content

Commit dc5a346

Browse files
committed
[#190] Supports transparent proxy on Linux
1 parent c5b9865 commit dc5a346

File tree

8 files changed

+550
-6
lines changed

8 files changed

+550
-6
lines changed

Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shadowsocks-rust"
3-
version = "1.8.7"
3+
version = "1.8.8"
44
authors = ["Y. T. CHUNG <[email protected]>"]
55
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
66
repository = "https://github.com/zonyitoo/shadowsocks-rust"
@@ -24,6 +24,10 @@ path = "src/bin/server.rs"
2424
name = "sstunnel"
2525
path = "src/bin/tunnel.rs"
2626

27+
[[bin]]
28+
name = "ssredir"
29+
path = "src/bin/redir.rs"
30+
2731
[[bin]]
2832
name = "ssurl"
2933
path = "src/bin/ssurl.rs"
@@ -40,6 +44,7 @@ aes-ctr = ["openssl"]
4044
camellia-cfb = ["openssl"]
4145
single-threaded = []
4246
trust-dns = ["trust-dns-resolver"]
47+
redir = []
4348

4449
[dependencies]
4550
log = "0.4"

build/build-release

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ function build() {
4949
"sslocal" \
5050
"ssserver" \
5151
"ssurl" \
52-
"sstunnel"
52+
"sstunnel" \
53+
"ssredir"
5354

5455
if [[ $? != "0" ]]; then
5556
exit $?

src/bin/redir.rs

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
//! This is a binary running in the local environment
2+
//!
3+
//! You have to provide all needed configuration attributes via command line parameters,
4+
//! or you could specify a configuration file. The format of configuration file is defined
5+
//! in mod `config`.
6+
7+
use clap::{App, Arg};
8+
use futures::{
9+
future::{self, Either},
10+
FutureExt,
11+
};
12+
use log::{debug, error, info};
13+
use tokio::runtime::Builder;
14+
15+
use shadowsocks::{
16+
plugin::PluginConfig,
17+
relay::socks5::Address,
18+
run_local,
19+
Config,
20+
ConfigType,
21+
Mode,
22+
ServerAddr,
23+
ServerConfig,
24+
};
25+
26+
mod logging;
27+
mod monitor;
28+
29+
fn main() {
30+
let matches = App::new("shadowsocks")
31+
.version(shadowsocks::VERSION)
32+
.about("A fast tunnel proxy that helps you bypass firewalls.")
33+
.arg(
34+
Arg::with_name("VERBOSE")
35+
.short("v")
36+
.multiple(true)
37+
.help("Set the level of debug"),
38+
)
39+
.arg(Arg::with_name("UDP_ONLY").short("u").help("Server mode UDP_ONLY"))
40+
.arg(Arg::with_name("TCP_AND_UDP").short("U").help("Server mode TCP_AND_UDP"))
41+
.arg(
42+
Arg::with_name("CONFIG")
43+
.short("c")
44+
.long("config")
45+
.takes_value(true)
46+
.help("Specify config file"),
47+
)
48+
.arg(
49+
Arg::with_name("SERVER_ADDR")
50+
.short("s")
51+
.long("server-addr")
52+
.takes_value(true)
53+
.help("Server address"),
54+
)
55+
.arg(
56+
Arg::with_name("LOCAL_ADDR")
57+
.short("b")
58+
.long("local-addr")
59+
.takes_value(true)
60+
.help("Local address, listen only to this address if specified"),
61+
)
62+
.arg(
63+
Arg::with_name("PASSWORD")
64+
.short("k")
65+
.long("password")
66+
.takes_value(true)
67+
.help("Password"),
68+
)
69+
.arg(
70+
Arg::with_name("ENCRYPT_METHOD")
71+
.short("m")
72+
.long("encrypt-method")
73+
.takes_value(true)
74+
.help("Encryption method"),
75+
)
76+
.arg(
77+
Arg::with_name("PLUGIN")
78+
.long("plugin")
79+
.takes_value(true)
80+
.help("Enable SIP003 plugin"),
81+
)
82+
.arg(
83+
Arg::with_name("PLUGIN_OPT")
84+
.long("plugin-opts")
85+
.takes_value(true)
86+
.help("Set SIP003 plugin options"),
87+
)
88+
.arg(
89+
Arg::with_name("URL")
90+
.long("server-url")
91+
.takes_value(true)
92+
.help("Server address in SIP002 URL"),
93+
)
94+
.arg(
95+
Arg::with_name("NO_DELAY")
96+
.long("no-delay")
97+
.takes_value(false)
98+
.help("Set no-delay option for socket"),
99+
)
100+
.arg(
101+
Arg::with_name("NOFILE")
102+
.short("n")
103+
.long("nofile")
104+
.takes_value(true)
105+
.help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"),
106+
)
107+
.get_matches();
108+
109+
let debug_level = matches.occurrences_of("VERBOSE");
110+
logging::init(debug_level, "ssredir");
111+
112+
let mut has_provided_config = false;
113+
114+
let mut config = match matches.value_of("CONFIG") {
115+
Some(cpath) => match Config::load_from_file(cpath, ConfigType::RedirLocal) {
116+
Ok(cfg) => {
117+
has_provided_config = true;
118+
cfg
119+
}
120+
Err(err) => {
121+
error!("{:?}", err);
122+
return;
123+
}
124+
},
125+
None => Config::new(ConfigType::RedirLocal),
126+
};
127+
128+
let mut has_provided_server_config = match (
129+
matches.value_of("SERVER_ADDR"),
130+
matches.value_of("PASSWORD"),
131+
matches.value_of("ENCRYPT_METHOD"),
132+
) {
133+
(Some(svr_addr), Some(password), Some(method)) => {
134+
let method = match method.parse() {
135+
Ok(m) => m,
136+
Err(err) => {
137+
panic!("Does not support {:?} method: {:?}", method, err);
138+
}
139+
};
140+
141+
let sc = ServerConfig::new(
142+
svr_addr
143+
.parse::<ServerAddr>()
144+
.expect("`server-addr` invalid, \"IP:Port\" or \"Domain:Port\""),
145+
password.to_owned(),
146+
method,
147+
None,
148+
None,
149+
);
150+
151+
config.server.push(sc);
152+
true
153+
}
154+
(None, None, None) => {
155+
// Does not provide server config
156+
false
157+
}
158+
_ => {
159+
panic!("`server-addr`, `method` and `password` should be provided together");
160+
}
161+
};
162+
163+
if let Some(url) = matches.value_of("URL") {
164+
let svr_addr = url.parse::<ServerConfig>().expect("Failed to parse `url`");
165+
166+
has_provided_server_config = true;
167+
168+
config.server.push(svr_addr);
169+
}
170+
171+
let has_provided_local_config = match matches.value_of("LOCAL_ADDR") {
172+
Some(local_addr) => {
173+
let local_addr = local_addr
174+
.parse::<ServerAddr>()
175+
.expect("`local-addr` invalid, \"IP:Port\" or \"Domain:Port\"");
176+
177+
config.local = Some(local_addr);
178+
true
179+
}
180+
None => false,
181+
};
182+
183+
if !(has_provided_config || (has_provided_server_config && has_provided_local_config)) {
184+
println!("You have to specify a configuration file or pass arguments by argument list");
185+
println!("{}", matches.usage());
186+
return;
187+
}
188+
189+
if let Some(url) = matches.value_of("FORWARD_ADDR") {
190+
let forward_addr = url.parse::<Address>().expect("Failed to parse `url`");
191+
192+
config.forward = Some(forward_addr);
193+
}
194+
195+
if matches.is_present("UDP_ONLY") {
196+
if config.mode.enable_tcp() {
197+
config.mode = Mode::TcpAndUdp;
198+
} else {
199+
config.mode = Mode::UdpOnly;
200+
}
201+
}
202+
203+
if matches.is_present("TCP_AND_UDP") {
204+
config.mode = Mode::TcpAndUdp;
205+
}
206+
207+
if matches.is_present("NO_DELAY") {
208+
config.no_delay = true;
209+
}
210+
211+
if let Some(p) = matches.value_of("PLUGIN") {
212+
let plugin = PluginConfig {
213+
plugin: p.to_owned(),
214+
plugin_opt: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned),
215+
};
216+
217+
// Overrides config in file
218+
for svr in config.server.iter_mut() {
219+
svr.set_plugin(plugin.clone());
220+
}
221+
};
222+
223+
if let Some(nofile) = matches.value_of("NOFILE") {
224+
config.nofile = Some(
225+
nofile
226+
.parse::<u64>()
227+
.expect("Expecting an unsigned integer for `nofile`"),
228+
);
229+
}
230+
231+
info!("ShadowSocks {}", shadowsocks::VERSION);
232+
233+
debug!("Config: {:?}", config);
234+
235+
let mut builder = Builder::new();
236+
if cfg!(feature = "single-threaded") {
237+
builder.basic_scheduler();
238+
} else {
239+
builder.threaded_scheduler();
240+
}
241+
let mut runtime = builder.enable_all().build().expect("Unable to create Tokio Runtime");
242+
let rt_handle = runtime.handle().clone();
243+
244+
runtime.block_on(async move {
245+
let abort_signal = monitor::create_signal_monitor();
246+
match future::select(run_local(config, rt_handle).boxed(), abort_signal.boxed()).await {
247+
// Server future resolved without an error. This should never happen.
248+
Either::Left(_) => panic!("Server exited unexpectly"),
249+
// The abort signal future resolved. Means we should just exit.
250+
Either::Right(_) => (),
251+
}
252+
})
253+
}

src/config.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,11 @@ pub enum ConfigType {
497497
/// Requires `local` and `forward` configuration
498498
TunnelLocal,
499499

500+
/// Config for redir local
501+
///
502+
/// Requires `local` configuration
503+
RedirLocal,
504+
500505
/// Config for server
501506
Server,
502507
}
@@ -505,15 +510,15 @@ impl ConfigType {
505510
/// Check if it is local server type
506511
pub fn is_local(self) -> bool {
507512
match self {
508-
ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal => true,
513+
ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal | ConfigType::RedirLocal => true,
509514
ConfigType::Server => false,
510515
}
511516
}
512517

513518
/// Check if it is remote server type
514519
pub fn is_server(self) -> bool {
515520
match self {
516-
ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal => false,
521+
ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal | ConfigType::RedirLocal => false,
517522
ConfigType::Server => true,
518523
}
519524
}

src/relay/local.rs

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub async fn run(mut config: Config, rt: Handle) -> io::Result<()> {
6565
ConfigType::TunnelLocal => config.mode.enable_tcp(),
6666
// HTTP must be TCP
6767
ConfigType::HttpLocal => true,
68+
// Redir must be TCP
69+
ConfigType::RedirLocal => true,
6870

6971
_ => false,
7072
};

src/relay/tcprelay/local.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::io;
44

5-
use super::{http_local, socks5_local, tunnel_local};
5+
use super::{http_local, redir_local, socks5_local, tunnel_local};
66
use crate::{config::ConfigType, context::SharedContext};
77

88
/// Starts a TCP local server
@@ -11,6 +11,7 @@ pub async fn run(context: SharedContext) -> io::Result<()> {
1111
ConfigType::TunnelLocal => tunnel_local::run(context).await,
1212
ConfigType::Socks5Local => socks5_local::run(context).await,
1313
ConfigType::HttpLocal => http_local::run(context).await,
14+
ConfigType::RedirLocal => redir_local::run(context).await,
1415
ConfigType::Server => unreachable!(),
1516
}
1617
}

src/relay/tcprelay/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ mod crypto_io;
3535
mod http_local;
3636
pub mod local;
3737
mod monitor;
38+
mod redir_local;
3839
pub mod server;
3940
mod server_context;
4041
mod socks5_local;
@@ -235,7 +236,7 @@ async fn connect_proxy_server(context: &Context, svr_cfg: &ServerConfig) -> io::
235236

236237
let svr_addr = match context.config().config_type {
237238
ConfigType::Server => svr_cfg.addr(),
238-
ConfigType::Socks5Local | ConfigType::TunnelLocal | ConfigType::HttpLocal => {
239+
ConfigType::Socks5Local | ConfigType::TunnelLocal | ConfigType::HttpLocal | ConfigType::RedirLocal => {
239240
svr_cfg.plugin_addr().as_ref().unwrap_or_else(|| svr_cfg.addr())
240241
}
241242
};

0 commit comments

Comments
 (0)