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}