links/
certs.rs

1//! Links server certificate handling.
2
3use std::{
4	fmt::{Debug, Formatter, Result as FmtResult},
5	sync::{Arc, RwLock},
6};
7
8use links_domainmap::{Domain, DomainMap};
9use tokio_rustls::rustls::{
10	server::{ClientHello, ResolvesServerCert},
11	sign::CertifiedKey,
12};
13use tracing::debug;
14
15use crate::util::Unpoison;
16
17/// A per-domain [`ResolvesServerCert`] implementor with fallback.
18///
19/// Resolves TLS certificates based on the domain name using `links-domainmap`.
20/// The default certificate for unknown or unrecognized domain names can be
21/// specified using `default`.
22///
23/// [`ResolvesServerCert`]: https://docs.rs/rustls/latest/rustls/server/trait.ResolvesServerCert.html
24pub struct CertificateResolver {
25	/// The map containing all certificates
26	certs: RwLock<DomainMap<Arc<CertifiedKey>>>,
27	/// Default certificate/key for unknown and unrecognized domain names
28	default: RwLock<Option<Arc<CertifiedKey>>>,
29}
30
31impl CertificateResolver {
32	/// Create a new empty `CertificateResolver` from a [`CertifiedKey`]
33	#[must_use]
34	pub const fn new() -> Self {
35		Self {
36			certs: RwLock::new(DomainMap::new()),
37			default: RwLock::new(None),
38		}
39	}
40
41	/// Get the default `CertifiedKey` if one is configured
42	fn get_default(&self) -> Option<Arc<CertifiedKey>> {
43		self.default.read().unpoison().as_ref().map(Arc::clone)
44	}
45
46	/// Get the matching `CertifiedKey` for the given reference identifier
47	/// domain name
48	pub fn get(&self, domain: Option<&Domain>) -> Option<Arc<CertifiedKey>> {
49		domain.map_or_else(
50			|| self.get_default(),
51			|domain| {
52				self.certs
53					.read()
54					.unpoison()
55					.get(domain)
56					.map_or_else(|| self.get_default(), |certkey| Some(Arc::clone(certkey)))
57			},
58		)
59	}
60
61	/// Set the cert-key pair for the given domain. All future calls to `get` or
62	/// `resolve` with this domain name will return this new `CertifiedKey`.
63	pub fn set(&self, domain: Domain, certkey: Arc<CertifiedKey>) {
64		self.certs.write().unpoison().set(domain, certkey);
65	}
66
67	/// Set the default cert-key pair for unknown or unrecognized domains. All
68	/// future calls to `get_default` or `resolve` without a domain name or a
69	/// domain name not found in any other certificate sources will return this
70	/// new `CertifiedKey`. Setting the default certificate to `None` will
71	/// reject requests for unknown or unrecognized domains.
72	pub fn set_default(&self, certkey: Option<Arc<CertifiedKey>>) {
73		*self.default.write().unpoison() = certkey;
74	}
75
76	/// Remove the cert-key pair for the given domain. All future calls to `get`
77	/// or `resolve` with this domain name will return nothing.
78	pub fn remove(&self, domain: &Domain) {
79		self.certs.write().unpoison().remove(domain);
80	}
81}
82
83impl Default for CertificateResolver {
84	fn default() -> Self {
85		Self::new()
86	}
87}
88
89impl ResolvesServerCert for CertificateResolver {
90	fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
91		let cert = self.get(
92			client_hello
93				.server_name()
94				.map(Domain::reference)
95				.and_then(Result::ok)
96				.as_ref(),
97		);
98
99		if cert.is_none() {
100			debug!(
101				"Certificate for {:?} not resolved",
102				client_hello.server_name()
103			);
104		}
105
106		cert
107	}
108}
109
110impl Debug for CertificateResolver {
111	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
112		f.debug_struct("CertificateResolver")
113			.field("current", &"Arc<[REDACTED]>")
114			.finish()
115	}
116}