fix: implement IPv6 for arpa_inet/inet_pton()

This commit is contained in:
sourceturner
2026-06-10 21:46:43 +02:00
parent b390ee651b
commit 16abb5d342
2 changed files with 305 additions and 6 deletions
+93 -6
View File
@@ -12,8 +12,11 @@ use crate::{
header::{
bits_arpainet::ntohl,
errno::{EAFNOSUPPORT, ENOSPC},
netinet_in::{INADDR_NONE, in_addr, in_addr_t},
sys_socket::{constants::AF_INET, socklen_t},
netinet_in::{INADDR_NONE, in_addr, in_addr_t, in6_addr},
sys_socket::{
constants::{AF_INET, AF_INET6},
socklen_t,
},
},
io::Write,
platform::{
@@ -260,10 +263,7 @@ pub unsafe extern "C" fn inet_ntop(
/// 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 {
platform::ERRNO.set(EAFNOSUPPORT);
-1
} else {
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>(),
@@ -288,5 +288,92 @@ pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_vo
} 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 == ':' as u8).count();
if colons > 7 || colons < 2 {
return 0;
}
let dots = src_str.bytes().filter(|&c| c == '.' as u8).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(':')
{
if 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
}
}