Skip to content

Commit 14a4dd6

Browse files
committed
Conf: borrow identifiers as &str from configuration pool.
This allows us to skip revalidation of UTF-8 identifier strings and reallocation of the order object. See also 511be2a.
1 parent e5dad32 commit 14a4dd6

File tree

5 files changed

+52
-77
lines changed

5 files changed

+52
-77
lines changed

src/conf.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub struct AcmeServerConfig {
5353
pub issuer: ngx_str_t,
5454
// Only one certificate order per server block is currently allowed. For multiple entries we
5555
// will have to implement certificate selection in the variable handler.
56-
pub order: Option<CertificateOrder<ngx_str_t, Pool>>,
56+
pub order: Option<CertificateOrder<&'static str, Pool>>,
5757
}
5858

5959
pub static mut NGX_HTTP_ACME_COMMANDS: [ngx_command_t; 4] = [
@@ -308,7 +308,7 @@ extern "C" fn cmd_add_certificate(
308308
return c"\"issuer\" is missing".as_ptr().cast_mut();
309309
}
310310

311-
let mut order = CertificateOrder::<ngx_str_t, Pool>::new_in(cf.pool());
311+
let mut order = CertificateOrder::<&'static str, Pool>::new_in(cf.pool());
312312

313313
for value in &args[2..] {
314314
if let Some(key) = value.strip_prefix(b"key=") {
@@ -320,7 +320,17 @@ extern "C" fn cmd_add_certificate(
320320
continue;
321321
}
322322

323-
if let Err(err) = order.try_add_identifier(value) {
323+
if value.is_empty() {
324+
return NGX_CONF_INVALID_VALUE;
325+
}
326+
327+
// SAFETY: the value is not empty, well aligned, and the conversion result is assigned to an
328+
// object in the same pool.
329+
let Ok(value) = (unsafe { conf_value_to_str(value) }) else {
330+
return NGX_CONF_INVALID_VALUE;
331+
};
332+
333+
if let Err(err) = order.try_add_identifier(cf, value) {
324334
return cf.error(args[0], &err);
325335
}
326336
}
@@ -779,7 +789,7 @@ fn conf_check_nargs(cmd: &ngx_command_t, nargs: ngx_uint_t) -> bool {
779789
/// all the configuration objects. But this process role is not capable of serving connections or
780790
/// running background tasks, and thus will not create additional borrows with potentially extended
781791
/// lifetime.
782-
unsafe fn conf_value_to_str(value: &ngx_str_t) -> Result<&'static str, core::str::Utf8Error> {
792+
pub unsafe fn conf_value_to_str(value: &ngx_str_t) -> Result<&'static str, core::str::Utf8Error> {
783793
if value.len == 0 {
784794
Ok("")
785795
} else {

src/conf/identifier.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,6 @@ impl<S> Identifier<S> {
3939
}
4040
}
4141

42-
/// Borrows the current identifier with a str reference as an underlying data.
43-
pub fn as_str(&self) -> Result<Identifier<&str>, str::Utf8Error>
44-
where
45-
S: AsRef<[u8]>,
46-
{
47-
match self {
48-
Identifier::Dns(value) => str::from_utf8(value.as_ref()).map(Identifier::Dns),
49-
Identifier::Ip(value) => str::from_utf8(value.as_ref()).map(Identifier::Ip),
50-
Identifier::Other { kind, value } => Ok(Identifier::Other {
51-
kind: str::from_utf8(kind.as_ref())?,
52-
value: str::from_utf8(value.as_ref())?,
53-
}),
54-
}
55-
}
56-
5742
pub fn value(&self) -> &S {
5843
match self {
5944
Identifier::Dns(value) => value,

src/conf/issuer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub struct Issuer {
6060
// Generated fields
6161
// ngx_ssl_t stores a pointer to itself in SSL_CTX ex_data.
6262
pub ssl: Box<NgxSsl, Pool>,
63-
pub orders: RbTreeMap<CertificateOrder<ngx_str_t, Pool>, CertificateContext, Pool>,
63+
pub orders: RbTreeMap<CertificateOrder<&'static str, Pool>, CertificateContext, Pool>,
6464
pub pkey: Option<PKey<Private>>,
6565
pub data: Option<&'static RwLock<IssuerContext>>,
6666
}
@@ -228,7 +228,7 @@ impl Issuer {
228228
pub fn add_certificate_order(
229229
&mut self,
230230
cf: &mut ngx_conf_t,
231-
order: &CertificateOrder<ngx_str_t, Pool>,
231+
order: &CertificateOrder<&'static str, Pool>,
232232
) -> Result<(), Status> {
233233
if self.orders.get(order).is_none() {
234234
ngx_log_debug!(
@@ -412,7 +412,7 @@ impl StateDir {
412412
pub fn load_certificate(
413413
&self,
414414
cf: &mut ngx_conf_t,
415-
order: &CertificateOrder<ngx_str_t, Pool>,
415+
order: &CertificateOrder<&'static str, Pool>,
416416
) -> Result<CertificateContextInner<Pool>, CachedCertificateError> {
417417
use openssl_foreign_types::ForeignType;
418418
#[cfg(ngx_ssl_cache)]

src/conf/order.rs

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// This source code is licensed under the Apache License, Version 2.0 license found in the
44
// LICENSE file in the root directory of this source tree.
55

6-
use core::fmt::{self, Write};
6+
use core::fmt;
77
use core::hash::{self, Hash, Hasher};
88
use core::net::IpAddr;
99
use core::str::Utf8Error;
@@ -60,20 +60,6 @@ where
6060
dns.or_else(|| self.identifiers.first())
6161
.map(Identifier::value)
6262
}
63-
64-
pub fn to_str_order<NewA>(&self, alloc: NewA) -> CertificateOrder<&str, NewA>
65-
where
66-
NewA: Allocator + Clone,
67-
S: AsRef<[u8]>,
68-
{
69-
let mut identifiers = Vec::<Identifier<&str>, NewA>::new_in(alloc);
70-
identifiers.extend(self.identifiers.iter().map(|x| x.as_str().unwrap()));
71-
72-
CertificateOrder {
73-
identifiers,
74-
key: self.key.clone(),
75-
}
76-
}
7763
}
7864

7965
impl<S: Hash, A> Hash for CertificateOrder<S, A>
@@ -150,8 +136,6 @@ where
150136
pub enum IdentifierError {
151137
#[error("memory allocation failed")]
152138
Alloc(#[from] AllocError),
153-
#[error("empty server name")]
154-
Empty,
155139
#[error("invalid server name")]
156140
Invalid,
157141
#[error("invalid UTF-8 string")]
@@ -160,9 +144,9 @@ pub enum IdentifierError {
160144
Wildcard,
161145
}
162146

163-
impl CertificateOrder<ngx_str_t, Pool> {
147+
impl CertificateOrder<&'static str, Pool> {
164148
#[inline]
165-
fn push(&mut self, id: Identifier<ngx_str_t>) -> Result<(), AllocError> {
149+
fn push(&mut self, id: Identifier<&'static str>) -> Result<(), AllocError> {
166150
self.identifiers.try_reserve(1).map_err(|_| AllocError)?;
167151
self.identifiers.push(id);
168152
Ok(())
@@ -190,29 +174,30 @@ impl CertificateOrder<ngx_str_t, Pool> {
190174
continue;
191175
}
192176

193-
self.try_add_identifier(&server_name.name)?;
177+
// SAFETY: the value is not empty, well aligned, and the conversion result is assigned
178+
// to an object in the same pool.
179+
let value = unsafe { super::conf_value_to_str(&server_name.name)? };
180+
181+
self.try_add_identifier(cf, value)?;
194182
}
195183

196184
Ok(())
197185
}
198186

199-
pub fn try_add_identifier(&mut self, value: &ngx_str_t) -> Result<(), IdentifierError> {
200-
if value.is_empty() {
201-
return Err(IdentifierError::Empty);
202-
}
203-
204-
if core::str::from_utf8(value.as_ref())?
205-
.parse::<IpAddr>()
206-
.is_ok()
207-
{
208-
return self.push(Identifier::Ip(*value)).map_err(Into::into);
187+
pub fn try_add_identifier(
188+
&mut self,
189+
cf: &ngx_conf_t,
190+
value: &'static str,
191+
) -> Result<(), IdentifierError> {
192+
if value.parse::<IpAddr>().is_ok() {
193+
return self.push(Identifier::Ip(value)).map_err(Into::into);
209194
}
210195

211-
if value.as_bytes().contains(&b'*') {
196+
if value.contains('*') {
212197
return Err(IdentifierError::Wildcard);
213198
}
214199

215-
let host = validate_host(self.identifiers.allocator(), *value).map_err(|st| {
200+
let host = validate_host(cf, value).map_err(|st| {
216201
if st == Status::NGX_ERROR {
217202
IdentifierError::Alloc(AllocError)
218203
} else {
@@ -226,18 +211,14 @@ impl CertificateOrder<ngx_str_t, Pool> {
226211
* See <https://nginx.org/en/docs/http/server_names.html>
227212
*/
228213

229-
if let Some(host) = host.strip_prefix(b".") {
230-
let mut www = NgxString::new_in(self.identifiers.allocator());
231-
www.try_reserve_exact(host.len + 4)
214+
if let Some(host) = host.strip_prefix(".") {
215+
let mut www = Vec::new_in(self.identifiers.allocator().clone());
216+
www.try_reserve_exact(host.len() + 4)
232217
.map_err(|_| AllocError)?;
233-
// write to a buffer of sufficient size will succeed
234-
let _ = write!(&mut www, "www.{host}");
235-
236-
let parts = www.into_raw_parts();
237-
let www = ngx_str_t {
238-
data: parts.0,
239-
len: parts.1,
240-
};
218+
www.extend_from_slice(b"www.");
219+
www.extend_from_slice(host.as_bytes());
220+
// The buffer is owned by ngx_pool_t and does not leak.
221+
let www = core::str::from_utf8(www.leak())?;
241222

242223
self.push(Identifier::Dns(www))?;
243224
self.push(Identifier::Dns(host))?;
@@ -273,11 +254,17 @@ where
273254
}
274255
}
275256

276-
fn validate_host(pool: &Pool, mut host: ngx_str_t) -> Result<ngx_str_t, Status> {
277-
let mut pool = pool.clone();
278-
let rc = Status(unsafe { nginx_sys::ngx_http_validate_host(&mut host, pool.as_mut(), 1) });
257+
/// Checks if the value is a valid domain name and returns a canonical (lowercase) form,
258+
/// reallocated on the configuration pool if necessary.
259+
fn validate_host(cf: &ngx_conf_t, host: &'static str) -> Result<&'static str, Status> {
260+
let mut host = ngx_str_t {
261+
data: host.as_ptr().cast_mut(),
262+
len: host.len(),
263+
};
264+
let rc = Status(unsafe { nginx_sys::ngx_http_validate_host(&mut host, cf.pool, 0) });
279265
if rc != Status::NGX_OK {
280266
return Err(rc);
281267
}
282-
Ok(host)
268+
269+
unsafe { super::conf_value_to_str(&host) }.map_err(|_| Status::NGX_ERROR)
283270
}

src/lib.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use nginx_sys::{
1414
ngx_conf_t, ngx_cycle_t, ngx_http_add_variable, ngx_http_module_t, ngx_int_t, ngx_module_t,
1515
ngx_uint_t, NGX_HTTP_MODULE, NGX_LOG_ERR, NGX_LOG_INFO, NGX_LOG_NOTICE, NGX_LOG_WARN,
1616
};
17-
use ngx::allocator::AllocError;
1817
use ngx::core::{Status, NGX_CONF_ERROR, NGX_CONF_OK};
1918
use ngx::http::{HttpModule, HttpModuleMainConf, HttpModuleServerConf, Merge};
2019
use ngx::log::ngx_cycle_log;
@@ -334,13 +333,7 @@ async fn ngx_http_acme_update_certificates_for_issuer(
334333
}
335334
}
336335

337-
let alloc = crate::util::OwnedPool::new(nginx_sys::NGX_DEFAULT_POOL_SIZE as _, log)
338-
.map_err(|_| AllocError)?;
339-
340-
// Acme client wants &str and we already validated that the identifiers are valid UTF-8.
341-
let str_order = order.to_str_order(&*alloc);
342-
343-
let cert_next = match client.new_certificate(&str_order).await {
336+
let cert_next = match client.new_certificate(order).await {
344337
Ok(ref val) => {
345338
let pkey = Zeroizing::new(val.pkey.private_key_to_pem_pkcs8()?);
346339
let x509 = X509::from_pem(&val.chain)?;

0 commit comments

Comments
 (0)