rustls_acme/
resolver.rs

1use crate::is_tls_alpn_challenge;
2use futures_rustls::rustls::{
3    server::{ClientHello, ResolvesServerCert},
4    sign::CertifiedKey,
5};
6use std::fmt::Debug;
7use std::sync::Arc;
8use std::sync::Mutex;
9
10#[derive(Debug)]
11pub struct ResolvesServerCertAcme {
12    inner: Mutex<Inner>,
13}
14
15#[derive(Debug)]
16struct Inner {
17    cert: Option<Arc<CertifiedKey>>,
18    challenge_data: Option<ChallengeData>,
19}
20
21#[derive(Debug)]
22enum ChallengeData {
23    TlsAlpn01 { sni: String, cert: Arc<CertifiedKey> },
24    Http01 { token: String, key_auth: String },
25}
26
27impl ResolvesServerCertAcme {
28    pub(crate) fn new() -> Arc<Self> {
29        Arc::new(Self {
30            inner: Mutex::new(Inner {
31                cert: None,
32                challenge_data: None,
33            }),
34        })
35    }
36    pub(crate) fn set_cert(&self, cert: Arc<CertifiedKey>) {
37        self.inner.lock().unwrap().cert = Some(cert);
38    }
39    pub(crate) fn set_tls_alpn_01_challenge_data(&self, domain: String, cert: Arc<CertifiedKey>) {
40        self.inner.lock().unwrap().challenge_data = Some(ChallengeData::TlsAlpn01 { sni: domain, cert });
41    }
42    pub(crate) fn set_http_01_challenge_data(&self, token: String, key_auth: String) {
43        self.inner.lock().unwrap().challenge_data = Some(ChallengeData::Http01 { token, key_auth })
44    }
45    pub(crate) fn clear_challenge_data(&self) {
46        self.inner.lock().unwrap().challenge_data = None;
47    }
48    pub fn get_http_01_key_auth(&self, challenge_token: &str) -> Option<String> {
49        match &self.inner.lock().unwrap().challenge_data {
50            Some(ChallengeData::Http01 { token, key_auth }) => {
51                if token == challenge_token {
52                    Some(key_auth.clone())
53                } else {
54                    None
55                }
56            }
57            _ => None,
58        }
59    }
60}
61
62impl ResolvesServerCert for ResolvesServerCertAcme {
63    fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
64        let is_acme_challenge = is_tls_alpn_challenge(&client_hello);
65        if is_acme_challenge {
66            match client_hello.server_name() {
67                None => {
68                    log::debug!("client did not supply SNI");
69                    None
70                }
71                Some(domain) => match &self.inner.lock().unwrap().challenge_data {
72                    Some(ChallengeData::TlsAlpn01 { sni, cert }) => {
73                        if sni == domain {
74                            Some(cert.clone())
75                        } else {
76                            None
77                        }
78                    }
79                    _ => None,
80                },
81            }
82        } else {
83            self.inner.lock().unwrap().cert.clone()
84        }
85    }
86}