Add support for TLS over socks5 proxy
Some checks failed
CI / Test (push) Has been cancelled

This commit is contained in:
Alekos Filini 2020-11-27 15:13:35 +01:00
parent 2477ab4440
commit b08c59815d
No known key found for this signature in database
GPG Key ID: 5E8AFC3034FDFA4F
3 changed files with 59 additions and 15 deletions

View File

@ -87,17 +87,20 @@ macro_rules! impl_inner_call {
}
impl ClientType {
/// Constructor that supports multiple backends and allows configuration through
/// Constructor that supports multiple backends and allows configuration through
/// the [Config]
pub fn from_config(url: &str, config: &Config) -> Result<Self, Error> {
if url.starts_with("ssl://") {
if config.socks5().is_some() {
return Err(Error::SSLOverSocks5);
}
let url = url.replacen("ssl://", "", 1);
let client =
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?;
let client = match config.socks5() {
Some(socks5) => {
RawClient::new_proxy_ssl(url.as_str(), config.validate_domain(), socks5)?
}
None => {
RawClient::new_ssl(url.as_str(), config.validate_domain(), config.timeout())?
}
};
Ok(ClientType::SSL(client))
} else {
let url = url.replacen("tcp://", "", 1);
@ -127,9 +130,6 @@ impl Client {
/// Generic constructor that supports multiple backends and allows configuration through
/// the [Config]
///
/// **NOTE**: SSL-over-socks5 is currently not supported and will generate a runtime error.
///
pub fn from_config(url: &str, config: Config) -> Result<Self, Error> {
let client_type = RwLock::new(ClientType::from_config(url, &config)?);

View File

@ -27,7 +27,7 @@ use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode};
use rustls::{ClientConfig, ClientSession, StreamOwned};
#[cfg(any(feature = "default", feature = "proxy"))]
use socks::{Socks5Stream, ToTargetAddr};
use socks::{Socks5Stream, TargetAddr, ToTargetAddr};
use stream::ClonableStream;
@ -75,6 +75,28 @@ impl ToSocketAddrsDomain for (&str, u16) {
}
}
impl ToSocketAddrsDomain for TargetAddr {
fn domain(&self) -> Option<&str> {
match self {
TargetAddr::Ip(_) => None,
TargetAddr::Domain(domain, _) => Some(domain.as_str()),
}
}
}
macro_rules! impl_to_socket_addrs_domain {
( $ty:ty ) => {
impl ToSocketAddrsDomain for $ty {}
};
}
impl_to_socket_addrs_domain!(std::net::SocketAddr);
impl_to_socket_addrs_domain!(std::net::SocketAddrV4);
impl_to_socket_addrs_domain!(std::net::SocketAddrV6);
impl_to_socket_addrs_domain!((std::net::IpAddr, u16));
impl_to_socket_addrs_domain!((std::net::Ipv4Addr, u16));
impl_to_socket_addrs_domain!((std::net::Ipv6Addr, u16));
/// Instance of an Electrum client
///
/// A `Client` maintains a constant connection with an Electrum server and exposes methods to
@ -327,8 +349,8 @@ impl RawClient<ElectrumSslStream> {
pub type ElectrumProxyStream = Socks5Stream;
#[cfg(any(feature = "default", feature = "proxy"))]
impl RawClient<ElectrumProxyStream> {
/// Creates a new socks client and tries to connect to `target_addr` using `proxy_addr` as an
/// unauthenticated socks proxy server. The DNS resolution of `target_addr`, if required, is done
/// Creates a new socks client and tries to connect to `target_addr` using `proxy_addr` as a
/// socks proxy server. The DNS resolution of `target_addr`, if required, is done
/// through the proxy. This allows to specify, for instance, `.onion` addresses.
pub fn new_proxy<T: ToTargetAddr>(
target_addr: T,
@ -346,6 +368,30 @@ impl RawClient<ElectrumProxyStream> {
Ok(stream.into())
}
#[cfg(any(feature = "use-openssl", feature = "use-rustls"))]
/// Creates a new TLS client that connects to `target_addr` using `proxy_addr` as a socks proxy
/// server. The DNS resolution of `target_addr`, if required, is done through the proxy. This
/// allows to specify, for instance, `.onion` addresses.
pub fn new_proxy_ssl<T: ToTargetAddr>(
target_addr: T,
validate_domain: bool,
proxy: &crate::Socks5Config,
) -> Result<RawClient<ElectrumSslStream>, Error> {
let target = target_addr.to_target_addr()?;
let stream = match proxy.credentials.as_ref() {
Some(cred) => Socks5Stream::connect_with_password(
&proxy.addr,
target_addr,
&cred.username,
&cred.password,
)?,
None => Socks5Stream::connect(&proxy.addr, target.clone())?,
};
RawClient::new_ssl_from_stream(target, validate_domain, stream.into_inner())
}
}
#[derive(Debug)]

View File

@ -287,8 +287,6 @@ pub enum Error {
InvalidDNSNameError(String),
/// Missing domain while it was explicitly asked to validate it
MissingDomain,
/// SSL over a socks5 proxy is currently not supported
SSLOverSocks5,
/// Made one or multiple attempts, always in Error
AllAttemptsErrored(Vec<Error>),
/// There was an io error reading the socket, to be shared between threads