1fb1638615
Per NETWORKING-AND-DRIVERS-CODE-ASSESSMENT-2026-07-27.md §15 Tier A + Tier B quick wins (Q3/Q4/Q5/Q6/Q2): - arpa_inet::inet_ntop: add IPv6 branch (RFC 5952). Real longest-run '::' compression with tie-break; rejects AFs other than INET/INET6; proper ENOSPC on size<46. 9 unit tests cover unspecified, loopback, routable, link-local, no-compression, tie-break, trailing-run, size-too-small, and unsupported-family cases. - netinet_ip: populate the 3-line skeleton with the glibc netinet/ip.h constant set (IP_OPTIONS, IP_TTL, IP_HDRINCL, IP_RECVOPTS, IP_RECVDSTADDR, IP_PKTINFO, IP_RECVPKTINFO, IP_MINTTL, IP_RECVERR, IP_DONTFRAG, IP_PMTUDISC_*, IP_BOUND_IF, IP_ORIGDSTADDR, etc.) plus struct ip (BSD) and struct iphdr (Linux) and four accessors. 5 unit tests cover roundtrip / alignment / constant values / PMTUDISC distinctness. - netdb::lookup: add lookup_host_v6 (AAAA DNS query type 0x001c, 16-byte answer parse) and parse_ipv6_string delegating to inet_pton(AF_INET6, ...). Shared lookup_host_query helper for A + AAAA. lookup_host refactored to share the query path. - netdb::getaddrinfo: full POSIX hints implementation. Honors ai_family (AF_UNSPEC / AF_INET / AF_INET6), ai_socktype, ai_protocol. Validates flag combinations (EAI_BADFLAGS for unknown bits, AI_NUMERICSERV with no service, AI_CANONNAME with no node). Implements AI_NUMERICHOST (v4 and v6), AI_NUMERICSERV (with numeric and getservbyname fallback), AI_PASSIVE (0.0.0.0 vs 127.0.0.1 default), AI_CANONNAME (sets canonname on first emitted entry), AI_V4MAPPED (synthesize ::ffff:a.b.c.d from A results when no AAAA and AF_INET6 requested). Emits IPv6 entries with proper sockaddr_in6 (16 bytes) when v6 results exist. 9 unit tests cover numeric-v4, socktype/protocol filter, unknown-flag rejection, canonname-without-node rejection, numerichost+unparseable, numericserv rejection, numericserv+numeric success, IPv6 parse roundtrip, garbage rejection, and IPv4-mapped-IPv6 construction. - sys_ioctl::ioctl_inner: add FIONREAD handler that attempts dup_read(fd, 'fionread', &count). Returns ENOTTY if the scheme does not advertise a fionread path (consistent with Linux's TIOCNOTTY behavior for unsupported ioctls). - PalSocket::accept4: new trait method. Linux impl uses __NR_accept4 (288 on x86_64, 242 on aarch64) via sc::syscall5 with EINVAL on unknown flag bits. Redox impl falls back to accept() + fcntl() for SOCK_CLOEXEC / SOCK_NONBLOCK (mirrors the Linux 2.6.27 pre-syscall situation; the race is documented). Public C-callable accept4 added in header/sys_socket/mod.rs. Closes the #1 gap for Qt / glib compatibility. Three pre-existing errors in ld_so/dso.rs and ld_so/linker.rs remain in the working tree but are unrelated to this commit.
578 lines
18 KiB
Rust
578 lines
18 KiB
Rust
//! `arpa/inet.h` implementation.
|
|
//!
|
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
|
|
|
|
use core::{
|
|
ptr, slice,
|
|
str::{self, FromStr},
|
|
};
|
|
|
|
use crate::{
|
|
c_str::CStr,
|
|
header::{
|
|
bits_arpainet::ntohl,
|
|
errno::{EAFNOSUPPORT, ENOSPC},
|
|
netinet_in::{INADDR_NONE, in_addr, in_addr_t, in6_addr},
|
|
sys_socket::{
|
|
constants::{AF_INET, AF_INET6},
|
|
socklen_t,
|
|
},
|
|
},
|
|
io::Write,
|
|
platform::{
|
|
self,
|
|
types::{c_char, c_int, c_void},
|
|
},
|
|
raw_cell::RawCell,
|
|
};
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
|
|
///
|
|
/// Converts the string pointed to by `cp`, in the standard IPv4 dotted
|
|
/// decimal notation, to an integer value suitable for use as an Internet
|
|
/// address.
|
|
///
|
|
/// # Deprecated
|
|
/// The `inet_addr()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 8.
|
|
///
|
|
/// Applications should prefer `inet_pton()` over `inet_addr()` for the
|
|
/// following reasons:
|
|
/// - The return value from `inet_addr()` when converting 255.255.255.255 is
|
|
/// indistinguishable from an error.
|
|
/// - The `inet_pton()` function supports multiple address families.
|
|
/// - The alternative textual representations supported by `inet_addr()` (but
|
|
/// not `inet_pton()`) are often used maliciously to confuse or mislead
|
|
/// users (e.g, for phishing).
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_addr(cp: *const c_char) -> in_addr_t {
|
|
let mut val: in_addr = in_addr { s_addr: 0 };
|
|
|
|
if unsafe { inet_aton(cp, &raw mut val) } > 0 {
|
|
val.s_addr
|
|
} else {
|
|
INADDR_NONE
|
|
}
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/inet_aton.3.html>.
|
|
///
|
|
/// Converts the Internet host address `cp` from the IPv4 numbers-and-dots
|
|
/// notation into binary form (in network byte order) and stores it in the
|
|
/// structure that `inp` points to.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
|
|
let cp_cstr = unsafe { CStr::from_ptr(cp) };
|
|
let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') };
|
|
let count = parts.clone().count();
|
|
if count > 4 {
|
|
return 0;
|
|
}
|
|
let mut result = 0;
|
|
let mut parts_iter = parts.peekable();
|
|
let mut index = 0u32;
|
|
while index < 4
|
|
&& let Some(part) = parts_iter.next()
|
|
{
|
|
if let Ok(parsed_value) = {
|
|
if let Some(hex_or_oct) = part.strip_prefix('0')
|
|
&& part.len() > 1
|
|
{
|
|
match hex_or_oct.bytes().next() {
|
|
Some(b'x' | b'X') => u32::from_str_radix(&hex_or_oct[1..], 16),
|
|
// While it is true that C2Y accepts 0o and 0O as octal prefixes, C17 doesn't
|
|
// The POSIX spec defers to C17
|
|
// see https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html
|
|
_ => u32::from_str_radix(hex_or_oct, 8),
|
|
}
|
|
} else {
|
|
part.parse::<u32>()
|
|
}
|
|
} {
|
|
if parts_iter.peek().is_some() {
|
|
// this is not the last part
|
|
if parsed_value > 0xff {
|
|
return 0;
|
|
}
|
|
result += parsed_value << (24 - index * 8);
|
|
} else {
|
|
// this is the last part
|
|
if index > 0 && parsed_value >= 1 << (32 - index * 8) {
|
|
return 0;
|
|
} else {
|
|
result += parsed_value;
|
|
}
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
index += 1;
|
|
}
|
|
unsafe { (*inp.cast::<in_addr>()).s_addr = result.to_be() };
|
|
1
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_lnaof.html>.
|
|
///
|
|
/// Takes an Internet host address specified by `in` and extracts the local
|
|
/// network address part, in host byte order.
|
|
///
|
|
/// # Deprecation
|
|
/// The `inet_lnaof()` function was specified in Networking Services Issue 5,
|
|
/// but not in the Open Group Base Specifications Issue 6 and later.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn inet_lnaof(r#in: in_addr) -> in_addr_t {
|
|
if r#in.s_addr >> 24 < 128 {
|
|
r#in.s_addr & 0xff_ffff
|
|
} else if r#in.s_addr >> 24 < 192 {
|
|
r#in.s_addr & 0xffff
|
|
} else {
|
|
r#in.s_addr & 0xff
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_makeaddr.html>.
|
|
///
|
|
/// Takes the Internet network number specified by `net` and the local network
|
|
/// address specified by `lna`, both in host byte order, and constructs an
|
|
/// Internet address from them.
|
|
///
|
|
/// # Deprecation
|
|
/// The `inet_makeaddr()` function was specified in Networking Services Issue
|
|
/// 5, but not in the Open Group Base Specifications Issue 6 and later.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn inet_makeaddr(net: in_addr_t, lna: in_addr_t) -> in_addr {
|
|
let mut output: in_addr = in_addr { s_addr: 0 };
|
|
|
|
if net < 256 {
|
|
output.s_addr = lna | net << 24;
|
|
} else if net < 65536 {
|
|
output.s_addr = lna | net << 16;
|
|
} else {
|
|
output.s_addr = lna | net << 8;
|
|
}
|
|
|
|
output
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_netof.html>.
|
|
///
|
|
/// Takes an Internet host address specified by `in` and extracts the network
|
|
/// number part, in host byte order.
|
|
///
|
|
/// # Deprecation
|
|
/// The `inet_netof()` function was specified in Networking Services Issue 5,
|
|
/// but not in the Open Group Base Specifications Issue 6 and later.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn inet_netof(r#in: in_addr) -> in_addr_t {
|
|
if r#in.s_addr >> 24 < 128 {
|
|
r#in.s_addr & 0xff_ffff
|
|
} else if r#in.s_addr >> 24 < 192 {
|
|
r#in.s_addr & 0xffff
|
|
} else {
|
|
r#in.s_addr & 0xff
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_network.html>.
|
|
///
|
|
/// Converts the string pointed to by `cp`, in the Internet standard dot
|
|
/// notation, to an integer value suitable for use as an Internet network
|
|
/// number.
|
|
///
|
|
/// # Deprecation
|
|
/// The `inet_network()` function was specified in Networking Services Issue 5,
|
|
/// but not in the Open Group Base Specifications Issue 6 and later.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_network(cp: *const c_char) -> in_addr_t {
|
|
ntohl(unsafe {
|
|
#[allow(deprecated)]
|
|
inet_addr(cp)
|
|
})
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
|
|
///
|
|
/// Converts the Internet host address specified by `in` to a string in the
|
|
/// Internet standard dot notation.
|
|
///
|
|
/// # Deprecation
|
|
/// The `inet_ntoa()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 8.
|
|
///
|
|
/// Applications should prefer `inet_ntop()` over `inet_ntoa()` as it supports
|
|
/// multiple address families and is thread-safe.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_ntoa(r#in: in_addr) -> *mut c_char {
|
|
static NTOA_ADDR: RawCell<[c_char; 16]> = RawCell::new([0; 16]);
|
|
|
|
unsafe {
|
|
let ptr = inet_ntop(
|
|
AF_INET,
|
|
ptr::from_ref::<in_addr>(&r#in).cast::<c_void>(),
|
|
NTOA_ADDR.unsafe_mut().as_mut_ptr(),
|
|
NTOA_ADDR.unsafe_ref().len() as socklen_t,
|
|
);
|
|
// Mutable pointer is required, inet_ntop returns destination as const pointer
|
|
ptr.cast_mut()
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
|
|
///
|
|
/// Converts a numeric address into a text string suitable for presentation.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_ntop(
|
|
af: c_int,
|
|
src: *const c_void,
|
|
dst: *mut c_char,
|
|
size: socklen_t,
|
|
) -> *const c_char {
|
|
if af == AF_INET {
|
|
if (size as usize) < 16 {
|
|
platform::ERRNO.set(ENOSPC);
|
|
return ptr::null();
|
|
}
|
|
let s_addr = unsafe {
|
|
slice::from_raw_parts(
|
|
ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
|
|
4,
|
|
)
|
|
};
|
|
let mut w = platform::StringWriter(dst, size as usize);
|
|
let _ = write!(
|
|
w,
|
|
"{}.{}.{}.{}\0",
|
|
s_addr[0], s_addr[1], s_addr[2], s_addr[3]
|
|
);
|
|
dst
|
|
} else if af == AF_INET6 {
|
|
// IPv6 textual representation per RFC 5952:
|
|
// - 8 groups of 4 lowercase hex digits separated by colons
|
|
// - Longest run of consecutive all-zero groups compressed to "::"
|
|
// - Ties broken by first occurrence
|
|
// - Single zero group is NOT compressed
|
|
// INET6_ADDRSTRLEN == 46 (39 chars + trailing NUL + slack)
|
|
if (size as usize) < crate::header::netinet_in::INET6_ADDRSTRLEN as usize {
|
|
platform::ERRNO.set(ENOSPC);
|
|
return ptr::null();
|
|
}
|
|
let bytes = unsafe {
|
|
slice::from_raw_parts(
|
|
ptr::from_ref(&(*(src.cast::<in6_addr>())).s6_addr).cast::<u8>(),
|
|
16,
|
|
)
|
|
};
|
|
let mut groups = [0u16; 8];
|
|
for (i, g) in groups.iter_mut().enumerate() {
|
|
*g = u16::from_be_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
|
|
}
|
|
|
|
let mut best_start = usize::MAX;
|
|
let mut best_len = 0usize;
|
|
let mut cur_start = usize::MAX;
|
|
let mut cur_len = 0usize;
|
|
for i in 0..8 {
|
|
if groups[i] == 0 {
|
|
if cur_start == usize::MAX {
|
|
cur_start = i;
|
|
cur_len = 1;
|
|
} else {
|
|
cur_len += 1;
|
|
}
|
|
if cur_len > best_len {
|
|
best_len = cur_len;
|
|
best_start = cur_start;
|
|
}
|
|
} else {
|
|
cur_start = usize::MAX;
|
|
cur_len = 0;
|
|
}
|
|
}
|
|
if best_len < 2 {
|
|
best_start = usize::MAX;
|
|
}
|
|
|
|
let mut w = platform::StringWriter(dst, size as usize);
|
|
let mut i = 0usize;
|
|
let mut after_ellipsis = false;
|
|
while i < 8 {
|
|
if i == best_start {
|
|
let _ = write!(w, "::");
|
|
after_ellipsis = true;
|
|
i += best_len;
|
|
continue;
|
|
}
|
|
if !after_ellipsis && i > 0 {
|
|
let _ = write!(w, ":");
|
|
}
|
|
let _ = write!(w, "{:x}", groups[i]);
|
|
after_ellipsis = false;
|
|
i += 1;
|
|
}
|
|
let _ = write!(w, "\0");
|
|
dst
|
|
} else {
|
|
platform::ERRNO.set(EAFNOSUPPORT);
|
|
ptr::null()
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
|
|
///
|
|
/// Converts an address in its standard text presentation form into its
|
|
/// numeric binary form.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int {
|
|
if af == AF_INET {
|
|
let s_addr = unsafe {
|
|
slice::from_raw_parts_mut(
|
|
ptr::from_mut(&mut (*dst.cast::<in_addr>()).s_addr).cast::<u8>(),
|
|
4,
|
|
)
|
|
};
|
|
let src_cstr = unsafe { CStr::from_ptr(src) };
|
|
let mut octets = unsafe { str::from_utf8_unchecked(src_cstr.to_bytes()).split('.') };
|
|
for part in s_addr.iter_mut().take(4) {
|
|
if let Some(n) = octets
|
|
.next()
|
|
.filter(|x| x.len() <= 3)
|
|
.and_then(|x| u8::from_str(x).ok())
|
|
{
|
|
*part = n;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
if octets.next().is_none() {
|
|
1 // Success
|
|
} else {
|
|
0
|
|
}
|
|
} else if af == AF_INET6 {
|
|
let src_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(src).to_bytes()) };
|
|
let mut chunks = vec![src_str];
|
|
let colons = src_str.bytes().filter(|&c| c == b':').count();
|
|
if !(2..=7).contains(&colons) {
|
|
return 0;
|
|
}
|
|
let dots = src_str.bytes().filter(|&c| c == b'.').count();
|
|
if dots != 0 && dots != 3 {
|
|
return 0;
|
|
}
|
|
let double_colon = src_str.find("::");
|
|
if colons < 2
|
|
|| double_colon.is_some() && ((dots == 0 && colons > 7) || (dots == 3 && colons > 6))
|
|
|| double_colon.is_none() && ((dots == 0 && colons != 7) || (dots == 3 && colons != 6))
|
|
{
|
|
return 0;
|
|
}
|
|
if dots == 3
|
|
&& let Some(first_dot) = src_str.find('.')
|
|
&& let Some(last_colon) = src_str.find(':')
|
|
&& last_colon > first_dot
|
|
{
|
|
return 0;
|
|
}
|
|
if let Some(first) = src_str.find("::")
|
|
&& let Some(last) = src_str.rfind("::")
|
|
{
|
|
// :: is allowed only once
|
|
if first != last {
|
|
return 0;
|
|
}
|
|
chunks = vec![&src_str[..first], &src_str[(first + 2)..]]
|
|
}
|
|
|
|
let s6_addr = unsafe {
|
|
slice::from_raw_parts_mut(
|
|
ptr::from_mut(&mut (*dst.cast::<in6_addr>()).s6_addr).cast::<u16>(),
|
|
8,
|
|
)
|
|
};
|
|
s6_addr.iter_mut().for_each(|w| *w = 0);
|
|
|
|
for (count, &chunk) in chunks
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, &chunk)| !chunk.is_empty())
|
|
{
|
|
let mut parts = s6_addr
|
|
.iter_mut()
|
|
.skip(count * (8 - chunk.split(':').count() - dots / 3));
|
|
let mut words = chunk.split(':');
|
|
while let Some(word) = words.next()
|
|
&& let Some(part) = parts.next()
|
|
{
|
|
if word.is_empty() {
|
|
break;
|
|
} else if word.len() <= 4
|
|
&& let Some(n) = u16::from_str_radix(word, 16).ok()
|
|
{
|
|
*part = n.to_be();
|
|
} else if word.contains('.') {
|
|
let bytes =
|
|
unsafe { slice::from_raw_parts_mut(ptr::from_mut(part).cast::<u8>(), 4) };
|
|
let mut octets = word.split('.');
|
|
for byte in bytes {
|
|
if let Some(octet) = octets.next() {
|
|
if octet.len() > 1 && octet.starts_with('0') {
|
|
return 0;
|
|
}
|
|
if let Ok(value) = u8::from_str(octet) {
|
|
*byte = value
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
1 // Success
|
|
} else {
|
|
platform::ERRNO.set(EAFNOSUPPORT);
|
|
-1
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn format_v6(bytes: [u8; 16]) -> Option<alloc::string::String> {
|
|
let addr = in6_addr { s6_addr: bytes };
|
|
let mut buf = [0u8; 64];
|
|
let ret = unsafe {
|
|
inet_ntop(
|
|
AF_INET6,
|
|
ptr::from_ref(&addr).cast::<c_void>(),
|
|
buf.as_mut_ptr().cast::<c_char>(),
|
|
buf.len() as socklen_t,
|
|
)
|
|
};
|
|
if ret.is_null() {
|
|
None
|
|
} else {
|
|
let nul = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
|
|
core::str::from_utf8(&buf[..nul])
|
|
.ok()
|
|
.map(|s| alloc::string::String::from(s))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_unspecified() {
|
|
assert_eq!(format_v6([0u8; 16]).as_deref(), Some("::"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_loopback() {
|
|
let mut bytes = [0u8; 16];
|
|
bytes[15] = 1;
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("::1"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_routable() {
|
|
let mut bytes = [0u8; 16];
|
|
bytes[0] = 0x20;
|
|
bytes[1] = 0x01;
|
|
bytes[2] = 0x0d;
|
|
bytes[3] = 0xb8;
|
|
bytes[15] = 0x01;
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("2001:db8::1"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_link_local() {
|
|
let mut bytes = [0u8; 16];
|
|
bytes[0] = 0xfe;
|
|
bytes[1] = 0x80;
|
|
bytes[15] = 0x01;
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("fe80::1"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_no_compression_single_zero() {
|
|
let bytes = [
|
|
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
|
|
0x00, 0x05,
|
|
];
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("2001:db8:0:1:2:3:4:5"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_tie_break_first_wins() {
|
|
let bytes = [
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
|
|
];
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("::1:0:0:1"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_trailing_run() {
|
|
let bytes = [
|
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00,
|
|
];
|
|
assert_eq!(format_v6(bytes).as_deref(), Some("1::"));
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v4_loopback() {
|
|
let addr = in_addr {
|
|
s_addr: 0x7f000001u32.to_be(),
|
|
};
|
|
let mut buf = [0u8; 32];
|
|
let ret = unsafe {
|
|
inet_ntop(
|
|
AF_INET,
|
|
ptr::from_ref(&addr).cast::<c_void>(),
|
|
buf.as_mut_ptr().cast::<c_char>(),
|
|
buf.len() as socklen_t,
|
|
)
|
|
};
|
|
assert!(!ret.is_null());
|
|
let nul = buf.iter().position(|&b| b == 0).unwrap();
|
|
assert_eq!(core::str::from_utf8(&buf[..nul]).unwrap(), "127.0.0.1");
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_unsupported_family() {
|
|
let mut buf = [0u8; 32];
|
|
let ret = unsafe {
|
|
inet_ntop(
|
|
9999,
|
|
ptr::null(),
|
|
buf.as_mut_ptr().cast::<c_char>(),
|
|
buf.len() as socklen_t,
|
|
)
|
|
};
|
|
assert!(ret.is_null());
|
|
}
|
|
|
|
#[test]
|
|
fn inet_ntop_v6_size_too_small() {
|
|
let addr = in6_addr { s6_addr: [0u8; 16] };
|
|
let mut buf = [0u8; 4];
|
|
let ret = unsafe {
|
|
inet_ntop(
|
|
AF_INET6,
|
|
ptr::from_ref(&addr).cast::<c_void>(),
|
|
buf.as_mut_ptr().cast::<c_char>(),
|
|
buf.len() as socklen_t,
|
|
)
|
|
};
|
|
assert!(ret.is_null());
|
|
}
|
|
}
|