Skip to main content

proxychains_masq/proxy/
https.rs

1//! HTTP CONNECT over TLS (HTTPS proxy).
2//!
3//! Establishes a TLS session to the proxy, then performs a standard HTTP CONNECT
4//! handshake inside that session.  Certificate verification is intentionally
5//! disabled so that self-signed or internal-CA certificates are accepted — a
6//! common requirement for corporate HTTPS proxies.
7
8use std::{net::IpAddr, sync::Arc};
9
10use anyhow::{Context, Result};
11use rustls::{
12    client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
13    pki_types::{CertificateDer, IpAddr as PkiIpAddr, ServerName, UnixTime},
14    DigitallySignedStruct, Error as TlsError, SignatureScheme,
15};
16use tokio_rustls::TlsConnector;
17
18use super::{http, BoxStream, Target};
19
20// ─── Insecure certificate verifier ───────────────────────────────────────────
21
22/// A [`ServerCertVerifier`] that accepts any certificate without validation.
23///
24/// This deliberately skips all certificate chain and hostname checks, making
25/// TLS connections immune to self-signed or otherwise untrusted proxy certs.
26/// Use only when the proxy host and network path are trusted by other means.
27#[derive(Debug)]
28struct SkipCertVerify;
29
30impl ServerCertVerifier for SkipCertVerify {
31    fn verify_server_cert(
32        &self,
33        _end_entity: &CertificateDer<'_>,
34        _intermediates: &[CertificateDer<'_>],
35        _server_name: &ServerName<'_>,
36        _ocsp_response: &[u8],
37        _now: UnixTime,
38    ) -> Result<ServerCertVerified, TlsError> {
39        Ok(ServerCertVerified::assertion())
40    }
41
42    fn verify_tls12_signature(
43        &self,
44        _message: &[u8],
45        _cert: &CertificateDer<'_>,
46        _dss: &DigitallySignedStruct,
47    ) -> Result<HandshakeSignatureValid, TlsError> {
48        Ok(HandshakeSignatureValid::assertion())
49    }
50
51    fn verify_tls13_signature(
52        &self,
53        _message: &[u8],
54        _cert: &CertificateDer<'_>,
55        _dss: &DigitallySignedStruct,
56    ) -> Result<HandshakeSignatureValid, TlsError> {
57        Ok(HandshakeSignatureValid::assertion())
58    }
59
60    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
61        // Accept all schemes that rustls (ring provider) supports.
62        rustls::crypto::ring::default_provider()
63            .signature_verification_algorithms
64            .supported_schemes()
65    }
66}
67
68// ─── Public API ───────────────────────────────────────────────────────────────
69
70/// Connect through an HTTPS CONNECT proxy.
71///
72/// 1. Wraps `stream` in a TLS session directed at the proxy (cert validation skipped).
73/// 2. Sends an HTTP `CONNECT target HTTP/1.0` request over the TLS channel.
74/// 3. Returns the TLS stream positioned after the proxy's `200` response,
75///    ready to relay tunnel traffic.
76///
77/// # Arguments
78///
79/// * `stream` — a connected TCP stream to the proxy's HTTPS port.
80/// * `target` — the tunnel destination requested in the CONNECT line.
81/// * `username` / `password` — optional `Proxy-Authorization: Basic` credentials.
82/// * `proxy_addr` — the proxy's IP address, used as the TLS SNI value.
83///
84/// # Errors
85///
86/// Returns an error if the TLS handshake or HTTP CONNECT negotiation fails.
87pub async fn connect(
88    stream: BoxStream,
89    target: &Target,
90    username: Option<&str>,
91    password: Option<&str>,
92    proxy_addr: IpAddr,
93) -> Result<BoxStream> {
94    let tls_stream = tls_wrap(stream, proxy_addr).await?;
95    // HTTP CONNECT over the TLS channel is identical to plain HTTP CONNECT.
96    http::connect(Box::new(tls_stream), target, username, password).await
97}
98
99// ─── TLS helpers ─────────────────────────────────────────────────────────────
100
101async fn tls_wrap(
102    stream: BoxStream,
103    proxy_addr: IpAddr,
104) -> Result<tokio_rustls::client::TlsStream<BoxStream>> {
105    let config = Arc::new(
106        rustls::ClientConfig::builder()
107            .dangerous()
108            .with_custom_certificate_verifier(Arc::new(SkipCertVerify))
109            .with_no_client_auth(),
110    );
111
112    let server_name: ServerName<'static> = match proxy_addr {
113        IpAddr::V4(v4) => ServerName::IpAddress(PkiIpAddr::V4(v4.octets().into())),
114        IpAddr::V6(v6) => ServerName::IpAddress(PkiIpAddr::V6(v6.into())),
115    };
116
117    TlsConnector::from(config)
118        .connect(server_name, stream)
119        .await
120        .context("HTTPS proxy: TLS handshake failed")
121}