io_proxy/lib.rs
1#![no_std]
2#![deny(missing_docs)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! # io-proxy
6//!
7//! I/O-free proxy client coroutines. A proxy handshake is a resumable
8//! state machine that emits read and write requests instead of
9//! performing I/O itself: the caller owns the socket and pumps the
10//! coroutine with the bytes it read, whatever the runtime (blocking,
11//! async, in-memory tests). The `client` feature ships a ready-made
12//! std-blocking pump for callers who just want a tunnelled socket.
13//!
14//! ## Scope
15//!
16//! Client-side tunnelling only — enough to carry an arbitrary TCP
17//! connection (IMAP, SMTP, HTTP, ...) through a proxy:
18//!
19//! - [`socks`] — SOCKS5 `CONNECT` ([RFC 1928] + [RFC 1929]), behind the
20//! `socks5` feature. The proxy resolves the target hostname (socks5h
21//! semantics).
22//! - [`http`] — HTTP `CONNECT` tunnelling ([RFC 9110 §9.3.6]), behind the
23//! `http` feature. `CONNECT` exists only to establish a proxy tunnel,
24//! so it lives here rather than in a general HTTP crate.
25//!
26//! `BIND`, `UDP ASSOCIATE`, plaintext HTTP forward proxying and the
27//! server side of either protocol are out of scope.
28//!
29//! ## Layout
30//!
31//! One module per protocol, each behind a cargo feature, versioned from
32//! within (`socks::v5`) so a future SOCKS4 or protocol revision slots in
33//! alongside. [`coroutine`] spans them and holds the shared contract; the
34//! optional [`client`] module (`client` feature) is the std-blocking pump.
35//!
36//! ## The coroutine contract
37//!
38//! Every coroutine implements [`coroutine::ProxyCoroutine`]: a resume
39//! method taking the bytes read since the last step and returning either
40//! an intermediate yield or a terminal completion. The read yield carries
41//! an exact byte count ([`coroutine::ProxyYield::WantsRead`]): SOCKS5
42//! reads its length-framed messages directly, and HTTP CONNECT scans for
43//! the header terminator one byte at a time — so neither ever consumes
44//! tunnel payload past the handshake, leaving the socket positioned
45//! exactly at the start of the tunnel.
46//!
47//! ## Conventions
48//!
49//! The conventions every Pimalaya repository shares are described in the
50//! org
51//! [ARCHITECTURE](https://github.com/pimalaya/.github/blob/master/ARCHITECTURE.md)
52//! and
53//! [GUIDELINES](https://github.com/pimalaya/.github/blob/master/GUIDELINES.md);
54//! this crate's own deviations and its build matrix live in
55//! CONTRIBUTING.md, and its living spec and history in the cairn/ folder.
56//! Logging follows the library rules: debug marks the lifecycle points,
57//! trace carries the data; credentials never reach the logs.
58//!
59//! A runnable example driving the SOCKS5 pump lives in examples/.
60//!
61//! [RFC 1928]: https://www.rfc-editor.org/rfc/rfc1928
62//! [RFC 1929]: https://www.rfc-editor.org/rfc/rfc1929
63//! [RFC 9110 §9.3.6]: https://www.rfc-editor.org/rfc/rfc9110#section-9.3.6
64
65#[cfg(any(feature = "socks5", feature = "http"))]
66#[macro_use]
67extern crate alloc;
68#[cfg(all(feature = "client", any(feature = "socks5", feature = "http")))]
69extern crate std;
70
71#[cfg(all(feature = "client", any(feature = "socks5", feature = "http")))]
72pub mod client;
73#[cfg(any(feature = "socks5", feature = "http"))]
74pub mod coroutine;
75#[cfg(feature = "http")]
76pub mod http;
77#[cfg(feature = "socks5")]
78pub mod socks;