webpki/subject_name/
name.rs

1// Copyright 2015-2020 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15use crate::DnsNameRef;
16
17use super::ip_address::{self, IpAddrRef};
18
19/// A DNS name or IP address, which borrows its text representation.
20#[derive(Debug, Clone, Copy)]
21pub enum SubjectNameRef<'a> {
22    /// A valid DNS name
23    DnsName(DnsNameRef<'a>),
24
25    /// A valid IP address
26    IpAddress(IpAddrRef<'a>),
27}
28
29/// An error indicating that a `SubjectNameRef` could not built
30/// because the input is not a syntactically-valid DNS Name or IP
31/// address.
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct InvalidSubjectNameError;
34
35impl<'a> SubjectNameRef<'a> {
36    /// Attempts to decode an encodingless string as either an IPv4 address, IPv6 address or
37    /// DNS name; in that order.  In practice this space is non-overlapping because
38    /// DNS name components are separated by periods but cannot be wholly numeric (so cannot
39    /// overlap with a valid IPv4 address), and IPv6 addresses are separated by colons but
40    /// cannot contain periods.
41    ///
42    /// The IPv6 address encoding supported here is extremely simplified; it does not support
43    /// compression, all leading zeroes must be present in each 16-bit word, etc.  Generally
44    /// this is not suitable as a parse for human-provided addresses for this reason.  Instead:
45    /// consider parsing these with `std::net::IpAddr` and then using
46    /// `IpAddr::from<std::net::IpAddr>`.
47    pub fn try_from_ascii(subject_name: &'a [u8]) -> Result<Self, InvalidSubjectNameError> {
48        if let Ok(ip_address) = ip_address::parse_ipv4_address(subject_name) {
49            return Ok(SubjectNameRef::IpAddress(ip_address));
50        } else if let Ok(ip_address) = ip_address::parse_ipv6_address(subject_name) {
51            return Ok(SubjectNameRef::IpAddress(ip_address));
52        } else {
53            Ok(SubjectNameRef::DnsName(
54                DnsNameRef::try_from_ascii(subject_name).map_err(|_| InvalidSubjectNameError)?,
55            ))
56        }
57    }
58
59    /// Constructs a `SubjectNameRef` from the given input if the
60    /// input is a syntactically-valid DNS name or IP address.
61    pub fn try_from_ascii_str(subject_name: &'a str) -> Result<Self, InvalidSubjectNameError> {
62        Self::try_from_ascii(subject_name.as_bytes())
63    }
64}
65
66impl<'a> From<DnsNameRef<'a>> for SubjectNameRef<'a> {
67    fn from(dns_name: DnsNameRef<'a>) -> SubjectNameRef {
68        SubjectNameRef::DnsName(DnsNameRef(dns_name.0))
69    }
70}
71
72impl<'a> From<IpAddrRef<'a>> for SubjectNameRef<'a> {
73    fn from(dns_name: IpAddrRef<'a>) -> SubjectNameRef {
74        match dns_name {
75            IpAddrRef::V4(ip_address, ip_address_octets) => {
76                SubjectNameRef::IpAddress(IpAddrRef::V4(ip_address, ip_address_octets))
77            }
78            IpAddrRef::V6(ip_address, ip_address_octets) => {
79                SubjectNameRef::IpAddress(IpAddrRef::V6(ip_address, ip_address_octets))
80            }
81        }
82    }
83}
84
85impl AsRef<[u8]> for SubjectNameRef<'_> {
86    #[inline]
87    fn as_ref(&self) -> &[u8] {
88        match self {
89            SubjectNameRef::DnsName(dns_name) => dns_name.0,
90            SubjectNameRef::IpAddress(ip_address) => match ip_address {
91                IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => ip_address,
92            },
93        }
94    }
95}