Skip to content

Commit 09884ed

Browse files
authored
feat: Add support for SOCKS4 (#610) (#2400)
* feat: Add support for SOCKS4 (#610) * chore: bump tokio-socks to 0.5.2
1 parent a13a6bc commit 09884ed

File tree

3 files changed

+66
-26
lines changed

3 files changed

+66
-26
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ async-compression = { version = "0.4.0", default-features = false, features = ["
154154
tokio-util = { version = "0.7.9", default-features = false, features = ["codec", "io"], optional = true }
155155

156156
## socks
157-
tokio-socks = { version = "0.5.1", optional = true }
157+
tokio-socks = { version = "0.5.2", optional = true }
158158

159159
## hickory-dns
160160
hickory-resolver = { version = "0.24", optional = true, features = ["tokio-runtime"] }

src/connect.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ impl Connector {
206206
#[cfg(feature = "socks")]
207207
async fn connect_socks(&self, dst: Uri, proxy: ProxyScheme) -> Result<Conn, BoxError> {
208208
let dns = match proxy {
209+
ProxyScheme::Socks4 { .. } => socks::DnsResolve::Local,
209210
ProxyScheme::Socks5 {
210211
remote_dns: false, ..
211212
} => socks::DnsResolve::Local,
@@ -367,6 +368,8 @@ impl Connector {
367368
ProxyScheme::Http { host, auth } => (into_uri(Scheme::HTTP, host), auth),
368369
ProxyScheme::Https { host, auth } => (into_uri(Scheme::HTTPS, host), auth),
369370
#[cfg(feature = "socks")]
371+
ProxyScheme::Socks4 { .. } => return self.connect_socks(dst, proxy_scheme).await,
372+
#[cfg(feature = "socks")]
370373
ProxyScheme::Socks5 { .. } => return self.connect_socks(dst, proxy_scheme).await,
371374
};
372375

@@ -1031,7 +1034,7 @@ mod socks {
10311034

10321035
use http::Uri;
10331036
use tokio::net::TcpStream;
1034-
use tokio_socks::tcp::Socks5Stream;
1037+
use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
10351038

10361039
use super::{BoxError, Scheme};
10371040
use crate::proxy::ProxyScheme;
@@ -1064,28 +1067,33 @@ mod socks {
10641067
}
10651068
}
10661069

1067-
let (socket_addr, auth) = match proxy {
1068-
ProxyScheme::Socks5 { addr, auth, .. } => (addr, auth),
1069-
_ => unreachable!(),
1070-
};
1071-
1072-
// Get a Tokio TcpStream
1073-
let stream = if let Some((username, password)) = auth {
1074-
Socks5Stream::connect_with_password(
1075-
socket_addr,
1076-
(host.as_str(), port),
1077-
&username,
1078-
&password,
1079-
)
1080-
.await
1081-
.map_err(|e| format!("socks connect error: {e}"))?
1082-
} else {
1083-
Socks5Stream::connect(socket_addr, (host.as_str(), port))
1084-
.await
1085-
.map_err(|e| format!("socks connect error: {e}"))?
1086-
};
1070+
match proxy {
1071+
ProxyScheme::Socks4 { addr } => {
1072+
let stream = Socks4Stream::connect(addr, (host.as_str(), port))
1073+
.await
1074+
.map_err(|e| format!("socks connect error: {e}"))?;
1075+
Ok(stream.into_inner())
1076+
}
1077+
ProxyScheme::Socks5 { addr, ref auth, .. } => {
1078+
let stream = if let Some((username, password)) = auth {
1079+
Socks5Stream::connect_with_password(
1080+
addr,
1081+
(host.as_str(), port),
1082+
&username,
1083+
&password,
1084+
)
1085+
.await
1086+
.map_err(|e| format!("socks connect error: {e}"))?
1087+
} else {
1088+
Socks5Stream::connect(addr, (host.as_str(), port))
1089+
.await
1090+
.map_err(|e| format!("socks connect error: {e}"))?
1091+
};
10871092

1088-
Ok(stream.into_inner())
1093+
Ok(stream.into_inner())
1094+
}
1095+
_ => unreachable!(),
1096+
}
10891097
}
10901098
}
10911099

src/proxy.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ pub enum ProxyScheme {
106106
host: http::uri::Authority,
107107
},
108108
#[cfg(feature = "socks")]
109+
Socks4 { addr: SocketAddr },
110+
#[cfg(feature = "socks")]
109111
Socks5 {
110112
addr: SocketAddr,
111113
auth: Option<(String, String)>,
@@ -577,6 +579,16 @@ impl ProxyScheme {
577579
})
578580
}
579581

582+
/// Proxy traffic via the specified socket address over SOCKS4
583+
///
584+
/// # Note
585+
///
586+
/// Current SOCKS4 support is provided via blocking IO.
587+
#[cfg(feature = "socks")]
588+
fn socks4(addr: SocketAddr) -> crate::Result<Self> {
589+
Ok(ProxyScheme::Socks4 { addr })
590+
}
591+
580592
/// Proxy traffic via the specified socket address over SOCKS5
581593
///
582594
/// # Note
@@ -628,6 +640,10 @@ impl ProxyScheme {
628640
*auth = Some(header);
629641
}
630642
#[cfg(feature = "socks")]
643+
ProxyScheme::Socks4 { .. } => {
644+
panic!("Socks4 is not supported for this method")
645+
}
646+
#[cfg(feature = "socks")]
631647
ProxyScheme::Socks5 { ref mut auth, .. } => {
632648
*auth = Some((username.into(), password.into()));
633649
}
@@ -643,8 +659,12 @@ impl ProxyScheme {
643659
*auth = Some(header_value);
644660
}
645661
#[cfg(feature = "socks")]
662+
ProxyScheme::Socks4 { .. } => {
663+
panic!("Socks4 is not supported for this method")
664+
}
665+
#[cfg(feature = "socks")]
646666
ProxyScheme::Socks5 { .. } => {
647-
panic!("Socks is not supported for this method")
667+
panic!("Socks5 is not supported for this method")
648668
}
649669
}
650670
}
@@ -662,6 +682,8 @@ impl ProxyScheme {
662682
}
663683
}
664684
#[cfg(feature = "socks")]
685+
ProxyScheme::Socks4 { .. } => {}
686+
#[cfg(feature = "socks")]
665687
ProxyScheme::Socks5 { .. } => {}
666688
}
667689

@@ -670,7 +692,7 @@ impl ProxyScheme {
670692

671693
/// Convert a URL into a proxy scheme
672694
///
673-
/// Supported schemes: HTTP, HTTPS, (SOCKS5, SOCKS5H if `socks` feature is enabled).
695+
/// Supported schemes: HTTP, HTTPS, (SOCKS4, SOCKS5, SOCKS5H if `socks` feature is enabled).
674696
// Private for now...
675697
fn parse(url: Url) -> crate::Result<Self> {
676698
use url::Position;
@@ -680,7 +702,7 @@ impl ProxyScheme {
680702
let to_addr = || {
681703
let addrs = url
682704
.socket_addrs(|| match url.scheme() {
683-
"socks5" | "socks5h" => Some(1080),
705+
"socks4" | "socks5" | "socks5h" => Some(1080),
684706
_ => None,
685707
})
686708
.map_err(crate::error::builder)?;
@@ -694,6 +716,8 @@ impl ProxyScheme {
694716
"http" => Self::http(&url[Position::BeforeHost..Position::AfterPort])?,
695717
"https" => Self::https(&url[Position::BeforeHost..Position::AfterPort])?,
696718
#[cfg(feature = "socks")]
719+
"socks4" => Self::socks4(to_addr()?)?,
720+
#[cfg(feature = "socks")]
697721
"socks5" => Self::socks5(to_addr()?)?,
698722
#[cfg(feature = "socks")]
699723
"socks5h" => Self::socks5h(to_addr()?)?,
@@ -715,6 +739,8 @@ impl ProxyScheme {
715739
ProxyScheme::Http { .. } => "http",
716740
ProxyScheme::Https { .. } => "https",
717741
#[cfg(feature = "socks")]
742+
ProxyScheme::Socks4 { .. } => "socks4",
743+
#[cfg(feature = "socks")]
718744
ProxyScheme::Socks5 { .. } => "socks5",
719745
}
720746
}
@@ -725,6 +751,8 @@ impl ProxyScheme {
725751
ProxyScheme::Http { host, .. } => host.as_str(),
726752
ProxyScheme::Https { host, .. } => host.as_str(),
727753
#[cfg(feature = "socks")]
754+
ProxyScheme::Socks4 { .. } => panic!("socks4"),
755+
#[cfg(feature = "socks")]
728756
ProxyScheme::Socks5 { .. } => panic!("socks5"),
729757
}
730758
}
@@ -736,6 +764,10 @@ impl fmt::Debug for ProxyScheme {
736764
ProxyScheme::Http { auth: _auth, host } => write!(f, "http://{host}"),
737765
ProxyScheme::Https { auth: _auth, host } => write!(f, "https://{host}"),
738766
#[cfg(feature = "socks")]
767+
ProxyScheme::Socks4 { addr } => {
768+
write!(f, "socks4://{addr}")
769+
}
770+
#[cfg(feature = "socks")]
739771
ProxyScheme::Socks5 {
740772
addr,
741773
auth: _auth,

0 commit comments

Comments
 (0)