- CHANGELOG: the 0.1.0 entry finalized for publish — the alkcall 0.8.1 / alktunnels 0.2.1 bumps and the fuzzing adoption folded in (the crate is unpublished, so no separate hardening cut — the alktunnels 0.2.1 precedent only applies post-publish); the fuzz/ workspace bullet added (three targets, 129 seeds, corpus-replay gate, campaigns clean) - README: the Version 0.1 note aligned (alkcall 0.8.1 — the duplicate-adopt fix is directly relevant to the consumer open/adopt paths; alktunnels 0.2.1; the fuzz posture noted) - docs/architecture/overview.md: dep tree 0.8.1 / 0.2.1 Verification: cargo test (376 default / 638 all-features), fuzz/shared corpus replay (3 pass), clippy --all-targets -D warnings (default, all-features, wasm32-unknown-unknown), fmt --check, doc, wasm32 check, and cargo publish --dry-run --allow-dirty all green (64 packaged files — fuzz/, docs/research|reviews|plans/, tasks/, .opencode/, AGENTS.md all excluded)
115 lines
6.6 KiB
Markdown
115 lines
6.6 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to this crate are documented here. The format is
|
|
based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
this crate adheres to [Semantic Versioning](https://semver.org/).
|
|
|
|
## [Unreleased]
|
|
|
|
## [0.1.0] - 2026-09-27
|
|
|
|
The initial release: SOCKS5 (RFC 1928) as a produced resource on
|
|
alkcall channels — the `-D` flavor as a first-class protocol crate,
|
|
over alkcall 0.8.1. The defining posture: the protocol never binds a
|
|
port; kernel binds exist only as explicit, feature-gated assembly
|
|
shapes. Composed against by the alktunnels 0.2.1 graduation (the
|
|
direct op + the bind-first/forwarded flow power the composed
|
|
providers). Fuzzing adopted (the `fuzz/` workspace — excluded from
|
|
the published package) over the crate's two attacker-reachable
|
|
parsers: the ASSOCIATE datagram codec and the RFC 1928 handshake;
|
|
campaigns clean, no crate bugs.
|
|
|
|
### Added
|
|
|
|
- **The RFC 1928 core (`src/rfc1928/`, ADR-013)** — the targeted fork
|
|
of fast-socks5 v1.0.0's `T`-generic subset, extracted in-tree as
|
|
fully owned code (MIT notice bundled; extraction per ADR-011): the
|
|
wire consts, codecs, errors, the explicit typestate server API
|
|
(`Socks5ServerProtocol<T, states::*>`, generic over
|
|
`T: AsyncRead + AsyncWrite + Unpin`), and the client `use_stream`
|
|
path. Two deliberate changes carried from the fork point: BIND
|
|
support in the base (ADR-007) and the `new_reply` publication. No
|
|
kernel-net code — the default crate is wasm-clean by construction.
|
|
- **The wire codec (`wire`, ADR-003)** — the ASSOCIATE datagram frame
|
|
(`[len: u16 BE][addr + data]`), framing applied exactly once at
|
|
this layer; the `DatagramReader` incremental parser; the
|
|
`MAX_INNER_LEN` oversize guard.
|
|
- **The wire-stable open-op surface (`params`, ADR-001/006)** — the
|
|
`alk/socks5` ALPN, the `channels/socks5/sub` op id, the trivial
|
|
`{}` params (the RFC conversation is the data plane; nothing to
|
|
negotiate at open time), and the two scope constants
|
|
(`socks5:open`, `socks5:bind` — two grants, never implied). The
|
|
typed open-error vocabulary rides `error` (the ADR-049 reasons).
|
|
- **The three seams (`egress`, ADR-004)** — `DialFn` /
|
|
`EgressFactory` / `BindAcceptFactory`: functions, not traits, at
|
|
the three I/O planes; ownership rides the handle structurally
|
|
(`TargetHandle` / `BindTargetHandle` / `BindAccept`); the egress
|
|
readiness contract (`next_reply` `Pending` when nothing is ready)
|
|
is load-bearing API.
|
|
- **The producer half (`producer`)** — the session-gate establisher
|
|
(validates the opener identity; the target is unknown at open
|
|
time), the command-dispatch pump handler (CONNECT pass-through /
|
|
ASSOCIATE datagram stage / BIND phase model — reply#1 → quiet wait
|
|
→ exactly one accept → reply#2 → pass-through), the command gate
|
|
(`socks5:bind` at BIND dispatch), the identity witness, and both
|
|
wiring shapes: the free functions
|
|
(`register_socks5_openable` / `socks5_session_establisher` /
|
|
`make_socks5_pump_handler` / `serve_socks5_over`) and the builder
|
|
(`Socks5Producer::builder()` — dial/egress/accept/bind_policy).
|
|
Data planes ride `alkcall::channels::pump_bidi` (ADR-050).
|
|
- **The consumer half (`consumer`)** — the typed channels-native
|
|
sessions, each owning its channel's teardown (`Drop` aborts +
|
|
reaps; no `Clone`): [`Socks5ChannelSession`] (CONNECT — raw
|
|
pass-through, the `split`/`into_inner` postures),
|
|
[`AssociateSession`] (the framed datagram stage —
|
|
`send_datagram`/`recv_datagram`/`split`), [`BindSession`] (the
|
|
reply#1/reply#2 reader + the `BindDataPlane` pass-through), and the
|
|
token-carrying surface (`TokenClient` + the `*_with_token`
|
|
constructor twins — the cred-bearing front door's re-origination).
|
|
- **The `local` feature (ADR-008/009)** — the socket-backed seam
|
|
providers (`tcp_dial` / `udp_egress` / `bind_accept` — the explicit
|
|
bind decisions, resolving via the non-blocking
|
|
`tokio::net::lookup_host` resolver), plus both doors: the
|
|
consumer-side front doors (`FrontDoor` — the NoAuth raw pipe + the
|
|
RFC 1929 cred-bearing door with the credential→identity mapping,
|
|
ADR-005; `UdpFrontDoor` — one association per bound relay socket,
|
|
the RFC codec at the door boundary only, the peer-pin posture: the
|
|
client's source IP pins on the first parsed datagram, replies
|
|
follow the latest-seen port from that IP, foreign-IP datagrams are
|
|
dropped per RFC 1928's MUST — OQ-SK2-09) and the producer-side
|
|
listener (`ProducerDoor` — vanilla clients dial it, each accepted
|
|
connection is served via `serve_socks5_over`; the ADR-014
|
|
idle-first-byte window + max-sessions cap knobs).
|
|
- **The `composed` feature (ADR-008/009)** — the alktunnels 0.2.1
|
|
composed providers: `direct_egress` (the direct-op egress — a lazy
|
|
destination→tunnel table, `DEFAULT_MAX_TUNNELS` bounded), and
|
|
`bindfirst_accept` + `register_forwarded_serving` (the BIND accept
|
|
whose listener lives on a bind-first listen resource; the
|
|
`CorrelationRegistry` correlates the forwarded op's peer metadata
|
|
into `ForwardedSession`s).
|
|
- **The idle-timeout posture (ADR-014)** — every phase
|
|
teardown-governed, no protocol-layer idle bound; the doors own the
|
|
accept-path defense.
|
|
- **The suites** — the differential suite against the upstream
|
|
reference checkout (`tests/rfc1928-differential.rs`), the
|
|
protocol-core integration suite (`tests/core-integration.rs`), and
|
|
the assembly-layer integration suite (`tests/assembly-integration.rs`).
|
|
- **Fuzzing (`fuzz/` workspace, `docs/research/fuzzing.md`)** — the
|
|
alkcall §5 pattern adopted for this crate's two attacker-reachable
|
|
parser classes: `datagram_codec` (the `wire` codec — no-panic,
|
|
infallible decode, size bound, re-frame round-trip, the
|
|
`retained_bytes` bound) and `rfc1928_codec` (the RFC 1928 handshake
|
|
state machine, fed attacker bytes from both protocol directions),
|
|
plus `params_parse`. Invariant logic lives in `fuzz/shared/` — a
|
|
stable-toolchain crate replayed as plain `cargo test` (the corpus
|
|
replay is the fuzz gate). 129 committed seeds; detached campaigns
|
|
clean (`oom/timeout/crash: 0/0/0`, coverage saturated). The one
|
|
diagnostic added: `DatagramReader::retained_bytes()`. `fuzz/` is
|
|
excluded from the workspace and from the published package.
|
|
|
|
[`Socks5ChannelSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.Socks5ChannelSession.html
|
|
[`AssociateSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.AssociateSession.html
|
|
[`BindSession`]: https://docs.rs/alksocks/latest/alksocks/consumer/struct.BindSession.html
|
|
|
|
[Unreleased]: https://git.alk.dev/alkdev/alksocks/compare/v0.1.0...HEAD
|
|
[0.1.0]: https://git.alk.dev/alkdev/alksocks/releases/tag/v0.1.0 |