Skip to content

Commit 4f9b9dd

Browse files
committed
Conf: use canonical textual form for IP identifiers.
As stated in RFC8738 § 3: : The value field of the identifier MUST contain the textual form of the : address as defined in Section 2.1 of [RFC1123] for IPv4 and in Section : 4 of [RFC5952] for IPv6. This is the last piece required before declaring RFC8738 support. Fixes #5.
1 parent ab7386f commit 4f9b9dd

File tree

1 file changed

+38
-3
lines changed

1 file changed

+38
-3
lines changed

src/conf/order.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
use core::fmt;
77
use core::hash::{self, Hash, Hasher};
8-
use core::net::IpAddr;
98
use core::str::Utf8Error;
109

1110
use nginx_sys::{ngx_conf_t, ngx_http_server_name_t, ngx_str_t};
@@ -16,6 +15,7 @@ use ngx::ngx_log_error;
1615
use siphasher::sip::SipHasher;
1716
use thiserror::Error;
1817

18+
use crate::conf::ext::NgxConfExt;
1919
use crate::conf::identifier::Identifier;
2020
use 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.
259294
fn validate_host(cf: &ngx_conf_t, host: &'static str) -> Result<&'static str, Status> {

0 commit comments

Comments
 (0)