- Rewrote em dash prose across the whole tree outside docs/reviews/ (the historical reviews stay verbatim, matching the alktunnels and alktty precedent): src doc comments and inline comments (587), docs/architecture (319), docs/research + sdd_process (484), tests (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77), AGENTS.md (85), and the .opencode/agents prompts (109) - Fenced content follows the precedent: comments, ASCII-diagram label separators, and template placeholders rewritten; wire data, JSON/BAST documents, byte diagrams, and transcripts survive verbatim; assert, expect, panic, error-display, and format message literals rewritten as prose; no code, fixture, or test data change - src/rfc1928/LICENSE: the two dashes in alksocks' own provenance header rewritten; the MIT legal text untouched - AGENTS.md adds Project Convention 18: no em dashes in prose; varied restructure, no spaced-hyphen substitutes, fenced prose rule, en dash numeric ranges exempt, docs/reviews/ exempt from the greps - tasks/*.md frontmatter: the name values rewritten by the sweep are now quoted (a raw colon inside an unquoted YAML scalar parsed as null); taskgraph validate reports 37 tasks, no issues Verification: cargo test; cargo clippy --all-targets -D warnings; cargo fmt --check; cargo doc --no-deps; fuzz corpus replay (cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and clippy for wasm32-unknown-unknown with -D warnings; em dash grep over the repo returns matches only in docs/reviews/ and the bast.md BAST JSON data fence
120 lines
4.1 KiB
Rust
120 lines
4.1 KiB
Rust
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs`;
|
|
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
|
|
//!
|
|
//! Upstream carries the `read_exact!`/`ready!` macros, `ConnectError`,
|
|
//! and the `tcp_connect*` dialers; only the `read_exact!` macro +
|
|
//! `ConnectError` rode the extraction (the dialers and the `ready!`
|
|
//! macro are the cut kernel-net region: the crate's local backend is
|
|
//! first-party tokio code, ADR-008, and nothing in the extracted
|
|
//! region used `ready!`). Extraction-time adjustment: upstream
|
|
//! exports the macro at the crate root via `#[macro_export]`; here it
|
|
//! is a plain `macro_rules!` scoped by `pub(crate) use` so it stays
|
|
//! internal to the RFC 1928 core (implementation detail, not alksocks
|
|
//! public API).
|
|
//!
|
|
//! [`ConnectError`] is currently constructed nowhere in-tree (review
|
|
//! 001 C-1: upstream's sole construction site was the cut
|
|
//! `tcp_connect` dialer). The keep-or-cut trigger (cut if the
|
|
//! producer layer landed without consuming it) fired and the
|
|
//! disposition is **keep** (review 002 N-1): it stays as the
|
|
//! hand-port map's companion (the dial-error → reply-code mapping
|
|
//! (`to_reply_error`) is what a future kernel-net port (e.g. the
|
|
//! dial-callback shape, ADR-004) would reuse, and its mapping is
|
|
//! pinned by the module's test.
|
|
|
|
/// Easy to destructure bytes buffers by naming each fields:
|
|
///
|
|
/// # Examples (before)
|
|
///
|
|
/// ```ignore
|
|
/// let mut buf = [0u8; 2];
|
|
/// stream.read_exact(&mut buf).await?;
|
|
/// let [version, method_len] = buf;
|
|
///
|
|
/// assert_eq!(version, 0x05);
|
|
/// ```
|
|
///
|
|
/// # Examples (after)
|
|
///
|
|
/// ```ignore
|
|
/// let [version, method_len] = read_exact!(stream, [0u8; 2]);
|
|
///
|
|
/// assert_eq!(version, 0x05);
|
|
/// ```
|
|
macro_rules! read_exact {
|
|
($stream: expr, $array: expr) => {{
|
|
let mut x = $array;
|
|
$stream.read_exact(&mut x).await.map(|_| x)
|
|
}};
|
|
}
|
|
|
|
pub(crate) use read_exact;
|
|
|
|
use crate::rfc1928::ReplyError;
|
|
use std::io;
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum ConnectError {
|
|
#[error("Connection timed out")]
|
|
ConnectionTimeout,
|
|
#[error("Connection refused: {0}")]
|
|
ConnectionRefused(#[source] io::Error),
|
|
#[error("Connection aborted: {0}")]
|
|
ConnectionAborted(#[source] io::Error),
|
|
#[error("Connection reset: {0}")]
|
|
ConnectionReset(#[source] io::Error),
|
|
#[error("Not connected: {0}")]
|
|
NotConnected(#[source] io::Error),
|
|
#[error("Other i/o error: {0}")]
|
|
Other(#[source] io::Error),
|
|
}
|
|
|
|
impl ConnectError {
|
|
pub fn to_reply_error(&self) -> ReplyError {
|
|
match self {
|
|
ConnectError::ConnectionTimeout => ReplyError::ConnectionTimeout,
|
|
ConnectError::ConnectionRefused(_) => ReplyError::ConnectionRefused,
|
|
ConnectError::ConnectionAborted(_) | ConnectError::ConnectionReset(_) => {
|
|
ReplyError::ConnectionNotAllowed
|
|
}
|
|
ConnectError::NotConnected(_) => ReplyError::NetworkUnreachable,
|
|
ConnectError::Other(_) => ReplyError::GeneralFailure,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::ConnectError;
|
|
use crate::rfc1928::ReplyError;
|
|
use std::io::ErrorKind;
|
|
|
|
#[test]
|
|
fn to_reply_error_mapping() {
|
|
assert!(matches!(
|
|
ConnectError::ConnectionTimeout.to_reply_error(),
|
|
ReplyError::ConnectionTimeout
|
|
));
|
|
assert!(matches!(
|
|
ConnectError::ConnectionRefused(ErrorKind::ConnectionRefused.into()).to_reply_error(),
|
|
ReplyError::ConnectionRefused
|
|
));
|
|
assert!(matches!(
|
|
ConnectError::ConnectionAborted(ErrorKind::ConnectionAborted.into()).to_reply_error(),
|
|
ReplyError::ConnectionNotAllowed
|
|
));
|
|
assert!(matches!(
|
|
ConnectError::ConnectionReset(ErrorKind::ConnectionReset.into()).to_reply_error(),
|
|
ReplyError::ConnectionNotAllowed
|
|
));
|
|
assert!(matches!(
|
|
ConnectError::NotConnected(ErrorKind::NotConnected.into()).to_reply_error(),
|
|
ReplyError::NetworkUnreachable
|
|
));
|
|
assert!(matches!(
|
|
ConnectError::Other(ErrorKind::Other.into()).to_reply_error(),
|
|
ReplyError::GeneralFailure
|
|
));
|
|
}
|
|
}
|