diff --git a/grammers-mtsender/Cargo.toml b/grammers-mtsender/Cargo.toml index 30c46bd9..af2d0144 100644 --- a/grammers-mtsender/Cargo.toml +++ b/grammers-mtsender/Cargo.toml @@ -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" @@ -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" \ No newline at end of file diff --git a/grammers-mtsender/DEPS.md b/grammers-mtsender/DEPS.md index 98b1bc15..c206506a 100644 --- a/grammers-mtsender/DEPS.md +++ b/grammers-mtsender/DEPS.md @@ -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 diff --git a/grammers-mtsender/src/net/tcp.rs b/grammers-mtsender/src/net/tcp.rs index 76845d40..f7c3b904 100644 --- a/grammers-mtsender/src/net/tcp.rs +++ b/grammers-mtsender/src/net/tcp.rs @@ -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), @@ -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),