1use std::{
2 collections::HashMap,
3 error::Error,
4 future::Future,
5 net::IpAddr,
6 ops::{Deref, Sub},
7 path::PathBuf,
8 pin::Pin,
9 sync::Arc,
10 time::{Duration, SystemTime},
11};
12
13use base64::Engine;
14use bytes::Bytes;
15use hyper::Request;
16use hyper_util::client::legacy::Client as HyperClient;
17use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
18use instant_acme::{
19 Account, AccountCredentials, AuthorizationStatus, BodyWrapper, BytesResponse, CertificateIdentifier, ChallengeType,
20 ExternalAccountKey, HttpClient, Identifier, NewAccount, NewOrder, OrderStatus, RenewalInfo, RetryPolicy,
21};
22use rcgen::{CertificateParams, CustomExtension, KeyPair};
23use rustls::{
24 crypto::CryptoProvider,
25 server::{ClientHello, ResolvesServerCert},
26 sign::CertifiedKey,
27 ClientConfig,
28};
29use rustls_pki_types::{CertificateDer, PrivateKeyDer};
30use serde::{Deserialize, Serialize};
31use tokio::{io::AsyncWriteExt, sync::RwLock, time::Instant};
32use x509_parser::prelude::{FromDer, X509Certificate};
33use xxhash_rust::xxh3::xxh3_128;
34
35use crate::tls_util::load_host_resolver;
36use ferron_common::dns::DnsProvider;
37
38pub const ACME_TLS_ALPN_NAME: &[u8] = b"acme-tls/1";
39const SECONDS_BEFORE_RENEWAL: u64 = 86400; pub type TlsAlpn01DataLock = Arc<RwLock<Option<(Arc<CertifiedKey>, String)>>>;
42pub type Http01DataLock = Arc<RwLock<Option<(String, String)>>>;
43
44pub struct AcmeConfig {
46 pub rustls_client_config: ClientConfig,
48 pub domains: Vec<String>,
50 pub challenge_type: ChallengeType,
52 pub contact: Vec<String>,
54 pub directory: String,
56 pub eab_key: Option<Arc<ExternalAccountKey>>,
58 pub profile: Option<String>,
60 pub account_cache: AcmeCache,
62 pub certificate_cache: AcmeCache,
64 pub certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>,
66 pub tls_alpn_01_data_lock: TlsAlpn01DataLock,
68 pub http_01_data_lock: Http01DataLock,
70 pub dns_provider: Option<Arc<dyn DnsProvider + Send + Sync>>,
72 pub renewal_info: Option<(RenewalInfo, Instant)>,
74 pub account: Option<Account>,
76}
77
78pub enum AcmeCache {
80 Memory(Arc<RwLock<HashMap<String, Vec<u8>>>>),
82 File(PathBuf),
84}
85
86#[derive(Serialize, Deserialize)]
87struct CertificateCacheData {
88 certificate_chain_pem: String,
89 private_key_pem: String,
90}
91
92async fn get_from_cache(cache: &AcmeCache, key: &str) -> Option<Vec<u8>> {
94 match cache {
95 AcmeCache::Memory(cache) => cache.read().await.get(key).cloned(),
96 AcmeCache::File(path) => tokio::fs::read(path.join(key)).await.ok(),
97 }
98}
99
100pub struct AcmeOnDemandConfig {
102 pub rustls_client_config: ClientConfig,
104 pub challenge_type: ChallengeType,
106 pub contact: Vec<String>,
108 pub directory: String,
110 pub eab_key: Option<Arc<ExternalAccountKey>>,
112 pub profile: Option<String>,
114 pub cache_path: Option<PathBuf>,
116 #[allow(clippy::type_complexity)]
118 pub sni_resolver_lock: Arc<RwLock<Vec<(String, Arc<dyn ResolvesServerCert>)>>>,
119 pub tls_alpn_01_resolver_lock: Arc<RwLock<Vec<TlsAlpn01DataLock>>>,
121 pub http_01_resolver_lock: Arc<RwLock<Vec<Http01DataLock>>>,
123 pub dns_provider: Option<Arc<dyn DnsProvider + Send + Sync>>,
125 pub sni_hostname: Option<String>,
127 pub port: u16,
129}
130
131async fn set_in_cache(cache: &AcmeCache, key: &str, value: Vec<u8>) -> Result<(), Box<dyn Error + Send + Sync>> {
133 match cache {
134 AcmeCache::Memory(cache) => {
135 cache.write().await.insert(key.to_string(), value);
136 Ok(())
137 }
138 AcmeCache::File(path) => {
139 tokio::fs::create_dir_all(path).await.unwrap_or_default();
140 let mut open_options = tokio::fs::OpenOptions::new();
141 open_options.write(true).create(true);
142
143 #[cfg(unix)]
144 open_options.mode(0o600); let mut file = open_options.open(path.join(key)).await?;
147 file.write_all(&value).await?;
148 file.flush().await.unwrap_or_default();
149
150 Ok(())
151 }
152 }
153}
154
155async fn remove_from_cache(cache: &AcmeCache, key: &str) {
157 match cache {
158 AcmeCache::Memory(cache) => {
159 cache.write().await.remove(key);
160 }
161 AcmeCache::File(path) => {
162 let _ = tokio::fs::remove_file(path.join(key)).await;
163 }
164 }
165}
166
167fn check_certificate_validity(
169 certificate: &CertificateDer,
170 renewal_info: Option<&RenewalInfo>,
171) -> Result<bool, Box<dyn Error + Send + Sync>> {
172 if let Some(renewal_info) = renewal_info {
173 return Ok(SystemTime::now() < renewal_info.suggested_window.start);
174 }
175 let (_, x509_certificate) = X509Certificate::from_der(certificate)?;
176 let validity = x509_certificate.validity();
177 if let Some(time_to_expiration) = validity.time_to_expiration() {
178 let time_before_expiration = if let Some(valid_duration) = validity.not_after.sub(validity.not_before) {
179 (valid_duration.whole_seconds().unsigned_abs() / 2).min(SECONDS_BEFORE_RENEWAL)
180 } else {
181 SECONDS_BEFORE_RENEWAL
182 };
183 if time_to_expiration >= Duration::from_secs(time_before_expiration) {
184 return Ok(true);
185 }
186 }
187 Ok(false)
188}
189
190fn get_account_cache_key(config: &AcmeConfig) -> String {
192 format!(
193 "account_{}",
194 base64::engine::general_purpose::URL_SAFE_NO_PAD
195 .encode(xxh3_128(format!("{};{}", &config.contact.join(","), &config.directory).as_bytes()).to_be_bytes())
196 )
197}
198
199fn get_certificate_cache_key(config: &AcmeConfig) -> String {
201 format!(
202 "certificate_{}",
203 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(
204 xxh3_128(
205 format!(
206 "{}{}",
207 config.domains.join(","),
208 config.profile.as_ref().map_or("".to_string(), |p| format!(";{p}"))
209 )
210 .as_bytes()
211 )
212 .to_be_bytes()
213 )
214 )
215}
216
217fn get_hostname_cache_key(config: &AcmeOnDemandConfig) -> String {
219 format!(
220 "hostname_{}",
221 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(
222 xxh3_128(
223 format!(
224 "{}{}",
225 &config.port,
226 config.sni_hostname.as_ref().map_or("".to_string(), |h| format!(";{h}"))
227 )
228 .as_bytes()
229 )
230 .to_be_bytes()
231 )
232 )
233}
234
235pub async fn check_certificate_validity_or_install_cached(
237 config: &mut AcmeConfig,
238 acme_account: Option<&Account>,
239) -> Result<bool, Box<dyn Error + Send + Sync>> {
240 if let Some(certified_key) = config.certified_key_lock.read().await.as_deref() {
241 if let Some(certificate) = certified_key.cert.first() {
242 if let Some(acme_account) = acme_account {
243 if config
244 .renewal_info
245 .as_ref()
246 .is_none_or(|v| v.1.elapsed() > Duration::ZERO)
247 {
248 if let Ok(certificate_id) = CertificateIdentifier::try_from(certificate) {
249 if let Ok(renewal_info) = acme_account.renewal_info(&certificate_id).await {
250 let mut renewal_instant = Instant::now();
251 renewal_instant += renewal_info.1;
252 config.renewal_info = Some((renewal_info.0, renewal_instant));
253 }
254 }
255 }
256 }
257 if check_certificate_validity(certificate, config.renewal_info.as_ref().map(|i| &i.0))? {
258 return Ok(true);
259 }
260 }
261 }
262
263 let certificate_cache_key = get_certificate_cache_key(config);
264
265 if let Some(serialized_certificate_cache_data) =
266 get_from_cache(&config.certificate_cache, &certificate_cache_key).await
267 {
268 let certificate_data = serde_json::from_slice::<CertificateCacheData>(&serialized_certificate_cache_data)?;
269 let certs = rustls_pemfile::certs(&mut std::io::Cursor::new(
270 certificate_data.certificate_chain_pem.as_bytes(),
271 ))
272 .collect::<Result<Vec<_>, _>>()?;
273 if let Some(certificate) = certs.first() {
274 if let Some(acme_account) = acme_account {
275 if config
276 .renewal_info
277 .as_ref()
278 .is_none_or(|v| v.1.elapsed() > Duration::ZERO)
279 {
280 if let Ok(certificate_id) = CertificateIdentifier::try_from(certificate) {
281 if let Ok(renewal_info) = acme_account.renewal_info(&certificate_id).await {
282 let mut renewal_instant = Instant::now();
283 renewal_instant -= renewal_info.1;
284 config.renewal_info = Some((renewal_info.0, renewal_instant));
285 }
286 }
287 }
288 }
289 if check_certificate_validity(certificate, config.renewal_info.as_ref().map(|i| &i.0))? {
290 let private_key =
291 (match rustls_pemfile::private_key(&mut std::io::Cursor::new(certificate_data.private_key_pem.as_bytes())) {
292 Ok(Some(private_key)) => Ok(private_key),
293 Ok(None) => Err(std::io::Error::new(
294 std::io::ErrorKind::InvalidData,
295 "Invalid private key",
296 )),
297 Err(err) => Err(err),
298 })?;
299
300 let signing_key = CryptoProvider::get_default()
301 .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
302 .key_provider
303 .load_private_key(private_key)?;
304
305 *config.certified_key_lock.write().await = Some(Arc::new(CertifiedKey::new(certs, signing_key)));
306
307 return Ok(true);
308 }
309 }
310 }
311
312 Ok(false)
313}
314
315pub async fn provision_certificate(config: &mut AcmeConfig) -> Result<(), Box<dyn Error + Send + Sync>> {
317 let account_cache_key = get_account_cache_key(config);
318 let certificate_cache_key = get_certificate_cache_key(config);
319
320 let acme_account = if let Some(acme_account) = config.account.take() {
321 acme_account
322 } else {
323 let acme_account_builder =
324 Account::builder_with_http(Box::new(HttpsClientForAcme::new(config.rustls_client_config.clone())));
325
326 if let Some(account_credentials_serialized) = get_from_cache(&config.account_cache, &account_cache_key).await {
327 let account_credentials = serde_json::from_slice::<AccountCredentials>(&account_credentials_serialized)?;
328 acme_account_builder.from_credentials(account_credentials).await?
329 } else {
330 let (account, account_credentials) = acme_account_builder
331 .create(
332 &NewAccount {
333 contact: config.contact.iter().map(|s| s.deref()).collect::<Vec<_>>().as_slice(),
334 terms_of_service_agreed: true,
335 only_return_existing: false,
336 },
337 config.directory.clone(),
338 config.eab_key.as_deref(),
339 )
340 .await?;
341
342 set_in_cache(
343 &config.account_cache,
344 &account_cache_key,
345 serde_json::to_vec(&account_credentials)?,
346 )
347 .await?;
348 account
349 }
350 };
351
352 if check_certificate_validity_or_install_cached(config, Some(&acme_account)).await? {
353 config.account.replace(acme_account);
355 return Ok(());
356 }
357
358 let acme_identifiers_vec = config
359 .domains
360 .iter()
361 .map(|s| {
362 if let Ok(ip) = s.parse::<IpAddr>() {
363 Identifier::Ip(ip)
364 } else {
365 Identifier::Dns(s.to_string())
366 }
367 })
368 .collect::<Vec<_>>();
369
370 let mut acme_new_order = NewOrder::new(&acme_identifiers_vec);
371 if let Some(profile) = &config.profile {
372 acme_new_order = acme_new_order.profile(profile);
373 }
374
375 let mut acme_order = match acme_account.new_order(&acme_new_order).await {
376 Ok(order) => order,
377 Err(instant_acme::Error::Api(problem)) => {
378 if problem.r#type.as_deref() == Some("urn:ietf:params:acme:error:accountDoesNotExist") {
379 remove_from_cache(&config.account_cache, &account_cache_key).await;
381 }
382 Err(instant_acme::Error::Api(problem))?
383 }
384 Err(err) => Err(err)?,
385 };
386 let mut dns_01_identifiers = Vec::new();
387 let mut acme_authorizations = acme_order.authorizations();
388 while let Some(acme_authorization) = acme_authorizations.next().await {
389 let mut acme_authorization = acme_authorization?;
390 match acme_authorization.status {
391 AuthorizationStatus::Pending => {}
392 AuthorizationStatus::Valid => continue,
393 _ => Err(anyhow::anyhow!("Invalid ACME authorization status"))?,
394 }
395
396 let mut challenge = acme_authorization
397 .challenge(config.challenge_type.clone())
398 .ok_or(anyhow::anyhow!(
399 "The ACME server doesn't support the requested challenge type"
400 ))?;
401
402 let identifier = match challenge.identifier().identifier {
403 Identifier::Dns(identifier) => identifier.to_string(),
404 Identifier::Ip(ip) => ip.to_string(),
405 _ => Err(anyhow::anyhow!("Unsupported ACME identifier type",))?,
406 };
407
408 let key_authorization = challenge.key_authorization();
409 match config.challenge_type {
410 ChallengeType::TlsAlpn01 => {
411 let mut params = CertificateParams::new(vec![identifier.clone()])?;
412 params.custom_extensions.push(CustomExtension::new_acme_identifier(
413 key_authorization.digest().as_ref(),
414 ));
415 let key_pair = KeyPair::generate()?;
416 let certificate = params.self_signed(&key_pair)?;
417 let private_key = PrivateKeyDer::try_from(key_pair.serialize_der())?;
418
419 let signing_key = CryptoProvider::get_default()
420 .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
421 .key_provider
422 .load_private_key(private_key)?;
423
424 *config.tls_alpn_01_data_lock.write().await = Some((
425 Arc::new(CertifiedKey::new(vec![certificate.der().to_owned()], signing_key)),
426 identifier.clone(),
427 ));
428 }
429 ChallengeType::Http01 => {
430 let key_auth_value = key_authorization.as_str();
431 *config.http_01_data_lock.write().await = Some((challenge.token.clone(), key_auth_value.to_string()));
432 }
433 ChallengeType::Dns01 => {
434 if let Some(dns_provider) = &config.dns_provider {
435 dns_provider
436 .remove_acme_txt_record(&identifier)
437 .await
438 .unwrap_or_default();
439 dns_provider
440 .set_acme_txt_record(&identifier, &key_authorization.dns_value())
441 .await?;
442 tokio::time::sleep(Duration::from_secs(60)).await;
444 dns_01_identifiers.push(identifier.clone());
445 } else {
446 Err(anyhow::anyhow!("No DNS provider configured."))?;
447 }
448 }
449 _ => (),
450 }
451
452 challenge.set_ready().await?;
453 }
454
455 let acme_order_status = acme_order.poll_ready(&RetryPolicy::default()).await?;
456 if acme_order_status != OrderStatus::Ready {
457 Err(anyhow::anyhow!("ACME order is not ready",))?;
458 }
459
460 let finalize_closure = async {
461 let private_key_pem = acme_order.finalize().await?;
462 let certificate_chain_pem = acme_order.poll_certificate(&RetryPolicy::default()).await?;
463
464 let certificate_cache_data = CertificateCacheData {
465 certificate_chain_pem: certificate_chain_pem.clone(),
466 private_key_pem: private_key_pem.clone(),
467 };
468
469 set_in_cache(
470 &config.certificate_cache,
471 &certificate_cache_key,
472 serde_json::to_vec(&certificate_cache_data)?,
473 )
474 .await?;
475
476 let certs = rustls_pemfile::certs(&mut std::io::Cursor::new(certificate_chain_pem.as_bytes()))
477 .collect::<Result<Vec<_>, _>>()?;
478 let private_key = (match rustls_pemfile::private_key(&mut std::io::Cursor::new(private_key_pem.as_bytes())) {
479 Ok(Some(private_key)) => Ok(private_key),
480 Ok(None) => Err(std::io::Error::new(
481 std::io::ErrorKind::InvalidData,
482 "Invalid private key",
483 )),
484 Err(err) => Err(err),
485 })?;
486
487 let signing_key = CryptoProvider::get_default()
488 .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
489 .key_provider
490 .load_private_key(private_key)?;
491
492 config.account.replace(acme_account);
493
494 *config.certified_key_lock.write().await = Some(Arc::new(CertifiedKey::new(certs, signing_key)));
495
496 Ok::<_, Box<dyn Error + Send + Sync>>(())
497 };
498
499 let result = finalize_closure.await;
500
501 if let Some(dns_provider) = &config.dns_provider {
503 for identifier in dns_01_identifiers {
504 dns_provider
505 .remove_acme_txt_record(&identifier)
506 .await
507 .unwrap_or_default();
508 }
509 }
510
511 result?;
512
513 Ok(())
514}
515
516pub async fn get_cached_domains(config: &AcmeOnDemandConfig) -> Vec<String> {
518 if let Some(pathbuf) = config.cache_path.clone() {
519 let hostname_cache_key = get_hostname_cache_key(config);
520 let hostname_cache = AcmeCache::File(pathbuf);
521 let cache_data = get_from_cache(&hostname_cache, &hostname_cache_key).await;
522 if let Some(data) = cache_data {
523 serde_json::from_slice(&data).unwrap_or_default()
524 } else {
525 Vec::new()
526 }
527 } else {
528 Vec::new()
529 }
530}
531
532pub async fn add_domain_to_cache(
534 config: &AcmeOnDemandConfig,
535 domain: &str,
536) -> Result<(), Box<dyn Error + Send + Sync>> {
537 if let Some(pathbuf) = config.cache_path.clone() {
538 let hostname_cache_key = get_hostname_cache_key(config);
539 let hostname_cache = AcmeCache::File(pathbuf);
540 let mut cached_domains = get_cached_domains(config).await;
541 cached_domains.push(domain.to_string());
542 let data = serde_json::to_vec(&cached_domains)?;
543 set_in_cache(&hostname_cache, &hostname_cache_key, data).await?;
544 }
545 Ok(())
546}
547
548pub async fn convert_on_demand_config(
550 config: &AcmeOnDemandConfig,
551 sni_hostname: String,
552 memory_acme_account_cache_data: Arc<RwLock<HashMap<String, Vec<u8>>>>,
553) -> AcmeConfig {
554 let (account_cache_path, cert_cache_path) = if let Some(mut pathbuf) = config.cache_path.clone() {
555 let base_pathbuf = pathbuf.clone();
556 let append_hash = base64::engine::general_purpose::URL_SAFE_NO_PAD
557 .encode(xxh3_128(format!("{}-{sni_hostname}", config.port).as_bytes()).to_be_bytes());
558 pathbuf.push(append_hash);
559 (Some(base_pathbuf), Some(pathbuf))
560 } else {
561 (None, None)
562 };
563
564 let certified_key_lock = Arc::new(tokio::sync::RwLock::new(None));
565 let tls_alpn_01_data_lock = Arc::new(tokio::sync::RwLock::new(None));
566 let http_01_data_lock = Arc::new(tokio::sync::RwLock::new(None));
567
568 load_host_resolver(
570 &mut *config.sni_resolver_lock.write().await,
571 &sni_hostname,
572 Arc::new(AcmeResolver::new(certified_key_lock.clone())),
573 );
574 match config.challenge_type {
575 ChallengeType::TlsAlpn01 => {
576 config
577 .tls_alpn_01_resolver_lock
578 .write()
579 .await
580 .push(tls_alpn_01_data_lock.clone());
581 }
582 ChallengeType::Http01 => {
583 config
584 .http_01_resolver_lock
585 .write()
586 .await
587 .push(http_01_data_lock.clone());
588 }
589 _ => (),
590 };
591
592 AcmeConfig {
593 rustls_client_config: config.rustls_client_config.clone(),
594 domains: vec![sni_hostname],
595 challenge_type: config.challenge_type.clone(),
596 contact: config.contact.clone(),
597 directory: config.directory.clone(),
598 eab_key: config.eab_key.clone(),
599 profile: config.profile.clone(),
600 account_cache: if let Some(account_cache_path) = account_cache_path {
601 AcmeCache::File(account_cache_path)
602 } else {
603 AcmeCache::Memory(memory_acme_account_cache_data.clone())
604 },
605 certificate_cache: if let Some(cert_cache_path) = cert_cache_path {
606 AcmeCache::File(cert_cache_path)
607 } else {
608 AcmeCache::Memory(Arc::new(tokio::sync::RwLock::new(HashMap::new())))
609 },
610 certified_key_lock: certified_key_lock.clone(),
611 tls_alpn_01_data_lock: tls_alpn_01_data_lock.clone(),
612 http_01_data_lock: http_01_data_lock.clone(),
613 dns_provider: config.dns_provider.clone(),
614 renewal_info: None,
615 account: None,
616 }
617}
618
619#[derive(Debug)]
621pub struct AcmeResolver {
622 certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>,
623}
624
625impl AcmeResolver {
626 pub fn new(certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>) -> Self {
628 Self { certified_key_lock }
629 }
630}
631
632impl ResolvesServerCert for AcmeResolver {
633 fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
634 self.certified_key_lock.blocking_read().clone()
635 }
636}
637
638struct HttpsClientForAcme(HyperClient<hyper_rustls::HttpsConnector<HttpConnector>, BodyWrapper<Bytes>>);
639
640impl HttpsClientForAcme {
641 fn new(tls_config: ClientConfig) -> Self {
642 Self(
643 HyperClient::builder(TokioExecutor::new()).build(
644 hyper_rustls::HttpsConnectorBuilder::new()
645 .with_tls_config(tls_config)
646 .https_or_http()
647 .enable_http1()
648 .enable_http2()
649 .build(),
650 ),
651 )
652 }
653}
654
655impl HttpClient for HttpsClientForAcme {
656 fn request(
657 &self,
658 req: Request<BodyWrapper<Bytes>>,
659 ) -> Pin<Box<dyn Future<Output = Result<BytesResponse, instant_acme::Error>> + Send>> {
660 HttpClient::request(&self.0, req)
661 }
662}
663
664#[derive(Debug)]
666pub struct TlsAlpn01Resolver {
667 resolvers: Arc<tokio::sync::RwLock<Vec<TlsAlpn01DataLock>>>,
668}
669
670impl TlsAlpn01Resolver {
671 #[allow(dead_code)]
673 pub fn new() -> Self {
674 Self {
675 resolvers: Arc::new(tokio::sync::RwLock::new(Vec::new())),
676 }
677 }
678
679 pub fn with_resolvers(resolvers: Arc<tokio::sync::RwLock<Vec<TlsAlpn01DataLock>>>) -> Self {
681 Self { resolvers }
682 }
683
684 pub fn load_resolver(&self, resolver: TlsAlpn01DataLock) {
686 self.resolvers.blocking_write().push(resolver);
687 }
688}
689
690impl ResolvesServerCert for TlsAlpn01Resolver {
691 fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
692 let hostname = client_hello.server_name().map(|hn| hn.strip_suffix('.').unwrap_or(hn));
693
694 #[cfg(feature = "runtime-monoio")]
696 let resolver_locks = self.resolvers.blocking_read();
697 #[cfg(feature = "runtime-tokio")]
698 let resolver_locks = futures_executor::block_on(async { self.resolvers.read().await });
699
700 for resolver_lock in &*resolver_locks {
701 if let Some(hostname) = hostname {
702 #[cfg(feature = "runtime-monoio")]
703 let resolver_data = resolver_lock.blocking_read().clone();
704 #[cfg(feature = "runtime-tokio")]
705 let resolver_data = futures_executor::block_on(async { resolver_lock.read().await }).clone();
706 if let Some(resolver_data) = resolver_data {
707 let (cert, host) = resolver_data;
708 if host == hostname {
709 return Some(cert);
710 }
711 }
712 }
713 }
714 None
715 }
716}