Skip to content

Commit 76d3875

Browse files
committed
bitreq: add SOCKS5 proxy support
Add SOCKS5 proxy support (RFC 1928) alongside existing HTTP CONNECT. Uses domain-based addressing (ATYP 0x03) so DNS resolution happens at the proxy, enabling .onion routing through Tor. New public API: Proxy::new_socks5(addr) creates a SOCKS5 proxy config. Both sync and async handshake paths implemented. Motivation: ldk-node needs to route HTTP calls (RGS, scoring) through Tor's SOCKS proxy. See lightningdevkit/ldk-node#834. Tests: 5 parsing tests + 5 mock handshake tests covering success, .onion domains, server rejection, port encoding, and domain length validation.
1 parent 774a655 commit 76d3875

3 files changed

Lines changed: 415 additions & 11 deletions

File tree

bitreq/README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,29 @@ This crate is a fork for the very nice
77
rename it because I wanted to totally gut it and provide a crate with
88
different goals. Many thanks to the original author.
99

10-
Simple, minimal-dependency HTTP client. Optional features for http
11-
proxies (`proxy`), async support (`async`, `async-https`), and https
12-
with various TLS implementations (`https-rustls`, `https-rustls-probe`,
13-
and `https` which is an alias for `https-rustls`).
10+
Simple, minimal-dependency HTTP client. Optional features for HTTP
11+
proxies and SOCKS5 proxies (`proxy`), async support (`async`,
12+
`async-https`), and https with various TLS implementations
13+
(`https-rustls`, `https-rustls-probe`, and `https` which is an alias
14+
for `https-rustls`).
15+
16+
### Proxy Support
17+
18+
The `proxy` feature enables both HTTP CONNECT and SOCKS5 proxies:
19+
20+
```rust
21+
// HTTP CONNECT proxy
22+
let proxy = bitreq::Proxy::new_http("http://proxy.example.com:8080").unwrap();
23+
let response = bitreq::get("http://example.com").with_proxy(proxy).send();
24+
25+
// SOCKS5 proxy (e.g., Tor)
26+
let proxy = bitreq::Proxy::new_socks5("127.0.0.1:9050").unwrap();
27+
let response = bitreq::get("http://example.com").with_proxy(proxy).send();
28+
```
29+
30+
SOCKS5 proxies use domain-based addressing (RFC 1928 ATYP 0x03), so
31+
DNS resolution happens at the proxy. This is required for `.onion`
32+
routing through Tor.
1433

1534
Without any optional features, my casual testing indicates about 100
1635
KB additional executable size for stripped release builds using this

bitreq/src/connection.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,14 @@ impl AsyncConnection {
336336
async fn connect(params: ConnectionParams<'_>) -> Result<AsyncTcpStream, Error> {
337337
#[cfg(feature = "proxy")]
338338
match &params.proxy {
339+
Some(proxy) if proxy.is_socks5() => {
340+
// SOCKS5 proxy
341+
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port).await?;
342+
proxy.socks5_handshake_async(&mut tcp, params.host, params.port).await?;
343+
Ok(tcp)
344+
}
339345
Some(proxy) => {
340-
// do proxy things
346+
// HTTP CONNECT proxy
341347
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port).await?;
342348

343349
let proxy_request = proxy.connect(params.host, params.port);
@@ -709,8 +715,14 @@ impl Connection {
709715
) -> Result<TcpStream, Error> {
710716
#[cfg(feature = "proxy")]
711717
match &params.proxy {
718+
Some(proxy) if proxy.is_socks5() => {
719+
// SOCKS5 proxy
720+
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port, timeout_at)?;
721+
proxy.socks5_handshake_sync(&mut tcp, params.host, params.port)?;
722+
Ok(tcp)
723+
}
712724
Some(proxy) => {
713-
// do proxy things
725+
// HTTP CONNECT proxy
714726
let mut tcp = Self::tcp_connect(&proxy.server, proxy.port, timeout_at)?;
715727

716728
write!(tcp, "{}", proxy.connect(params.host, params.port))?;

0 commit comments

Comments
 (0)