Skip to content
This repository was archived by the owner on Feb 10, 2026. It is now read-only.

Commit 05ab39d

Browse files
committed
Add SOCKS4 and HTTP CONNECT proxy support
Use tokio-socks for SOCKS4 and async-http-proxy for HTTP CONNECT, instead of manual protocol implementations.
1 parent 342bed0 commit 05ab39d

3 files changed

Lines changed: 60 additions & 9 deletions

File tree

grammers-mtsender/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ categories = ["api-bindings", "network-programming"]
1414
edition = "2024"
1515

1616
[features]
17-
proxy = ["tokio-socks", "hickory-resolver", "url"]
17+
proxy = ["tokio-socks", "hickory-resolver", "url", "async-http-proxy"]
1818

1919
[dependencies]
2020
bytes = "1.10.1"
@@ -29,8 +29,9 @@ tokio = { version = "1.47.1", default-features = false, features = ["io-util", "
2929
tokio-socks = { version = "0.5.2", optional = true }
3030
hickory-resolver = { version = "0.25.2", optional = true }
3131
url = { version = "2.5.7", optional = true }
32+
async-http-proxy = { version = "1.2.5", features = ["runtime-tokio", "basic-auth"], optional = true }
3233

3334
[dev-dependencies]
3435
simple_logger = { version = "5.0.0", default-features = false, features = ["colors"] }
3536
toml = "0.9.8"
36-
socks5-server = "0.10.1"
37+
socks5-server = "0.10.1"

grammers-mtsender/DEPS.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ Used to look up the IP address of the proxy host if a domain is provided.
6363

6464
## tokio-socks
6565

66-
SOCKS5 proxy support.
66+
SOCKS4 and SOCKS5 proxy support.
67+
68+
## async-http-proxy
69+
70+
HTTP CONNECT proxy support.
6771

6872
## socks5-server
6973

grammers-mtsender/src/net/tcp.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ impl NetStream {
6464
))?;
6565
let username = proxy.username();
6666
let password = proxy.password().unwrap_or("");
67-
let socks_addr = match host {
67+
let proxy_addr = match host {
6868
Host::Domain(domain) => {
6969
let resolver = Resolver::builder_tokio().unwrap().build();
7070
let response = resolver.lookup_ip(domain).await?;
71-
let socks_ip_addr = response.into_iter().next().ok_or(io::Error::new(
71+
let ip_addr = response.into_iter().next().ok_or(io::Error::new(
7272
ErrorKind::NotFound,
7373
format!("proxy host did not return any ip address: {}", domain),
7474
))?;
75-
SocketAddr::new(socks_ip_addr, port)
75+
SocketAddr::new(ip_addr, port)
7676
}
7777
Host::Ipv4(v4) => SocketAddr::new(IpAddr::from(v4), port),
7878
Host::Ipv6(v6) => SocketAddr::new(IpAddr::from(v6), port),
@@ -82,24 +82,70 @@ impl NetStream {
8282
"socks5" => {
8383
if username.is_empty() {
8484
Ok(NetStream::ProxySocks5(
85-
tokio_socks::tcp::Socks5Stream::connect(socks_addr, addr)
85+
tokio_socks::tcp::Socks5Stream::connect(proxy_addr, addr)
8686
.await
8787
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?,
8888
))
8989
} else {
9090
Ok(NetStream::ProxySocks5(
9191
tokio_socks::tcp::Socks5Stream::connect_with_password(
92-
socks_addr, addr, username, password,
92+
proxy_addr, addr, username, password,
9393
)
9494
.await
9595
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?,
9696
))
9797
}
9898
}
99+
"socks4" => {
100+
if username.is_empty() {
101+
Ok(NetStream::Tcp(
102+
tokio_socks::tcp::Socks4Stream::connect(proxy_addr, addr)
103+
.await
104+
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?
105+
.into_inner(),
106+
))
107+
} else {
108+
Ok(NetStream::Tcp(
109+
tokio_socks::tcp::Socks4Stream::connect_with_userid(
110+
proxy_addr, addr, username,
111+
)
112+
.await
113+
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?
114+
.into_inner(),
115+
))
116+
}
117+
}
118+
"http" => {
119+
let mut stream = TcpStream::connect(proxy_addr).await?;
120+
let target_host = addr.ip().to_string();
121+
let target_port = addr.port();
122+
123+
if username.is_empty() {
124+
async_http_proxy::http_connect_tokio(
125+
&mut stream,
126+
&target_host,
127+
target_port,
128+
)
129+
.await
130+
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?;
131+
} else {
132+
async_http_proxy::http_connect_tokio_with_basic_auth(
133+
&mut stream,
134+
&target_host,
135+
target_port,
136+
username,
137+
password,
138+
)
139+
.await
140+
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?;
141+
}
142+
143+
Ok(NetStream::Tcp(stream))
144+
}
99145
scheme => Err(io::Error::new(
100146
ErrorKind::ConnectionAborted,
101147
format!("proxy scheme not supported: {}", scheme),
102148
)),
103149
}
104150
}
105-
}
151+
}

0 commit comments

Comments
 (0)