TLS-F: Fetcher V2

This commit is contained in:
Alexey 2026-02-20 13:36:54 +03:00
parent 32a9405002
commit 487aa8fbce
No known key found for this signature in database
3 changed files with 47 additions and 34 deletions

View File

@ -1,19 +1,22 @@
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use anyhow::Result;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::time::timeout; use tokio::time::timeout;
use tokio_rustls::client::TlsStream; use tokio_rustls::client::TlsStream;
use tokio_rustls::TlsConnector; use tokio_rustls::TlsConnector;
use tracing::{debug, warn}; use tracing::debug;
use rustls::client::{ClientConfig, ServerCertVerifier, ServerName}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::client::ClientConfig;
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{DigitallySignedStruct, Error as RustlsError}; use rustls::{DigitallySignedStruct, Error as RustlsError};
use rustls::pki_types::{ServerName as PkiServerName, UnixTime, CertificateDer};
use crate::tls_front::types::{ParsedServerHello, TlsFetchResult}; use crate::tls_front::types::{ParsedServerHello, TlsFetchResult};
/// No-op verifier: accept any certificate (we only need lengths and metadata). /// No-op verifier: accept any certificate (we only need lengths and metadata).
#[derive(Debug)]
struct NoVerify; struct NoVerify;
impl ServerCertVerifier for NoVerify { impl ServerCertVerifier for NoVerify {
@ -21,11 +24,11 @@ impl ServerCertVerifier for NoVerify {
&self, &self,
_end_entity: &CertificateDer<'_>, _end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>], _intermediates: &[CertificateDer<'_>],
_server_name: &PkiServerName<'_>, _server_name: &ServerName<'_>,
_ocsp: &[u8], _ocsp: &[u8],
_now: UnixTime, _now: UnixTime,
) -> Result<rustls::client::ServerCertVerified, RustlsError> { ) -> Result<ServerCertVerified, RustlsError> {
Ok(rustls::client::ServerCertVerified::assertion()) Ok(ServerCertVerified::assertion())
} }
fn verify_tls12_signature( fn verify_tls12_signature(
@ -33,8 +36,8 @@ impl ServerCertVerifier for NoVerify {
_message: &[u8], _message: &[u8],
_cert: &CertificateDer<'_>, _cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct, _dss: &DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, RustlsError> { ) -> Result<HandshakeSignatureValid, RustlsError> {
Ok(rustls::client::HandshakeSignatureValid::assertion()) Ok(HandshakeSignatureValid::assertion())
} }
fn verify_tls13_signature( fn verify_tls13_signature(
@ -42,57 +45,67 @@ impl ServerCertVerifier for NoVerify {
_message: &[u8], _message: &[u8],
_cert: &CertificateDer<'_>, _cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct, _dss: &DigitallySignedStruct,
) -> Result<rustls::client::HandshakeSignatureValid, RustlsError> { ) -> Result<HandshakeSignatureValid, RustlsError> {
Ok(rustls::client::HandshakeSignatureValid::assertion()) Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
use rustls::SignatureScheme::*;
vec![
RSA_PKCS1_SHA256,
RSA_PSS_SHA256,
ECDSA_NISTP256_SHA256,
ECDSA_NISTP384_SHA384,
]
} }
} }
fn build_client_config() -> Arc<ClientConfig> { fn build_client_config() -> Arc<ClientConfig> {
let mut root = rustls::RootCertStore::empty(); let root = rustls::RootCertStore::empty();
// Optionally load system roots; failure is non-fatal.
let _ = root.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
Arc::new( let provider = rustls::crypto::ring::default_provider();
ClientConfig::builder() let mut config = ClientConfig::builder_with_provider(Arc::new(provider))
.with_safe_default_cipher_suites() .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
.with_safe_default_kx_groups() .expect("protocol versions")
.with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12]) .with_root_certificates(root)
.unwrap() .with_no_client_auth();
.with_custom_certificate_verifier(Arc::new(NoVerify))
.with_root_certificates(root) config
.with_no_client_auth(), .dangerous()
) .set_certificate_verifier(Arc::new(NoVerify));
Arc::new(config)
} }
/// Try to fetch real TLS metadata for the given SNI. /// Fetch real TLS metadata for the given SNI: negotiated cipher and cert lengths.
pub async fn fetch_real_tls( pub async fn fetch_real_tls(
host: &str, host: &str,
port: u16, port: u16,
sni: &str, sni: &str,
connect_timeout: Duration, connect_timeout: Duration,
) -> anyhow::Result<TlsFetchResult> { ) -> Result<TlsFetchResult> {
let addr = format!("{}:{}", host, port); let addr = format!("{host}:{port}");
let stream = timeout(connect_timeout, TcpStream::connect(addr)).await??; let stream = timeout(connect_timeout, TcpStream::connect(addr)).await??;
let config = build_client_config(); let config = build_client_config();
let connector = TlsConnector::from(config); let connector = TlsConnector::from(config);
let server_name = ServerName::try_from(sni) let server_name = ServerName::try_from(sni.to_owned())
.or_else(|_| ServerName::try_from(host)) .or_else(|_| ServerName::try_from(host.to_owned()))
.map_err(|_| RustlsError::General("invalid SNI".into()))?; .map_err(|_| RustlsError::General("invalid SNI".into()))?;
let mut tls_stream: TlsStream<TcpStream> = connector.connect(server_name, stream).await?; let tls_stream: TlsStream<TcpStream> = connector.connect(server_name, stream).await?;
// Extract negotiated parameters and certificates // Extract negotiated parameters and certificates
let (session, _io) = tls_stream.get_ref(); let (_io, session) = tls_stream.get_ref();
let cipher_suite = session let cipher_suite = session
.negotiated_cipher_suite() .negotiated_cipher_suite()
.map(|s| s.suite().get_u16().to_be_bytes()) .map(|s| u16::from(s.suite()).to_be_bytes())
.unwrap_or([0x13, 0x01]); .unwrap_or([0x13, 0x01]);
let certs: Vec<CertificateDer<'static>> = session let certs: Vec<CertificateDer<'static>> = session
.peer_certificates() .peer_certificates()
.map(|slice| slice.iter().cloned().collect()) .map(|slice| slice.to_vec())
.unwrap_or_default(); .unwrap_or_default();
let total_cert_len: usize = certs.iter().map(|c| c.len()).sum::<usize>().max(1024); let total_cert_len: usize = certs.iter().map(|c| c.len()).sum::<usize>().max(1024);

View File

@ -138,7 +138,7 @@ pub fn resolve_interface_ip(name: &str, want_ipv6: bool) -> Option<IpAddr> {
} }
} else if let Some(v6) = address.as_sockaddr_in6() { } else if let Some(v6) = address.as_sockaddr_in6() {
if want_ipv6 { if want_ipv6 {
return Some(IpAddr::V6(v6.ip().to_std())); return Some(IpAddr::V6(v6.ip().clone()));
} }
} }
} }

View File

@ -609,7 +609,7 @@ impl UpstreamManager {
} }
let result = tokio::time::timeout( let result = tokio::time::timeout(
Duration::from_secs(DC_PING_TIMEOUT_SECS), Duration::from_secs(DC_PING_TIMEOUT_SECS),
self.ping_single_dc(&upstream_config, addr) self.ping_single_dc(&upstream_config, Some(bind_rr.clone()), addr)
).await; ).await;
let ping_result = match result { let ping_result = match result {