Skip to content

Commit f47b5ce

Browse files
fix(resolv): match glibc resolver label predicates (bd-vo0toc)
1 parent a1042d6 commit f47b5ce

2 files changed

Lines changed: 45 additions & 9 deletions

File tree

crates/frankenlibc-abi/src/glibc_internal_abi.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,36 +1662,36 @@ pub unsafe extern "C" fn __res_state() -> *mut c_void {
16621662

16631663
fn hostname_labels_ok(bytes: &[u8]) -> bool {
16641664
if bytes.is_empty() {
1665-
return false;
1665+
return true;
16661666
}
16671667

16681668
let mut label_len: usize = 0;
1669-
let mut last = b'.';
1669+
let mut at_label_start = true;
16701670
for &c in bytes {
16711671
if c == b'.' {
1672-
if label_len == 0 || last == b'-' {
1672+
if label_len == 0 {
16731673
return false;
16741674
}
16751675
label_len = 0;
1676-
last = c;
1677-
} else if c.is_ascii_alphanumeric() || c == b'-' {
1678-
if label_len == 0 && c == b'-' {
1676+
at_label_start = true;
1677+
} else if c.is_ascii_alphanumeric() || c == b'-' || c == b'_' {
1678+
if at_label_start && c == b'-' {
16791679
return false;
16801680
}
16811681
label_len += 1;
16821682
if label_len > 63 {
16831683
return false;
16841684
}
1685-
last = c;
1685+
at_label_start = false;
16861686
} else {
16871687
return false;
16881688
}
16891689
}
16901690

1691-
label_len != 0 && last != b'-'
1691+
label_len != 0 || bytes.last() == Some(&b'.')
16921692
}
16931693

1694-
// res_hnok: hostname — letters, digits, hyphens only (RFC 952)
1694+
// res_hnok: glibc-compatible hostname-label predicate.
16951695
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
16961696
pub unsafe extern "C" fn res_hnok(dn: *const c_char) -> c_int {
16971697
let Some(bytes) = (unsafe { dns_c_string_bytes(dn) }) else {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![cfg(target_os = "linux")]
2+
#![allow(unsafe_code)]
3+
//! Live host-glibc differential coverage for DNS label predicates.
4+
//!
5+
//! These predicates are consumed before resolver queries are assembled, so
6+
//! boundary labels must follow the host contract exactly rather than merely
7+
//! accepting the common alphanumeric cases.
8+
9+
use frankenlibc_abi::glibc_internal_abi as fl;
10+
use std::ffi::CString;
11+
use std::os::raw::{c_char, c_int};
12+
13+
unsafe extern "C" {
14+
fn res_dnok(name: *const c_char) -> c_int;
15+
fn res_hnok(name: *const c_char) -> c_int;
16+
}
17+
18+
#[test]
19+
fn resolver_label_boundaries_match_glibc() {
20+
for raw in [
21+
"",
22+
"-bad.example",
23+
"bad-.example",
24+
"example.com.",
25+
"two..dots",
26+
"_service._tcp.example",
27+
] {
28+
let name = CString::new(raw).unwrap();
29+
let fl_hnok = unsafe { fl::res_hnok(name.as_ptr()) };
30+
let host_hnok = unsafe { res_hnok(name.as_ptr()) };
31+
let fl_dnok = unsafe { fl::res_dnok(name.as_ptr()) };
32+
let host_dnok = unsafe { res_dnok(name.as_ptr()) };
33+
assert_eq!(fl_hnok, host_hnok, "res_hnok({raw:?})");
34+
assert_eq!(fl_dnok, host_dnok, "res_dnok({raw:?})");
35+
}
36+
}

0 commit comments

Comments
 (0)