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.
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
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
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
`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.
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.
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>