publish-prep: the v0.1 pre-publish round — the README, the CHANGELOG, the license files, the readme field
- README.md: the alktunnels-shape quick start (producer registration, the three consumer sessions, both doors, the producer-side listener), the architecture table, the feature matrix, the verification commands — self-contained, since docs/architecture/ rides the package but docs/reviews|research|plans do not - CHANGELOG.md: the Keep-a-Changelog 0.1.0 entry (the sibling shape: Unreleased section + tag links) - LICENSE-MIT / LICENSE-APACHE: copied from the siblings (crates.io license-file convention; the crate license is dual from Cargo.toml) - Cargo.toml: `readme = "README.md"` (the sibling pattern; the packaged crate showed `readme = false`) Verification: cargo test + cargo test --all-features (15 suites each), clippy --all-targets --all-features -D warnings, fmt --check, doc --no-deps (0 warnings), wasm32-unknown-unknown check (default + composed), publish --dry-run (64 files, 278.3 KiB).
This commit is contained in:
1 parent
13c6209b07
commit
4d9796be64
5 files changed
+557
No files matched your search
@@ -0,0 +1,96 @@
|
||||
# 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.0. 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.0 graduation (the
|
||||
direct op + the bind-first/forwarded flow power the composed
|
||||
providers).
|
||||
|
||||
### 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), 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) 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.0
|
||||
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`).
|
||||
|
||||
[`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
|
||||
@@ -6,6 +6,7 @@ rust-version = "1.88"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "SOCKS5 (RFC 1928) proxy protocol over alkcall channels: targeted-forked fast-socks5 typestate core plus producer/consumer sessions. Producer/consumer protocol crate."
|
||||
repository = "https://git.alk.dev/alkdev/alksocks"
|
||||
readme = "README.md"
|
||||
keywords = ["socks5", "proxy", "rfc1928", "channels", "alkcall"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
exclude = [".opencode/", "AGENTS.md", "docs/reviews/", "docs/research/", "docs/plans/", "tasks/", "docs/sdd_process.md"]
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name), or refer to, the Work.
|
||||
(Note: Derivative Works shall not include works that remain separable from,
|
||||
or merely link (or bind by name) to the interfaces of, the Work and
|
||||
Derivative Works thereof.)
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2025-2026 Alk Development
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025-2026 Alk Development
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,247 @@
|
||||
# alksocks
|
||||
|
||||
SOCKS5 (RFC 1928) as a produced resource on
|
||||
[alkcall](https://crates.io/crates/alkcall) channels: the `ssh -D`
|
||||
(dynamic / SOCKS5 proxy) flavor as a first-class protocol crate —
|
||||
the sibling of [alktty](https://crates.io/crates/alktty) (terminals)
|
||||
and [alktunnels](https://crates.io/crates/alktunnels) (`-L`/`-R`
|
||||
tunnels). The RFC 1928 conversation rides inside a channels data
|
||||
channel like any other produced resource, and target dials happen on
|
||||
the producing side.
|
||||
|
||||
The defining posture: **the protocol never binds a port unless
|
||||
explicitly configured to.** Kernel binds exist only as explicit,
|
||||
feature-gated assembly shapes on either side — the consumer may
|
||||
expose the resource on a local port (the front door vanilla clients —
|
||||
curl, browsers, tun2proxy — point at), and the producer may serve
|
||||
from a real listener. A producer/consumer protocol crate, no
|
||||
transport dependencies; the transport is whatever alkcall runs over.
|
||||
|
||||
## Quick start
|
||||
|
||||
### Producer — register the SOCKS5 open op
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
|
||||
use alkcall::channels::ChannelCore;
|
||||
use alkcall::registry::registration::OperationRegistry;
|
||||
use alkcall::core::auth::AuthContext;
|
||||
use alksocks::producer::Socks5Producer;
|
||||
|
||||
// The three seams (ADR-004): dial (CONNECT), egress (UDP ASSOCIATE),
|
||||
// accept (BIND). Missing seams = those commands refuse
|
||||
// RFC-faithfully; behind `local`, alksocks::local::{tcp_dial,
|
||||
// udp_egress, bind_accept} are the reference socket-backed
|
||||
// constructors, and the composed providers (feature `composed`) ride
|
||||
// alktunnels' direct-op/bind-first resources instead.
|
||||
let producer = Socks5Producer::builder()
|
||||
.dial(alksocks::local::tcp_dial())
|
||||
.egress(alksocks::local::udp_egress())
|
||||
.accept(alksocks::local::bind_accept("127.0.0.1".parse()?))
|
||||
.build();
|
||||
|
||||
let core = ChannelCore::new(manager, default_policy());
|
||||
let op_registry = Arc::new(OperationRegistry::new());
|
||||
|
||||
// Registers `channels/socks5/sub` on the `alk/socks5` ALPN: the
|
||||
// registry's ACL (scope `socks5:open`) gates the open op, the
|
||||
// session-gate establisher checks the opener identity, and the pump
|
||||
// handler drives the RFC 1928 state machine in-stream over the
|
||||
// channel's `BiStream`. Wire `core` + `op_registry` into your
|
||||
// channels serving stack — this call is the socks5-specific step:
|
||||
producer.register(&core, &op_registry, AuthContext::anonymous(b"alk/socks5"), None)?;
|
||||
```
|
||||
|
||||
### Consumer — open a SOCKS5 channel (CONNECT)
|
||||
|
||||
```rust
|
||||
use alkcall::channels::client::ChannelClient;
|
||||
use alksocks::rfc1928::util::target_addr::TargetAddr;
|
||||
use alksocks::Socks5ChannelSession;
|
||||
|
||||
let client: ChannelClient = /* your alk/channels connection */;
|
||||
|
||||
// One session = one channel = one SOCKS5 session. The RFC handshake
|
||||
// runs in-stream over the channel; a refused target surfaces the RFC
|
||||
// reply code (typed errors, no phantom channel — the adopted channel
|
||||
// is torn down before the error returns).
|
||||
let mut session = Socks5ChannelSession::connect(
|
||||
&client,
|
||||
TargetAddr::Domain { addr: "example.internal".into(), port: 80 },
|
||||
).await?;
|
||||
|
||||
let (mut read, mut write) = session.split().expect("pipe");
|
||||
// ... raw pass-through: the channel IS the target connection ...
|
||||
```
|
||||
|
||||
### Consumer — UDP ASSOCIATE (the framed datagram stage)
|
||||
|
||||
```rust
|
||||
use alksocks::AssociateSession;
|
||||
use alksocks::rfc1928::util::target_addr::TargetAddr;
|
||||
|
||||
// One channel per association; datagrams ride `[len: u16 BE]
|
||||
// [addr + data]` frames in-band (ADR-003) — the RFC codec exists at
|
||||
// the front doors only.
|
||||
let mut assoc = AssociateSession::associate(&client).await?;
|
||||
let dest = TargetAddr::Domain { addr: "dns.internal".into(), port: 53 };
|
||||
assoc.send_datagram(&dest, b"query").await?;
|
||||
let (src, payload) = assoc.recv_datagram().await?.expect("a datagram");
|
||||
```
|
||||
|
||||
### Consumer — BIND (the reply#1/reply#2 reader)
|
||||
|
||||
```rust
|
||||
use alksocks::BindSession;
|
||||
use alksocks::rfc1928::util::target_addr::TargetAddr;
|
||||
|
||||
// Requires the `socks5:bind` grant on the producing side (a separate
|
||||
// capability from `socks5:open` — never implied, ADR-006). The
|
||||
// session drives the phase model: reply#1 → quiet wait → exactly one
|
||||
// accept → reply#2 → raw pass-through (ADR-007).
|
||||
let mut bind = BindSession::bind(&client, TargetAddr::Ip("0.0.0.0:0".parse()?)).await?;
|
||||
let reply1 = bind.reply1().await?; // the BND.ADDR the producer bound
|
||||
// ... hand reply1's address to the peer, await the accept + reply#2 ...
|
||||
```
|
||||
|
||||
### Consumer — the front door (vanilla clients)
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
|
||||
use alkcall::channels::client::ChannelClient;
|
||||
use alksocks::{CredentialTable, FrontDoor, UdpFrontDoor};
|
||||
|
||||
let client: Arc<ChannelClient> = /* your alk/channels connection */;
|
||||
|
||||
// The ssh `-D` local-exposure shape: a real `socks5://127.0.0.1:port`
|
||||
// endpoint relaying each accepted connection into a channel. NoAuth
|
||||
// raw pipe by default; a `CredentialTable` turns the door into an
|
||||
// RFC 1929 cred-bearing endpoint whose credentials map to producer
|
||||
// identities (ADR-005). `UdpFrontDoor` serves one association per
|
||||
// bound relay socket.
|
||||
let door = FrontDoor::builder()
|
||||
.bind("127.0.0.1:1080".parse()?)
|
||||
.build()?;
|
||||
door.serve(client).await?;
|
||||
```
|
||||
|
||||
### Producer — the local listener shape (the other door)
|
||||
|
||||
```rust
|
||||
use alksocks::producer::Socks5Producer;
|
||||
|
||||
// Feature `local`: serve a produced SOCKS5 resource from a real
|
||||
// listener — vanilla clients dial it, each accepted connection is
|
||||
// served via `serve_socks5_over` (the NoAuth in-stream RFC
|
||||
// conversation; the seams dial locally). The bind is explicit — the
|
||||
// builder requires it; the protocol layer never binds.
|
||||
let producer = Socks5Producer::builder()
|
||||
.dial(alksocks::local::tcp_dial())
|
||||
.build();
|
||||
let door = alksocks::ProducerDoor::builder()
|
||||
.producer(producer)
|
||||
.bind("127.0.0.1:1081".parse()?)
|
||||
.build()?;
|
||||
let guard = door.serve().await?;
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
Two roles, symmetric per the alk convention (avoid "server"/"client":
|
||||
both sides of a connection can be either; role follows the resource):
|
||||
|
||||
| Role | Via `alk/channels` |
|
||||
|------|--------------------|
|
||||
| **Producer** (dials targets, serves the RFC conversation) | [`Socks5Producer`](https://docs.rs/alksocks/latest/alksocks/producer/struct.Socks5Producer.html) / [`register_socks5_openable`](https://docs.rs/alksocks/latest/alksocks/producer/fn.register_socks5_openable.html) — the open op (`channels/socks5/sub`) with the session-gate establisher and the command-dispatch pump; the RFC 1928 typestate core drives negotiation, command read, and replies in-stream over the channel's `BiStream`; CONNECT/BIND data planes ride [`alkcall::channels::pump_bidi`](https://docs.rs/alkcall/latest/alkcall/channels/pump/fn.pump_bidi.html) |
|
||||
| **Consumer** (wants the proxy) | [`Socks5ChannelSession`](https://docs.rs/alksocks/latest/alksocks/consumer/struct.Socks5ChannelSession.html) (CONNECT) / [`AssociateSession`](https://docs.rs/alksocks/latest/alksocks/consumer/struct.AssociateSession.html) (ASSOCIATE) / [`BindSession`](https://docs.rs/alksocks/latest/alksocks/consumer/struct.BindSession.html) (BIND) — typed channels-native sessions, each owning its channel's teardown |
|
||||
|
||||
One connection hosts many SOCKS5 sessions (one per channel); sessions
|
||||
are independent. Both roles are callable from either connection side
|
||||
(producer and consumer are roles, not sides); RFC 1928's own
|
||||
client/server roles keep their RFC names inside the protocol layer.
|
||||
|
||||
### The RFC conversation is the data plane
|
||||
|
||||
The open op's params are deliberately trivial (`{}`, ADR-001) —
|
||||
there is nothing to negotiate at open time (the SOCKS5 target is
|
||||
unknown until the RFC command arrives). Everything else is RFC 1928,
|
||||
in-stream:
|
||||
|
||||
- **CONNECT**: raw pass-through, 0 B overhead — the channel's halves
|
||||
are the target connection (the two-pump shape).
|
||||
- **ASSOCIATE**: the reply's BND.ADDR is a pure RFC handle (the
|
||||
sentinel in-band); datagrams ride `[len: u16 BE][addr + data]`
|
||||
frames in-band (ADR-003, the handle swap) to the egress seam.
|
||||
One channel per association; the association lifetime is the
|
||||
channel's.
|
||||
- **BIND**: reply#1 → quiet wait → exactly one accept (the accept
|
||||
seam) → reply#2 → raw pass-through (ADR-007).
|
||||
|
||||
Authorization is two grants, never implied (ADR-006): the open gate
|
||||
(`socks5:open`) at the registry, the command gate (`socks5:bind`) at
|
||||
BIND dispatch. The target policy is the dialer (ADR-012): the
|
||||
producer's dial/egress/accept closures are the security boundary —
|
||||
whatever they allow is what the resource reaches.
|
||||
|
||||
### The seams
|
||||
|
||||
The protocol layer is substrate-agnostic by construction (ADR-004):
|
||||
`DialFn` / `EgressFactory` / `BindAcceptFactory` are *functions, not
|
||||
traits* at the three I/O planes; ownership rides the handle
|
||||
structurally. A channels `BiStream`, a `TcpStream`, or an in-process
|
||||
pipe drops in behind the same seams — no socket type leaks into the
|
||||
protocol layer.
|
||||
|
||||
### WASM target
|
||||
|
||||
The default crate (no features) compiles to `wasm32-unknown-unknown`
|
||||
— the protocol layer for a sandboxed TS/Python adapter. The RFC 1928
|
||||
core is a targeted fork of
|
||||
[fast-socks5](https://github.com/dizda/fast-socks5) v1.0.0's
|
||||
`T`-generic subset, extracted in-tree as fully owned code
|
||||
(`src/rfc1928/`, MIT notice bundled; ADR-011/013): no kernel-net
|
||||
code, no `net` feature — the socket I/O rides `local` alone, and the
|
||||
default build is wasm-clean by construction.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Contents | wasm |
|
||||
|---------|----------|------|
|
||||
| *(default)* | the RFC 1928 core (`src/rfc1928/` — codecs, typestate, client `use_stream`), the ASSOCIATE wire codec, the open-op spec, the three seams, the producer + consumer session types — protocol only | yes |
|
||||
| `local` | the socket-backed seam providers ([`tcp_dial`](https://docs.rs/alksocks/latest/alksocks/local/fn.tcp_dial.html) / [`udp_egress`](https://docs.rs/alksocks/latest/alksocks/local/fn.udp_egress.html) / [`bind_accept`](https://docs.rs/alksocks/latest/alksocks/local/fn.bind_accept.html)), the front doors ([`FrontDoor`](https://docs.rs/alksocks/latest/alksocks/door/struct.FrontDoor.html) + [`UdpFrontDoor`](https://docs.rs/alksocks/latest/alksocks/door/struct.UdpFrontDoor.html), the consumer-side exposure; [`ProducerDoor`](https://docs.rs/alksocks/latest/alksocks/local/producer_door/struct.ProducerDoor.html), the producer-side listener) | no |
|
||||
| `composed` | the alktunnels-composed providers: [`direct_egress`](https://docs.rs/alksocks/latest/alksocks/composed/fn.direct_egress.html) (the direct-op egress, a lazy destination→tunnel table) + [`bindfirst_accept`](https://docs.rs/alksocks/latest/alksocks/composed/fn.bindfirst_accept.html) (the BIND accept whose listener lives on a bind-first listen resource) | yes |
|
||||
|
||||
## Version 0.1
|
||||
|
||||
The first release rides alkcall 0.8.0 (the establisher reply
|
||||
projection is not consumed yet — the open op's params are trivial) and
|
||||
alktunnels 0.2.0 (the direct op + the bind-first/forwarded flow power
|
||||
the composed providers). Wire-stable surface: the `alk/socks5` ALPN,
|
||||
the `channels/socks5/sub` open op with `{}` params, and the two scope
|
||||
constants — all pinned by ADRs 001–014.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cargo test # default crate (wasm-clean)
|
||||
cargo test --features local # + real-socket suites
|
||||
cargo test --all-features # + the composed suites
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
cargo fmt --check
|
||||
cargo check --target wasm32-unknown-unknown # the wasm-clean guard
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Architecture docs](docs/architecture/README.md) — the
|
||||
authoritative spec set (in the repository, not the published
|
||||
package): ADRs (001–014), the wire format, the producer/consumer
|
||||
contracts, and the fork provenance.
|
||||
- [API docs](https://docs.rs/alksocks) — full crate documentation on
|
||||
docs.rs.
|
||||
|
||||
## License
|
||||
|
||||
MIT OR Apache-2.0
|
||||
Reference in new issue
Block a user