1#[derive(Debug, thiserror::Error, PartialEq, Eq)]
4pub enum TrustDomainParseError {
5 #[error("empty trust-domain")]
6 Empty,
7 #[error("malformed DNS trust-domain: {0:?}")]
8 BadDns(String),
9}
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum TrustDomainKind {
13 Dns,
14 Local,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct ParsedTrustDomain {
19 pub kind: TrustDomainKind,
20 pub value: String,
21 pub raw: String,
22}
23
24pub fn parse_trust_domain(s: &str) -> Result<ParsedTrustDomain, TrustDomainParseError> {
25 if s.is_empty() {
26 return Err(TrustDomainParseError::Empty);
27 }
28 if let Some(rest) = s.strip_prefix("local/") {
29 if rest.is_empty() {
30 return Err(TrustDomainParseError::Empty);
31 }
32 return Ok(ParsedTrustDomain {
33 kind: TrustDomainKind::Local,
34 value: rest.to_owned(),
35 raw: s.to_owned(),
36 });
37 }
38 if !is_dns_like(s) {
39 return Err(TrustDomainParseError::BadDns(s.to_owned()));
40 }
41 Ok(ParsedTrustDomain {
42 kind: TrustDomainKind::Dns,
43 value: s.to_ascii_lowercase(),
44 raw: s.to_owned(),
45 })
46}
47
48pub fn trust_domain_equals(a: &str, b: &str) -> bool {
49 match (parse_trust_domain(a), parse_trust_domain(b)) {
50 (Ok(pa), Ok(pb)) => pa.kind == pb.kind && pa.value == pb.value,
51 _ => false,
52 }
53}
54
55fn is_dns_like(s: &str) -> bool {
56 let bytes = s.as_bytes();
57 if bytes.is_empty() {
58 return false;
59 }
60 let first = bytes[0];
61 let last = bytes[bytes.len() - 1];
62 if !is_alnum(first) || !is_alnum(last) {
63 return false;
64 }
65 bytes
66 .iter()
67 .all(|b| is_alnum(*b) || *b == b'.' || *b == b'-')
68}
69
70fn is_alnum(b: u8) -> bool {
71 b.is_ascii_alphanumeric()
72}