use crate::endpoint_lib::diagnostic::DiagnosticCollector;
pub(crate) fn is_valid_host_label(label: &str, allow_dots: bool, e: &mut DiagnosticCollector) -> bool {
let bytes = label.as_bytes();
if allow_dots {
let mut start = 0;
for i in 0..bytes.len() {
if bytes[i] == b'.' {
if !is_valid_segment(bytes, start, i, e) {
return false;
}
start = i + 1;
}
}
is_valid_segment(bytes, start, bytes.len(), e)
} else {
is_valid_segment(bytes, 0, bytes.len(), e)
}
}
#[inline]
fn is_valid_segment(bytes: &[u8], start: usize, end: usize, e: &mut DiagnosticCollector) -> bool {
let len = end - start;
if len == 0 || len > 63 {
e.report_error("host was too short or too long");
return false;
}
if bytes[start] == b'-' {
e.report_error("cannot start with `-`");
return false;
}
for &b in &bytes[start..end] {
if !b.is_ascii_alphanumeric() && b != b'-' {
return false;
}
}
true
}
#[cfg(all(test, feature = "gated-tests"))]
mod test {
use proptest::proptest;
fn is_valid_host_label(label: &str, allow_dots: bool) -> bool {
super::is_valid_host_label(label, allow_dots, &mut DiagnosticCollector::new())
}
#[allow(clippy::bool_assert_comparison)]
#[test]
fn basic_cases() {
assert_eq!(is_valid_host_label("", false), false);
assert_eq!(is_valid_host_label("", true), false);
assert_eq!(is_valid_host_label(".", true), false);
assert_eq!(is_valid_host_label("a.b", true), true);
assert_eq!(is_valid_host_label("a.b", false), false);
assert_eq!(is_valid_host_label("a.b.", true), false);
assert_eq!(is_valid_host_label("a.b.c", true), true);
assert_eq!(is_valid_host_label("a_b", true), false);
assert_eq!(is_valid_host_label(&"a".repeat(64), false), false);
assert_eq!(is_valid_host_label(&format!("{}.{}", "a".repeat(63), "a".repeat(63)), true), true);
}
#[allow(clippy::bool_assert_comparison)]
#[test]
fn start_bounds() {
assert_eq!(is_valid_host_label("-foo", false), false);
assert_eq!(is_valid_host_label("-foo", true), false);
assert_eq!(is_valid_host_label(".foo", true), false);
assert_eq!(is_valid_host_label("a-b.foo", true), true);
}
#[allow(clippy::bool_assert_comparison)]
#[test]
fn non_ascii_rejected() {
assert_eq!(is_valid_host_label("café", false), false);
assert_eq!(is_valid_host_label("bücher", false), false);
assert_eq!(is_valid_host_label("日本語", false), false);
assert_eq!(is_valid_host_label("a.café.b", true), false);
assert_eq!(is_valid_host_label("🚀rocket", false), false);
assert_eq!(is_valid_host_label("abc123", false), true);
assert_eq!(is_valid_host_label("a-b-c", false), true);
}
use crate::endpoint_lib::diagnostic::DiagnosticCollector;
use proptest::prelude::*;
proptest! {
#[test]
fn no_panics(s in any::<String>(), dots in any::<bool>()) {
is_valid_host_label(&s, dots);
}
}
}