Skip to content
This repository has been archived by the owner on Feb 7, 2025. It is now read-only.

feat(connect): Add PSK extension #52

Merged
merged 3 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ Cargo.lock
.vscode
.direnv
result
curl
curl
.idea
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors = ["0x676e67 <[email protected]>"]
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
rust-version = "1.63.0"
rust-version = "1.79"
autotests = true

[package.metadata.docs.rs]
Expand Down
22 changes: 11 additions & 11 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use crate::dns::hickory::HickoryDnsResolver;
use crate::dns::{gai::GaiResolver, DnsResolverWithOverrides, DynResolver, Resolve};
use crate::error;
#[cfg(feature = "impersonate")]
use crate::impersonate::{profile::ClientProfile, Impersonate, ImpersonateContext};
use crate::impersonate::{Impersonate, ImpersonateContext};
use crate::into_url::{expect_uri, try_uri};
use crate::redirect::{self, remove_sensitive_headers};
#[cfg(feature = "__tls")]
Expand Down Expand Up @@ -158,7 +158,7 @@ struct Config {
dns_overrides: HashMap<String, Vec<SocketAddr>>,
dns_resolver: Option<Arc<dyn Resolve>>,
#[cfg(feature = "impersonate")]
profile: ClientProfile,
impersonate: Impersonate,
#[cfg(feature = "impersonate")]
enable_ech_grease: bool,
#[cfg(feature = "impersonate")]
Expand Down Expand Up @@ -253,7 +253,7 @@ impl ClientBuilder {
quic_send_window: None,
dns_resolver: None,
#[cfg(feature = "impersonate")]
profile: ClientProfile::Chrome,
impersonate: Impersonate::default(),
#[cfg(feature = "impersonate")]
enable_ech_grease: false,
#[cfg(feature = "impersonate")]
Expand All @@ -264,21 +264,21 @@ impl ClientBuilder {

/// Sets the necessary values to mimic the specified impersonate version.
#[cfg(feature = "__impersonate")]
pub fn impersonate(mut self, ver: Impersonate) -> ClientBuilder {
pub fn impersonate(mut self, impersonate: Impersonate) -> ClientBuilder {
use crate::impersonate::profile::configure_impersonate;

self.config.profile = ver.profile();
configure_impersonate(ver, self)
self.config.impersonate = impersonate;
configure_impersonate(impersonate, self)
}

/// Sets the necessary values to mimic the specified impersonate version. (websocket)
#[cfg(feature = "__impersonate")]
pub fn impersonate_websocket(mut self, ver: Impersonate) -> ClientBuilder {
pub fn impersonate_websocket(mut self, impersonate: Impersonate) -> ClientBuilder {
use crate::impersonate::profile::configure_impersonate;

self.config.profile = ver.profile();
self.config.impersonate = impersonate;
self = self.http1_only();
configure_impersonate(ver, self)
configure_impersonate(impersonate, self)
}

/// Enable Encrypted Client Hello (Secure SNI)
Expand Down Expand Up @@ -410,7 +410,7 @@ impl ClientBuilder {
config.nodelay,
config.tls_info,
ImpersonateContext {
profile: config.profile,
impersonate: config.impersonate,
certs_verification: config.certs_verification,
enable_ech_grease: config.enable_ech_grease,
permute_extensions: config.permute_extensions,
Expand Down Expand Up @@ -743,7 +743,7 @@ impl ClientBuilder {
builder.http2_keep_alive_while_idle(true);
}

builder.http2_agent_profile(config.profile.into());
builder.http2_agent_profile(config.impersonate.profile().into());
builder.pool_idle_timeout(config.pool_idle_timeout);
builder.pool_max_idle_per_host(config.pool_max_idle_per_host);
connector.set_keepalive(config.tcp_keepalive);
Expand Down
20 changes: 10 additions & 10 deletions src/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,11 @@ impl Connector {
}
}
#[cfg(feature = "__boring")]
Inner::BoringTls { tls, .. } => {
Inner::BoringTls { http, tls, .. } => {
if dst.scheme() == Some(&Scheme::HTTPS) {
let host = dst.host().ok_or("no host in url")?.to_string();
let conn = socks::connect(proxy, dst, dns).await?;
let conf = tls.create_connect_configuration(&self.impersonate_context)?;
let conn = socks::connect(proxy, dst.clone(), dns).await?;
let conf = tls.create_connector_configuration(&self.impersonate_context, http.clone(), &dst, &host).await?;
let io = tokio_boring::connect(conf, &host, conn).await?;
return Ok(Conn {
inner: self.verbose.wrap(BoringTlsConn { inner: io }),
Expand Down Expand Up @@ -420,7 +420,7 @@ impl Connector {
http.set_nodelay(true);
}

let mut http = tls.create_https_connector(&self.impersonate_context, http)?;
let mut http = tls.create_https_connector(&self.impersonate_context, http).await?;
let io = http.call(dst).await?;

if let hyper_boring::MaybeHttpsStream::Https(stream) = io {
Expand Down Expand Up @@ -531,21 +531,21 @@ impl Connector {
let port = dst.port().map(|p| p.as_u16()).unwrap_or(443);

let http = http.clone();
let mut http = tls.create_https_connector(&self.impersonate_context, http)?;
// Create a new HTTPS connector with the proxy settings
let mut https = tls.create_https_connector(&self.impersonate_context, http.clone()).await?;

let conn = http.call(proxy_dst).await?;
// Connect to the proxy
let conn = https.call(proxy_dst).await?;
log::trace!("tunneling HTTPS over proxy");
let tunneled = tunnel(
conn,
host.ok_or("no host in url")?.to_string(),
port,
self.user_agent.clone(),
auth,
)
.await?;

let conf = tls.create_connect_configuration(&self.impersonate_context)?;
).await?;

let conf = https.configure_and_setup(&dst, host.ok_or("no host in url")?)?;
let io = tokio_boring::connect(conf, host.ok_or("no host in url")?, tunneled)
.await?;
return Ok(Conn {
Expand Down
74 changes: 54 additions & 20 deletions src/impersonate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,86 @@ pub mod profile;
mod safari;

use crate::connect::HttpConnector;
use boring::ssl::{ConnectConfiguration, SslConnectorBuilder};
use boring::{
error::ErrorStack,
ssl::{ConnectConfiguration, SslConnectorBuilder},
};
use http::HeaderMap;
use hyper_boring::HttpsConnector;
use profile::ClientProfile;
pub use profile::Impersonate;
use std::sync::Arc;
use tokio::sync::OnceCell;

/// A wrapper around a `SslConnectorBuilder` that allows for additional settings.
#[derive(Clone)]
pub struct BoringTlsConnector(Arc<dyn Fn() -> SslConnectorBuilder + Send + Sync>);
pub struct BoringTlsConnector {
/// The inner `SslConnectorBuilder` function.
inner: Arc<dyn Fn() -> SslConnectorBuilder + Send + Sync>,
/// The cached `HttpsConnector`.
connector: Arc<OnceCell<HttpsConnector<HttpConnector>>>,
}

impl BoringTlsConnector {
/// Create a new `BoringTlsConnector` with the given function.
pub fn new(inner: Arc<dyn Fn() -> SslConnectorBuilder + Send + Sync>) -> BoringTlsConnector {
Self(inner)
Self {
inner,
connector: Arc::new(OnceCell::new()),
}
}

/// Create a new `HttpsConnector` with the settings from the `ImpersonateContext`.
pub(crate) fn create_https_connector(
pub(crate) async fn create_https_connector(
&self,
context: &ImpersonateContext,
http: HttpConnector,
) -> Result<hyper_boring::HttpsConnector<HttpConnector>, boring::error::ErrorStack> {
let mut builder = self.0();
) -> Result<HttpsConnector<HttpConnector>, ErrorStack> {
let mut builder = (self.inner)();
alpn_and_cert_settings(context, &mut builder);

let mut http = hyper_boring::HttpsConnector::with_connector(http, builder)?;
let context = context.clone();
http.set_callback(move |conf, _| {
add_application_settings(conf, &context);
Ok(())
});
let http = match context.impersonate {
Impersonate::Chrome117
| Impersonate::Chrome120
| Impersonate::Chrome123
| Impersonate::Chrome124
| Impersonate::Chrome126 => self
.connector
.get_or_try_init(|| async { Self::create_connector(context, http, builder) })
.await?
.clone(),
_ => Self::create_connector(context, http, builder)?,
};

Ok(http)
}

/// Create a new `SslConnector` with the settings from the `ImpersonateContext`.
pub(crate) fn create_connect_configuration(
#[cfg(feature = "socks")]
pub(crate) async fn create_connector_configuration(
&self,
context: &ImpersonateContext,
) -> Result<ConnectConfiguration, boring::ssl::Error> {
let mut builder = self.0();
alpn_and_cert_settings(context, &mut builder);

let mut conf = builder.build().configure()?;
http: HttpConnector,
uri: &uri::Uri,
host: &str,
) -> Result<ConnectConfiguration, ErrorStack> {
let connector = self.create_https_connector(context, http).await?;
let mut conf = connector.configure_and_setup(uri, host)?;
add_application_settings(&mut conf, context);
Ok(conf)
}

/// Create a new `HttpsConnector` with the settings from the `ImpersonateContext`.
fn create_connector(
context: &ImpersonateContext,
http: HttpConnector,
builder: SslConnectorBuilder,
) -> Result<HttpsConnector<HttpConnector>, ErrorStack> {
let mut http = HttpsConnector::with_connector(http, builder)?;
let context = context.clone();
http.set_callback(move |conf, _| Ok(add_application_settings(conf, &context)));
Ok(http)
}
}

/// Configure the ALPN and certificate settings for the given `SslConnectorBuilder`.
Expand All @@ -70,7 +103,7 @@ fn alpn_and_cert_settings(context: &ImpersonateContext, builder: &mut SslConnect
/// Add application settings to the given `ConnectConfiguration`.
fn add_application_settings(conf: &mut ConnectConfiguration, ctx: &ImpersonateContext) {
use foreign_types::ForeignTypeRef;
match ctx.profile {
match ctx.impersonate.profile() {
ClientProfile::Chrome | ClientProfile::Edge => {
// Enable random TLS extensions
if ctx.permute_extensions {
Expand Down Expand Up @@ -124,9 +157,10 @@ pub(crate) struct Http2Data {
/// Context for impersonating a client.
#[derive(Clone)]
pub(crate) struct ImpersonateContext {
pub profile: ClientProfile,
pub impersonate: Impersonate,
pub enable_ech_grease: bool,
pub permute_extensions: bool,
pub certs_verification: bool,
pub h2: bool,
}

6 changes: 6 additions & 0 deletions src/impersonate/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ pub enum Impersonate {
Edge122,
}

impl Default for Impersonate {
fn default() -> Self {
Impersonate::Chrome126
}
}

/// Impersonate version from string
impl FromStr for Impersonate {
type Err = &'static str;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@
//! [Proxy]: ./struct.Proxy.html
//! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section

#[cfg(all(feature = "http3", not(reqwest_unstable)))]
#[cfg(all(feature = "http3"))]
compile_error!(
"\
The `http3` feature is unstable, and requires the \
Expand Down
Loading