Lands the composed accept (ADR-007 variant 2 / ADR-008): the BIND accept whose listener lives on an alktunnels 0.2.0 bind-first listen resource, at the graduation's vanilla fidelity. - `composed::bindfirst_accept` — the BindAcceptFactory: opens the listen channel (reply#1 advertises the open-op reply's real `bound`, the 0.8.0 reply projection — no expected-addr stand-in), inserts the minted `listen` token into the correlation registry, and returns the accept future parked on the token's oneshot. - `composed::register_forwarded_serving` — the forwarded-op serving half on the SOCKS5 producer's connection: the shared handler correlates by token and owns the middle pump (either-EOF completion; a three-party per-direction shutdown cascade would deadlock). - The Sync-bound wrinkle: `SyncHalves`/`OwnedWrite` (mutex Sync-proof, no unsafe) over the forwarded halves; the owner (`ForwardedOwner`, the TunnelListener) rides inside the write payload — dropping the handle drops the session, halves, and listener together (teardown structural, compiler-enforced). - `tests/composed-accept.rs`: the full story (real bound, real peer, raw pass-through round trip), client-gone teardown drop-witness, distinct tokens for concurrent BINDs, per-accept isolation of a FORBIDDEN forwarded open, late-forwarded correlation miss. - The producer-side adopted forwarded entry rides the per-adopter contract (transport-EOF reap), as upstream's bind-first suites concede; the no-leak probe reflects that shape. Verification: cargo test (default + --all-features), wasm check/clippy (hard gate), clippy --all-targets --all-features -D warnings, fmt --check — all clean.
134 lines
5.7 KiB
Rust
134 lines
5.7 KiB
Rust
//! alksocks — SOCKS5 (RFC 1928) as a produced resource on alkcall
|
|
//! channels.
|
|
//!
|
|
//! A producer/consumer protocol crate (the alktty/alktunnels shape):
|
|
//! the producer half registers the `channels/socks5/sub` open op and
|
|
//! drives the RFC 1928 conversation in-stream over the channel's
|
|
//! `BiStream`; the consumer half opens SOCKS5 channels and speaks the
|
|
//! protocol inside them. The defining posture: the protocol never
|
|
//! binds a port — kernel binds exist only as explicit, feature-gated
|
|
//! assembly shapes (`local`), never in the protocol layer.
|
|
//!
|
|
//! ## The landed surface (the protocol core)
|
|
//!
|
|
//! - **The wire vocabulary and the explicit typestate** live in
|
|
//! [`rfc1928`]: a targeted fork of fast-socks5 v1.0.0's `T`-generic
|
|
//! subset (MIT, third-party upstream) carrying two deliberate
|
|
//! changes (BIND support, `new_reply` publication) — see
|
|
//! `docs/architecture/rfc1928.md`, ADR-013, and ADR-011.
|
|
//! - **The wire codec** ([`wire`]): the ASSOCIATE datagram frame
|
|
//! (`[len: u16 BE][addr + data]`, ADR-003) — framing is applied
|
|
//! exactly once, by this layer; the egress speaks inner bytes.
|
|
//! - **The wire-stable open-op surface** ([`params`]): the
|
|
//! `channels/socks5/sub` op id, the `alk/socks5` ALPN, the
|
|
//! trivial `{}` params (ADR-001), and the two scope constants
|
|
//! (`socks5:open`, `socks5:bind` — two grants, never implied,
|
|
//! ADR-006). The typed open-error vocabulary lives in [`error`].
|
|
//! - **The three seams** ([`egress`]): `DialFn` / `EgressFactory` /
|
|
//! `BindAcceptFactory` (ADR-004) — functions, not traits, at the
|
|
//! three I/O planes; ownership rides the handle structurally; the
|
|
//! egress readiness contract (`next_reply` `Pending` when nothing
|
|
//! is ready) is load-bearing API.
|
|
//! - **The producer half** ([`producer`]): the session-gate
|
|
//! establisher, the command-dispatch pump (CONNECT pass-through /
|
|
//! ASSOCIATE datagram stage / BIND phase model), the scope gates
|
|
//! (the open gate `socks5:open` at the registry; the command gate
|
|
//! `socks5:bind` at BIND dispatch), and
|
|
//! [`register_socks5_openable`] — substrate-agnostic over the
|
|
//! seams; never a bind in the protocol layer.
|
|
//! - **The consumer half** ([`consumer`]): the typed channels-native
|
|
//! sessions — [`Socks5ChannelSession`] (CONNECT),
|
|
//! [`AssociateSession`] (the framed datagram stage),
|
|
//! [`BindSession`] (the reply#1/reply#2 reader) — each owning its
|
|
//! channel's teardown; no `Clone`.
|
|
//!
|
|
//! `local` (the socket-backed assembly-layer providers —
|
|
//! [`crate::local::tcp_dial`] / [`crate::local::udp_egress`] /
|
|
//! [`crate::local::bind_accept`], ADR-008/009) is feature-gated: the
|
|
//! default crate is protocol-only and wasm-clean by construction, and
|
|
//! the composed providers ride `composed`
|
|
//! ([`crate::composed::direct_egress`] — the alktunnels direct-op
|
|
//! egress, ADR-008/009).
|
|
//!
|
|
//! Architecture: `docs/architecture/overview.md` (spec set:
|
|
//! wire.md, producer.md, consumer.md, rfc1928.md); the cross-cutting
|
|
//! behavior is pinned end to end by `tests/core-integration.rs`
|
|
//! (one registration, all three commands; concurrency; the teardown
|
|
//! matrix; the identity/scope matrix; the readiness contract).
|
|
|
|
#![forbid(unsafe_code)]
|
|
|
|
pub mod rfc1928;
|
|
|
|
pub mod consumer;
|
|
pub mod egress;
|
|
pub mod error;
|
|
pub mod params;
|
|
pub mod producer;
|
|
pub mod wire;
|
|
|
|
#[cfg(feature = "local")]
|
|
pub mod local;
|
|
|
|
#[cfg(feature = "composed")]
|
|
pub mod composed;
|
|
|
|
#[cfg(feature = "local")]
|
|
pub mod door;
|
|
|
|
/// The shared test helpers (review 002 N-5): the single-sourced
|
|
/// harness shapes, compiled into the lib test crate. The file lives
|
|
/// under `tests/common/` so the integration suite reaches the same
|
|
/// copy through its own `mod` declaration (`tests/harness.rs`
|
|
/// re-exports the consumed subset). `#[cfg(test)]` keeps it out of
|
|
/// the published crate; the `#[path]` resolves relative to `src/`.
|
|
#[cfg(test)]
|
|
#[path = "../tests/common/mod.rs"]
|
|
pub(crate) mod test_common;
|
|
|
|
// The shared module is written once against the extern-crate paths
|
|
// (`alksocks::`) the integration suite resolves; inside the lib test
|
|
// crate those same paths resolve via this alias (`extern crate`
|
|
// self-linkage, the standard `mod common` companion).
|
|
#[cfg(test)]
|
|
extern crate self as alksocks;
|
|
|
|
pub use consumer::{
|
|
AssociateSession, BindDataPlane, BindReply1, BindSession, Socks5ChannelSession,
|
|
SpentSessionError, TakenAssociateInner,
|
|
};
|
|
pub use egress::{
|
|
egress_factory, BindAccept, BindAcceptError, BindAcceptFactory, BindAcceptFactoryError,
|
|
BindTargetHandle, DialFn, Egress, EgressError, EgressFactory, SocksDialError, TargetHandle,
|
|
};
|
|
pub use error::{
|
|
ReplyError, SocksError, SocksEstablishmentReason, SocksOpenError, SocksServerError,
|
|
SocksSessionError,
|
|
};
|
|
pub use params::{
|
|
establishment_reason, socks5_open_spec, Socks5Params, OP_SOCKS5_OPEN, SOCKS5_ALPN,
|
|
SOCKS5_BIND_SCOPE, SOCKS5_OPEN_SCOPE,
|
|
};
|
|
pub use producer::{
|
|
make_socks5_pump_handler, register_socks5_openable, serve_socks5_over,
|
|
socks5_session_establisher, IdentityWitness, Socks5Producer, Socks5ProducerBuilder,
|
|
};
|
|
pub use wire::{addr_encoding_len, frame, frame_inner, DatagramReader, WireError, MAX_INNER_LEN};
|
|
|
|
#[cfg(feature = "local")]
|
|
pub use local::{bind_accept, tcp_dial, udp_egress, LocalEgress};
|
|
|
|
#[cfg(feature = "local")]
|
|
pub use local::{ProducerDoor, ProducerDoorBuilder, ProducerDoorGuard};
|
|
|
|
#[cfg(feature = "composed")]
|
|
pub use composed::{
|
|
bindfirst_accept, direct_egress, key_of, register_forwarded_serving, substrate_addr_of,
|
|
ComposedEgress, CorrelationRegistry, ForwardedSession, SessionOwner, DEFAULT_MAX_TUNNELS,
|
|
};
|
|
|
|
#[cfg(feature = "local")]
|
|
pub use consumer::TokenClient;
|
|
#[cfg(feature = "local")]
|
|
pub use door::{CredentialTable, DoorCredential, FrontDoor, UdpFrontDoor};
|