921 Commits
Author SHA1 Message Date
Max Lv e697eb292b chore: fix clippy 1.95 lints (io_other_error, collapsible_match, large_enum_variant) 2026-10-22 10:22:37 +08:00
Max Lv dc43297211 fix(relay): bound idle time of half-closed TCP tunnels
With a SIP003 plugin, every proxied connection is a loopback connection
from sslocal to the plugin. After the client half-closes, sslocal shuts
down its write side and waits for EOF from the plugin. Many plugins
(simple-obfs, v2ray-plugin, kcptun/smux, ...) never close their side, so
the socket stays in FIN_WAIT_2 forever (tcp_fin_timeout only applies to
orphaned sockets, keepalive is answered by the live plugin), leaking an
ephemeral port and fd per connection until local ports are exhausted.

- CopyBidirectional now fails with TimedOut if the remaining direction
  stays idle for HALF_CLOSE_IDLE_TIMEOUT (60s) after the other direction
  has been shut down. The timer restarts on every transferred byte, so
  long downloads after a half-close keep working.
- Plugin::wait_started no longer spins on connect(), and rejects a
  self-connected socket (src == dst) that would otherwise be mistaken
  for a started plugin.
2026-10-22 10:22:37 +08:00
Qiu Yuzhouandty f23366dc0e feat(local): hot-reload the ACL file at runtime via SIGUSR2 (#2183)
* feat(acl): add AclHandle to swap ACL rules atomically at runtime

AccessControl is immutable after loading, and ServiceContext used to store
it directly, so rules were frozen for the lifetime of the process.

Wrap the active rules in an ArcSwap-based AclHandle. Readers take a cheap
Arc snapshot which stays valid across a swap, enabling hot-reloading
without interrupting the service. reload() re-parses the retained file
path and only swaps the new rules in when parsing succeeds.

* feat(local): hot-reload the global ACL file through ServiceContext

Store the ACL as an AclHandle in ServiceContext. set_acl keeps its
signature and wraps the rules in a handle; cloned contexts share it, so
every listener sees the same swappable rules. acl() now returns an
Arc<AccessControl> snapshot instead of a reference, which cannot be
preserved across an atomic swap.

ServiceContext::reload_acl() re-reads the ACL file, swaps the new rules
in atomically and clears the DNS relay's reverse-lookup cache, which
caches decisions derived from the old rules. On failure the previous
rules stay active.

Server keeps the global context and exposes acl_reload_context() so
embedders can trigger reloads. Contexts with a private ACL keep their
own handle and are not covered by the global reload.

* feat(local): reload the ACL file on SIGUSR2 in sslocal

sslocal already reloads its server list from the config file on SIGUSR1.
Add a second reload trigger on SIGUSR2 for the ACL file, so routing
rules can be updated without restarting the process or interrupting
established connections.

Like the SIGUSR1 reloader, the task is Unix-only and compiles away on
Windows. ssservice's local mode shares this module and inherits it.

Document both signals in the README and cover the reload with an
integration test that verifies new connections are routed by the new
rules after each reload.

* refactor(acl): address review feedback for ACL hot-reload

- Rename `AclHandle` to `AccessControlHandle`, aligning the name with `AccessControl`
- Rename `Server::acl_reload_context` to `Server::context`, so the shared global context can be reused for other runtime operations
- Fold reload logging into `AccessControlHandle::reload`, keeping `ServiceContext::reload_acl` thin
- Record the config file and the ACL file as absolute paths at load time, so both SIGUSR1 and SIGUSR2 reloads keep working after daemonizing (which `chdir`s to `/`); symlinks are deliberately preserved by resolving with cwd instead of canonicalizing

* refactor(local): drop needless pending() in the non-Unix ACL reload task stub

The task may complete immediately: the main loop already tolerates early task exit, mirroring the existing Windows behavior of the SIGUSR1 reload task. This also releases the ServiceContext clone right away instead of holding it forever.

---------

Co-authored-by: ty <zonyitoo@gmail.com>
2026-09-29 11:47:28 +08:00
ty a981de7bbf fix(relay): pick non-zero AEAD-2022 padding size for empty first payload
SIP022 requires that in a request header, either initial payload or
padding MUST be present. When the first payload is empty (e.g. handshake
triggered by an empty write for server-first protocols), the client
could pick padding 0 with probability 1/901, generating a header that
servers MUST reject per the spec, causing ~1/901 handshakes to fail
against ssserver built from the same crate.

Pick the padding size from 1..=AEAD2022_MAX_PADDING_SIZE instead.

fix #2184
2026-09-29 10:27:31 +08:00
ty e01605cdff fix(tcprelay): report actual plaintext length when retrying after Pending
EncryptedWriter.poll_write_encrypted keeps the encrypted buffer and its
on-wire offset across Poll::Pending, but returned the length of the
buffer passed on the *retried* call instead of the plaintext length that
was actually assembled into the pending frame.

Tokio's copy/copy_bidirectional may append freshly read data to the
input slice while the writer is Pending, so the reported count could
exceed what was actually encrypted and sent, silently dropping the
appended bytes.

Fixes #2175
2026-09-25 11:36:14 +08:00
ty 7dee109d70 fix(local-tun): enable smoltcp "auto-icmp-echo-reply" feature to reply ICMP Echo Request
smoltcp 0.13 (PR smoltcp-rs/smoltcp#1106) made automatic ICMP echo
replies opt-in via the "auto-icmp-echo-reply" feature flag. It is only
part of smoltcp's default features, which this crate disables with
default-features = false. Since the smoltcp 0.13 upgrade, the Tun local
silently drops ICMP Echo Requests and no longer responds to ping.

Fixes #2178
2026-09-25 11:22:04 +08:00
renovate[bot] 1e4ed226e1 fix(deps): update rust crate zstd to 0.14 2026-09-04 22:06:22 +00:00
renovate[bot] e66fcff6ec fix(deps): update rust crate brotli to v9 2026-09-03 13:42:02 +08:00
ty 2de264a374 fix: eliminate warnings under default features
- Replace catch-all arm in OutboundProxyStream::try_into_tcp with
  explicit cfg-gated variant arms, fixing unreachable_patterns warning
  when local-http features are disabled
- Gate utils module behind local/server features, fixing dead_code
  warning for ServerHandle when neither module is compiled
2026-08-27 02:40:36 +08:00
ty b8cce0d519 Bump shadowsocks-crypto to 0.8.0 2026-08-27 02:20:13 +08:00
ty 65f6a8e166 refactor: split inbound_udp_allow_fragmentation from outbound option
Add a dedicated  config option for
inbound (listener) UDP sockets, instead of reusing the outbound option.

- SSConfig / SSServerExtConfig: new
  JSON field (global + per-server)
- Config / ServerInstanceConfig: new field, defaults to false
- server/manager/local: accept_opts.udp.allow_fragmentation now sourced
  from the inbound option; per-server override split into inbound/outbound
- README: document the new option in both JSON examples
- tests/udp.rs: fix unused import of AcceptOpts on non-Linux platforms
2026-08-27 02:07:27 +08:00
peng weikang 668ff171f4 fix: allow fragmentation for inbound UDP replies 2026-08-27 02:07:27 +08:00
renovate[bot] 76a4317a73 fix(deps): update rust crate smoltcp to 0.14 2026-08-17 02:53:17 +00:00
renovate[bot] 89bbabda85 fix(deps): update rust crate rocksdb to 0.25 2026-08-16 12:46:54 +00:00
gengjiawen db65eaa824 fix(net): make udp batch cfgs match the bsd module routing for tvos/watchos
`net/sys/unix/bsd/mod.rs` already routes macos, ios, watchos and tvos
to `bsd/macos.rs`, and that module unconditionally imports
`net::udp::{BatchRecvMessage, BatchSendMessage}`. But the nine cfg
gates in `net/udp.rs` list only macos and ios among the Apple
platforms, so on watchos and tvos those items are configured out while
the importer is still compiled -- E0432, the crate does not build.

Add watchos and tvos to the nine gates so the two lists agree.
2026-08-08 23:20:32 +08:00
renovate[bot] 6b96a84b99 fix(deps): update rust crate base64 to 0.23 2026-07-23 18:43:11 +00:00
renovate[bot] b8e495edfd fix(deps): update rust crate sealed to 0.7 2026-07-23 12:34:56 +00:00
renovate[bot] 402e0288cc fix(deps): update rust crate etherparse to 0.21 2026-07-23 10:06:56 +00:00
renovate[bot] e5257f7e01 fix(deps): update rust crate spin to 0.12 (#2136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-20 18:26:39 +00:00
renovate[bot]andzonyitoo d817982cd1 fix(deps): update rust crate hickory-resolver to 0.26 (#2114)
* fix(deps): update rust crate hickory-resolver to 0.26

* fix: compat hickory-resolver 0.26

* fix: remove unused import

* fix: removed unnecessary deref

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: zonyitoo <zonyitoo@gmail.com>
2026-05-05 17:14:39 +08:00
Linus Färnstrand 9fca88bc93 Replace ring with aws-lc-rs across the dependency tree
Switches the cryptographic backend so that ring is no longer pulled
into shadowsocks-rust on any supported target.

* shadowsocks-crypto: use the new aws-lc-rs backend in place of
  the ring feature
* tokio-rustls: select the aws_lc_rs provider instead of ring.
* hickory-resolver: use tls-aws-lc-rs, https-aws-lc-rs and
  h3-aws-lc-rs in place of the *-ring variants.

The only remaining ring path in the dependency tree is via quinn-proto on
wasm32-unknown-unknown, which aws-lc-rs cannot target and which
shadowsocks does not support.
2026-05-05 17:13:29 +08:00
renovate[bot]andzonyitoo 75d44c7956 fix(deps): update rust crate aes to 0.9 (#2107)
* fix(deps): update rust crate aes to 0.9

* feat: compat cipher 0.5

* fix: replaced deprecated from_slice

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: zonyitoo <zonyitoo@gmail.com>
2026-05-01 22:41:59 +08:00
ty fc8cd59981 feat(service): outbound UDP relay through SOCKS5-only chains
Phase 2 of the outbound proxy refactor. The new
OutboundProxyDatagram type implements DatagramSocket / DatagramSend /
DatagramReceive on top of a freshly-bound local UDP socket plus one
SOCKS5 UDP ASSOCIATE per chain hop. Each datagram on the wire carries
N nested UdpAssociateHeader frames (one per hop), the inner-most one
addressing the ss-server's UDP external endpoint.

Per-hop control connections are built by reusing the existing TCP
chain builder (connect_chain_for_udp_associate) so all of the
shadowsocks DNS resolver / ConnectOpts behaviour still applies.

Integration:

* OutboundProxyClient::associate_udp + supports_udp().
* OutboundProxyClient::supports_udp() returns true iff every hop is
  SOCKS5; HTTP/HTTPS hops cannot transport UDP and downgrade the
  whole chain to 'unsupported'.
* local::net::udp::association::create_proxied_socket dispatches
  based on supports_udp(): when true the per-association socket is
  ProxySocket<OutboundProxyDatagram> wrapping the chained datagram;
  otherwise we keep the previous direct ProxySocket<ShadowUdpSocket>.
* When the chain is configured but unable to relay UDP, sslocal /
  ssserver emit a single startup WARN ('UDP traffic will bypass the
  chain') instead of failing — runtime traffic transparently falls
  back to a direct connection to the ss-server.

OutboundProxyStream gains try_into_tcp() so SOCKS5-only chains can
recover the bare TcpStream as keep-alive control connections —
Sync-friendly storage required because the HTTP CONNECT variant
internally holds a hyper::upgrade::Upgraded which is !Sync.
2026-05-01 22:23:51 +08:00
ty 250ae60446 refactor(service)!: rework outbound proxy chain into a unified client
Replace the previous parallel outbound proxy implementations
(net::outbound_proxy + local::net::tcp::outbound_proxy) with a single
self-contained module at net::outbound, exposing OutboundProxyClient,
OutboundProxyStream, Socks5Auth, HttpProxyAuth and a TcpDialer trait.

Highlights:

* OutboundProxyStream is now a closed-set enum (Bypassed / Https / Http)
  with statically-dispatched poll_* — no more Box<dyn ProxyStream>. The
  HTTPS variant boxes its inner OutboundProxyStream solely to break the
  recursive type instantiation required by the TLS libraries; this is
  not dynamic dispatch.
* HTTP CONNECT client is rebuilt on top of hyper::client::conn::http1
  + hyper::upgrade. The hand-rolled status-line parser (which could
  drop bytes that arrived in the same read() as the header terminator,
  did not handle 100-Continue, etc.) is gone.
* Auth parameters are now strongly typed:
    Socks5Auth::{None, UsernamePassword{..}}
    HttpProxyAuth::{None, Basic{..}}
  Both enums are #[non_exhaustive] for forward compatibility.
* AutoProxyClientStream's ProxiedViaChain variant is folded back into a
  single Proxied(ProxyClientStream<MonProxyStream<OutboundTransport>>)
  variant, with OutboundTransport = Direct(TcpStream) | Chained(...).
* ServiceContext (both server and local) now caches an
  Arc<OutboundProxyClient> built once at set_outbound_proxies time
  instead of re-parsing the chain on every connection.
* The standalone Socks5TcpClient / Socks5UdpClient implementations
  previously duplicated across net::socks5_client and
  local::socks::client::socks5 are unified into the latter location;
  net::outbound::socks5 only keeps the chain-friendly Socks5Negotiator.
* When UDP is enabled but the configured chain contains a non-SOCKS5
  hop, sslocal/ssserver emits a single startup warning. Phase 2 will
  add UDP relay support for SOCKS5-only chains.

BREAKING CHANGE: the following items have moved or been removed:

* crate::net::outbound_proxy — removed; use crate::net::OutboundProxyClient
* crate::net::http_connect::HttpConnectClient — moved to
  crate::net::HttpConnectClient (only available with the local-http feature)
* crate::net::Socks5TcpClient (the chain-mixing wrapper) — split: the
  pure SOCKS5 client is now crate::local::socks::client::socks5::Socks5TcpClient;
  the in-band negotiator is crate::net::Socks5Negotiator
* crate::local::net::tcp::outbound_proxy — removed
* ServiceContext::outbound_proxies() — replaced by outbound_client()
  returning Option<&Arc<OutboundProxyClient>>
2026-05-01 22:23:51 +08:00
Alessandro Riva bb039a6ac0 feat: add eih support 2026-04-26 01:24:30 +08:00
renovate[bot] 3d5c09512c fix(deps): update rust crate etherparse to 0.20 2026-04-20 08:40:57 +00:00
Linus Färnstrand d37fd371d1 Remove unused imports in outbound_proxy (#2111)
* Remove unused imports in outbound_proxy

* Upgrade rustls-webpki to a version without advisories

* Upgrade rand to a version without advisories
2026-04-19 22:34:55 +08:00
Yuwei BaandCopilot a71d8a0f40 feat: expose user_key() on CryptoStream and ProxyServerStream (#2109)
After an AEAD2022 handshake with a ServerUserManager, the authenticated
user's raw key is available inside DecryptedReader but not reachable
from the public API.  Expose it via:

  CryptoStream::user_key()       -> Option<&[u8]>
  ProxyServerStream::user_key()  -> Option<&[u8]>

This lets callers (e.g. clash-rs) identify which multi-user identity
was authenticated without having to manually peek and re-derive the
identity subkey via BLAKE3 + AES-256-ECB before the handshake.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 22:15:25 +08:00
zonyitoo 6df217c09a feat(proxy-client): refactor outbound_proxy to use concrete proxy clients
- Extract HttpConnectClient to dedicated http_connect.rs module
- Add conduct_handshake_and_connect method to Socks5TcpClient for stream-based SOCKS5 negotiation
- Refactor outbound_proxy to use Socks5TcpClient and HttpConnectClient methods instead of direct protocol implementation
- Update module exports in net/mod.rs
2026-04-12 22:14:49 +08:00
zonyitoo 948cd5ad9f refactor: extract HttpConnectClient to dedicated http_connect module
- Move HttpConnectClient from socks5_client.rs to standalone http_connect.rs
- HttpConnectClient encapsulates HTTP CONNECT protocol implementation
- socks5_client.rs focuses exclusively on SOCKS5 protocol handling
- Update imports in outbound_proxy.rs to use new http_connect module
- Add http_connect module export in net/mod.rs
- All feature combinations compile successfully
2026-04-12 22:13:37 +08:00
zonyitoo 4d7b9e1203 feat(proxy-chain): refactor outbound proxy architecture
- Move HTTP CONNECT logic to shared outbound_proxy helper module
- Simplify Socks5TcpClient to only handle SOCKS5 protocol, remove OutboundProxyAuth dependency
- Extract proxy chain utilities into separate net/outbound_proxy.rs with HTTP/HTTPS support
- Add UDP mode warnings in local and server startup when proxy chain is configured
- Share ProxyStream trait between modules for better code organization
- Gate HTTPS proxy support with feature flags (local-http-native-tls or local-http-rustls)
2026-04-12 01:12:21 +08:00
zonyitoo c18abd4062 fix: make native-tls optional 2026-04-11 22:48:23 +08:00
Fan Yang 743d0b3931 [Feature]: Allow to chain ssserver to another socks5/http/https proxy #1397 (#2106)
* Add outbound SOCKS5 proxy chains with auth

* Add HTTP and HTTPS CONNECT support to outbound proxy chains

* Initialize outbound proxy chains in manager server configs

* Fix outbound proxy chain handling and share HTTPS transport

* docs: update README for outbound proxy chains
2026-04-11 22:10:08 +08:00
zonyitoo 7d28db736b feat: bump version v1.25.0
- MSRV upgraded to v1.91, due to smoltcp v0.13.
- Upgrade dependencies
- Add cursor rule orientation
2026-03-23 00:03:27 +08:00
renovate[bot]andzonyitoo 2d755f05d6 fix(deps): update rust crate smoltcp to 0.13 (#2094)
* fix(deps): update rust crate smoltcp to 0.13

* chore: raise MSRV to 1.91 for smoltcp 0.13

smoltcp 0.13 requires Rust 1.91. Align rust-version, clippy msrv,
and GitHub MSRV workflow with that toolchain.

Made-with: Cursor

* fix(deps): bump aws-lc-rs for RUSTSEC-2026-0048

Update aws-lc-sys to 0.39.0 via aws-lc-rs 1.16.2 (CRL IDP validation fix).

Made-with: Cursor

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: zonyitoo <zonyitoo@gmail.com>
2026-03-22 23:55:12 +08:00
Aaron Chen ae80b84e22 fix: remove unnecessary double checks in PingBalancer (#2093) 2026-03-19 19:07:24 +08:00
renovate[bot]andzonyitoo ef671c38ad fix(deps): update rust crate rand to 0.10 (#2074)
* fix(deps): update rust crate rand to 0.10

* fix: small_rng enabled by default

* fix: small_rng enabled by default

* fix: from_os_rng replaced by make_rng

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: zonyitoo <zonyitoo@gmail.com>
2026-02-11 00:37:52 +08:00
Aaron Chen 46c96ac1fb chore: fix typos 2026-02-09 15:53:16 +08:00
Aaron Chen 80c1be04a8 chore: fix a warning 2026-02-09 15:52:49 +08:00
renovate[bot] cc9e8faa70 fix(deps): update rust crate nix to 0.31 (#2066)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-23 16:21:06 +08:00
arloor 2dbd8468f3 fix(http): add is_ready method and enhance connection checks (#2062)
* fix(http): add is_ready method and enhance connection checks

* fix(http): enhance connection caching by ensuring readiness before storing
2026-01-19 10:26:41 +08:00
Aaron Chen d83d6d2246 chore: clippy fix (#2059) 2026-01-06 11:59:22 +08:00
zonyitoo 8133713b4a feat: optimized socket2::Socket with socket2::SockRef 2026-01-05 18:07:22 +08:00
ty 380db70ea7 fix(shadowsocks-service): fixed build without local-http (#2057) 2025-12-19 15:33:03 +08:00
zonyitoo b28fbf51f4 feat(local-http): HTTP basic auth optimize log (#1994) 2025-12-19 03:36:27 +08:00
zonyitoo 1008eeec1d feat(local-http): support HTTP Basic auth (#1994) 2025-12-19 03:26:17 +08:00
zonyitoo bb276eda01 chore: example.com returns HTTP/1.1 2025-12-11 07:54:16 +08:00
zonyitoo 7ee1aa9223 chore: release v1.24.0 2025-12-11 07:38:59 +08:00
zonyitoo db7a2a8d12 feat: applied json5-rs callum-oakley/json5-rs#53 2025-12-09 02:20:55 +08:00
Aaron Chen aa9156a794 fix: bump msrv to 1.88 (#2054) 2025-12-09 02:19:35 +08:00