* 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 <[email protected]>
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
* systemd: Use network-online.target for server
Fixes#2186
ss-server needs to wait for the network to be up to be able to bind to non-wildcard IPs. Interface is configured at `network-online.target` but not at `network.target`.
ss-local unit is kept the same, with `After=network.target` for proper ordering at shutdown - unit is stopped before network is shutdown.
* systemd unit: improvements
`exec` is [recommended](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Type=).
Add `NoNewPriviledges` which has been used in [Arch packaging](https://gitlab.archlinux.org/archlinux/packaging/packages/shadowsocks-rust/-/blob/main/shadowsocks-rust-server%40.service?ref_type=heads).
Add `CAP_NET_ADMIN` in ss-local which is needed for TUN mode.
Removed `CAP_NET_BIND_SERVICE` from ss-local. Do you really need to bind to priviledged port in *client*?
Debian unit left un-hardened. I'm not sure why it's different from the other ss-server unit.
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
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
- 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
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