diff --git a/src/main.rs b/src/main.rs index 7453ed4..5bdf0d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,6 +138,11 @@ struct Client { #[arg(long, value_name = "DOMAIN_NAME", value_parser = parse_sni_override, verbatim_doc_comment)] tls_sni_override: Option, + /// Disable sending SNI during TLS handshake + /// Warning: Most reverse proxies rely on it + #[arg(long, verbatim_doc_comment)] + tls_sni_disable: bool, + /// Enable TLS certificate verification. /// Disabled by default. The client will happily connect to any server with self signed certificate. #[arg(long, verbatim_doc_comment)] @@ -557,6 +562,7 @@ fn parse_server_url(arg: &str) -> Result { #[derive(Clone)] pub struct TlsClientConfig { + pub tls_sni_disabled: bool, pub tls_sni_override: Option, pub tls_verify_certificate: bool, pub tls_connector: TlsConnector, @@ -680,16 +686,26 @@ async fn main() { { TransportScheme::Ws | TransportScheme::Http => None, TransportScheme::Wss => Some(TlsClientConfig { - tls_connector: tls::tls_connector(args.tls_verify_certificate, Some(vec![b"http/1.1".to_vec()])) - .expect("Cannot create tls connector"), + tls_connector: tls::tls_connector( + args.tls_verify_certificate, + Some(vec![b"http/1.1".to_vec()]), + !args.tls_sni_disable, + ) + .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, }), TransportScheme::Https => Some(TlsClientConfig { - tls_connector: tls::tls_connector(args.tls_verify_certificate, Some(vec![b"h2".to_vec()])) - .expect("Cannot create tls connector"), + tls_connector: tls::tls_connector( + args.tls_verify_certificate, + Some(vec![b"h2".to_vec()]), + !args.tls_sni_disable, + ) + .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, }), }; diff --git a/src/tls.rs b/src/tls.rs index 292091a..e507da2 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -66,6 +66,7 @@ pub fn load_private_key_from_file(path: &Path) -> anyhow::Result { pub fn tls_connector( tls_verify_certificate: bool, alpn_protocols: Option>>, + enable_sni: bool, ) -> anyhow::Result { let mut root_store = rustls::RootCertStore::empty(); @@ -74,7 +75,7 @@ pub fn tls_connector( for cert in certs { if let Err(err) = root_store.add(&Certificate(cert.as_ref().to_vec())) { warn!("cannot load a system certificate: {:?}", err); - continue + continue; } } @@ -83,6 +84,8 @@ pub fn tls_connector( .with_root_certificates(root_store) .with_no_client_auth(); + config.enable_sni = enable_sni; + // To bypass certificate verification if !tls_verify_certificate { config.dangerous().set_certificate_verifier(Arc::new(NullVerifier)); @@ -110,19 +113,28 @@ pub fn tls_acceptor(tls_cfg: &TlsServerConfig, alpn_protocols: Option anyhow::Result> { let sni = client_cfg.tls_server_name(); - info!( - "Doing TLS handshake using sni {sni:?} with the server {}:{}", - client_cfg.remote_addr.host(), - client_cfg.remote_addr.port() - ); - - let tls_connector = match &client_cfg.remote_addr { - TransportAddr::Wss { tls, .. } => &tls.tls_connector, - TransportAddr::Https { tls, .. } => &tls.tls_connector, + let (tls_connector, sni_disabled) = match &client_cfg.remote_addr { + TransportAddr::Wss { tls, .. } => (&tls.tls_connector, tls.tls_sni_disabled), + TransportAddr::Https { tls, .. } => (&tls.tls_connector, tls.tls_sni_disabled), TransportAddr::Http { .. } | TransportAddr::Ws { .. } => { return Err(anyhow!("Transport does not support TLS: {}", client_cfg.remote_addr.scheme())) } }; + + if sni_disabled { + info!( + "Doing TLS handshake without SNI with the server {}:{}", + client_cfg.remote_addr.host(), + client_cfg.remote_addr.port() + ); + } else { + info!( + "Doing TLS handshake using SNI {sni:?} with the server {}:{}", + client_cfg.remote_addr.host(), + client_cfg.remote_addr.port() + ); + } + let tls_stream = tls_connector.connect(sni, tcp_stream).await.with_context(|| { format!( "failed to do TLS handshake with the server {}:{}",