Skip to content

Commit 6fcaf6c

Browse files
committed
support TFO on Linux, FreeBSD, macOS and Windows
- ref #184
1 parent 1746c62 commit 6fcaf6c

File tree

34 files changed

+1731
-623
lines changed

34 files changed

+1731
-623
lines changed

.github/workflows/build-and-test.yml

+20-2
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ jobs:
3030
profile: minimal
3131
- name: Build & Test (Default)
3232
run: cargo test --verbose --no-fail-fast
33+
- name: Build & Test (Default) - shadowsocks
34+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-fail-fast
3335
- name: Build & Test (--no-default-features)
3436
run: cargo test --verbose --no-default-features --no-fail-fast
37+
- name: Build & Test (--no-default-features) - shadowsocks
38+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-default-features --no-fail-fast
3539
- name: Build with All Features Enabled
36-
run: cargo build --verbose --features "local-redir local-dns dns-over-tls dns-over-https stream-cipher"
40+
run: cargo build --verbose --features "local-dns dns-over-tls dns-over-https stream-cipher"
41+
- name: Build with All Features Enabled - shadowsocks
42+
run: cargo build --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --features "stream-cipher"
3743

3844
build-windows:
3945
runs-on: windows-latest
@@ -53,10 +59,16 @@ jobs:
5359
profile: minimal
5460
- name: Build & Test (Default)
5561
run: cargo test --verbose --no-fail-fast
62+
- name: Build & Test (Default) - shadowsocks
63+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-fail-fast
5664
- name: Build & Test (--no-default-features)
5765
run: cargo test --verbose --no-default-features --no-fail-fast
66+
- name: Build & Test (--no-default-features) - shadowsocks
67+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-default-features --no-fail-fast
5868
- name: Build with All Features Enabled
5969
run: cargo build --verbose --features "local-dns dns-over-tls dns-over-https stream-cipher"
70+
- name: Build with All Features Enabled - shadowsocks
71+
run: cargo build --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --features "stream-cipher"
6072

6173
build-macos:
6274
runs-on: macos-latest
@@ -81,7 +93,13 @@ jobs:
8193
profile: minimal
8294
- name: Build & Test (Default)
8395
run: cargo test --verbose --no-fail-fast
96+
- name: Build & Test (Default) - shadowsocks
97+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-fail-fast
8498
- name: Build & Test (--no-default-features)
8599
run: cargo test --verbose --no-default-features --no-fail-fast
100+
- name: Build & Test (--no-default-features) - shadowsocks
101+
run: cargo test --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --no-default-features --no-fail-fast
86102
- name: Build with All Features Enabled
87-
run: cargo build --verbose --features "local-redir local-dns dns-over-tls dns-over-https stream-cipher"
103+
run: cargo build --verbose --features "local-dns dns-over-tls dns-over-https stream-cipher"
104+
- name: Build with All Features Enabled - shadowsocks
105+
run: cargo build --manifest-path ./crates/shadowsocks/Cargo.toml --verbose --features "stream-cipher"

Cargo.lock

+5-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shadowsocks-rust"
3-
version = "1.10.4"
3+
version = "1.11.0"
44
authors = ["Shadowsocks Contributors"]
55
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
66
repository = "https://github.com/shadowsocks/shadowsocks-rust"
@@ -132,3 +132,6 @@ byteorder = "1.3"
132132
env_logger = "0.8"
133133
byte_string = "1.0"
134134
tokio = { version = "1", features = ["net", "time", "macros", "io-util"]}
135+
136+
[patch.crates-io]
137+
tokio = { git = "https://github.com/tokio-rs/tokio.git", features = ["full"] }

bin/sslocal.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn main() {
6363

6464

6565
(@arg NO_DELAY: --("no-delay") !takes_value "Set TCP_NODELAY option for socket")
66+
(@arg FAST_OPEN: --("fast-open") !takes_value "Enable TCP Fast Open (TFO)")
6667
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
6768
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
6869
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
@@ -339,14 +340,18 @@ fn main() {
339340
config.no_delay = true;
340341
}
341342

343+
if matches.is_present("FAST_OPEN") {
344+
config.fast_open = true;
345+
}
346+
342347
#[cfg(any(target_os = "linux", target_os = "android"))]
343348
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
344349
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
345350
}
346351

347352
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
348353
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
349-
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
354+
config.outbound_bind_interface = Some(iface.to_owned());
350355
}
351356

352357
if let Some(nofile) = matches.value_of("NOFILE") {

bin/ssmanager.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ fn main() {
5151
(@arg SERVER_HOST: -s --("server-host") +takes_value "Host name or IP address of your remote server")
5252

5353
(@arg NO_DELAY: --("no-delay") !takes_value "Set TCP_NODELAY option for socket")
54+
(@arg FAST_OPEN: --("fast-open") !takes_value "Enable TCP Fast Open (TFO)")
5455

5556
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value {validator::validate_manager_addr} "ShadowSocks Manager (ssmgr) address, could be ip:port, domain:port or /path/to/unix.sock")
5657
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(available_ciphers()) "Default encryption method")
@@ -143,14 +144,18 @@ fn main() {
143144
config.no_delay = true;
144145
}
145146

147+
if matches.is_present("FAST_OPEN") {
148+
config.fast_open = true;
149+
}
150+
146151
#[cfg(any(target_os = "linux", target_os = "android"))]
147152
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
148153
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
149154
}
150155

151-
#[cfg(any(target_os = "linux", target_os = "android"))]
156+
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
152157
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
153-
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
158+
config.outbound_bind_interface = Some(iface.to_owned());
154159
}
155160

156161
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {

bin/ssserver.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ fn main() {
5757
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value "ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\"")
5858

5959
(@arg NO_DELAY: --("no-delay") !takes_value "Set TCP_NODELAY option for socket")
60+
(@arg FAST_OPEN: --("fast-open") !takes_value "Enable TCP Fast Open (TFO)")
6061
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
6162
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
6263
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
@@ -68,8 +69,6 @@ fn main() {
6869
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
6970
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
7071
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
71-
72-
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
7372
);
7473

7574
#[cfg(feature = "logging")]
@@ -188,14 +187,18 @@ fn main() {
188187
config.no_delay = true;
189188
}
190189

190+
if matches.is_present("FAST_OPEN") {
191+
config.fast_open = true;
192+
}
193+
191194
#[cfg(any(target_os = "linux", target_os = "android"))]
192195
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
193196
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
194197
}
195198

196-
#[cfg(any(target_os = "linux", target_os = "android"))]
199+
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
197200
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
198-
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
201+
config.outbound_bind_interface = Some(iface.to_owned());
199202
}
200203

201204
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {

crates/shadowsocks-service/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shadowsocks-service"
3-
version = "1.10.3"
3+
version = "1.11.0"
44
authors = ["Shadowsocks Contributors"]
55
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
66
repository = "https://github.com/shadowsocks/shadowsocks-rust"

crates/shadowsocks-service/src/config.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
//!
4242
//! These defined server will be used with a load balancing algorithm.
4343
44-
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
45-
use std::ffi::OsString;
44+
#[cfg(any(unix, features = "local-flow-stat"))]
45+
use std::path::PathBuf;
4646
use std::{
4747
convert::{From, Infallible},
4848
default::Default,
@@ -51,7 +51,7 @@ use std::{
5151
io::Read,
5252
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
5353
option::Option,
54-
path::{Path, PathBuf},
54+
path::Path,
5555
str::FromStr,
5656
string::ToString,
5757
time::Duration,
@@ -127,6 +127,8 @@ struct SSConfig {
127127
nofile: Option<u64>,
128128
#[serde(skip_serializing_if = "Option::is_none")]
129129
ipv6_first: Option<bool>,
130+
#[serde(skip_serializing_if = "Option::is_none")]
131+
fast_open: Option<bool>,
130132
}
131133

132134
#[derive(Serialize, Deserialize, Debug, Default)]
@@ -717,6 +719,8 @@ pub struct Config {
717719

718720
/// Set `TCP_NODELAY` socket option
719721
pub no_delay: bool,
722+
/// Set `TCP_FASTOPEN` socket option
723+
pub fast_open: bool,
720724
/// `RLIMIT_NOFILE` option for *nix systems
721725
pub nofile: Option<u64>,
722726

@@ -725,7 +729,7 @@ pub struct Config {
725729
pub outbound_fwmark: Option<u32>,
726730
/// Set `SO_BINDTODEVICE` socket option for outbound sockets
727731
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
728-
pub outbound_bind_interface: Option<OsString>,
732+
pub outbound_bind_interface: Option<String>,
729733
/// Path to protect callback unix address, only for Android
730734
#[cfg(target_os = "android")]
731735
pub outbound_vpn_protect_path: Option<PathBuf>,
@@ -829,6 +833,7 @@ impl Config {
829833
ipv6_first: false,
830834

831835
no_delay: false,
836+
fast_open: false,
832837
nofile: None,
833838

834839
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -1275,6 +1280,11 @@ impl Config {
12751280
nconfig.no_delay = b;
12761281
}
12771282

1283+
// TCP fast open
1284+
if let Some(b) = config.fast_open {
1285+
nconfig.fast_open = b;
1286+
}
1287+
12781288
// UDP
12791289
nconfig.udp_timeout = config.udp_timeout.map(Duration::from_secs);
12801290

@@ -1758,6 +1768,10 @@ impl fmt::Display for Config {
17581768
jconf.no_delay = Some(self.no_delay);
17591769
}
17601770

1771+
if self.fast_open {
1772+
jconf.fast_open = Some(self.fast_open);
1773+
}
1774+
17611775
match self.dns {
17621776
DnsConfig::System => {}
17631777
#[cfg(feature = "trust-dns")]

crates/shadowsocks-service/src/local/dns/upstream.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use shadowsocks::{
1818
use tokio::net::UnixStream;
1919
use tokio::{
2020
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
21-
net::{TcpStream, UdpSocket},
21+
net::UdpSocket,
2222
time,
2323
};
2424
use trust_dns_resolver::proto::{
@@ -31,7 +31,7 @@ use crate::net::{FlowStat, MonProxySocket, MonProxyStream};
3131
/// Collection of various DNS connections
3232
pub enum DnsClient {
3333
TcpLocal {
34-
stream: TcpStream,
34+
stream: ShadowTcpStream,
3535
},
3636
UdpLocal {
3737
socket: UdpSocket,
@@ -42,7 +42,7 @@ pub enum DnsClient {
4242
stream: UnixStream,
4343
},
4444
TcpRemote {
45-
stream: ProxyClientStream<MonProxyStream<TcpStream>>,
45+
stream: ProxyClientStream<MonProxyStream<ShadowTcpStream>>,
4646
},
4747
UdpRemote {
4848
socket: MonProxySocket,
@@ -53,7 +53,7 @@ pub enum DnsClient {
5353
impl DnsClient {
5454
/// Connect to local provided TCP DNS server
5555
pub async fn connect_tcp_local(ns: SocketAddr, connect_opts: &ConnectOpts) -> io::Result<DnsClient> {
56-
let stream = ShadowTcpStream::connect_with_opts(&ns, connect_opts).await?.into();
56+
let stream = ShadowTcpStream::connect_with_opts(&ns, connect_opts).await?;
5757
Ok(DnsClient::TcpLocal { stream })
5858
}
5959

crates/shadowsocks-service/src/local/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use futures::{
99
stream::{FuturesUnordered, StreamExt},
1010
FutureExt,
1111
};
12-
use log::{error, trace, warn};
12+
use log::{error, trace};
1313
use shadowsocks::{
1414
config::Mode,
1515
net::{AcceptOpts, ConnectOpts},
@@ -53,7 +53,7 @@ pub async fn run(mut config: Config) -> io::Result<()> {
5353
#[cfg(feature = "stream-cipher")]
5454
for server in config.server.iter() {
5555
if server.method().is_stream() {
56-
warn!("stream cipher {} for server {} have inherent weaknesses (see discussion in https://github.com/shadowsocks/shadowsocks-org/issues/36). \
56+
log::warn!("stream cipher {} for server {} have inherent weaknesses (see discussion in https://github.com/shadowsocks/shadowsocks-org/issues/36). \
5757
DO NOT USE. It will be removed in the future.", server.method(), server.addr());
5858
}
5959
}
@@ -62,7 +62,7 @@ pub async fn run(mut config: Config) -> io::Result<()> {
6262
if let Some(nofile) = config.nofile {
6363
use crate::sys::set_nofile;
6464
if let Err(err) = set_nofile(nofile) {
65-
warn!("set_nofile {} failed, error: {}", nofile, err);
65+
log::warn!("set_nofile {} failed, error: {}", nofile, err);
6666
}
6767
}
6868

@@ -82,12 +82,14 @@ pub async fn run(mut config: Config) -> io::Result<()> {
8282
};
8383
connect_opts.tcp.send_buffer_size = config.outbound_send_buffer_size;
8484
connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size;
85+
connect_opts.tcp.fastopen = config.fast_open;
8586
context.set_connect_opts(connect_opts);
8687

8788
let mut accept_opts = AcceptOpts::default();
8889
accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size;
8990
accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size;
9091
accept_opts.tcp.nodelay = config.no_delay;
92+
accept_opts.tcp.fastopen = config.fast_open;
9193

9294
if let Some(resolver) = build_dns_resolver(config.dns, config.ipv6_first, context.connect_opts_ref()).await {
9395
context.set_dns_resolver(Arc::new(resolver));

0 commit comments

Comments
 (0)