55
66use core:: fmt;
77use core:: hash:: { self , Hash , Hasher } ;
8- use core:: net:: IpAddr ;
98use core:: str:: Utf8Error ;
109
1110use nginx_sys:: { ngx_conf_t, ngx_http_server_name_t, ngx_str_t} ;
@@ -16,6 +15,7 @@ use ngx::ngx_log_error;
1615use siphasher:: sip:: SipHasher ;
1716use thiserror:: Error ;
1817
18+ use crate :: conf:: ext:: NgxConfExt ;
1919use crate :: conf:: identifier:: Identifier ;
2020use crate :: conf:: pkey:: PrivateKey ;
2121
@@ -189,8 +189,8 @@ impl CertificateOrder<&'static str, Pool> {
189189 cf : & ngx_conf_t ,
190190 value : & ' static str ,
191191 ) -> Result < ( ) , IdentifierError > {
192- if value . parse :: < IpAddr > ( ) . is_ok ( ) {
193- return self . push ( Identifier :: Ip ( value ) ) . map_err ( Into :: into) ;
192+ if let Some ( addr ) = parse_ip_identifier ( cf , value ) ? {
193+ return self . push ( Identifier :: Ip ( addr ) ) . map_err ( Into :: into) ;
194194 }
195195
196196 if value. contains ( '*' ) {
@@ -254,6 +254,41 @@ where
254254 }
255255}
256256
257+ /// Attempts to parse the value as an IP address, returning `Some(...)` on success.
258+ ///
259+ /// The address will be converted to a canonical textual form and reallocated on the
260+ /// configuration pool if necessary.
261+ fn parse_ip_identifier (
262+ cf : & ngx_conf_t ,
263+ value : & ' static str ,
264+ ) -> Result < Option < & ' static str > , AllocError > {
265+ const INET6_ADDRSTRLEN : usize = "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255" . len ( ) ;
266+
267+ let Ok ( addr) = value. parse :: < core:: net:: IpAddr > ( ) else {
268+ return Ok ( None ) ;
269+ } ;
270+
271+ let mut buf = [ 0u8 ; INET6_ADDRSTRLEN ] ;
272+ let mut cur = std:: io:: Cursor :: new ( & mut buf[ ..] ) ;
273+ // Formatting IP address to a sufficiently large buffer should always succeed
274+ let _ = std:: io:: Write :: write_fmt ( & mut cur, format_args ! ( "{addr}" ) ) ;
275+ let len = cur. position ( ) as usize ;
276+ let buf = & buf[ ..len] ;
277+
278+ if buf == value. as_bytes ( ) {
279+ return Ok ( Some ( value) ) ;
280+ }
281+
282+ let mut out = Vec :: new_in ( cf. pool ( ) ) ;
283+ out. try_reserve_exact ( buf. len ( ) ) . map_err ( |_| AllocError ) ?;
284+ out. extend_from_slice ( buf) ;
285+ // SAFETY: formatted IpAddr is always a valid ASCII string.
286+ // The buffer is owned by the ngx_pool_t and does not leak.
287+ let out = unsafe { core:: str:: from_utf8_unchecked ( out. leak ( ) ) } ;
288+
289+ Ok ( Some ( out) )
290+ }
291+
257292/// Checks if the value is a valid domain name and returns a canonical (lowercase) form,
258293/// reallocated on the configuration pool if necessary.
259294fn validate_host ( cf : & ngx_conf_t , host : & ' static str ) -> Result < & ' static str , Status > {
0 commit comments