Skip to content
This repository was archived by the owner on Feb 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions grammers-mtsender/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ categories = ["api-bindings", "network-programming"]
edition = "2024"

[features]
proxy = ["tokio-socks", "hickory-resolver", "url"]
proxy = ["tokio-socks", "hickory-resolver", "url", "async-http-proxy"]

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

[dev-dependencies]
simple_logger = { version = "5.0.0", default-features = false, features = ["colors"] }
toml = "0.9.8"
socks5-server = "0.10.1"
socks5-server = "0.10.1"
6 changes: 5 additions & 1 deletion grammers-mtsender/DEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ Used to look up the IP address of the proxy host if a domain is provided.

## tokio-socks

SOCKS5 proxy support.
SOCKS4 and SOCKS5 proxy support.

## async-http-proxy

HTTP CONNECT proxy support.

## socks5-server

Expand Down
52 changes: 47 additions & 5 deletions grammers-mtsender/src/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ impl NetStream {
))?;
let username = proxy.username();
let password = proxy.password().unwrap_or("");
let socks_addr = match host {
let proxy_addr = match host {
Host::Domain(domain) => {
let resolver = Resolver::builder_tokio().unwrap().build();
let response = resolver.lookup_ip(domain).await?;
let socks_ip_addr = response.into_iter().next().ok_or(io::Error::new(
let ip_addr = response.into_iter().next().ok_or(io::Error::new(
ErrorKind::NotFound,
format!("proxy host did not return any ip address: {}", domain),
))?;
SocketAddr::new(socks_ip_addr, port)
SocketAddr::new(ip_addr, port)
}
Host::Ipv4(v4) => SocketAddr::new(IpAddr::from(v4), port),
Host::Ipv6(v6) => SocketAddr::new(IpAddr::from(v6), port),
Expand All @@ -82,20 +82,62 @@ impl NetStream {
"socks5" => {
if username.is_empty() {
Ok(NetStream::ProxySocks5(
tokio_socks::tcp::Socks5Stream::connect(socks_addr, addr)
tokio_socks::tcp::Socks5Stream::connect(proxy_addr, addr)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?,
))
} else {
Ok(NetStream::ProxySocks5(
tokio_socks::tcp::Socks5Stream::connect_with_password(
socks_addr, addr, username, password,
proxy_addr, addr, username, password,
)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?,
))
}
}
"socks4" => {
if username.is_empty() {
Ok(NetStream::Tcp(
tokio_socks::tcp::Socks4Stream::connect(proxy_addr, addr)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?
.into_inner(),
))
} else {
Ok(NetStream::Tcp(
tokio_socks::tcp::Socks4Stream::connect_with_userid(
proxy_addr, addr, username,
)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?
.into_inner(),
))
}
}
"http" => {
let mut stream = TcpStream::connect(proxy_addr).await?;
let target_host = addr.ip().to_string();
let target_port = addr.port();

if username.is_empty() {
async_http_proxy::http_connect_tokio(&mut stream, &target_host, target_port)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?;
} else {
async_http_proxy::http_connect_tokio_with_basic_auth(
&mut stream,
&target_host,
target_port,
username,
password,
)
.await
.map_err(|err| io::Error::new(ErrorKind::ConnectionAborted, err))?;
}

Ok(NetStream::Tcp(stream))
}
scheme => Err(io::Error::new(
ErrorKind::ConnectionAborted,
format!("proxy scheme not supported: {}", scheme),
Expand Down