feat(client): land the -R leg — the acceptor registry + the reverse-listen rows (w4-reverse-listen)
- src/client_accept.rs: the assembly-mutable acceptor registry (per-type fixed slots under `tunnel`; refuse-never-replace install; payload = the `TunnelChannel` duplex + the open's echo/originator fields — the harness `ForwardedAcceptor` vocabulary grown), the reverse-listen rows (bound-port surfaced; the listen/cancel forms; the runtime-Unix gate at the listen), and the accepted-opens' spawned handoff tasks. - src/client_handler.rs: the `forwarded-tcpip` / `forwarded-streamlocal` hooks route through the registry at arrival time — registered + admitted → accept + handoff; unregistered/unfeatured/cap-exhausted → the clean `AdministrativelyProhibited` decline (never accept-and-drop); pre-install arrivals decline, never buffered. - src/client.rs: the dial wires the empty registry into the handler + the connection (`SshConnection::acceptors`, the late-install accessor) + the listener budget's dedicated counter. - src/error.rs: `OpError::NoAcceptor` / `ListenRefused` / `ListenCapExceeded`; `RequestDenied` → `ListenRefused` (the listen/cancel forms' wire decline class; the port-0 mint rides alone at this pin — an explicit port's bound *is* the requested one, surfaced). - tests/common/sshd_double.rs: the fake `sshd`'s `-R` half — the listen/cancel hooks bind real sockets (the out-param mint is the real bound port), the accept loops open the server-initiated forwarded channels + pump per accepted connection, cancels match by the raw wire strings and tear the listener down. - tests/client_reverse_listen.rs: the contract rows (listen + data leg, the mint, the pre-wire gates, the declines, late-install on/off, the cancels, the ceiling, the unix twins). - tests/tunnel_mapping.rs: the full-circle client ↔ server-direction smoke (both `-R` legs through the alkcall fabric double); real- `sshd` `-R` stays wave-5 e2e (recorded). - client.md: the acceptor's landing record (shape, cap relation, gates, unix-gate placement, port-0 parity). - fix(w4-op-rows, test cfg): the test mod's `family_exit_code` import gated to the exit-consuming builds (`--features tunnel` did not compile its tests before). Verification: cargo test (default / each feature / --all-features) green; cargo clippy --all-targets -- -D warnings (none + --all-features) clean; cargo fmt --check clean; cargo doc --no-deps clean (default build; all-features carries only the pre-existing client_ops private-link warnings).
This commit is contained in:
1 parent
2371c2e169
commit
7e5989f574
11 files changed
+2314
-54
No files matched your search
@@ -69,6 +69,39 @@ half-capability, which the family forbids (ADR-003 symmetry +
|
||||
consumers turn capabilities on/off per connection; the shape is an
|
||||
assembly-mutable acceptor registry).
|
||||
|
||||
> **The landed acceptor shape (`w4-reverse-listen` — recorded at
|
||||
> landing):** the registry is **per-type fixed slots** — exactly the
|
||||
> two `-R` data-leg types the client direction can serve
|
||||
> (`forwarded-tcpip`, `forwarded-streamlocal`, both under `tunnel`;
|
||||
> no map, no generic key), one acceptor trait per type; install is
|
||||
> **refuse-never-replace** (the `ListenRouter::register` recorded
|
||||
> posture, mirrored client-side); the slots are read **at arrival
|
||||
> time** by the dial handler's hooks — a pre-install/unfeatured open
|
||||
> is declined at arrival, never buffered. The payload an accepted
|
||||
> open hands the assembly: the channel duplex (family-typed — the
|
||||
> `-L` rows' `TunnelChannel` byte duplex, one type both legs ride) +
|
||||
> the open's echo/originator fields; **the pump is the acceptor's**
|
||||
> from the handoff on (the family handler shape) — the crate keeps no
|
||||
> accepted-open ledger. Bounded bookkeeping, the cap relation: the
|
||||
> registry bounded by construction (two slots); the
|
||||
> **reverse-listen count** and the **accepted-open handoff count**
|
||||
> are two dedicated counters, each against the same
|
||||
> `Limits::max_concurrent_channels` ceiling (the server-side
|
||||
> listener/ListenerCap posture mirrored — independent counts, one
|
||||
> ceiling; the listens live at the peer, the counter exists so a dial
|
||||
> cannot mint an unbounded listener set). The **forward-request rows
|
||||
> gate on the registered acceptor** (the issuing-side disposition:
|
||||
> a listen whose data leg cannot serve is never asked for —
|
||||
> install-first, `OpError::NoAcceptor` pre-wire), plus
|
||||
> `OpError::ListenRefused` (the peer's `RequestDenied` — denied
|
||||
> listens and unknown-listener cancels, sshd raw-string parity) and
|
||||
> `OpError::ListenCapExceeded` (the ceiling, pre-wire). The
|
||||
> **runtime-Unix gate** rides the unix *listen* rows only (the bind
|
||||
> is the platform-bound op; the `forwarded-streamlocal` ingestion is
|
||||
> platform-neutral byte pumping). The `tcpip_forward` reply's mint
|
||||
> rides only for `port = 0` requests (russh/sshd parity: an explicit
|
||||
> port's bound *is* the requested port, which the row surfaces).
|
||||
|
||||
## Lifecycle primitives (OQ-12 resolution)
|
||||
|
||||
- Forward listeners return the **actually-bound port** (russh
|
||||
|
||||
Reference in new issue
Block a user