1414
1515#[ cfg( feature = "alloc" ) ]
1616use alloc:: string:: String ;
17+ use core:: fmt:: Write ;
1718
1819/// A DNS Name suitable for use in the TLS Server Name Indication (SNI)
1920/// extension and/or for use as the reference hostname for which to verify a
2021/// certificate.
2122///
2223/// A `DnsName` is guaranteed to be syntactically valid. The validity rules are
2324/// specified in [RFC 5280 Section 7.2], except that underscores are also
24- /// allowed.
25+ /// allowed. `DnsName`s do not include wildcard labels.
2526///
2627/// `DnsName` stores a copy of the input it was constructed from in a `String`
27- /// and so it is only available when the `std` default feature is enabled.
28- ///
29- /// `Eq`, `PartialEq`, etc. are not implemented because name comparison
30- /// frequently should be done case-insensitively and/or with other caveats that
31- /// depend on the specific circumstances in which the comparison is done.
28+ /// and so it is only available when the `alloc` default feature is enabled.
3229///
3330/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2
3431///
@@ -69,14 +66,10 @@ impl From<DnsNameRef<'_>> for DnsName {
6966///
7067/// A `DnsNameRef` is guaranteed to be syntactically valid. The validity rules
7168/// are specified in [RFC 5280 Section 7.2], except that underscores are also
72- /// allowed.
73- ///
74- /// `Eq`, `PartialEq`, etc. are not implemented because name comparison
75- /// frequently should be done case-insensitively and/or with other caveats that
76- /// depend on the specific circumstances in which the comparison is done.
69+ /// allowed. `DnsNameRef`s do not include wildcard labels.
7770///
7871/// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2
79- #[ derive( Clone , Copy ) ]
72+ #[ derive( Clone , Copy , Eq , PartialEq , Hash ) ]
8073pub struct DnsNameRef < ' a > ( pub ( crate ) & ' a [ u8 ] ) ;
8174
8275impl AsRef < [ u8 ] > for DnsNameRef < ' _ > {
@@ -105,7 +98,11 @@ impl<'a> DnsNameRef<'a> {
10598 /// Constructs a `DnsNameRef` from the given input if the input is a
10699 /// syntactically-valid DNS name.
107100 pub fn try_from_ascii ( dns_name : & ' a [ u8 ] ) -> Result < Self , InvalidDnsNameError > {
108- if !is_valid_reference_dns_id ( untrusted:: Input :: from ( dns_name) ) {
101+ if !is_valid_dns_id (
102+ untrusted:: Input :: from ( dns_name) ,
103+ IdRole :: Reference ,
104+ AllowWildcards :: No ,
105+ ) {
109106 return Err ( InvalidDnsNameError ) ;
110107 }
111108
@@ -130,19 +127,18 @@ impl<'a> DnsNameRef<'a> {
130127 }
131128}
132129
133- /// Requires the `alloc` feature.
134- #[ cfg( feature = "alloc" ) ]
135130impl core:: fmt:: Debug for DnsNameRef < ' _ > {
136131 fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
137- let lowercase = self . clone ( ) . to_owned ( ) ;
138- f. debug_tuple ( "DnsNameRef" ) . field ( & lowercase. 0 ) . finish ( )
139- }
140- }
132+ f. write_str ( "DnsNameRef(\" " ) ?;
141133
142- #[ cfg( not( feature = "alloc" ) ) ]
143- impl core:: fmt:: Debug for DnsNameRef < ' _ > {
144- fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
145- f. debug_tuple ( "DnsNameRef" ) . field ( & self . 0 ) . finish ( )
134+ // Convert each byte of the underlying ASCII string to a `char` and
135+ // downcase it prior to formatting it. We avoid self.clone().to_owned()
136+ // since it requires allocation.
137+ for & ch in self . 0 {
138+ f. write_char ( char:: from ( ch) . to_ascii_lowercase ( ) ) ?;
139+ }
140+
141+ f. write_str ( "\" )" )
146142 }
147143}
148144
@@ -154,6 +150,94 @@ impl<'a> From<DnsNameRef<'a>> for &'a str {
154150 }
155151}
156152
153+ /// A DNS name that may be either a DNS name identifier presented by a server (which may include
154+ /// wildcards), or a DNS name identifier referenced by a client for matching purposes (wildcards
155+ /// not permitted).
156+ pub enum GeneralDnsNameRef < ' name > {
157+ /// a reference to a DNS name that may be used for matching purposes.
158+ DnsName ( DnsNameRef < ' name > ) ,
159+ /// a reference to a presented DNS name that may include a wildcard.
160+ Wildcard ( WildcardDnsNameRef < ' name > ) ,
161+ }
162+
163+ impl < ' a > From < GeneralDnsNameRef < ' a > > for & ' a str {
164+ fn from ( d : GeneralDnsNameRef < ' a > ) -> Self {
165+ match d {
166+ GeneralDnsNameRef :: DnsName ( name) => name. into ( ) ,
167+ GeneralDnsNameRef :: Wildcard ( name) => name. into ( ) ,
168+ }
169+ }
170+ }
171+
172+ /// A reference to a DNS Name presented by a server that may include a wildcard.
173+ ///
174+ /// A `WildcardDnsNameRef` is guaranteed to be syntactically valid. The validity rules
175+ /// are specified in [RFC 5280 Section 7.2], except that underscores are also
176+ /// allowed.
177+ ///
178+ /// Additionally, while [RFC6125 Section 4.1] says that a wildcard label may be of the form
179+ /// `<x>*<y>.<DNSID>`, where `<x>` and/or `<y>` may be empty, we follow a stricter policy common
180+ /// to most validation libraries (e.g. NSS) and only accept wildcard labels that are exactly `*`.
181+ ///
182+ /// [RFC 5280 Section 7.2]: https://tools.ietf.org/html/rfc5280#section-7.2
183+ /// [RFC 6125 Section 4.1]: https://www.rfc-editor.org/rfc/rfc6125#section-4.1
184+ #[ derive( Clone , Copy , Eq , PartialEq , Hash ) ]
185+ pub struct WildcardDnsNameRef < ' a > ( & ' a [ u8 ] ) ;
186+
187+ impl < ' a > WildcardDnsNameRef < ' a > {
188+ /// Constructs a `WildcardDnsNameRef` from the given input if the input is a
189+ /// syntactically-valid DNS name.
190+ pub fn try_from_ascii ( dns_name : & ' a [ u8 ] ) -> Result < Self , InvalidDnsNameError > {
191+ if !is_valid_dns_id (
192+ untrusted:: Input :: from ( dns_name) ,
193+ IdRole :: Reference ,
194+ AllowWildcards :: Yes ,
195+ ) {
196+ return Err ( InvalidDnsNameError ) ;
197+ }
198+
199+ Ok ( Self ( dns_name) )
200+ }
201+
202+ /// Constructs a `WildcardDnsNameRef` from the given input if the input is a
203+ /// syntactically-valid DNS name.
204+ pub fn try_from_ascii_str ( dns_name : & ' a str ) -> Result < Self , InvalidDnsNameError > {
205+ Self :: try_from_ascii ( dns_name. as_bytes ( ) )
206+ }
207+ }
208+
209+ impl core:: fmt:: Debug for WildcardDnsNameRef < ' _ > {
210+ fn fmt ( & self , f : & mut core:: fmt:: Formatter ) -> Result < ( ) , core:: fmt:: Error > {
211+ f. write_str ( "WildcardDnsNameRef(\" " ) ?;
212+
213+ // Convert each byte of the underlying ASCII string to a `char` and
214+ // downcase it prior to formatting it. We avoid self.to_owned() since
215+ // it requires allocation.
216+ for & ch in self . 0 {
217+ f. write_char ( char:: from ( ch) . to_ascii_lowercase ( ) ) ?;
218+ }
219+
220+ f. write_str ( "\" )" )
221+ }
222+ }
223+
224+ impl < ' a > From < WildcardDnsNameRef < ' a > > for & ' a str {
225+ fn from ( WildcardDnsNameRef ( d) : WildcardDnsNameRef < ' a > ) -> Self {
226+ // The unwrap won't fail because WildcardDnsNameRef are guaranteed to be ASCII
227+ // and ASCII is a subset of UTF-8.
228+ core:: str:: from_utf8 ( d) . unwrap ( )
229+ }
230+ }
231+
232+ impl AsRef < str > for WildcardDnsNameRef < ' _ > {
233+ #[ inline]
234+ fn as_ref ( & self ) -> & str {
235+ // The unwrap won't fail because WildcardDnsNameRef are guaranteed to be ASCII
236+ // and ASCII is a subset of UTF-8.
237+ core:: str:: from_utf8 ( self . 0 ) . unwrap ( )
238+ }
239+ }
240+
157241pub ( super ) fn presented_id_matches_reference_id (
158242 presented_dns_id : untrusted:: Input ,
159243 reference_dns_id : untrusted:: Input ,
@@ -445,10 +529,6 @@ enum IdRole {
445529 NameConstraint ,
446530}
447531
448- fn is_valid_reference_dns_id ( hostname : untrusted:: Input ) -> bool {
449- is_valid_dns_id ( hostname, IdRole :: Reference , AllowWildcards :: No )
450- }
451-
452532// https://tools.ietf.org/html/rfc5280#section-4.2.1.6:
453533//
454534// When the subjectAltName extension contains a domain name system
0 commit comments