Skip to content

Commit b08c598

Browse files
committed
Add support for TLS over socks5 proxy
1 parent 2477ab4 commit b08c598

File tree

3 files changed

+59
-15
lines changed

3 files changed

+59
-15
lines changed

src/client.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,20 @@ macro_rules! impl_inner_call {
8787
}
8888

8989
impl ClientType {
90-
/// Constructor that supports multiple backends and allows configuration through
90+
/// Constructor that supports multiple backends and allows configuration through
9191
/// the [Config]
9292
pub fn from_config(url: &str, config: &Config) -> Result<Self, Error> {
9393
if url.starts_with("ssl://") {
94-
if config.socks5().is_some() {
95-
return Err(Error::SSLOverSocks5);
96-
}
97-
9894
let url = url.replacen("ssl://", "", 1);
99-
let client =
100-
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?;
95+
let client = match config.socks5() {
96+
Some(socks5) => {
97+
RawClient::new_proxy_ssl(url.as_str(), config.validate_domain(), socks5)?
98+
}
99+
None => {
100+
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?
101+
}
102+
};
103+
101104
Ok(ClientType::SSL(client))
102105
} else {
103106
let url = url.replacen("tcp://", "", 1);
@@ -127,9 +130,6 @@ impl Client {
127130

128131
/// Generic constructor that supports multiple backends and allows configuration through
129132
/// the [Config]
130-
///
131-
/// **NOTE**: SSL-over-socks5 is currently not supported and will generate a runtime error.
132-
///
133133
pub fn from_config(url: &str, config: Config) -> Result<Self, Error> {
134134
let client_type = RwLock::new(ClientType::from_config(url, &config)?);
135135

src/raw_client.rs

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
2727
use rustls::{ClientConfig, ClientSession, StreamOwned};
2828

2929
#[cfg(any(feature = "default", feature = "proxy"))]
30-
use socks::{Socks5Stream, ToTargetAddr};
30+
use socks::{Socks5Stream, TargetAddr, ToTargetAddr};
3131

3232
use stream::ClonableStream;
3333

@@ -75,6 +75,28 @@ impl ToSocketAddrsDomain for (&str, u16) {
7575
}
7676
}
7777

78+
impl ToSocketAddrsDomain for TargetAddr {
79+
fn domain(&self) -> Option<&str> {
80+
match self {
81+
TargetAddr::Ip(_) => None,
82+
TargetAddr::Domain(domain, _) => Some(domain.as_str()),
83+
}
84+
}
85+
}
86+
87+
macro_rules! impl_to_socket_addrs_domain {
88+
( $ty:ty ) => {
89+
impl ToSocketAddrsDomain for $ty {}
90+
};
91+
}
92+
93+
impl_to_socket_addrs_domain!(std::net::SocketAddr);
94+
impl_to_socket_addrs_domain!(std::net::SocketAddrV4);
95+
impl_to_socket_addrs_domain!(std::net::SocketAddrV6);
96+
impl_to_socket_addrs_domain!((std::net::IpAddr, u16));
97+
impl_to_socket_addrs_domain!((std::net::Ipv4Addr, u16));
98+
impl_to_socket_addrs_domain!((std::net::Ipv6Addr, u16));
99+
78100
/// Instance of an Electrum client
79101
///
80102
/// A `Client` maintains a constant connection with an Electrum server and exposes methods to
@@ -327,8 +349,8 @@ impl RawClient<ElectrumSslStream> {
327349
pub type ElectrumProxyStream = Socks5Stream;
328350
#[cfg(any(feature = "default", feature = "proxy"))]
329351
impl RawClient<ElectrumProxyStream> {
330-
/// Creates a new socks client and tries to connect to `target_addr` using `proxy_addr` as an
331-
/// unauthenticated socks proxy server. The DNS resolution of `target_addr`, if required, is done
352+
/// Creates a new socks client and tries to connect to `target_addr` using `proxy_addr` as a
353+
/// socks proxy server. The DNS resolution of `target_addr`, if required, is done
332354
/// through the proxy. This allows to specify, for instance, `.onion` addresses.
333355
pub fn new_proxy<T: ToTargetAddr>(
334356
target_addr: T,
@@ -346,6 +368,30 @@ impl RawClient<ElectrumProxyStream> {
346368

347369
Ok(stream.into())
348370
}
371+
372+
#[cfg(any(feature = "use-openssl", feature = "use-rustls"))]
373+
/// Creates a new TLS client that connects to `target_addr` using `proxy_addr` as a socks proxy
374+
/// server. The DNS resolution of `target_addr`, if required, is done through the proxy. This
375+
/// allows to specify, for instance, `.onion` addresses.
376+
pub fn new_proxy_ssl<T: ToTargetAddr>(
377+
target_addr: T,
378+
validate_domain: bool,
379+
proxy: &crate::Socks5Config,
380+
) -> Result<RawClient<ElectrumSslStream>, Error> {
381+
let target = target_addr.to_target_addr()?;
382+
383+
let stream = match proxy.credentials.as_ref() {
384+
Some(cred) => Socks5Stream::connect_with_password(
385+
&proxy.addr,
386+
target_addr,
387+
&cred.username,
388+
&cred.password,
389+
)?,
390+
None => Socks5Stream::connect(&proxy.addr, target.clone())?,
391+
};
392+
393+
RawClient::new_ssl_from_stream(target, validate_domain, stream.into_inner())
394+
}
349395
}
350396

351397
#[derive(Debug)]

src/types.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,6 @@ pub enum Error {
287287
InvalidDNSNameError(String),
288288
/// Missing domain while it was explicitly asked to validate it
289289
MissingDomain,
290-
/// SSL over a socks5 proxy is currently not supported
291-
SSLOverSocks5,
292290
/// Made one or multiple attempts, always in Error
293291
AllAttemptsErrored(Vec<Error>),
294292
/// There was an io error reading the socket, to be shared between threads

0 commit comments

Comments
 (0)