Skip to main content

native_ossl/
ssl.rs

1//! TLS — `SSL_CTX`, `SSL`, and `SSL_SESSION` wrappers.
2//!
3//! # Types
4//!
5//! | Type           | Owned / Shared | Description                               |
6//! |----------------|---------------|-------------------------------------------|
7//! | [`SslCtx`]     | Shared (Clone) | TLS context — configuration, certs, keys  |
8//! | [`Ssl`]        | Exclusive      | Per-connection TLS object                 |
9//! | [`SslSession`] | Shared (Clone) | Resumable TLS session handle              |
10//!
11//! # Protocol version
12//!
13//! `SSL_CTX_set_min_proto_version` / `SSL_CTX_set_max_proto_version` are C macros
14//! that expand to `SSL_CTX_ctrl(ctx, 123 / 124, version, NULL)`.  This module
15//! calls `SSL_CTX_ctrl` directly since bindgen cannot expose C macros as functions.
16//!
17//! # SNI hostname
18//!
19//! `SSL_set_tlsext_host_name` is a C macro expanding to
20//! `SSL_ctrl(s, 55, 0, name)`.  Use [`Ssl::set_hostname`] to set the SNI extension.
21//!
22//! # BIO ownership
23//!
24//! `SSL_set_bio` transfers ownership of the supplied `BIO*` pointers to the `SSL`
25//! object.  [`Ssl::set_bio_duplex`] accepts a single [`crate::bio::Bio`] for the
26//! common case where the same BIO serves as both read and write channel (e.g. the
27//! output of `BIO_new_bio_pair`).
28
29use crate::bio::Bio;
30use crate::error::ErrorStack;
31use crate::pkey::{HasPrivate, Pkey};
32use crate::x509::X509;
33use native_ossl_sys as sys;
34use std::ffi::CStr;
35use std::mem::ManuallyDrop;
36
37// ── Role sealed trait and markers ─────────────────────────────────────────────
38
39mod private {
40    pub trait Sealed {}
41}
42
43/// Marker for a TLS server context builder.
44pub struct Server;
45/// Marker for a TLS client context builder.
46pub struct Client;
47
48/// Role marker for [`SslCtxBuilder`]. Sealed — cannot be implemented externally.
49pub trait Role: private::Sealed {}
50
51impl private::Sealed for Server {}
52impl private::Sealed for Client {}
53impl Role for Server {}
54impl Role for Client {}
55
56// ── TLS version ───────────────────────────────────────────────────────────────
57
58/// TLS protocol version selector.
59///
60/// Passed to [`SslCtx::set_min_proto_version`] and [`SslCtx::set_max_proto_version`].
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum TlsVersion {
63    /// TLS 1.2 (`0x0303`)
64    Tls12 = 0x0303,
65    /// TLS 1.3 (`0x0304`)
66    Tls13 = 0x0304,
67}
68
69// ── Verify mode ───────────────────────────────────────────────────────────────
70
71/// Certificate verification mode flags.
72///
73/// Combine with bitwise OR using [`SslVerifyMode::or`].
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct SslVerifyMode(i32);
76
77impl SslVerifyMode {
78    /// Do not verify the peer certificate (`SSL_VERIFY_NONE`).
79    pub const NONE: Self = SslVerifyMode(0x00);
80    /// Verify the peer certificate (`SSL_VERIFY_PEER`).
81    pub const PEER: Self = SslVerifyMode(0x01);
82    /// Fail if the peer does not present a certificate (`SSL_VERIFY_FAIL_IF_NO_PEER_CERT`).
83    pub const FAIL_IF_NO_PEER_CERT: Self = SslVerifyMode(0x02);
84
85    /// Combine two mode values with bitwise OR.
86    #[must_use]
87    pub fn or(self, other: Self) -> Self {
88        SslVerifyMode(self.0 | other.0)
89    }
90}
91
92// ── Hostname verification flags ───────────────────────────────────────────────
93
94/// Flags controlling hostname verification behaviour during certificate checks.
95///
96/// Passed to [`SslCtxBuilder::<Client>::verify_hostname_flags`].
97/// Combine flags using [`HostnameFlags::or`].
98///
99/// These map directly to the `X509_CHECK_FLAG_*` constants defined in
100/// `<openssl/x509_vfy.h>`.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
102pub struct HostnameFlags(u32);
103
104impl HostnameFlags {
105    /// No special hostname flags (default OpenSSL behaviour).
106    pub const NONE: Self = Self(0);
107
108    /// Disallow partial wildcards like `*.example.*` — only single trailing-label
109    /// wildcards such as `*.example.com` are permitted.
110    ///
111    /// Maps to `X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS` (`0x4`).
112    pub const NO_PARTIAL_WILDCARDS: Self = Self(0x4);
113
114    /// Allow wildcards to match multiple labels (e.g. `*.foo.example.com`).
115    ///
116    /// Maps to `X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS` (`0x8`).
117    pub const MULTI_LABEL_WILDCARDS: Self = Self(0x8);
118
119    /// Combine two flag values with bitwise OR.
120    #[must_use]
121    pub fn or(self, other: Self) -> Self {
122        Self(self.0 | other.0)
123    }
124}
125
126// ── SSL I/O error ─────────────────────────────────────────────────────────────
127
128/// Error returned by non-blocking SSL I/O operations.
129///
130/// `WantRead` / `WantWrite` indicate that the operation should be retried
131/// after the underlying BIO becomes ready.  For in-memory BIO pairs, retrying
132/// immediately after driving the peer is sufficient.
133#[derive(Debug)]
134pub enum SslIoError {
135    /// Retry after data arrives on the read BIO (`SSL_ERROR_WANT_READ`).
136    WantRead,
137    /// Retry after the write BIO drains (`SSL_ERROR_WANT_WRITE`).
138    WantWrite,
139    /// The peer closed the connection cleanly (`SSL_ERROR_ZERO_RETURN`).
140    ZeroReturn,
141    /// Underlying I/O error; see [`ErrorStack`] for details (`SSL_ERROR_SYSCALL`).
142    Syscall(ErrorStack),
143    /// OpenSSL protocol error (`SSL_ERROR_SSL`).
144    Ssl(ErrorStack),
145    /// Unexpected error code returned by `SSL_get_error`.
146    Other(i32),
147}
148
149impl std::fmt::Display for SslIoError {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        match self {
152            Self::WantRead => write!(f, "SSL want read"),
153            Self::WantWrite => write!(f, "SSL want write"),
154            Self::ZeroReturn => write!(f, "SSL zero return (peer closed)"),
155            Self::Syscall(e) => write!(f, "SSL syscall error: {e}"),
156            Self::Ssl(e) => write!(f, "SSL error: {e}"),
157            Self::Other(code) => write!(f, "SSL error code {code}"),
158        }
159    }
160}
161
162impl std::error::Error for SslIoError {}
163
164// ── ShutdownResult ────────────────────────────────────────────────────────────
165
166/// Result of [`Ssl::shutdown`].
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum ShutdownResult {
169    /// First shutdown stage sent; call `shutdown` again to complete.
170    Sent,
171    /// Bidirectional shutdown complete.
172    Complete,
173}
174
175// ── SslSession ────────────────────────────────────────────────────────────────
176
177/// A TLS session handle (`SSL_SESSION*`).
178///
179/// Cloneable via `SSL_SESSION_up_ref`.  Pass to [`Ssl::set_session`] to enable
180/// session resumption.
181pub struct SslSession {
182    ptr: *mut sys::SSL_SESSION,
183}
184
185// SAFETY: `SSL_SESSION` is reference-counted.
186unsafe impl Send for SslSession {}
187unsafe impl Sync for SslSession {}
188
189impl Clone for SslSession {
190    fn clone(&self) -> Self {
191        unsafe { sys::SSL_SESSION_up_ref(self.ptr) };
192        SslSession { ptr: self.ptr }
193    }
194}
195
196impl Drop for SslSession {
197    fn drop(&mut self) {
198        unsafe { sys::SSL_SESSION_free(self.ptr) };
199    }
200}
201
202// ── BorrowedSslSession ────────────────────────────────────────────────────────
203
204/// A borrowed view of the current TLS session (`SSL_SESSION*`), tied to the
205/// lifetime of the owning [`Ssl`] connection.
206///
207/// Obtained from [`Ssl::session`].  The pointer is **not** freed on drop —
208/// OpenSSL owns it for as long as the `SSL` object is alive.  Use
209/// [`Ssl::get1_session`] if you need an independent owned copy.
210///
211/// All read-only methods of [`SslSession`] are available via `Deref`.
212pub struct BorrowedSslSession<'ssl> {
213    inner: ManuallyDrop<SslSession>,
214    _marker: std::marker::PhantomData<&'ssl Ssl>,
215}
216
217// SAFETY: `SSL_SESSION*` is reference-counted and safe to move between threads.
218// `BorrowedSslSession` does not free the pointer, so moving it is at most as
219// permissive as moving the reference that backs it.
220unsafe impl Send for BorrowedSslSession<'_> {}
221
222impl std::ops::Deref for BorrowedSslSession<'_> {
223    type Target = SslSession;
224
225    fn deref(&self) -> &SslSession {
226        &self.inner
227    }
228}
229
230// ── SslCtx ────────────────────────────────────────────────────────────────────
231
232/// TLS context (`SSL_CTX*`).
233///
234/// Holds shared configuration such as certificates, private keys, and verify
235/// settings.  Multiple [`Ssl`] objects can be created from the same `SslCtx`.
236///
237/// Cloneable via `SSL_CTX_up_ref`; wrapping in `Arc<SslCtx>` is safe.
238pub struct SslCtx {
239    ptr: *mut sys::SSL_CTX,
240}
241
242// SAFETY: `SSL_CTX` is reference-counted.
243unsafe impl Send for SslCtx {}
244unsafe impl Sync for SslCtx {}
245
246impl Clone for SslCtx {
247    fn clone(&self) -> Self {
248        unsafe { sys::SSL_CTX_up_ref(self.ptr) };
249        SslCtx { ptr: self.ptr }
250    }
251}
252
253impl Drop for SslCtx {
254    fn drop(&mut self) {
255        unsafe { sys::SSL_CTX_free(self.ptr) };
256    }
257}
258
259impl SslCtx {
260    /// Create a new TLS context accepting any role (client or server).
261    ///
262    /// Uses the generic `TLS_method()`.  Call [`SslCtx::new_client`] or
263    /// [`SslCtx::new_server`] for role-specific method selection.
264    ///
265    /// # Errors
266    ///
267    /// Returns `Err` if `SSL_CTX_new` fails.
268    pub fn new() -> Result<Self, ErrorStack> {
269        let method = unsafe { sys::TLS_method() };
270        let ptr = unsafe { sys::SSL_CTX_new(method) };
271        if ptr.is_null() {
272            return Err(ErrorStack::drain());
273        }
274        Ok(SslCtx { ptr })
275    }
276
277    /// Create a new TLS context optimised for client connections (`TLS_client_method`).
278    ///
279    /// # Errors
280    pub fn new_client() -> Result<Self, ErrorStack> {
281        let method = unsafe { sys::TLS_client_method() };
282        let ptr = unsafe { sys::SSL_CTX_new(method) };
283        if ptr.is_null() {
284            return Err(ErrorStack::drain());
285        }
286        Ok(SslCtx { ptr })
287    }
288
289    /// Create a new TLS context optimised for server connections (`TLS_server_method`).
290    ///
291    /// # Errors
292    pub fn new_server() -> Result<Self, ErrorStack> {
293        let method = unsafe { sys::TLS_server_method() };
294        let ptr = unsafe { sys::SSL_CTX_new(method) };
295        if ptr.is_null() {
296            return Err(ErrorStack::drain());
297        }
298        Ok(SslCtx { ptr })
299    }
300
301    /// Set the minimum acceptable TLS protocol version.
302    ///
303    /// Internally calls `SSL_CTX_ctrl(ctx, 123 /*SSL_CTRL_SET_MIN_PROTO_VERSION*/, version, NULL)`.
304    ///
305    /// # Errors
306    pub fn set_min_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
307        let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 123, ver as i64, std::ptr::null_mut()) };
308        if rc != 1 {
309            return Err(ErrorStack::drain());
310        }
311        Ok(())
312    }
313
314    /// Set the maximum acceptable TLS protocol version.
315    ///
316    /// Internally calls `SSL_CTX_ctrl(ctx, 124 /*SSL_CTRL_SET_MAX_PROTO_VERSION*/, version, NULL)`.
317    ///
318    /// # Errors
319    pub fn set_max_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
320        let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 124, ver as i64, std::ptr::null_mut()) };
321        if rc != 1 {
322            return Err(ErrorStack::drain());
323        }
324        Ok(())
325    }
326
327    /// Set the peer certificate verification mode.
328    ///
329    /// Wraps `SSL_CTX_set_verify(ctx, mode, NULL)`.
330    pub fn set_verify(&self, mode: SslVerifyMode) {
331        unsafe { sys::SSL_CTX_set_verify(self.ptr, mode.0, None) };
332    }
333
334    /// Set the allowed cipher list (TLS 1.2 and below).
335    ///
336    /// `list` uses OpenSSL cipher string syntax (e.g. `c"HIGH:!aNULL:!MD5"`).
337    ///
338    /// # Errors
339    pub fn set_cipher_list(&self, list: &CStr) -> Result<(), ErrorStack> {
340        let rc = unsafe { sys::SSL_CTX_set_cipher_list(self.ptr, list.as_ptr()) };
341        if rc != 1 {
342            return Err(ErrorStack::drain());
343        }
344        Ok(())
345    }
346
347    /// Set the allowed TLS 1.3 ciphersuites.
348    ///
349    /// `list` uses OpenSSL ciphersuite syntax (e.g. `c"TLS_AES_256_GCM_SHA384"`).
350    ///
351    /// # Errors
352    pub fn set_ciphersuites(&self, list: &CStr) -> Result<(), ErrorStack> {
353        let rc = unsafe { sys::SSL_CTX_set_ciphersuites(self.ptr, list.as_ptr()) };
354        if rc != 1 {
355            return Err(ErrorStack::drain());
356        }
357        Ok(())
358    }
359
360    /// Load a certificate into the context.
361    ///
362    /// For a server, this is the certificate that will be presented to clients.
363    ///
364    /// # Errors
365    pub fn use_certificate(&self, cert: &X509) -> Result<(), ErrorStack> {
366        let rc = unsafe { sys::SSL_CTX_use_certificate(self.ptr, cert.as_ptr()) };
367        if rc != 1 {
368            return Err(ErrorStack::drain());
369        }
370        Ok(())
371    }
372
373    /// Load a private key into the context.
374    ///
375    /// The key must correspond to the certificate loaded via [`SslCtx::use_certificate`].
376    ///
377    /// # Errors
378    pub fn use_private_key<T: HasPrivate>(&self, key: &Pkey<T>) -> Result<(), ErrorStack> {
379        let rc = unsafe { sys::SSL_CTX_use_PrivateKey(self.ptr, key.as_ptr()) };
380        if rc != 1 {
381            return Err(ErrorStack::drain());
382        }
383        Ok(())
384    }
385
386    /// Verify that the loaded certificate and private key are consistent.
387    ///
388    /// # Errors
389    ///
390    /// Returns `Err` if the key/certificate pair is invalid or not loaded.
391    pub fn check_private_key(&self) -> Result<(), ErrorStack> {
392        let rc = unsafe { sys::SSL_CTX_check_private_key(self.ptr) };
393        if rc != 1 {
394            return Err(ErrorStack::drain());
395        }
396        Ok(())
397    }
398
399    /// Load the system default CA certificate store for verification.
400    ///
401    /// # Errors
402    pub fn set_default_verify_paths(&self) -> Result<(), ErrorStack> {
403        let rc = unsafe { sys::SSL_CTX_set_default_verify_paths(self.ptr) };
404        if rc != 1 {
405            return Err(ErrorStack::drain());
406        }
407        Ok(())
408    }
409
410    /// Disable TLS session caching on this context.
411    pub fn disable_session_cache(&self) {
412        // SSL_CTRL_SET_SESS_CACHE_MODE = 44, SSL_SESS_CACHE_OFF = 0
413        unsafe { sys::SSL_CTX_ctrl(self.ptr, 44, 0, std::ptr::null_mut()) };
414    }
415
416    /// Create a new [`Ssl`] connection object from this context.
417    ///
418    /// # Errors
419    ///
420    /// Returns `Err` if `SSL_new` fails.
421    pub fn new_ssl(&self) -> Result<Ssl, ErrorStack> {
422        let ptr = unsafe { sys::SSL_new(self.ptr) };
423        if ptr.is_null() {
424            return Err(ErrorStack::drain());
425        }
426        Ok(Ssl { ptr })
427    }
428}
429
430// ── SslCtxBuilder ─────────────────────────────────────────────────────────────
431
432/// Fluent typestate builder for [`SslCtx`].
433///
434/// Use [`SslCtxBuilder::<Server>::new()`] or [`SslCtxBuilder::<Client>::new()`] to
435/// start the builder chain, configure via consuming methods, then call
436/// [`build()`][SslCtxBuilder::build] to obtain a fully-configured [`SslCtx`].
437///
438/// # Role separation
439///
440/// The type parameter `R` (either [`Server`] or [`Client`]) ensures that
441/// role-specific methods — e.g. [`certificate`][SslCtxBuilder::certificate] for
442/// servers, [`verify_peer`][SslCtxBuilder::verify_peer] for clients — are only
443/// available on the appropriate builder type.
444///
445/// # Example — server
446///
447/// ```rust,ignore
448/// let ctx = SslCtxBuilder::<Server>::new()?
449///     .certificate(&cert)?
450///     .private_key(&key)?
451///     .check_private_key()?
452///     .build()?;
453/// ```
454///
455/// # Example — client
456///
457/// ```rust,ignore
458/// let ctx = SslCtxBuilder::<Client>::new()?
459///     .default_ca_paths()?
460///     .verify_peer()
461///     .verify_hostname("example.com")?
462///     .alpn_protos_list(&["h2", "http/1.1"])?
463///     .build()?;
464/// ```
465// NOTE: SslCtxBuilder is Send but !Sync.
466// Send: we hold exclusive ownership of the SSL_CTX* during construction; moving
467// it to another thread is safe because no other thread can reference the pointer.
468// !Sync: sharing a &SslCtxBuilder across threads would allow concurrent mutation
469// through the raw pointer, which is not safe.  No Arc is used here intentionally.
470#[must_use = "call .build() to obtain an SslCtx"]
471pub struct SslCtxBuilder<R: Role> {
472    ptr: *mut sys::SSL_CTX,
473    _role: std::marker::PhantomData<R>,
474}
475
476// SAFETY: SslCtxBuilder holds an SSL_CTX* with exclusive ownership during
477// construction.  No other reference to the pointer exists until build() is
478// called.  Moving the builder between threads is therefore safe.
479unsafe impl<R: Role> Send for SslCtxBuilder<R> {}
480
481impl<R: Role> Drop for SslCtxBuilder<R> {
482    fn drop(&mut self) {
483        // SAFETY: ptr is non-null by constructor invariant; SSL_CTX_free is safe
484        // to call exactly once on a valid pointer.  build() uses mem::forget to
485        // prevent this drop from running when ownership is transferred to SslCtx.
486        unsafe { sys::SSL_CTX_free(self.ptr) };
487    }
488}
489
490// ── Constructors ──────────────────────────────────────────────────────────────
491
492impl SslCtxBuilder<Server> {
493    /// Create a new server TLS context builder using `TLS_server_method()`.
494    ///
495    /// # Errors
496    ///
497    /// Returns `Err` if `SSL_CTX_new` fails (e.g. OOM).
498    pub fn new() -> Result<Self, ErrorStack> {
499        // SAFETY: TLS_server_method() returns a static method pointer; SSL_CTX_new
500        // returns null on OOM — checked immediately below.
501        let ptr = unsafe { sys::SSL_CTX_new(sys::TLS_server_method()) };
502        if ptr.is_null() {
503            return Err(ErrorStack::drain());
504        }
505        Ok(Self {
506            ptr,
507            _role: std::marker::PhantomData,
508        })
509    }
510}
511
512impl SslCtxBuilder<Client> {
513    /// Create a new client TLS context builder using `TLS_client_method()`.
514    ///
515    /// # Errors
516    ///
517    /// Returns `Err` if `SSL_CTX_new` fails (e.g. OOM).
518    pub fn new() -> Result<Self, ErrorStack> {
519        // SAFETY: TLS_client_method() returns a static method pointer; SSL_CTX_new
520        // returns null on OOM — checked immediately below.
521        let ptr = unsafe { sys::SSL_CTX_new(sys::TLS_client_method()) };
522        if ptr.is_null() {
523            return Err(ErrorStack::drain());
524        }
525        Ok(Self {
526            ptr,
527            _role: std::marker::PhantomData,
528        })
529    }
530}
531
532// ── Shared methods (both Server and Client) ───────────────────────────────────
533
534impl<R: Role> SslCtxBuilder<R> {
535    /// Set the minimum acceptable TLS protocol version.
536    ///
537    /// Uses `SSL_CTX_ctrl` with `SSL_CTRL_SET_MIN_PROTO_VERSION` (123).
538    ///
539    /// # Errors
540    pub fn min_proto_version(self, ver: TlsVersion) -> Result<Self, ErrorStack> {
541        // SAFETY: self.ptr is valid; SSL_CTX_ctrl with code 123 sets the minimum
542        // protocol version and returns 1 on success.
543        let rc = unsafe {
544            sys::SSL_CTX_ctrl(
545                self.ptr,
546                sys::SSL_CTRL_SET_MIN_PROTO_VERSION.cast_signed(),
547                ver as i64,
548                std::ptr::null_mut(),
549            )
550        };
551        if rc != 1 {
552            return Err(ErrorStack::drain());
553        }
554        Ok(self)
555    }
556
557    /// Set the maximum acceptable TLS protocol version.
558    ///
559    /// Uses `SSL_CTX_ctrl` with `SSL_CTRL_SET_MAX_PROTO_VERSION` (124).
560    ///
561    /// # Errors
562    pub fn max_proto_version(self, ver: TlsVersion) -> Result<Self, ErrorStack> {
563        // SAFETY: self.ptr is valid; SSL_CTX_ctrl with code 124 sets the maximum
564        // protocol version and returns 1 on success.
565        let rc = unsafe {
566            sys::SSL_CTX_ctrl(
567                self.ptr,
568                sys::SSL_CTRL_SET_MAX_PROTO_VERSION.cast_signed(),
569                ver as i64,
570                std::ptr::null_mut(),
571            )
572        };
573        if rc != 1 {
574            return Err(ErrorStack::drain());
575        }
576        Ok(self)
577    }
578
579    /// Set the allowed cipher list for TLS 1.2 and below.
580    ///
581    /// `list` uses OpenSSL cipher string syntax (e.g. `c"HIGH:!aNULL:!MD5"`).
582    ///
583    /// # Errors
584    pub fn cipher_list(self, list: &CStr) -> Result<Self, ErrorStack> {
585        // SAFETY: self.ptr is valid; list is a valid NUL-terminated C string.
586        crate::ossl_call!(sys::SSL_CTX_set_cipher_list(self.ptr, list.as_ptr()))?;
587        Ok(self)
588    }
589
590    /// Set the allowed TLS 1.3 ciphersuites.
591    ///
592    /// `list` uses OpenSSL ciphersuite syntax (e.g. `c"TLS_AES_256_GCM_SHA384"`).
593    ///
594    /// # Errors
595    pub fn ciphersuites(self, list: &CStr) -> Result<Self, ErrorStack> {
596        // SAFETY: self.ptr is valid; list is a valid NUL-terminated C string.
597        crate::ossl_call!(sys::SSL_CTX_set_ciphersuites(self.ptr, list.as_ptr()))?;
598        Ok(self)
599    }
600
601    /// Configure the session cache mode.
602    ///
603    /// Pass `0` (`SSL_SESS_CACHE_OFF`) to disable caching.
604    pub fn session_cache_mode(self, mode: i64) -> Self {
605        // SAFETY: self.ptr is valid; SSL_CTX_ctrl with code 44 sets session
606        // cache mode — no error return convention, always succeeds.
607        unsafe {
608            sys::SSL_CTX_ctrl(
609                self.ptr,
610                sys::SSL_CTRL_SET_SESS_CACHE_MODE.cast_signed(),
611                mode,
612                std::ptr::null_mut(),
613            )
614        };
615        self
616    }
617
618    /// Set the ALPN protocol list in OpenSSL wire format (length-prefixed bytes).
619    ///
620    /// Example: `b"\x02h2\x08http/1.1"` advertises H2 then HTTP/1.1.
621    ///
622    /// For a string-slice convenience, use [`alpn_protos_list`][Self::alpn_protos_list].
623    ///
624    /// # Errors
625    ///
626    /// Returns `Err` on failure.
627    ///
628    /// # Note
629    ///
630    /// `SSL_CTX_set_alpn_protos` uses an **inverted** return convention:
631    /// `0` means success, non-zero means failure (opposite to most OpenSSL functions).
632    pub fn alpn_protocols(self, protocols: &[u8]) -> Result<Self, ErrorStack> {
633        // SAFETY: self.ptr is valid; SSL_CTX_set_alpn_protos copies the buffer
634        // immediately — the slice does not need to outlive this call.
635        let len = u32::try_from(protocols.len()).map_err(|_| ErrorStack::drain())?;
636        let ret = unsafe { sys::SSL_CTX_set_alpn_protos(self.ptr, protocols.as_ptr(), len) };
637        // NOTE: inverted convention — 0 is success for SSL_CTX_set_alpn_protos.
638        if ret != 0 {
639            return Err(ErrorStack::drain());
640        }
641        Ok(self)
642    }
643
644    /// Set the ALPN protocol list from a slice of protocol name strings.
645    ///
646    /// Encodes `protos` into the wire-format length-prefixed byte string and
647    /// calls `SSL_CTX_set_alpn_protos`.  Protocols longer than 255 bytes are
648    /// rejected with `Err`.
649    ///
650    /// # Errors
651    ///
652    /// Returns `Err` if any protocol name exceeds 255 bytes or if the underlying
653    /// OpenSSL call fails.
654    pub fn alpn_protos_list(self, protos: &[&str]) -> Result<Self, ErrorStack> {
655        let mut wire = Vec::new();
656        for proto in protos {
657            let len = u8::try_from(proto.len()).map_err(|_| ErrorStack::drain())?;
658            wire.push(len);
659            wire.extend_from_slice(proto.as_bytes());
660        }
661        self.alpn_protocols(&wire)
662    }
663
664    /// Consume the builder and return a configured [`SslCtx`].
665    ///
666    /// # Errors
667    ///
668    /// Currently infallible, but returns `Result` for API consistency with
669    /// potential future validation steps.
670    pub fn build(self) -> Result<SslCtx, ErrorStack> {
671        let ptr = self.ptr;
672        // Prevent Drop from calling SSL_CTX_free — SslCtx takes ownership.
673        std::mem::forget(self);
674        Ok(SslCtx { ptr })
675    }
676}
677
678// ── Server-only methods ───────────────────────────────────────────────────────
679
680impl SslCtxBuilder<Server> {
681    /// Load a certificate that will be presented to clients during the handshake.
682    ///
683    /// Wraps `SSL_CTX_use_certificate`.
684    ///
685    /// # Errors
686    pub fn certificate(self, cert: &X509) -> Result<Self, ErrorStack> {
687        // SAFETY: self.ptr is valid; cert.as_ptr() is a valid X509* for the
688        // duration of this call; SSL_CTX_use_certificate increments the X509
689        // refcount internally — no ownership transfer.
690        let rc = unsafe { sys::SSL_CTX_use_certificate(self.ptr, cert.as_ptr()) };
691        if rc != 1 {
692            return Err(ErrorStack::drain());
693        }
694        Ok(self)
695    }
696
697    /// Append an intermediate CA certificate to the certificate chain.
698    ///
699    /// Uses `SSL_CTX_ctrl` with `SSL_CTRL_EXTRA_CHAIN_CERT` (14).  OpenSSL
700    /// takes ownership of the `X509*`, so this method calls `X509_up_ref` first
701    /// to keep `cert` alive after the call.
702    ///
703    /// # Errors
704    pub fn add_chain_cert(self, cert: &X509) -> Result<Self, ErrorStack> {
705        // SAFETY: X509_up_ref increments the refcount so that SSL_CTX_ctrl can
706        // take ownership of its own reference.  cert.as_ptr() remains valid for
707        // the caller because we only transferred the up-ref'd reference.
708        unsafe { sys::X509_up_ref(cert.as_ptr()) };
709        let rc = unsafe {
710            sys::SSL_CTX_ctrl(
711                self.ptr,
712                sys::SSL_CTRL_EXTRA_CHAIN_CERT.cast_signed(),
713                0,
714                cert.as_ptr().cast::<std::ffi::c_void>(),
715            )
716        };
717        if rc == 0 {
718            // SSL_CTX_ctrl failed; the up-ref'd X509 was not consumed — free it.
719            // SAFETY: we up-ref'd above; freeing once is correct.
720            unsafe { sys::X509_free(cert.as_ptr()) };
721            return Err(ErrorStack::drain());
722        }
723        Ok(self)
724    }
725
726    /// Load the private key corresponding to the certificate.
727    ///
728    /// Wraps `SSL_CTX_use_PrivateKey`.
729    ///
730    /// # Errors
731    pub fn private_key(self, key: &Pkey<crate::pkey::Private>) -> Result<Self, ErrorStack> {
732        // SAFETY: self.ptr is valid; key.as_ptr() is a valid EVP_PKEY* for the
733        // duration of this call; SSL_CTX_use_PrivateKey increments the key
734        // refcount — no ownership transfer.
735        let rc = unsafe { sys::SSL_CTX_use_PrivateKey(self.ptr, key.as_ptr()) };
736        if rc != 1 {
737            return Err(ErrorStack::drain());
738        }
739        Ok(self)
740    }
741
742    /// Verify that the loaded certificate and private key are consistent.
743    ///
744    /// # Errors
745    ///
746    /// Returns `Err` if the key/certificate pair is invalid or not yet loaded.
747    pub fn check_private_key(self) -> Result<Self, ErrorStack> {
748        // SAFETY: self.ptr is valid and has had certificate and key loaded.
749        crate::ossl_call!(sys::SSL_CTX_check_private_key(self.ptr))?;
750        Ok(self)
751    }
752
753    /// Request (or require) a client certificate during the TLS handshake.
754    ///
755    /// When `require` is `true`, combines `SSL_VERIFY_PEER` with
756    /// `SSL_VERIFY_FAIL_IF_NO_PEER_CERT` — the handshake fails if the client
757    /// does not present a certificate.  When `false`, only `SSL_VERIFY_PEER` is
758    /// set — the client certificate is requested but not mandatory.
759    pub fn verify_client(self, require: bool) -> Self {
760        let mode = if require {
761            (sys::SSL_VERIFY_PEER | sys::SSL_VERIFY_FAIL_IF_NO_PEER_CERT).cast_signed()
762        } else {
763            sys::SSL_VERIFY_PEER.cast_signed()
764        };
765        // SAFETY: self.ptr is valid; SSL_CTX_set_verify does not fail.
766        unsafe { sys::SSL_CTX_set_verify(self.ptr, mode, None) };
767        self
768    }
769}
770
771// ── Client-only methods ───────────────────────────────────────────────────────
772
773impl SslCtxBuilder<Client> {
774    /// Load the system default CA certificate bundle for peer verification.
775    ///
776    /// Wraps `SSL_CTX_set_default_verify_paths`.
777    ///
778    /// # Errors
779    pub fn default_ca_paths(self) -> Result<Self, ErrorStack> {
780        // SAFETY: self.ptr is valid; SSL_CTX_set_default_verify_paths uses the
781        // compiled-in default paths and returns 1 on success.
782        crate::ossl_call!(sys::SSL_CTX_set_default_verify_paths(self.ptr))?;
783        Ok(self)
784    }
785
786    /// Load CA certificates from a PEM file at `path`.
787    ///
788    /// Only the CA certificate file is loaded (not a directory).
789    ///
790    /// # Errors
791    ///
792    /// Returns `Err` if the path contains non-UTF-8 bytes, contains a NUL byte,
793    /// or if `SSL_CTX_load_verify_locations` fails.
794    pub fn ca_bundle_file(self, path: &std::path::Path) -> Result<Self, ErrorStack> {
795        let path_cstr = std::ffi::CString::new(path.to_str().ok_or_else(ErrorStack::drain)?)
796            .map_err(|_| ErrorStack::drain())?;
797        // SAFETY: self.ptr and path_cstr are valid; second arg (cadir) is null
798        // because we supply a file only.  SSL_CTX_load_verify_locations returns
799        // 1 on success.
800        let ret = unsafe {
801            sys::SSL_CTX_load_verify_locations(self.ptr, path_cstr.as_ptr(), std::ptr::null())
802        };
803        if ret != 1 {
804            return Err(ErrorStack::drain());
805        }
806        Ok(self)
807    }
808
809    /// Add a single CA certificate to the context's trust store.
810    ///
811    /// Calls `SSL_CTX_get_cert_store` then `X509_STORE_add_cert`.
812    /// Does not transfer ownership — `cert` remains valid after this call.
813    ///
814    /// # Errors
815    pub fn ca_cert(self, cert: &X509) -> Result<Self, ErrorStack> {
816        // SAFETY: SSL_CTX_get_cert_store returns a borrowed pointer owned by the
817        // SSL_CTX — do not free it.  X509_STORE_add_cert does not take ownership
818        // of cert; the X509 refcount is unchanged.
819        let store = unsafe { sys::SSL_CTX_get_cert_store(self.ptr) };
820        if store.is_null() {
821            return Err(ErrorStack::drain());
822        }
823        let ret = unsafe { sys::X509_STORE_add_cert(store, cert.as_ptr()) };
824        if ret != 1 {
825            return Err(ErrorStack::drain());
826        }
827        Ok(self)
828    }
829
830    /// Enable verification of the server's certificate.
831    ///
832    /// Sets `SSL_VERIFY_PEER`.  Call after loading CA certificates so that
833    /// OpenSSL has a trust anchor.
834    pub fn verify_peer(self) -> Self {
835        // SAFETY: self.ptr is valid; SSL_CTX_set_verify does not fail.
836        unsafe { sys::SSL_CTX_set_verify(self.ptr, sys::SSL_VERIFY_PEER.cast_signed(), None) };
837        self
838    }
839
840    /// Set the expected server hostname for automatic certificate hostname
841    /// verification on all connections derived from this context.
842    ///
843    /// Uses `SSL_CTX_get0_param` to get the context-level `X509_VERIFY_PARAM`
844    /// and then `X509_VERIFY_PARAM_set1_host` to set the hostname.  All [`Ssl`]
845    /// connections created from this [`SslCtx`] inherit the hostname check.
846    ///
847    /// For per-connection overrides (e.g. one `SslCtx` shared across connections
848    /// to different servers), use [`Ssl::set_verify_hostname`] instead.
849    ///
850    /// # Errors
851    ///
852    /// Returns `Err` if `hostname` contains a NUL byte or if
853    /// `X509_VERIFY_PARAM_set1_host` fails.
854    pub fn verify_hostname(self, hostname: &str) -> Result<Self, ErrorStack> {
855        let cstr = std::ffi::CString::new(hostname).map_err(|_| ErrorStack::drain())?;
856        // SAFETY: SSL_CTX_get0_param returns a borrowed pointer — do not free.
857        // X509_VERIFY_PARAM_set1_host copies the hostname string internally.
858        let param = unsafe { sys::SSL_CTX_get0_param(self.ptr) };
859        if param.is_null() {
860            return Err(ErrorStack::drain());
861        }
862        let ret = unsafe { sys::X509_VERIFY_PARAM_set1_host(param, cstr.as_ptr(), 0) };
863        if ret != 1 {
864            return Err(ErrorStack::drain());
865        }
866        Ok(self)
867    }
868
869    /// Set hostname verification flags on this context.
870    ///
871    /// Applied to every connection derived from this context.  Typically called
872    /// after [`verify_hostname`][Self::verify_hostname] to refine how wildcard
873    /// certificates are matched.
874    ///
875    /// Uses `SSL_CTX_get0_param` to obtain the context-level
876    /// `X509_VERIFY_PARAM`, then calls `X509_VERIFY_PARAM_set_hostflags`.
877    ///
878    /// # Errors
879    ///
880    /// Returns `Err` if `SSL_CTX_get0_param` returns a null pointer (should
881    /// not happen for a freshly created context).
882    pub fn verify_hostname_flags(self, flags: HostnameFlags) -> Result<Self, ErrorStack> {
883        // SAFETY: SSL_CTX_get0_param returns a borrowed pointer owned by the
884        // SSL_CTX — do not free it.  X509_VERIFY_PARAM_set_hostflags is void
885        // and always succeeds.
886        let param = unsafe { sys::SSL_CTX_get0_param(self.ptr) };
887        if param.is_null() {
888            return Err(ErrorStack::drain());
889        }
890        unsafe { sys::X509_VERIFY_PARAM_set_hostflags(param, flags.0) };
891        Ok(self)
892    }
893}
894
895// ── Ssl ───────────────────────────────────────────────────────────────────────
896
897/// Per-connection TLS object (`SSL*`).
898///
899/// Has exclusive ownership over its state; no `Clone`.  BIOs passed to
900/// [`Ssl::set_bio_duplex`] or [`Ssl::set_bio`] are owned by the `Ssl` thereafter.
901pub struct Ssl {
902    ptr: *mut sys::SSL,
903}
904
905// SAFETY: `SSL*` is not thread-safe for concurrent access, but `Ssl` has
906// exclusive ownership, so `Send` is safe for moving between threads.
907unsafe impl Send for Ssl {}
908
909impl Drop for Ssl {
910    fn drop(&mut self) {
911        unsafe { sys::SSL_free(self.ptr) };
912    }
913}
914
915impl Ssl {
916    /// Set a single duplex BIO for both reading and writing.
917    ///
918    /// Transfers ownership of `bio` to the `SSL` object; do not use `bio`
919    /// afterwards.  Suitable for `BIO_new_bio_pair` endpoints.
920    ///
921    /// When `rbio == wbio` (same pointer), OpenSSL only increments the
922    /// reference count once, so the single reference in `bio` is correct.
923    pub fn set_bio_duplex(&mut self, bio: Bio) {
924        let ptr = bio.as_ptr();
925        // Prevent our Drop from calling BIO_free — SSL now owns this BIO.
926        std::mem::forget(bio);
927        // Passing the same pointer for rbio and wbio: OpenSSL increments
928        // the ref count only once when rbio == wbio.
929        unsafe { sys::SSL_set_bio(self.ptr, ptr, ptr) };
930    }
931
932    /// Set separate read and write BIOs.
933    ///
934    /// Transfers ownership of both `rbio` and `wbio` to the `SSL` object.
935    pub fn set_bio(&mut self, rbio: Bio, wbio: Bio) {
936        let rbio_ptr = rbio.as_ptr();
937        let wbio_ptr = wbio.as_ptr();
938        // Prevent our Drop from calling BIO_free on each — SSL now owns them.
939        std::mem::forget(rbio);
940        std::mem::forget(wbio);
941        unsafe { sys::SSL_set_bio(self.ptr, rbio_ptr, wbio_ptr) };
942    }
943
944    /// Set the SNI hostname extension sent during the TLS handshake.
945    ///
946    /// Call before [`Self::connect`] on client connections to enable SNI.
947    /// `hostname` must be a NUL-terminated ASCII/UTF-8 hostname.
948    ///
949    /// `SSL_set_tlsext_host_name` is a C macro expanding to
950    /// `SSL_ctrl(s, 55 /*SSL_CTRL_SET_TLSEXT_HOSTNAME*/, 0 /*TLSEXT_NAMETYPE_host_name*/, name)`.
951    ///
952    /// # Errors
953    ///
954    /// Returns `Err` if the control call fails.
955    pub fn set_hostname(&mut self, hostname: &CStr) -> Result<(), ErrorStack> {
956        let rc = unsafe {
957            sys::SSL_ctrl(
958                self.ptr,
959                55, // SSL_CTRL_SET_TLSEXT_HOSTNAME
960                0,  // TLSEXT_NAMETYPE_host_name
961                // OpenSSL declares parg as *mut c_void but uses it as const here.
962                hostname.as_ptr() as *mut std::os::raw::c_void,
963            )
964        };
965        if rc != 1 {
966            return Err(ErrorStack::drain());
967        }
968        Ok(())
969    }
970
971    /// Set this SSL object to operate in client (connect) mode.
972    ///
973    /// Required before calling [`Self::do_handshake`] if neither [`Self::connect`] nor
974    /// [`Self::accept`] will be used.
975    pub fn set_connect_state(&mut self) {
976        unsafe { sys::SSL_set_connect_state(self.ptr) };
977    }
978
979    /// Set this SSL object to operate in server (accept) mode.
980    pub fn set_accept_state(&mut self) {
981        unsafe { sys::SSL_set_accept_state(self.ptr) };
982    }
983
984    /// Initiate a client-side TLS handshake (`SSL_connect`).
985    ///
986    /// Returns `Ok(())` on success, [`SslIoError::WantRead`] / [`SslIoError::WantWrite`]
987    /// when the operation must be retried after more data is available.
988    ///
989    /// # Errors
990    pub fn connect(&mut self) -> Result<(), SslIoError> {
991        let rc = unsafe { sys::SSL_connect(self.ptr) };
992        if rc == 1 {
993            return Ok(());
994        }
995        Err(self.ssl_io_error(rc))
996    }
997
998    /// Accept an incoming TLS connection (`SSL_accept`).
999    ///
1000    /// Returns `Ok(())` on success, [`SslIoError::WantRead`] / [`SslIoError::WantWrite`]
1001    /// on non-blocking retry.
1002    ///
1003    /// # Errors
1004    pub fn accept(&mut self) -> Result<(), SslIoError> {
1005        let rc = unsafe { sys::SSL_accept(self.ptr) };
1006        if rc == 1 {
1007            return Ok(());
1008        }
1009        Err(self.ssl_io_error(rc))
1010    }
1011
1012    /// Drive the TLS handshake in either role (`SSL_do_handshake`).
1013    ///
1014    /// The role must have been set via [`Self::set_connect_state`] or [`Self::set_accept_state`]
1015    /// (or implicitly by [`Self::connect`] / [`Self::accept`]).
1016    ///
1017    /// # Errors
1018    pub fn do_handshake(&mut self) -> Result<(), SslIoError> {
1019        let rc = unsafe { sys::SSL_do_handshake(self.ptr) };
1020        if rc == 1 {
1021            return Ok(());
1022        }
1023        Err(self.ssl_io_error(rc))
1024    }
1025
1026    /// Read decrypted application data (`SSL_read_ex`).
1027    ///
1028    /// Returns the number of bytes written into `buf` on success.
1029    ///
1030    /// # Errors
1031    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, SslIoError> {
1032        let mut readbytes: usize = 0;
1033        let rc = unsafe {
1034            sys::SSL_read_ex(
1035                self.ptr,
1036                buf.as_mut_ptr().cast(),
1037                buf.len(),
1038                std::ptr::addr_of_mut!(readbytes),
1039            )
1040        };
1041        if rc == 1 {
1042            return Ok(readbytes);
1043        }
1044        Err(self.ssl_io_error(rc))
1045    }
1046
1047    /// Write application data (`SSL_write_ex`).
1048    ///
1049    /// Returns the number of bytes consumed from `buf` on success.
1050    ///
1051    /// # Errors
1052    pub fn write(&mut self, buf: &[u8]) -> Result<usize, SslIoError> {
1053        let mut written: usize = 0;
1054        let rc = unsafe {
1055            sys::SSL_write_ex(
1056                self.ptr,
1057                buf.as_ptr().cast(),
1058                buf.len(),
1059                std::ptr::addr_of_mut!(written),
1060            )
1061        };
1062        if rc == 1 {
1063            return Ok(written);
1064        }
1065        Err(self.ssl_io_error(rc))
1066    }
1067
1068    /// Send a TLS close-notify alert (`SSL_shutdown`).
1069    ///
1070    /// Returns [`ShutdownResult::Sent`] after the first shutdown stage and
1071    /// [`ShutdownResult::Complete`] after a bidirectional shutdown.  Call
1072    /// twice on a non-blocking connection to complete the exchange.
1073    ///
1074    /// # Errors
1075    ///
1076    /// Returns `Err` on a fatal error during shutdown.
1077    pub fn shutdown(&mut self) -> Result<ShutdownResult, ErrorStack> {
1078        let rc = unsafe { sys::SSL_shutdown(self.ptr) };
1079        match rc {
1080            1 => Ok(ShutdownResult::Complete),
1081            0 => Ok(ShutdownResult::Sent),
1082            _ => Err(ErrorStack::drain()),
1083        }
1084    }
1085
1086    /// Return the peer's full certificate chain (leaf + intermediates), or `None`.
1087    ///
1088    /// Each certificate in the returned `Vec` has its reference count independently
1089    /// incremented via `X509_up_ref`, so the certificates outlive `self`.
1090    ///
1091    /// Returns `None` when:
1092    /// - the handshake has not yet completed, or
1093    /// - the peer did not present a certificate (e.g. a server without client-auth
1094    ///   configured will see `None` for the client chain).
1095    ///
1096    /// An empty `Vec` is returned if the stack exists but contains no elements
1097    /// (unusual in practice).
1098    ///
1099    /// Note: this calls `SSL_get_peer_cert_chain`, which on the **server** side
1100    /// does not include the leaf certificate — only intermediates.  On the
1101    /// **client** side the full chain including the server leaf is returned.
1102    /// Use [`Self::peer_certificate`] to obtain the leaf cert in all cases.
1103    // i < n where n came from OPENSSL_sk_num (i32), so the i as i32 cast is safe.
1104    #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
1105    #[must_use]
1106    pub fn peer_cert_chain(&self) -> Option<Vec<X509>> {
1107        // SAFETY:
1108        // - self.ptr is non-null (constructor invariant maintained by SslCtx::new_ssl)
1109        // - SSL_get_peer_cert_chain returns a borrowed stack valid for the lifetime
1110        //   of self; we immediately up_ref each cert so they are independently owned
1111        // - &self guarantees the SSL object is not mutated concurrently
1112        let stack = unsafe { sys::SSL_get_peer_cert_chain(self.ptr) };
1113        if stack.is_null() {
1114            return None;
1115        }
1116        // SAFETY: stack_st_X509 and OPENSSL_STACK (stack_st) are layout-compatible;
1117        // OpenSSL's typed stack macros cast between them internally.
1118        let n = unsafe { sys::OPENSSL_sk_num(stack.cast::<sys::OPENSSL_STACK>()) };
1119        let n = usize::try_from(n).unwrap_or(0);
1120        let mut certs = Vec::with_capacity(n);
1121        for i in 0..n {
1122            // SAFETY: i is in bounds (0..n); OPENSSL_sk_value returns a void*
1123            // which we cast to X509*. X509_up_ref is called before wrapping in X509
1124            // to ensure each element has its own reference.
1125            let raw =
1126                unsafe { sys::OPENSSL_sk_value(stack.cast::<sys::OPENSSL_STACK>(), i as i32) };
1127            if raw.is_null() {
1128                continue;
1129            }
1130            let cert_ptr = raw.cast::<sys::X509>();
1131            // SAFETY: cert_ptr is a valid X509* obtained from the stack; X509_up_ref
1132            // increments its reference count so the X509 wrapper owns an independent ref.
1133            unsafe { sys::X509_up_ref(cert_ptr) };
1134            certs.push(unsafe { X509::from_ptr(cert_ptr) });
1135        }
1136        Some(certs)
1137    }
1138
1139    /// Return the peer's certificate, or `None` if unavailable.
1140    ///
1141    /// The returned certificate has its reference count incremented, so it
1142    /// outlives `self`.
1143    #[must_use]
1144    pub fn peer_certificate(&self) -> Option<X509> {
1145        let ptr = unsafe { sys::SSL_get0_peer_certificate(self.ptr) };
1146        if ptr.is_null() {
1147            return None;
1148        }
1149        // get0 → no ownership; increment ref count to produce an owned X509.
1150        unsafe { sys::X509_up_ref(ptr) };
1151        Some(unsafe { X509::from_ptr(ptr) })
1152    }
1153
1154    /// Get an owned reference to the current session (`SSL_get1_session`).
1155    ///
1156    /// Returns `None` if no session is established.  The session can be passed
1157    /// to [`Self::set_session`] on a new `Ssl` for resumption.
1158    #[must_use]
1159    pub fn get1_session(&self) -> Option<SslSession> {
1160        let ptr = unsafe { sys::SSL_get1_session(self.ptr) };
1161        if ptr.is_null() {
1162            None
1163        } else {
1164            Some(SslSession { ptr })
1165        }
1166    }
1167
1168    /// Borrow the current session without incrementing the reference count
1169    /// (`SSL_get_session`).
1170    ///
1171    /// Returns `None` if no session is associated with this connection (e.g.
1172    /// the handshake has not yet completed or session caching is disabled).
1173    ///
1174    /// The returned [`BorrowedSslSession`] is valid for the lifetime of `self`
1175    /// and must **not** be retained beyond it.  Use [`Self::get1_session`]
1176    /// if you need a session that outlives the connection.
1177    ///
1178    /// # Lifetimes
1179    ///
1180    /// The borrow is tied to `'_` (the lifetime of `self`): the `SSL_SESSION*`
1181    /// is owned by the `SSL` object and is invalidated when the `Ssl` is dropped
1182    /// or a new session is negotiated.
1183    #[must_use]
1184    pub fn session(&self) -> Option<BorrowedSslSession<'_>> {
1185        // SAFETY: `self.ptr` is a valid, non-null `SSL*` for the lifetime of
1186        // `self`. `SSL_get_session` returns either NULL or a pointer whose
1187        // lifetime is bounded by the `SSL` object — no refcount bump occurs, so
1188        // we wrap it in `ManuallyDrop` to prevent `SSL_SESSION_free` from being
1189        // called when the `BorrowedSslSession` is dropped.
1190        let ptr = unsafe { sys::SSL_get_session(self.ptr) };
1191        if ptr.is_null() {
1192            None
1193        } else {
1194            Some(BorrowedSslSession {
1195                inner: ManuallyDrop::new(SslSession { ptr }),
1196                _marker: std::marker::PhantomData,
1197            })
1198        }
1199    }
1200
1201    /// Set a previously obtained session for resumption (`SSL_set_session`).
1202    ///
1203    /// Call before the handshake.
1204    ///
1205    /// # Errors
1206    pub fn set_session(&mut self, session: &SslSession) -> Result<(), ErrorStack> {
1207        let rc = unsafe { sys::SSL_set_session(self.ptr, session.ptr) };
1208        if rc != 1 {
1209            return Err(ErrorStack::drain());
1210        }
1211        Ok(())
1212    }
1213
1214    /// Override the expected server hostname for this connection's certificate
1215    /// verification.
1216    ///
1217    /// Use this when one [`SslCtx`] is shared across connections to different
1218    /// servers and per-connection hostname checking is needed.  Wraps
1219    /// `SSL_set1_host`.
1220    ///
1221    /// # Errors
1222    ///
1223    /// Returns `Err` if `hostname` contains a NUL byte or if `SSL_set1_host`
1224    /// fails.
1225    pub fn set_verify_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> {
1226        let cstr = std::ffi::CString::new(hostname).map_err(|_| ErrorStack::drain())?;
1227        // SAFETY: self.ptr is non-null (constructor invariant); cstr is valid for
1228        // the duration of this call; &mut self guarantees exclusive access.
1229        let ret = unsafe { sys::SSL_set1_host(self.ptr, cstr.as_ptr()) };
1230        if ret != 1 {
1231            return Err(ErrorStack::drain());
1232        }
1233        Ok(())
1234    }
1235
1236    /// Translate a non-positive SSL I/O return code into an [`SslIoError`].
1237    fn ssl_io_error(&self, ret: i32) -> SslIoError {
1238        let err = unsafe { sys::SSL_get_error(self.ptr, ret) };
1239        match err {
1240            2 => SslIoError::WantRead,
1241            3 => SslIoError::WantWrite,
1242            5 => SslIoError::Syscall(ErrorStack::drain()),
1243            6 => SslIoError::ZeroReturn,
1244            _ => {
1245                let stack = ErrorStack::drain();
1246                if stack.errors().next().is_none() {
1247                    SslIoError::Other(err)
1248                } else {
1249                    SslIoError::Ssl(stack)
1250                }
1251            }
1252        }
1253    }
1254}
1255
1256// ── Tests ─────────────────────────────────────────────────────────────────────
1257
1258#[cfg(test)]
1259mod tests {
1260    use super::*;
1261    use crate::pkey::{KeygenCtx, Pkey, Private, Public};
1262    use crate::x509::{X509Builder, X509NameOwned};
1263
1264    // ── Helpers ───────────────────────────────────────────────────────────────
1265
1266    /// Generate a fresh Ed25519 key pair.
1267    fn make_ed25519_key() -> (Pkey<Private>, Pkey<Public>) {
1268        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1269        let priv_key = kgen.generate().unwrap();
1270        let pub_key = Pkey::<Public>::from(priv_key.clone());
1271        (priv_key, pub_key)
1272    }
1273
1274    /// Build a self-signed Ed25519 certificate valid for 1 day.
1275    fn make_self_signed_cert(priv_key: &Pkey<Private>, pub_key: &Pkey<Public>) -> X509 {
1276        let mut name = X509NameOwned::new().unwrap();
1277        name.add_entry_by_txt(c"CN", b"test").unwrap();
1278
1279        X509Builder::new()
1280            .unwrap()
1281            .set_version(2)
1282            .unwrap()
1283            .set_serial_number(1)
1284            .unwrap()
1285            .set_not_before_offset(0)
1286            .unwrap()
1287            .set_not_after_offset(86400)
1288            .unwrap()
1289            .set_subject_name(&name)
1290            .unwrap()
1291            .set_issuer_name(&name)
1292            .unwrap()
1293            .set_public_key(pub_key)
1294            .unwrap()
1295            .sign(priv_key, None)
1296            .unwrap()
1297            .build()
1298    }
1299
1300    /// Drive a pair of SSL objects to a completed handshake using an in-memory
1301    /// BIO pair.  Returns `(client, server)` after the handshake.
1302    ///
1303    /// `BIO_new_bio_pair` creates two linked BIOs:
1304    ///   - data written to bio1 → readable from bio2  (client→server)
1305    ///   - data written to bio2 → readable from bio1  (server→client)
1306    fn do_handshake_pair(mut client: Ssl, mut server: Ssl) -> Result<(Ssl, Ssl), SslIoError> {
1307        let mut client_bio: *mut sys::BIO = std::ptr::null_mut();
1308        let mut server_bio: *mut sys::BIO = std::ptr::null_mut();
1309        let rc = unsafe {
1310            sys::BIO_new_bio_pair(
1311                std::ptr::addr_of_mut!(client_bio),
1312                0,
1313                std::ptr::addr_of_mut!(server_bio),
1314                0,
1315            )
1316        };
1317        assert_eq!(rc, 1, "BIO_new_bio_pair failed");
1318
1319        // Transfer ownership to the SSL objects.  Since rbio == wbio for each,
1320        // OpenSSL only takes one ref, matching the single ref from BIO_new_bio_pair.
1321        let client_bio_obj = unsafe { Bio::from_ptr_owned(client_bio) };
1322        let server_bio_obj = unsafe { Bio::from_ptr_owned(server_bio) };
1323        client.set_bio_duplex(client_bio_obj);
1324        server.set_bio_duplex(server_bio_obj);
1325
1326        // Alternate between client and server until both complete (up to 20 steps).
1327        let mut client_done = false;
1328        let mut server_done = false;
1329
1330        for _ in 0..20 {
1331            if !client_done {
1332                match client.connect() {
1333                    Ok(()) => client_done = true,
1334                    Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
1335                    Err(e) => return Err(e),
1336                }
1337            }
1338            if !server_done {
1339                match server.accept() {
1340                    Ok(()) => server_done = true,
1341                    Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
1342                    Err(e) => return Err(e),
1343                }
1344            }
1345            if client_done && server_done {
1346                return Ok((client, server));
1347            }
1348        }
1349        Err(SslIoError::Other(-1))
1350    }
1351
1352    // ── SslCtx construction ───────────────────────────────────────────────────
1353
1354    #[test]
1355    fn ctx_new_variants() {
1356        SslCtx::new().unwrap();
1357        SslCtx::new_client().unwrap();
1358        SslCtx::new_server().unwrap();
1359    }
1360
1361    #[test]
1362    fn ctx_clone() {
1363        let ctx = SslCtx::new().unwrap();
1364        let _clone = ctx.clone();
1365    }
1366
1367    #[test]
1368    fn ctx_proto_version() {
1369        let ctx = SslCtx::new().unwrap();
1370        ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1371        ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
1372    }
1373
1374    #[test]
1375    fn ctx_verify_mode() {
1376        let ctx = SslCtx::new().unwrap();
1377        ctx.set_verify(SslVerifyMode::NONE);
1378        ctx.set_verify(SslVerifyMode::PEER);
1379        ctx.set_verify(SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT));
1380    }
1381
1382    #[test]
1383    fn ctx_cipher_list() {
1384        let ctx = SslCtx::new().unwrap();
1385        ctx.set_cipher_list(c"HIGH:!aNULL").unwrap();
1386    }
1387
1388    // ── Certificate / key loading ─────────────────────────────────────────────
1389
1390    #[test]
1391    fn ctx_load_cert_and_key() {
1392        let (priv_key, pub_key) = make_ed25519_key();
1393        let cert = make_self_signed_cert(&priv_key, &pub_key);
1394
1395        let ctx = SslCtx::new_server().unwrap();
1396        ctx.use_certificate(&cert).unwrap();
1397        ctx.use_private_key(&priv_key).unwrap();
1398        ctx.check_private_key().unwrap();
1399    }
1400
1401    // ── In-memory TLS handshake ───────────────────────────────────────────────
1402
1403    #[test]
1404    fn tls13_handshake_ed25519() {
1405        let (priv_key, pub_key) = make_ed25519_key();
1406        let cert = make_self_signed_cert(&priv_key, &pub_key);
1407
1408        // Server context: present our self-signed cert.
1409        let server_ctx = SslCtx::new_server().unwrap();
1410        server_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
1411        server_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
1412        server_ctx.use_certificate(&cert).unwrap();
1413        server_ctx.use_private_key(&priv_key).unwrap();
1414        server_ctx.check_private_key().unwrap();
1415        server_ctx.disable_session_cache();
1416
1417        // Client context: do not verify the server cert (self-signed test cert).
1418        let client_ctx = SslCtx::new_client().unwrap();
1419        client_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
1420        client_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
1421        client_ctx.set_verify(SslVerifyMode::NONE);
1422        client_ctx.disable_session_cache();
1423
1424        let client_ssl = client_ctx.new_ssl().unwrap();
1425        let server_ssl = server_ctx.new_ssl().unwrap();
1426
1427        let (mut client, mut server) =
1428            do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.3 handshake failed");
1429
1430        // Exchange a small message.
1431        let msg = b"hello TLS 1.3";
1432        let n = client.write(msg).unwrap();
1433        assert_eq!(n, msg.len());
1434
1435        let mut buf = [0u8; 64];
1436        let n = server.read(&mut buf).unwrap();
1437        assert_eq!(&buf[..n], msg);
1438
1439        // Server replies.
1440        let reply = b"world";
1441        server.write(reply).unwrap();
1442        let n = client.read(&mut buf).unwrap();
1443        assert_eq!(&buf[..n], reply);
1444    }
1445
1446    #[test]
1447    fn tls12_handshake() {
1448        let (priv_key, pub_key) = make_ed25519_key();
1449        let cert = make_self_signed_cert(&priv_key, &pub_key);
1450
1451        let server_ctx = SslCtx::new_server().unwrap();
1452        server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1453        server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1454        server_ctx.use_certificate(&cert).unwrap();
1455        server_ctx.use_private_key(&priv_key).unwrap();
1456        server_ctx.check_private_key().unwrap();
1457        server_ctx.disable_session_cache();
1458
1459        let client_ctx = SslCtx::new_client().unwrap();
1460        client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1461        client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1462        client_ctx.set_verify(SslVerifyMode::NONE);
1463        client_ctx.disable_session_cache();
1464
1465        let client_ssl = client_ctx.new_ssl().unwrap();
1466        let server_ssl = server_ctx.new_ssl().unwrap();
1467
1468        let (mut client, mut server) =
1469            do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.2 handshake failed");
1470
1471        // Verify data exchange works after handshake.
1472        client.write(b"tls12").unwrap();
1473        let mut buf = [0u8; 16];
1474        let n = server.read(&mut buf).unwrap();
1475        assert_eq!(&buf[..n], b"tls12");
1476    }
1477
1478    #[test]
1479    fn peer_certificate_after_handshake() {
1480        let (priv_key, pub_key) = make_ed25519_key();
1481        let cert = make_self_signed_cert(&priv_key, &pub_key);
1482
1483        // Re-encode cert to DER for comparison.
1484        let cert_der = cert.to_der().unwrap();
1485
1486        let server_ctx = SslCtx::new_server().unwrap();
1487        server_ctx.use_certificate(&cert).unwrap();
1488        server_ctx.use_private_key(&priv_key).unwrap();
1489        server_ctx.disable_session_cache();
1490
1491        // Client requests peer cert verification (peer cert = server cert).
1492        let client_ctx = SslCtx::new_client().unwrap();
1493        client_ctx.set_verify(SslVerifyMode::NONE);
1494        client_ctx.disable_session_cache();
1495
1496        let (client, _server) =
1497            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
1498                .unwrap();
1499
1500        // The client should have the server's certificate.
1501        let peer_cert = client.peer_certificate().expect("no peer certificate");
1502        let peer_der = peer_cert.to_der().unwrap();
1503        assert_eq!(peer_der, cert_der, "peer cert DER mismatch");
1504    }
1505
1506    #[test]
1507    fn shutdown_sequence() {
1508        let (priv_key, pub_key) = make_ed25519_key();
1509        let cert = make_self_signed_cert(&priv_key, &pub_key);
1510
1511        let server_ctx = SslCtx::new_server().unwrap();
1512        server_ctx.use_certificate(&cert).unwrap();
1513        server_ctx.use_private_key(&priv_key).unwrap();
1514        server_ctx.disable_session_cache();
1515
1516        let client_ctx = SslCtx::new_client().unwrap();
1517        client_ctx.set_verify(SslVerifyMode::NONE);
1518        client_ctx.disable_session_cache();
1519
1520        let (mut client, mut server) =
1521            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
1522                .unwrap();
1523
1524        // Client sends close_notify → Sent.
1525        let r = client.shutdown().unwrap();
1526        assert_eq!(r, ShutdownResult::Sent);
1527
1528        // Server receives close_notify; its first shutdown call returns Sent.
1529        let r = server.shutdown().unwrap();
1530        assert_eq!(r, ShutdownResult::Sent);
1531
1532        // Client receives server's close_notify → Complete.
1533        let r = client.shutdown().unwrap();
1534        assert_eq!(r, ShutdownResult::Complete);
1535    }
1536
1537    #[test]
1538    fn verify_mode_bits() {
1539        let both = SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT);
1540        assert_eq!(both.0, 0x03);
1541    }
1542
1543    // ── peer_cert_chain ───────────────────────────────────────────────────────
1544
1545    #[test]
1546    fn peer_cert_chain_none_before_handshake() {
1547        // A fresh SSL object with no completed handshake must return None.
1548        let ctx = SslCtx::new_client().unwrap();
1549        let ssl = ctx.new_ssl().unwrap();
1550        assert!(
1551            ssl.peer_cert_chain().is_none(),
1552            "expected no peer cert chain before handshake"
1553        );
1554    }
1555
1556    #[test]
1557    fn peer_cert_chain_after_handshake() {
1558        let (priv_key, pub_key) = make_ed25519_key();
1559        let cert = make_self_signed_cert(&priv_key, &pub_key);
1560
1561        // Re-encode the server cert to DER for comparison.
1562        let cert_der = cert.to_der().unwrap();
1563
1564        let server_ctx = SslCtx::new_server().unwrap();
1565        server_ctx.use_certificate(&cert).unwrap();
1566        server_ctx.use_private_key(&priv_key).unwrap();
1567        server_ctx.disable_session_cache();
1568
1569        // Client: do not verify (self-signed test cert).
1570        let client_ctx = SslCtx::new_client().unwrap();
1571        client_ctx.set_verify(SslVerifyMode::NONE);
1572        client_ctx.disable_session_cache();
1573
1574        let (client, _server) =
1575            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
1576                .expect("handshake failed");
1577
1578        // SSL_get_peer_cert_chain on the client side returns the server's chain.
1579        // For a self-signed cert there is exactly one entry: the leaf.
1580        let chain = client
1581            .peer_cert_chain()
1582            .expect("expected Some peer cert chain after handshake");
1583        assert!(
1584            !chain.is_empty(),
1585            "peer cert chain must contain at least one certificate"
1586        );
1587        // The first entry in the chain is the server's leaf certificate.
1588        let leaf_der = chain[0].to_der().unwrap();
1589        assert_eq!(
1590            leaf_der, cert_der,
1591            "first cert in chain must match server leaf cert"
1592        );
1593    }
1594
1595    // ── session() borrowed accessor ───────────────────────────────────────────
1596
1597    #[test]
1598    fn session_none_before_handshake() {
1599        // A fresh SSL object with no handshake should have no session.
1600        let ctx = SslCtx::new_client().unwrap();
1601        let ssl = ctx.new_ssl().unwrap();
1602        assert!(
1603            ssl.session().is_none(),
1604            "expected no session before handshake"
1605        );
1606    }
1607
1608    #[test]
1609    fn session_some_after_handshake_tls12() {
1610        let (priv_key, pub_key) = make_ed25519_key();
1611        let cert = make_self_signed_cert(&priv_key, &pub_key);
1612
1613        let server_ctx = SslCtx::new_server().unwrap();
1614        server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1615        server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1616        server_ctx.use_certificate(&cert).unwrap();
1617        server_ctx.use_private_key(&priv_key).unwrap();
1618        server_ctx.check_private_key().unwrap();
1619        // Session caching must be enabled for a session to be stored.
1620        // (Default for a new SslCtx is enabled, so we do NOT call disable_session_cache.)
1621
1622        let client_ctx = SslCtx::new_client().unwrap();
1623        client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1624        client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1625        client_ctx.set_verify(SslVerifyMode::NONE);
1626
1627        let (client, _server) =
1628            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
1629                .expect("TLS 1.2 handshake failed");
1630
1631        // TLS 1.2 always produces a session object; get0 variant must return Some.
1632        let borrowed = client
1633            .session()
1634            .expect("expected Some session after TLS 1.2 handshake");
1635
1636        // The borrowed session must be backed by the same pointer as get1_session.
1637        let owned = client
1638            .get1_session()
1639            .expect("get1_session must also return Some");
1640        assert_eq!(
1641            borrowed.ptr, owned.ptr,
1642            "borrowed and owned sessions should point to the same SSL_SESSION"
1643        );
1644    }
1645
1646    #[test]
1647    fn borrowed_session_does_not_free() {
1648        // Verify that dropping BorrowedSslSession does not cause a use-after-free:
1649        // after drop, get1_session must still succeed (refcount not decreased).
1650        let (priv_key, pub_key) = make_ed25519_key();
1651        let cert = make_self_signed_cert(&priv_key, &pub_key);
1652
1653        let server_ctx = SslCtx::new_server().unwrap();
1654        server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1655        server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1656        server_ctx.use_certificate(&cert).unwrap();
1657        server_ctx.use_private_key(&priv_key).unwrap();
1658        server_ctx.check_private_key().unwrap();
1659
1660        let client_ctx = SslCtx::new_client().unwrap();
1661        client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
1662        client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
1663        client_ctx.set_verify(SslVerifyMode::NONE);
1664
1665        let (client, _server) =
1666            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
1667                .expect("handshake failed");
1668
1669        {
1670            let _borrowed = client.session().expect("session must be Some");
1671            // _borrowed is dropped here; must not call SSL_SESSION_free.
1672        }
1673
1674        // If the borrowed session incorrectly freed the pointer, this would crash.
1675        let _owned = client
1676            .get1_session()
1677            .expect("session still accessible after borrowed drop");
1678    }
1679
1680    // ── SslCtxBuilder tests ───────────────────────────────────────────────────
1681
1682    #[test]
1683    fn ssl_ctx_builder_server_basic() {
1684        let ctx = SslCtxBuilder::<Server>::new().unwrap().build().unwrap();
1685        // Verify that the resulting SslCtx can create an SSL object.
1686        ctx.new_ssl().unwrap();
1687    }
1688
1689    #[test]
1690    fn ssl_ctx_builder_client_basic() {
1691        let ctx = SslCtxBuilder::<Client>::new().unwrap().build().unwrap();
1692        ctx.new_ssl().unwrap();
1693    }
1694
1695    #[test]
1696    fn ssl_ctx_builder_client_cipher_list() {
1697        let ctx = SslCtxBuilder::<Client>::new()
1698            .unwrap()
1699            .cipher_list(c"HIGH:!aNULL")
1700            .unwrap()
1701            .build()
1702            .unwrap();
1703        ctx.new_ssl().unwrap();
1704    }
1705
1706    #[test]
1707    fn ssl_ctx_builder_client_alpn_protos_list() {
1708        let ctx = SslCtxBuilder::<Client>::new()
1709            .unwrap()
1710            .alpn_protos_list(&["h2", "http/1.1"])
1711            .unwrap()
1712            .build()
1713            .unwrap();
1714        ctx.new_ssl().unwrap();
1715    }
1716
1717    #[test]
1718    fn ssl_ctx_builder_client_default_ca_paths() {
1719        let ctx = SslCtxBuilder::<Client>::new()
1720            .unwrap()
1721            .default_ca_paths()
1722            .unwrap()
1723            .verify_peer()
1724            .build()
1725            .unwrap();
1726        ctx.new_ssl().unwrap();
1727    }
1728
1729    #[test]
1730    fn ssl_ctx_builder_client_verify_hostname() {
1731        let ctx = SslCtxBuilder::<Client>::new()
1732            .unwrap()
1733            .verify_hostname("example.com")
1734            .unwrap()
1735            .build()
1736            .unwrap();
1737        ctx.new_ssl().unwrap();
1738    }
1739
1740    #[test]
1741    fn ssl_ctx_builder_server_with_cert_key() {
1742        let (priv_key, pub_key) = make_ed25519_key();
1743        let cert = make_self_signed_cert(&priv_key, &pub_key);
1744
1745        let ctx = SslCtxBuilder::<Server>::new()
1746            .unwrap()
1747            .certificate(&cert)
1748            .unwrap()
1749            .private_key(&priv_key)
1750            .unwrap()
1751            .check_private_key()
1752            .unwrap()
1753            .build()
1754            .unwrap();
1755
1756        // Verify the resulting context can participate in a handshake.
1757        let client_ctx = SslCtx::new_client().unwrap();
1758        client_ctx.set_verify(SslVerifyMode::NONE);
1759        client_ctx.disable_session_cache();
1760
1761        let server_ssl = ctx.new_ssl().unwrap();
1762        let client_ssl = client_ctx.new_ssl().unwrap();
1763        do_handshake_pair(client_ssl, server_ssl).expect("builder server handshake failed");
1764    }
1765
1766    #[test]
1767    fn ssl_set_verify_hostname_per_connection() {
1768        // Verify that Ssl::set_verify_hostname does not error on a valid hostname.
1769        let ctx = SslCtx::new_client().unwrap();
1770        let mut ssl = ctx.new_ssl().unwrap();
1771        ssl.set_verify_hostname("example.com").unwrap();
1772    }
1773
1774    #[test]
1775    fn ssl_ctx_builder_server_proto_versions() {
1776        let ctx = SslCtxBuilder::<Server>::new()
1777            .unwrap()
1778            .min_proto_version(TlsVersion::Tls12)
1779            .unwrap()
1780            .max_proto_version(TlsVersion::Tls13)
1781            .unwrap()
1782            .build()
1783            .unwrap();
1784        ctx.new_ssl().unwrap();
1785    }
1786
1787    #[test]
1788    fn ssl_ctx_builder_verify_hostname_flags_no_partial_wildcards() {
1789        // Verify that setting HostnameFlags::NO_PARTIAL_WILDCARDS does not error.
1790        let ctx = SslCtxBuilder::<Client>::new()
1791            .unwrap()
1792            .verify_hostname_flags(HostnameFlags::NO_PARTIAL_WILDCARDS)
1793            .unwrap()
1794            .build()
1795            .unwrap();
1796        ctx.new_ssl().unwrap();
1797    }
1798}