mirror of
https://github.com/erebe/wstunnel.git
synced 2025-09-26 19:21:10 +08:00
Impl ech (#427)
Co-authored-by: Guillaume FOURRIER <guillaume.fourrier@devialet.com>
This commit is contained in:
@@ -62,7 +62,7 @@ tokio-util = { version = "0.7.15", features = ["io"] }
|
||||
tokio-fd = "0.3.0"
|
||||
|
||||
[target.'cfg(all(any(target_os = "linux", target_os = "macos"), any(target_arch = "x86_64", target_arch = "aarch64")))'.dependencies]
|
||||
tokio-rustls = { version = "0.26.2", features = [] }
|
||||
tokio-rustls = { version = "0.26.2", features = ["ring"] }
|
||||
rcgen = { version = "0.13.2", default-features = false, features = ["aws_lc_rs"] }
|
||||
hickory-resolver = { version = "0.25.2", features = ["tls-aws-lc-rs", "https-aws-lc-rs", "tokio", "rustls-platform-verifier"] }
|
||||
|
||||
|
@@ -97,6 +97,10 @@ pub struct Client {
|
||||
#[cfg_attr(feature = "clap", arg(long, verbatim_doc_comment))]
|
||||
pub tls_sni_disable: bool,
|
||||
|
||||
/// Enable ECH during TLS handshake
|
||||
#[cfg_attr(feature = "clap", arg(long, verbatim_doc_comment))]
|
||||
pub tls_ech_enable: bool,
|
||||
|
||||
/// Enable TLS certificate verification.
|
||||
/// Disabled by default. The client will happily connect to any server with self-signed certificate.
|
||||
#[cfg_attr(feature = "clap", arg(long, verbatim_doc_comment))]
|
||||
|
39
src/lib.rs
39
src/lib.rs
@@ -82,23 +82,26 @@ async fn create_client_tunnels(
|
||||
let transport_scheme = TransportScheme::from_str(args.remote_addr.scheme()).expect("invalid scheme in server url");
|
||||
let tls = match transport_scheme {
|
||||
TransportScheme::Ws | TransportScheme::Http => None,
|
||||
TransportScheme::Wss | TransportScheme::Https => Some(TlsClientConfig {
|
||||
tls_connector: Arc::new(RwLock::new(
|
||||
tls::tls_connector(
|
||||
args.tls_verify_certificate,
|
||||
transport_scheme.alpn_protocols(),
|
||||
!args.tls_sni_disable,
|
||||
tls_certificate,
|
||||
tls_key,
|
||||
)
|
||||
.expect("Cannot create tls connector"),
|
||||
)),
|
||||
tls_sni_override: args.tls_sni_override,
|
||||
tls_verify_certificate: args.tls_verify_certificate,
|
||||
tls_sni_disabled: args.tls_sni_disable,
|
||||
tls_certificate_path: args.tls_certificate.clone(),
|
||||
tls_key_path: args.tls_private_key.clone(),
|
||||
}),
|
||||
TransportScheme::Wss | TransportScheme::Https => {
|
||||
let opts = tls::tls_connector(
|
||||
args.tls_verify_certificate,
|
||||
transport_scheme.alpn_protocols(),
|
||||
!args.tls_sni_disable,
|
||||
tls_certificate,
|
||||
tls_key,
|
||||
).expect("Cannot create tls connector");
|
||||
|
||||
Some(TlsClientConfig {
|
||||
tls_connector: Arc::new(RwLock::new(opts.0)),
|
||||
root_store: opts.1,
|
||||
tls_sni_override: args.tls_sni_override,
|
||||
tls_verify_certificate: args.tls_verify_certificate,
|
||||
tls_sni_disabled: args.tls_sni_disable,
|
||||
tls_certificate_path: args.tls_certificate.clone(),
|
||||
tls_key_path: args.tls_private_key.clone(),
|
||||
tls_ech_enabled: args.tls_ech_enable
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Extract host header from http_headers
|
||||
@@ -143,6 +146,7 @@ async fn create_client_tunnels(
|
||||
http_proxy.clone(),
|
||||
SoMark::new(args.socket_so_mark),
|
||||
!args.dns_resolver_prefer_ipv4,
|
||||
args.tls_ech_enable
|
||||
)
|
||||
.expect("cannot create dns resolver"),
|
||||
http_proxy,
|
||||
@@ -488,6 +492,7 @@ async fn run_server_impl(args: Server, executor: impl TokioExecutor) -> anyhow::
|
||||
None,
|
||||
SoMark::new(args.socket_so_mark),
|
||||
!args.dns_resolver_prefer_ipv4,
|
||||
false
|
||||
)
|
||||
.expect("Cannot create DNS resolver"),
|
||||
restriction_config: args.restrict_config,
|
||||
|
@@ -2,13 +2,18 @@ use crate::protocols;
|
||||
use crate::somark::SoMark;
|
||||
use anyhow::{Context, anyhow};
|
||||
use futures_util::{FutureExt, TryFutureExt};
|
||||
use hickory_resolver::Resolver;
|
||||
use hickory_resolver::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts};
|
||||
use hickory_resolver::name_server::GenericConnector;
|
||||
use hickory_resolver::name_server::{ConnectionProvider, GenericConnector};
|
||||
use hickory_resolver::proto::rr::rdata::svcb::{SvcParamKey, SvcParamValue};
|
||||
use hickory_resolver::proto::rr::{RData, RecordType};
|
||||
use hickory_resolver::proto::runtime::iocompat::AsyncIoTokioAsStd;
|
||||
use hickory_resolver::proto::runtime::{RuntimeProvider, TokioHandle, TokioRuntimeProvider, TokioTime};
|
||||
use hickory_resolver::proto::xfer::Protocol;
|
||||
use hickory_resolver::{Resolver, ResolveError};
|
||||
use log::warn;
|
||||
use tokio_rustls::rustls::client::EchConfig;
|
||||
use tokio_rustls::rustls::crypto::aws_lc_rs::hpke::ALL_SUPPORTED_SUITES;
|
||||
use tokio_rustls::rustls::pki_types::EchConfigListBytes;
|
||||
use std::future::Future;
|
||||
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::pin::Pin;
|
||||
@@ -40,14 +45,23 @@ pub enum DnsResolver {
|
||||
TrustDns {
|
||||
resolver: Resolver<GenericConnector<TokioRuntimeProviderWithSoMark>>,
|
||||
prefer_ipv6: bool,
|
||||
ech_enabled: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl DnsResolver {
|
||||
pub async fn lookup_host(&self, domain: &str, port: u16) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
let addrs: Vec<SocketAddr> = match self {
|
||||
Self::System => tokio::net::lookup_host(format!("{}:{}", domain, port)).await?.collect(),
|
||||
Self::TrustDns { resolver, prefer_ipv6 } => {
|
||||
pub async fn lookup_host(&self, domain: &str, port: u16) -> anyhow::Result<(Vec<SocketAddr>, Option<EchConfig>)> {
|
||||
let addrs: (Vec<SocketAddr>, Option<EchConfig>) = match self {
|
||||
Self::System => {
|
||||
(tokio::net::lookup_host(format!("{}:{}", domain, port)).await?.collect(), None)
|
||||
},
|
||||
Self::TrustDns { resolver, prefer_ipv6, ech_enabled } => {
|
||||
|
||||
let mut ech_config: Option<EchConfig> = None;
|
||||
if *ech_enabled {
|
||||
ech_config = Self::lookup_ech_config(domain, resolver).await?;
|
||||
}
|
||||
|
||||
let addrs: Vec<_> = resolver
|
||||
.lookup_ip(domain)
|
||||
.await?
|
||||
@@ -57,18 +71,50 @@ impl DnsResolver {
|
||||
IpAddr::V6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
||||
})
|
||||
.collect();
|
||||
sort_socket_addrs(&addrs, *prefer_ipv6).copied().collect()
|
||||
let sorted_addr = sort_socket_addrs(&addrs, *prefer_ipv6).copied().collect();
|
||||
(sorted_addr, ech_config)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(addrs)
|
||||
}
|
||||
|
||||
async fn lookup_ech_config<P: ConnectionProvider>(domain: &str, resolver: &Resolver<P>) -> Result<Option<EchConfig>, ResolveError> {
|
||||
let lookup = resolver
|
||||
.lookup(domain, RecordType::HTTPS)
|
||||
.await?;
|
||||
|
||||
let mut ech_config_lists = Vec::new();
|
||||
for r in lookup.record_iter() {
|
||||
let RData::HTTPS(svcb) = r.data() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
ech_config_lists.extend(
|
||||
svcb.svc_params()
|
||||
.iter()
|
||||
.find_map(|sp| match sp {
|
||||
(SvcParamKey::EchConfigList, SvcParamValue::EchConfigList(e)) => {
|
||||
Some(EchConfigListBytes::from(e.clone().0))
|
||||
}
|
||||
_ => None,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
let ech_config = ech_config_lists
|
||||
.into_iter()
|
||||
.find_map(|list| EchConfig::new(list, ALL_SUPPORTED_SUITES).ok());
|
||||
|
||||
Ok(ech_config)
|
||||
}
|
||||
|
||||
pub fn new_from_urls(
|
||||
resolvers: &[Url],
|
||||
proxy: Option<Url>,
|
||||
so_mark: SoMark,
|
||||
prefer_ipv6: bool,
|
||||
ech_enabled: bool,
|
||||
) -> anyhow::Result<Self> {
|
||||
fn mk_resolver(
|
||||
cfg: ResolverConfig,
|
||||
@@ -144,6 +190,7 @@ impl DnsResolver {
|
||||
return Ok(Self::TrustDns {
|
||||
resolver: mk_resolver(cfg, opts, proxy, so_mark),
|
||||
prefer_ipv6,
|
||||
ech_enabled
|
||||
});
|
||||
};
|
||||
|
||||
@@ -161,6 +208,7 @@ impl DnsResolver {
|
||||
Ok(Self::TrustDns {
|
||||
resolver: mk_resolver(cfg, ResolverOpts::default(), proxy, so_mark),
|
||||
prefer_ipv6,
|
||||
ech_enabled
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -218,8 +266,10 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
|
||||
&DnsResolver::System, // not going to be used as host is directly an ip address
|
||||
)
|
||||
.map_err(std::io::Error::other)
|
||||
.map_ok(|s| s.0)
|
||||
.map(|s| s.map(AsyncIoTokioAsStd))
|
||||
.await
|
||||
|
||||
} else {
|
||||
protocols::tcp::connect(
|
||||
&host,
|
||||
@@ -229,6 +279,7 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
|
||||
&DnsResolver::System, // not going to be used as host is directly an ip address
|
||||
)
|
||||
.map_err(std::io::Error::other)
|
||||
.map_ok(|s| s.0)
|
||||
.map(|s| s.map(AsyncIoTokioAsStd))
|
||||
.await
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use tokio_rustls::rustls::client::EchConfig;
|
||||
use std::{io, vec};
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
@@ -53,23 +54,23 @@ pub async fn connect(
|
||||
so_mark: SoMark,
|
||||
connect_timeout: Duration,
|
||||
dns_resolver: &DnsResolver,
|
||||
) -> Result<TcpStream, anyhow::Error> {
|
||||
) -> Result<(TcpStream, Option<EchConfig>), anyhow::Error> {
|
||||
info!("Opening TCP connection to {}:{}", host, port);
|
||||
|
||||
let socket_addrs: Vec<SocketAddr> = match host {
|
||||
let socket_addrs: (Vec<SocketAddr>, Option<EchConfig>) = match host {
|
||||
Host::Domain(domain) => dns_resolver
|
||||
.lookup_host(domain.as_str(), port)
|
||||
.await
|
||||
.with_context(|| format!("cannot resolve domain: {}", domain))?,
|
||||
Host::Ipv4(ip) => vec![SocketAddr::V4(SocketAddrV4::new(*ip, port))],
|
||||
Host::Ipv6(ip) => vec![SocketAddr::V6(SocketAddrV6::new(*ip, port, 0, 0))],
|
||||
Host::Ipv4(ip) => (vec![SocketAddr::V4(SocketAddrV4::new(*ip, port))], None),
|
||||
Host::Ipv6(ip) => (vec![SocketAddr::V6(SocketAddrV6::new(*ip, port, 0, 0))], None),
|
||||
};
|
||||
|
||||
let mut cnx = None;
|
||||
let mut last_err = None;
|
||||
let mut join_set = JoinSet::new();
|
||||
|
||||
for (ix, addr) in socket_addrs.into_iter().enumerate() {
|
||||
for (ix, addr) in socket_addrs.0.into_iter().enumerate() {
|
||||
let socket = match &addr {
|
||||
SocketAddr::V4(_) => TcpSocket::new_v4(),
|
||||
SocketAddr::V6(_) => TcpSocket::new_v6(),
|
||||
@@ -115,7 +116,7 @@ pub async fn connect(
|
||||
"Connected to tcp endpoint {}, aborted all other connection attempts",
|
||||
stream.peer_addr()?
|
||||
);
|
||||
cnx = Some(stream);
|
||||
cnx = Some((stream, socket_addrs.1.clone()));
|
||||
}
|
||||
Ok(Err((addr, err))) => {
|
||||
debug!("Cannot connect to tcp endpoint {addr} reason {err}");
|
||||
@@ -141,13 +142,13 @@ pub async fn connect_with_http_proxy(
|
||||
so_mark: SoMark,
|
||||
connect_timeout: Duration,
|
||||
dns_resolver: &DnsResolver,
|
||||
) -> Result<TcpStream, anyhow::Error> {
|
||||
) -> Result<(TcpStream, Option<EchConfig>), anyhow::Error> {
|
||||
let proxy_host = proxy.host().context("Cannot parse proxy host")?.to_owned();
|
||||
let proxy_port = proxy.port_or_known_default().unwrap_or(80);
|
||||
|
||||
info!("Connecting to http proxy {}:{}", proxy_host, proxy_port);
|
||||
let mut socket = connect(&proxy_host, proxy_port, so_mark, connect_timeout, dns_resolver).await?;
|
||||
debug!("Connected to http proxy {}", socket.peer_addr().unwrap());
|
||||
debug!("Connected to http proxy {}", socket.0.peer_addr().unwrap());
|
||||
|
||||
let authorization = if let Some((user, password)) = proxy.password().map(|p| (proxy.username(), p)) {
|
||||
let user = urlencoding::decode(user).with_context(|| format!("Cannot urldecode proxy user: {}", user))?;
|
||||
@@ -161,11 +162,11 @@ pub async fn connect_with_http_proxy(
|
||||
|
||||
let connect_request = format!("CONNECT {host}:{port} HTTP/1.0\r\nHost: {host}:{port}\r\n{authorization}\r\n");
|
||||
debug!("Sending request:\n{}", connect_request);
|
||||
socket.write_all(connect_request.as_bytes()).await?;
|
||||
socket.0.write_all(connect_request.as_bytes()).await?;
|
||||
|
||||
let mut buf = BytesMut::with_capacity(1024);
|
||||
loop {
|
||||
let nb_bytes = tokio::time::timeout(connect_timeout, socket.read_buf(&mut buf)).await;
|
||||
let nb_bytes = tokio::time::timeout(connect_timeout, socket.0.read_buf(&mut buf)).await;
|
||||
match nb_bytes {
|
||||
Ok(Ok(0)) => {
|
||||
return Err(anyhow!(
|
||||
@@ -294,7 +295,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
client.write_all(b"GET / HTTP/1.1\r\n\r\n".as_slice()).await.unwrap();
|
||||
client.0.write_all(b"GET / HTTP/1.1\r\n\r\n".as_slice()).await.unwrap();
|
||||
let client_srv = server.accept().await.unwrap().0;
|
||||
pin_mut!(client_srv);
|
||||
|
||||
@@ -304,7 +305,7 @@ mod tests {
|
||||
client_srv.write_all(b"HTTP/1.1 200 OK\r\n\r\n").await.unwrap();
|
||||
|
||||
client_srv.get_mut().shutdown().await.unwrap();
|
||||
let _ = client.read(&mut buf).await.unwrap();
|
||||
let _ = client.0.read(&mut buf).await.unwrap();
|
||||
assert!(buf.starts_with(b"HTTP/1.1 200 OK\r\n\r\n"));
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use tokio_rustls::rustls::client::{EchConfig, EchMode};
|
||||
use tokio_rustls::rustls::crypto::ring;
|
||||
use std::fs::File;
|
||||
|
||||
use log::warn;
|
||||
@@ -8,13 +10,13 @@ use std::sync::Arc;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_rustls::client::TlsStream;
|
||||
|
||||
use crate::tunnel::client::WsClientConfig;
|
||||
use crate::tunnel::client::{TlsClientConfig, WsClientConfig};
|
||||
use crate::tunnel::server::TlsServerConfig;
|
||||
use crate::tunnel::transport::TransportAddr;
|
||||
use tokio_rustls::rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
|
||||
use tokio_rustls::rustls::server::WebPkiClientVerifier;
|
||||
use tokio_rustls::rustls::{ClientConfig, DigitallySignedStruct, Error, KeyLogFile, SignatureScheme};
|
||||
use tokio_rustls::rustls::{ClientConfig, DigitallySignedStruct, Error, KeyLogFile, RootCertStore, SignatureScheme};
|
||||
use tokio_rustls::{TlsAcceptor, TlsConnector, rustls};
|
||||
use tracing::info;
|
||||
|
||||
@@ -108,7 +110,7 @@ pub fn tls_connector(
|
||||
enable_sni: bool,
|
||||
tls_client_certificate: Option<Vec<CertificateDer<'static>>>,
|
||||
tls_client_key: Option<PrivateKeyDer<'static>>,
|
||||
) -> anyhow::Result<TlsConnector> {
|
||||
) -> anyhow::Result<(TlsConnector, RootCertStore)> {
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
|
||||
// Load system certificates and add them to the root store
|
||||
@@ -122,8 +124,10 @@ pub fn tls_connector(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rustls::crypto::ring::default_provider().install_default().expect("Failed to install rustls crypto provider");
|
||||
|
||||
let config_builder = ClientConfig::builder().with_root_certificates(root_store);
|
||||
let config_builder = ClientConfig::builder().with_root_certificates(root_store.clone());
|
||||
|
||||
let mut config = match (tls_client_certificate, tls_client_key) {
|
||||
(Some(tls_client_certificate), Some(tls_client_key)) => config_builder
|
||||
@@ -142,7 +146,7 @@ pub fn tls_connector(
|
||||
|
||||
config.alpn_protocols = alpn_protocols;
|
||||
let tls_connector = TlsConnector::from(Arc::new(config));
|
||||
Ok(tls_connector)
|
||||
Ok((tls_connector, root_store))
|
||||
}
|
||||
|
||||
pub fn tls_acceptor(tls_cfg: &TlsServerConfig, alpn_protocols: Option<Vec<Vec<u8>>>) -> anyhow::Result<TlsAcceptor> {
|
||||
@@ -173,7 +177,7 @@ pub fn tls_acceptor(tls_cfg: &TlsServerConfig, alpn_protocols: Option<Vec<Vec<u8
|
||||
Ok(TlsAcceptor::from(Arc::new(config)))
|
||||
}
|
||||
|
||||
pub async fn connect(client_cfg: &WsClientConfig, tcp_stream: TcpStream) -> anyhow::Result<TlsStream<TcpStream>> {
|
||||
pub async fn connect(client_cfg: &WsClientConfig, tcp_stream: (TcpStream, Option<EchConfig>)) -> anyhow::Result<TlsStream<TcpStream>> {
|
||||
let sni = client_cfg.tls_server_name();
|
||||
let (tls_connector, sni_disabled) = match &client_cfg.remote_addr {
|
||||
TransportAddr::Wss { tls, .. } => (tls.tls_connector(), tls.tls_sni_disabled),
|
||||
@@ -197,13 +201,54 @@ pub async fn connect(client_cfg: &WsClientConfig, tcp_stream: TcpStream) -> anyh
|
||||
);
|
||||
}
|
||||
|
||||
let tls_stream = tls_connector.connect(sni, tcp_stream).await.with_context(|| {
|
||||
let connector = tls_connector.clone();
|
||||
|
||||
let tls_config = match &client_cfg.remote_addr {
|
||||
TransportAddr::Wss { tls, .. } => Some(tls),
|
||||
TransportAddr::Https { tls, .. } => Some(tls),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let tls_stream = if let (Some(tls_config) , Some(ech_config), Some(true)) = (tls_config, tcp_stream.1, is_ech_enabled(tls_config)) {
|
||||
tls_connect_with_ech(ech_config, tls_config, tls_connector.clone(), client_cfg, tcp_stream.0, sni).await?
|
||||
} else {
|
||||
connector.connect(sni, tcp_stream.0).await?
|
||||
};
|
||||
|
||||
Ok(tls_stream)
|
||||
}
|
||||
|
||||
fn is_ech_enabled(tls_config: Option<&TlsClientConfig>) -> Option<bool> {
|
||||
match tls_config {
|
||||
Some(config) => return Some(config.tls_ech_enabled),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
async fn tls_connect_with_ech(
|
||||
ech_config: EchConfig,
|
||||
tls_config: &TlsClientConfig,
|
||||
original_connector: TlsConnector,
|
||||
client_cfg: &WsClientConfig,
|
||||
tcp_stream: TcpStream,
|
||||
sni: ServerName<'static>
|
||||
) -> anyhow::Result<TlsStream<TcpStream>> {
|
||||
let mut ech_client_config = ClientConfig::builder_with_provider(ring::default_provider().into())
|
||||
.with_ech(EchMode::from(ech_config))?
|
||||
.with_root_certificates(tls_config.root_store.clone())
|
||||
.with_no_client_auth();
|
||||
|
||||
let original_config = original_connector.config();
|
||||
ech_client_config.key_log = original_config.key_log.clone();
|
||||
ech_client_config.alpn_protocols = original_config.alpn_protocols.clone();
|
||||
|
||||
return TlsConnector::from(Arc::new(ech_client_config))
|
||||
.connect(sni, tcp_stream)
|
||||
.await.with_context(|| {
|
||||
format!(
|
||||
"failed to do TLS handshake with the server {}:{}",
|
||||
client_cfg.remote_addr.host(),
|
||||
client_cfg.remote_addr.port()
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(tls_stream)
|
||||
}
|
||||
});
|
||||
}
|
@@ -3,6 +3,7 @@ use futures_util::{Stream, stream};
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use pin_project::{pin_project, pinned_drop};
|
||||
use tokio_rustls::rustls::client::EchConfig;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::io::{Error, ErrorKind};
|
||||
@@ -343,20 +344,20 @@ pub async fn connect(
|
||||
) -> anyhow::Result<WsUdpSocket> {
|
||||
info!("Opening UDP connection to {}:{}", host, port);
|
||||
|
||||
let socket_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(ip) => vec![SocketAddr::V4(SocketAddrV4::new(*ip, port))],
|
||||
Host::Ipv6(ip) => vec![SocketAddr::V6(SocketAddrV6::new(*ip, port, 0, 0))],
|
||||
let socket_addrs: (Vec<SocketAddr>, Option<EchConfig>) = match host {
|
||||
Host::Domain(domain) => dns_resolver
|
||||
.lookup_host(domain.as_str(), port)
|
||||
.await
|
||||
.with_context(|| format!("cannot resolve domain: {}", domain))?,
|
||||
Host::Ipv4(ip) => (vec![SocketAddr::V4(SocketAddrV4::new(*ip, port))], None),
|
||||
Host::Ipv6(ip) => (vec![SocketAddr::V6(SocketAddrV6::new(*ip, port, 0, 0))], None),
|
||||
};
|
||||
|
||||
let mut cnx = None;
|
||||
let mut last_err = None;
|
||||
let mut join_set = JoinSet::new();
|
||||
|
||||
for (ix, addr) in socket_addrs.into_iter().enumerate() {
|
||||
for (ix, addr) in socket_addrs.0.into_iter().enumerate() {
|
||||
let socket = match &addr {
|
||||
SocketAddr::V4(_) => UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)).await,
|
||||
SocketAddr::V6(_) => UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)).await,
|
||||
|
@@ -25,7 +25,7 @@ use url::Host;
|
||||
|
||||
#[fixture]
|
||||
fn dns_resolver() -> DnsResolver {
|
||||
DnsResolver::new_from_urls(&[], None, SoMark::new(None), true).expect("Cannot create DNS resolver")
|
||||
DnsResolver::new_from_urls(&[], None, SoMark::new(None), true, false).expect("Cannot create DNS resolver")
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
@@ -148,7 +148,7 @@ async fn test_tcp_tunnel(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
client.write_all(b"Hello").await.unwrap();
|
||||
client.0.write_all(b"Hello").await.unwrap();
|
||||
let mut dd = tcp_listener.next().await.unwrap().unwrap();
|
||||
let mut buf = BytesMut::new();
|
||||
dd.read_buf(&mut buf).await.unwrap();
|
||||
@@ -156,7 +156,7 @@ async fn test_tcp_tunnel(
|
||||
buf.clear();
|
||||
|
||||
dd.write_all(b"world!").await.unwrap();
|
||||
client.read_buf(&mut buf).await.unwrap();
|
||||
client.0.read_buf(&mut buf).await.unwrap();
|
||||
assert_eq!(&buf[..6], b"world!");
|
||||
}
|
||||
|
||||
|
@@ -58,7 +58,7 @@ impl ManageConnection for WsConnection {
|
||||
let tls_stream = tls::connect(self, tcp_stream).await?;
|
||||
Ok(Some(TransportStream::from_client_tls(tls_stream, Bytes::default())))
|
||||
} else {
|
||||
Ok(Some(TransportStream::from_tcp(tcp_stream, Bytes::default())))
|
||||
Ok(Some(TransportStream::from_tcp(tcp_stream.0, Bytes::default())))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ use crate::somark::SoMark;
|
||||
use crate::tunnel::transport::TransportAddr;
|
||||
use hyper::header::{HeaderName, HeaderValue};
|
||||
use parking_lot::RwLock;
|
||||
use tokio_rustls::rustls::RootCertStore;
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
@@ -57,6 +58,8 @@ pub struct TlsClientConfig {
|
||||
pub tls_connector: Arc<RwLock<TlsConnector>>,
|
||||
pub tls_certificate_path: Option<PathBuf>,
|
||||
pub tls_key_path: Option<PathBuf>,
|
||||
pub root_store: RootCertStore,
|
||||
pub tls_ech_enabled: bool,
|
||||
}
|
||||
|
||||
impl TlsClientConfig {
|
||||
|
@@ -51,7 +51,7 @@ impl TunnelConnector for Socks5TunnelConnector<'_> {
|
||||
self.dns_resolver,
|
||||
)
|
||||
.await?;
|
||||
let (reader, writer) = stream.into_split();
|
||||
let (reader, writer) = stream.0.into_split();
|
||||
Ok((Socks5Reader::Tcp(reader), Socks5Writer::Tcp(writer)))
|
||||
}
|
||||
LocalProtocol::Udp { .. } => {
|
||||
@@ -84,7 +84,7 @@ impl TunnelConnector for Socks5TunnelConnector<'_> {
|
||||
self.dns_resolver,
|
||||
)
|
||||
.await?;
|
||||
let (reader, writer) = stream.into_split();
|
||||
let (reader, writer) = stream.0.into_split();
|
||||
Ok((Socks5Reader::Tcp(reader), Socks5Writer::Tcp(writer)))
|
||||
}
|
||||
_ => Err(anyhow!("Socks5 UDP cannot use http proxy to connect to destination")),
|
||||
|
@@ -46,7 +46,7 @@ impl TunnelConnector for TcpTunnelConnector<'_> {
|
||||
};
|
||||
|
||||
let stream = protocols::tcp::connect(host, port, self.so_mark, self.connect_timeout, self.dns_resolver).await?;
|
||||
Ok(stream.into_split())
|
||||
Ok(stream.0.into_split())
|
||||
}
|
||||
|
||||
async fn connect_with_http_proxy(
|
||||
@@ -68,6 +68,6 @@ impl TunnelConnector for TcpTunnelConnector<'_> {
|
||||
self.dns_resolver,
|
||||
)
|
||||
.await?;
|
||||
Ok(stream.into_split())
|
||||
Ok(stream.0.into_split())
|
||||
}
|
||||
}
|
||||
|
@@ -300,7 +300,7 @@ impl TlsReloader {
|
||||
return;
|
||||
}
|
||||
};
|
||||
*tls.tls_connector.write() = tls_connector;
|
||||
*tls.tls_connector.write() = tls_connector.0;
|
||||
this.tls_reload_certificate.store(true, Ordering::Relaxed);
|
||||
}
|
||||
(Err(err), _) | (_, Err(err)) => {
|
||||
@@ -343,7 +343,7 @@ impl TlsReloader {
|
||||
return;
|
||||
}
|
||||
};
|
||||
*tls.tls_connector.write() = tls_connector;
|
||||
*tls.tls_connector.write() = tls_connector.0;
|
||||
this.tls_reload_certificate.store(true, Ordering::Relaxed);
|
||||
}
|
||||
(Err(err), _) | (_, Err(err)) => {
|
||||
|
Reference in New Issue
Block a user