Skip to content
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ windows-registry = { version = "0.5.2", optional = true }
system-configuration = { version = "0.6.1", optional = true }

## interface binding
[target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "macos", target_os = "tvos", target_os = "watchos", target = "illumos", target = "solaris"))'.dependencies]
[target.'cfg(unix)'.dependencies]
libc = "0.2.173"

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ pub mod redirect;
pub mod tls;

pub use http::{Method, StatusCode, Uri, Version};
#[cfg(unix)]
use libc as _;

#[cfg(feature = "multipart")]
pub use self::client::multipart;
Expand Down
25 changes: 18 additions & 7 deletions src/tls/keylog/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
//! TLS Key Log Management
//!
//! This module provides utilities for managing TLS key logging, allowing session keys to be
//! written to a file for debugging or analysis (e.g., with Wireshark).
//!
//! The [`KeyLogPolicy`] enum lets you control key log behavior, either by respecting the
//! `SSLKEYLOGFILE` environment variable or by specifying a custom file path. Handles are cached
//! globally to avoid duplicate file access.
//!
//! Use [`KeyLogPolicy::open_handle`] to obtain a [`KeyLogHandle`] for writing keys.

mod handle;

use std::{
Expand All @@ -8,14 +19,11 @@ use std::{
sync::OnceLock,
};

pub use handle::KeyLogHandle;
use handle::KeyLogHandle;

use crate::sync::RwLock;

static GLOBAL_KEYLOG_FILE_MAPPING: OnceLock<RwLock<HashMap<PathBuf, KeyLogHandle>>> =
OnceLock::new();

/// Specifies the intent for a (TLS) keylogger to be used in a client or server configuration.
/// Specifies the intent for a (TLS) keylogger.
#[derive(Debug, Clone)]
pub enum KeyLogPolicy {
/// Uses the default behavior, respecting the `SSLKEYLOGFILE` environment variable.
Expand All @@ -34,7 +42,10 @@ pub enum KeyLogPolicy {

impl KeyLogPolicy {
/// Creates a new key log file handle based on the policy.
pub fn open_handle(self) -> Result<KeyLogHandle> {
pub(crate) fn open_handle(self) -> Result<KeyLogHandle> {
static GLOBAL_KEYLOG_FILE_MAPPING: OnceLock<RwLock<HashMap<PathBuf, KeyLogHandle>>> =
OnceLock::new();

let path = match self {
KeyLogPolicy::Environment => std::env::var("SSLKEYLOGFILE")
.map(PathBuf::from)
Expand Down Expand Up @@ -65,7 +76,7 @@ impl KeyLogPolicy {
}
}

pub fn normalize_path<'a, P>(path: P) -> PathBuf
fn normalize_path<'a, P>(path: P) -> PathBuf
where
P: Into<Cow<'a, Path>>,
{
Expand Down
115 changes: 113 additions & 2 deletions src/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
mod conn;
mod keylog;
mod options;
mod types;
mod x509;

pub use boring2::ssl::{CertificateCompressionAlgorithm, ExtensionType};
Expand All @@ -18,7 +17,6 @@ pub(crate) use self::conn::{
pub use self::{
keylog::KeyLogPolicy,
options::{TlsOptions, TlsOptionsBuilder},
types::{AlpnProtocol, AlpsProtocol, TlsVersion},
x509::{CertStore, CertStoreBuilder, Certificate, Identity},
};

Expand All @@ -35,3 +33,116 @@ impl TlsInfo {
self.peer_certificate.as_deref()
}
}

use boring2::ssl;
use bytes::{BufMut, Bytes, BytesMut};

/// A TLS protocol version.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TlsVersion(pub(super) ssl::SslVersion);

impl TlsVersion {
/// Version 1.0 of the TLS protocol.
pub const TLS_1_0: TlsVersion = TlsVersion(ssl::SslVersion::TLS1);

/// Version 1.1 of the TLS protocol.
pub const TLS_1_1: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_1);

/// Version 1.2 of the TLS protocol.
pub const TLS_1_2: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_2);

/// Version 1.3 of the TLS protocol.
pub const TLS_1_3: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_3);
}

/// A TLS ALPN protocol.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AlpnProtocol(&'static [u8]);

impl AlpnProtocol {
/// Prefer HTTP/1.1
pub const HTTP1: AlpnProtocol = AlpnProtocol(b"http/1.1");

/// Prefer HTTP/2
pub const HTTP2: AlpnProtocol = AlpnProtocol(b"h2");

/// Prefer HTTP/3
pub const HTTP3: AlpnProtocol = AlpnProtocol(b"h3");

#[inline]
pub(crate) fn encode(self) -> Bytes {
Self::encode_sequence(std::iter::once(&self))
}

#[inline]
pub(crate) fn encode_sequence<'a, I>(items: I) -> Bytes
where
I: IntoIterator<Item = &'a AlpnProtocol>,
{
let mut buf = BytesMut::new();
for item in items {
buf.put_u8(item.0.len() as u8);
buf.extend_from_slice(item.0);
}
buf.freeze()
}
}

/// Application-layer protocol settings for HTTP/1.1 and HTTP/2.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct AlpsProtocol(&'static [u8]);

impl AlpsProtocol {
/// Prefer HTTP/1.1
pub const HTTP1: AlpsProtocol = AlpsProtocol(b"http/1.1");

/// Prefer HTTP/2
pub const HTTP2: AlpsProtocol = AlpsProtocol(b"h2");

/// Prefer HTTP/3
pub const HTTP3: AlpsProtocol = AlpsProtocol(b"h3");

#[inline]
pub(crate) const fn value(self) -> &'static [u8] {
self.0
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn alpn_protocol_encode() {
let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP1, AlpnProtocol::HTTP2]);
assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h2"));

let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP3]);
assert_eq!(alpn, Bytes::from_static(b"\x02h3"));

let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP1, AlpnProtocol::HTTP3]);
assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h3"));

let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP2, AlpnProtocol::HTTP3]);
assert_eq!(alpn, Bytes::from_static(b"\x02h2\x02h3"));

let alpn = AlpnProtocol::encode_sequence(&[
AlpnProtocol::HTTP1,
AlpnProtocol::HTTP2,
AlpnProtocol::HTTP3,
]);
assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h2\x02h3"));
}

#[test]
fn alpn_protocol_encode_single() {
let alpn = AlpnProtocol::HTTP1.encode();
assert_eq!(alpn, b"\x08http/1.1".as_ref());

let alpn = AlpnProtocol::HTTP2.encode();
assert_eq!(alpn, b"\x02h2".as_ref());

let alpn = AlpnProtocol::HTTP3.encode();
assert_eq!(alpn, b"\x02h3".as_ref());
}
}
112 changes: 0 additions & 112 deletions src/tls/types.rs

This file was deleted.

Loading