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}