Skip to content

Commit ed82a03

Browse files
committed
Support IP addresses in server names (#302)
1 parent 4166da7 commit ed82a03

File tree

4 files changed

+71
-52
lines changed

4 files changed

+71
-52
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::convert::{TryFrom, TryInto};
23
use std::ffi::{CStr, OsStr};
34
use std::fs::File;
@@ -176,12 +177,15 @@ impl rustls_client_config_builder {
176177

177178
/// Input to a custom certificate verifier callback. See
178179
/// rustls_client_config_builder_dangerous_set_certificate_verifier().
180+
///
181+
/// server_name can contain a hostname, an IPv4 address in textual form, or an
182+
/// IPv6 address in textual form.
179183
#[allow(non_camel_case_types)]
180184
#[repr(C)]
181185
pub struct rustls_verify_server_cert_params<'a> {
182186
pub end_entity_cert_der: rustls_slice_bytes<'a>,
183187
pub intermediate_certs_der: &'a rustls_slice_slice_bytes<'a>,
184-
pub dns_name: rustls_str<'a>,
188+
pub server_name: rustls_str<'a>,
185189
pub ocsp_response: rustls_slice_bytes<'a>,
186190
}
187191

@@ -233,11 +237,12 @@ impl rustls::client::ServerCertVerifier for Verifier {
233237
_now: SystemTime,
234238
) -> Result<ServerCertVerified, rustls::Error> {
235239
let cb = self.callback;
236-
let dns_name: &str = match server_name {
237-
rustls::ServerName::DnsName(n) => n.as_ref(),
240+
let server_name: Cow<'_, str> = match server_name {
241+
rustls::ServerName::DnsName(n) => n.as_ref().into(),
242+
rustls::ServerName::IpAddress(ip) => ip.to_string().into(),
238243
_ => return Err(rustls::Error::General("unknown name type".to_string())),
239244
};
240-
let dns_name: rustls_str = match dns_name.try_into() {
245+
let server_name: rustls_str = match server_name.as_ref().try_into() {
241246
Ok(r) => r,
242247
Err(NulByte {}) => return Err(rustls::Error::General("NUL byte in SNI".to_string())),
243248
};
@@ -251,7 +256,7 @@ impl rustls::client::ServerCertVerifier for Verifier {
251256
let params = rustls_verify_server_cert_params {
252257
end_entity_cert_der: end_entity.as_ref().into(),
253258
intermediate_certs_der: &intermediates,
254-
dns_name,
259+
server_name,
255260
ocsp_response: ocsp_response.into(),
256261
};
257262
let userdata = userdata_get().map_err(|_| {
@@ -282,18 +287,10 @@ impl rustls_client_config_builder {
282287
/// to make such mutation safe.
283288
///
284289
/// The callback receives certificate chain information as raw bytes.
285-
/// Currently this library offers no functions for C code to parse the
286-
/// certificates, so you'll need to bring your own certificate parsing library
290+
/// Currently this library offers no functions to parse the certificates,
291+
/// so you'll need to bring your own certificate parsing library
287292
/// if you need to parse them.
288293
///
289-
/// If you intend to write a verifier that accepts all certificates, be aware
290-
/// that special measures are required for IP addresses. Rustls currently
291-
/// (0.20.0) doesn't support building a ClientConnection with an IP address
292-
/// (because it's not a valid DnsNameRef). One workaround is to detect IP
293-
/// addresses and rewrite them to `example.invalid`, and _also_ to disable
294-
/// SNI via rustls_client_config_builder_set_enable_sni (IP addresses don't
295-
/// need SNI).
296-
///
297294
/// If the custom verifier accepts the certificate, it should return
298295
/// RUSTLS_RESULT_OK. Otherwise, it may return any other rustls_result error.
299296
/// Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_*
@@ -534,25 +531,29 @@ impl rustls_client_config {
534531
/// non-error, the memory pointed to by `conn_out` is modified to point at a
535532
/// valid rustls_connection. The caller now owns the rustls_connection and must
536533
/// call `rustls_connection_free` when done with it.
534+
///
535+
/// The server_name parameter can contain a hostname or an IP address in
536+
/// textual form (IPv4 or IPv6). This function will return an error if it
537+
/// cannot be parsed as one of those types.
537538
#[no_mangle]
538539
pub extern "C" fn rustls_client_connection_new(
539540
config: *const rustls_client_config,
540-
hostname: *const c_char,
541+
server_name: *const c_char,
541542
conn_out: *mut *mut rustls_connection,
542543
) -> rustls_result {
543544
ffi_panic_boundary! {
544-
let hostname: &CStr = unsafe {
545-
if hostname.is_null() {
545+
let server_name: &CStr = unsafe {
546+
if server_name.is_null() {
546547
return NullParameter;
547548
}
548-
CStr::from_ptr(hostname)
549+
CStr::from_ptr(server_name)
549550
};
550551
let config: Arc<ClientConfig> = try_arc_from_ptr!(config);
551-
let hostname: &str = match hostname.to_str() {
552+
let server_name: &str = match server_name.to_str() {
552553
Ok(s) => s,
553554
Err(std::str::Utf8Error { .. }) => return rustls_result::InvalidDnsNameError,
554555
};
555-
let server_name: rustls::ServerName = match hostname.try_into() {
556+
let server_name: rustls::ServerName = match server_name.try_into() {
556557
Ok(sn) => sn,
557558
Err(_) => return rustls_result::InvalidDnsNameError,
558559
};
@@ -645,4 +646,21 @@ mod tests {
645646
);
646647
rustls_connection::rustls_connection_free(conn);
647648
}
649+
650+
#[test]
651+
#[cfg_attr(miri, ignore)]
652+
fn test_client_connection_new_ipaddress() {
653+
let builder: *mut rustls_client_config_builder =
654+
rustls_client_config_builder::rustls_client_config_builder_new();
655+
let config = rustls_client_config_builder::rustls_client_config_builder_build(builder);
656+
let mut conn: *mut rustls_connection = null_mut();
657+
let result = rustls_client_config::rustls_client_connection_new(
658+
config,
659+
"198.51.100.198\0".as_ptr() as *const c_char,
660+
&mut conn,
661+
);
662+
if !matches!(result, rustls_result::Ok) {
663+
panic!("expected RUSTLS_RESULT_OK, got {:?}", result);
664+
}
665+
}
648666
}

src/rustls.h

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,14 @@ typedef void *rustls_verify_server_cert_user_data;
327327
/**
328328
* Input to a custom certificate verifier callback. See
329329
* rustls_client_config_builder_dangerous_set_certificate_verifier().
330+
*
331+
* server_name can contain a hostname, an IPv4 address in textual form, or an
332+
* IPv6 address in textual form.
330333
*/
331334
typedef struct rustls_verify_server_cert_params {
332335
struct rustls_slice_bytes end_entity_cert_der;
333336
const struct rustls_slice_slice_bytes *intermediate_certs_der;
334-
struct rustls_str dns_name;
337+
struct rustls_str server_name;
335338
struct rustls_slice_bytes ocsp_response;
336339
} rustls_verify_server_cert_params;
337340

@@ -403,11 +406,12 @@ typedef struct rustls_slice_u16 {
403406

404407
/**
405408
* The TLS Client Hello information provided to a ClientHelloCallback function.
406-
* `sni_name` is the SNI servername provided by the client. If the client
407-
* did not provide an SNI, the length of this `rustls_string` will be 0. The
408-
* signature_schemes carries the values supplied by the client or, should
409-
* the client not use this TLS extension, the default schemes in the rustls
410-
* library. See: <https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
409+
* `server_name` is the value of the ServerNameIndication extension provided
410+
* by the client. If the client did not send an SNI, the length of this
411+
* `rustls_string` will be 0. The signature_schemes field carries the values
412+
* supplied by the client or, if the client did not send this TLS extension,
413+
* the default schemes in the rustls library. See:
414+
* <https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
411415
* `alpn` carries the list of ALPN protocol names that the client proposed to
412416
* the server. Again, the length of this list will be 0 if none were supplied.
413417
*
@@ -419,7 +423,7 @@ typedef struct rustls_slice_u16 {
419423
* the rustls library is re-evaluating their current approach to client hello handling.
420424
*/
421425
typedef struct rustls_client_hello {
422-
struct rustls_str sni_name;
426+
struct rustls_str server_name;
423427
struct rustls_slice_u16 signature_schemes;
424428
const struct rustls_slice_slice_bytes *alpn;
425429
} rustls_client_hello;
@@ -987,18 +991,10 @@ rustls_result rustls_client_config_builder_new_custom(const struct rustls_suppor
987991
* to make such mutation safe.
988992
*
989993
* The callback receives certificate chain information as raw bytes.
990-
* Currently this library offers no functions for C code to parse the
991-
* certificates, so you'll need to bring your own certificate parsing library
994+
* Currently this library offers no functions to parse the certificates,
995+
* so you'll need to bring your own certificate parsing library
992996
* if you need to parse them.
993997
*
994-
* If you intend to write a verifier that accepts all certificates, be aware
995-
* that special measures are required for IP addresses. Rustls currently
996-
* (0.20.0) doesn't support building a ClientConnection with an IP address
997-
* (because it's not a valid DnsNameRef). One workaround is to detect IP
998-
* addresses and rewrite them to `example.invalid`, and _also_ to disable
999-
* SNI via rustls_client_config_builder_set_enable_sni (IP addresses don't
1000-
* need SNI).
1001-
*
1002998
* If the custom verifier accepts the certificate, it should return
1003999
* RUSTLS_RESULT_OK. Otherwise, it may return any other rustls_result error.
10041000
* Feel free to use an appropriate error from the RUSTLS_RESULT_CERT_*
@@ -1101,9 +1097,13 @@ void rustls_client_config_free(const struct rustls_client_config *config);
11011097
* non-error, the memory pointed to by `conn_out` is modified to point at a
11021098
* valid rustls_connection. The caller now owns the rustls_connection and must
11031099
* call `rustls_connection_free` when done with it.
1100+
*
1101+
* The server_name parameter can contain a hostname or an IP address in
1102+
* textual form (IPv4 or IPv6). This function will return an error if it
1103+
* cannot be parsed as one of those types.
11041104
*/
11051105
rustls_result rustls_client_connection_new(const struct rustls_client_config *config,
1106-
const char *hostname,
1106+
const char *server_name,
11071107
struct rustls_connection **conn_out);
11081108

11091109
/**
@@ -1496,9 +1496,9 @@ rustls_result rustls_server_connection_new(const struct rustls_server_config *co
14961496
* <https://docs.rs/rustls/0.21.0/rustls/server/struct.ServerConnection.html#method.server_name>
14971497
*/
14981498
rustls_result rustls_server_connection_get_server_name(const struct rustls_connection *conn,
1499-
uint8_t *buf,
1500-
size_t count,
1501-
size_t *out_n);
1499+
uint8_t *buf,
1500+
size_t count,
1501+
size_t *out_n);
15021502

15031503
/**
15041504
* Register a callback to be invoked when a connection created from this config

src/server.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,12 @@ impl ResolvesServerCert for ResolvesServerCertFromChoices {
430430
}
431431

432432
/// The TLS Client Hello information provided to a ClientHelloCallback function.
433-
/// `sni_name` is the SNI servername provided by the client. If the client
434-
/// did not provide an SNI, the length of this `rustls_string` will be 0. The
435-
/// signature_schemes carries the values supplied by the client or, should
436-
/// the client not use this TLS extension, the default schemes in the rustls
437-
/// library. See: <https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
433+
/// `server_name` is the value of the ServerNameIndication extension provided
434+
/// by the client. If the client did not send an SNI, the length of this
435+
/// `rustls_string` will be 0. The signature_schemes field carries the values
436+
/// supplied by the client or, if the client did not send this TLS extension,
437+
/// the default schemes in the rustls library. See:
438+
/// <https://docs.rs/rustls/0.20.0/rustls/internal/msgs/enums/enum.SignatureScheme.html>.
438439
/// `alpn` carries the list of ALPN protocol names that the client proposed to
439440
/// the server. Again, the length of this list will be 0 if none were supplied.
440441
///
@@ -446,7 +447,7 @@ impl ResolvesServerCert for ResolvesServerCertFromChoices {
446447
/// the rustls library is re-evaluating their current approach to client hello handling.
447448
#[repr(C)]
448449
pub struct rustls_client_hello<'a> {
449-
sni_name: rustls_str<'a>,
450+
server_name: rustls_str<'a>,
450451
signature_schemes: rustls_slice_u16<'a>,
451452
alpn: *const rustls_slice_slice_bytes<'a>,
452453
}
@@ -502,13 +503,13 @@ impl ClientHelloResolver {
502503

503504
impl ResolvesServerCert for ClientHelloResolver {
504505
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
505-
let sni_name: &str = {
506+
let server_name: &str = {
506507
match client_hello.server_name() {
507508
Some(c) => c,
508509
None => "",
509510
}
510511
};
511-
let sni_name: rustls_str = match sni_name.try_into() {
512+
let server_name: rustls_str = match server_name.try_into() {
512513
Ok(r) => r,
513514
Err(_) => return None,
514515
};
@@ -526,7 +527,7 @@ impl ResolvesServerCert for ClientHelloResolver {
526527
let alpn = rustls_slice_slice_bytes { inner: &alpn };
527528
let signature_schemes: rustls_slice_u16 = (&*mapped_sigs).into();
528529
let hello = rustls_client_hello {
529-
sni_name,
530+
server_name,
530531
signature_schemes,
531532
alpn: &alpn,
532533
};

0 commit comments

Comments
 (0)