use std::{
collections::HashMap,
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
use bytes::BufMut;
use dashmap::DashMap;
use deadpool_redis::Pool;
use nom::{
IResult,
bytes::streaming::take,
number::streaming::{be_u32, be_u64},
};
use tokio::time::Instant;
use crate::policy::DomainPolicies;
pub fn cert_fingerprint(cert_der: &[u8]) -> [u8; 32] {
use ring::digest::{SHA256, digest};
let d = digest(&SHA256, cert_der);
d.as_ref().try_into().expect("SHA-256 is always 32 bytes")
}
pub fn cert_fingerprint_hex(cert_der: &[u8]) -> String {
cert_fingerprint(cert_der)
.iter()
.map(|b| format!("{b:02x}"))
.collect()
}
pub fn unix_now_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
#[derive(Debug, Clone)]
pub struct StoredRecord {
pub expire_unix_secs: u64,
pub fingerprint: [u8; 32],
pub dns: Vec<u8>,
pub cert: Vec<u8>,
}
impl StoredRecord {
pub fn encoding_size(&self) -> usize {
8 + 32 + 4 + self.dns.len() + 4 + self.cert.len()
}
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.encoding_size());
buf.put_stored_record(self);
buf
}
pub fn decode(data: &[u8]) -> Option<Self> {
be_stored_record(data).ok().map(|(_, r)| r)
}
}
pub trait WriteStoredRecord {
fn put_stored_record(&mut self, record: &StoredRecord);
}
impl<B: BufMut> WriteStoredRecord for B {
fn put_stored_record(&mut self, record: &StoredRecord) {
self.put_u64(record.expire_unix_secs);
self.put_slice(&record.fingerprint);
self.put_u32(record.dns.len() as u32);
self.put_slice(&record.dns);
self.put_u32(record.cert.len() as u32);
self.put_slice(&record.cert);
}
}
pub fn be_stored_record(input: &[u8]) -> IResult<&[u8], StoredRecord> {
let (input, expire_unix_secs) = be_u64(input)?;
let (input, fp_bytes) = take(32usize)(input)?;
let (input, dns_len) = be_u32(input)?;
let (input, dns) = take(dns_len as usize)(input)?;
let (input, cert_len) = be_u32(input)?;
let (input, cert) = take(cert_len as usize)(input)?;
Ok((
input,
StoredRecord {
expire_unix_secs,
fingerprint: fp_bytes.try_into().expect("took exactly 32 bytes"),
dns: dns.to_vec(),
cert: cert.to_vec(),
},
))
}
#[derive(Clone, Debug)]
pub struct Record {
pub dns_bytes: Vec<u8>,
pub cert_bytes: Vec<u8>,
pub expire: Instant,
pub published_at: Instant,
}
#[derive(Clone)]
pub struct MemoryStorage {
pub records: Arc<DashMap<String, HashMap<[u8; 32], Record>>>,
}
impl MemoryStorage {
pub fn new() -> Self {
Self {
records: Arc::new(DashMap::new()),
}
}
}
#[derive(Clone)]
pub enum Storage {
Redis(Pool),
Memory(MemoryStorage),
}
pub type LookupRecord = (Vec<u8>, Vec<u8>);
pub type SeedRecords = Arc<HashMap<String, Vec<LookupRecord>>>;
#[derive(Clone)]
pub struct AppState {
pub storage: Storage,
pub require_signature: bool,
pub ttl_secs: u64,
pub policies: Arc<DomainPolicies>,
pub seed_records: SeedRecords,
}