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
}
}
+212
View File
@@ -0,0 +1,212 @@
#include <assert.h>
#include <arpa/inet.h>
int main(void)
{
unsigned char ip[4];
const char* input;
assert(inet_pton(AF_INET, input = "11.22.33.44", ip) == 1);
assert(ip[0] == 11);
assert(ip[1] == 22);
assert(ip[2] == 33);
assert(ip[3] == 44);
assert(inet_pton(AF_INET, input = "0.0.0.0", ip) == 1);
assert(ip[0] == 0);
assert(ip[1] == 0);
assert(ip[2] == 0);
assert(ip[3] == 0);
assert(inet_pton(AF_INET, input = "255.255.255.255", ip) == 1);
assert(ip[0] == 255);
assert(ip[1] == 255);
assert(ip[2] == 255);
assert(ip[3] == 255);
assert(inet_pton(AF_INET, input = "0000.0.0.0", ip) == 0);
assert(inet_pton(AF_INET, input = "0001.2.3.4", ip) == 0);
assert(inet_pton(AF_INET, input = "256.256.256.256", ip) == 0);
assert(inet_pton(AF_INET, input = "1.2.3.0x4", ip) == 0);
assert(inet_pton(AF_INET, input = "1.2.3", ip) == 0);
assert(inet_pton(AF_INET, input = "1.2", ip) == 0);
assert(inet_pton(AF_INET, input = "123456789", ip) == 0);
assert(inet_pton(AF_INET, input = " 1.2.3.4", ip) == 0);
assert(inet_pton(AF_INET, input = "1.2.3.4 ", ip) == 0);
#ifdef AF_INET6
unsigned char ip6[16];
input = "1122:3344:5566:7788:99aa:bbcc:ddee:ff00";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0x11);
assert(ip6[1] == 0x22);
assert(ip6[2] == 0x33);
assert(ip6[3] == 0x44);
assert(ip6[4] == 0x55);
assert(ip6[5] == 0x66);
assert(ip6[6] == 0x77);
assert(ip6[7] == 0x88);
assert(ip6[8] == 0x99);
assert(ip6[9] == 0xaa);
assert(ip6[10] == 0xbb);
assert(ip6[11] == 0xcc);
assert(ip6[12] == 0xdd);
assert(ip6[13] == 0xee);
assert(ip6[14] == 0xff);
assert(ip6[15] == 0x00);
// Test an address with :: notation in the middle.
input = "1:2::ff";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0x00);
assert(ip6[1] == 0x01);
assert(ip6[2] == 0x00);
assert(ip6[3] == 0x02);
assert(ip6[4] == 0x00);
assert(ip6[5] == 0x00);
assert(ip6[6] == 0x00);
assert(ip6[7] == 0x00);
assert(ip6[8] == 0x00);
assert(ip6[9] == 0x00);
assert(ip6[10] == 0x00);
assert(ip6[11] == 0x00);
assert(ip6[12] == 0x00);
assert(ip6[13] == 0x00);
assert(ip6[14] == 0x00);
assert(ip6[15] == 0xff);
input = "::12:0:abcd";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0x00);
assert(ip6[1] == 0x00);
assert(ip6[2] == 0x00);
assert(ip6[3] == 0x00);
assert(ip6[4] == 0x00);
assert(ip6[5] == 0x00);
assert(ip6[6] == 0x00);
assert(ip6[7] == 0x00);
assert(ip6[8] == 0x00);
assert(ip6[9] == 0x00);
assert(ip6[10] == 0x00);
assert(ip6[11] == 0x12);
assert(ip6[12] == 0x00);
assert(ip6[13] == 0x00);
assert(ip6[14] == 0xab);
assert(ip6[15] == 0xcd);
// Test an address with omitted zeros and :: notation.
input = "abcd:0:1::";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0xab);
assert(ip6[1] == 0xcd);
assert(ip6[2] == 0x00);
assert(ip6[3] == 0x00);
assert(ip6[4] == 0x00);
assert(ip6[5] == 0x01);
assert(ip6[6] == 0x00);
assert(ip6[7] == 0x00);
assert(ip6[8] == 0x00);
assert(ip6[9] == 0x00);
assert(ip6[10] == 0x00);
assert(ip6[11] == 0x00);
assert(ip6[12] == 0x00);
assert(ip6[13] == 0x00);
assert(ip6[14] == 0x00);
assert(ip6[15] == 0x00);
input = "::";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0x00);
assert(ip6[1] == 0x00);
assert(ip6[2] == 0x00);
assert(ip6[3] == 0x00);
assert(ip6[4] == 0x00);
assert(ip6[5] == 0x00);
assert(ip6[6] == 0x00);
assert(ip6[7] == 0x00);
assert(ip6[8] == 0x00);
assert(ip6[9] == 0x00);
assert(ip6[10] == 0x00);
assert(ip6[11] == 0x00);
assert(ip6[12] == 0x00);
assert(ip6[13] == 0x00);
assert(ip6[14] == 0x00);
assert(ip6[15] == 0x00);
input = "::FFFF:1.2.3.4";
assert(inet_pton(AF_INET6, input, ip6) == 1);
assert(ip6[0] == 0x00);
assert(ip6[1] == 0x00);
assert(ip6[2] == 0x00);
assert(ip6[3] == 0x00);
assert(ip6[4] == 0x00);
assert(ip6[5] == 0x00);
assert(ip6[6] == 0x00);
assert(ip6[7] == 0x00);
assert(ip6[8] == 0x00);
assert(ip6[9] == 0x00);
assert(ip6[10] == 0xff);
assert(ip6[11] == 0xff);
assert(ip6[12] == 0x01);
assert(ip6[13] == 0x02);
assert(ip6[14] == 0x03);
assert(ip6[15] == 0x04);
input = "abcd:efg::";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "ab::cd::ef";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "ab:::cd";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "1111:2222:3333:4444:5555:6666:7777";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "1111:2222:3333:4444:5555:6666:7777:8888:9999";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = " 1234::";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "1234:: ";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::ffff:1.2.3";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::ffff:1.2.3.4.5";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::ffff: 1.2.3.4";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::ffff:1.2.3.04";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::ffff:0x1.2.3.4";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "12345:::";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "::01234";
assert(inet_pton(AF_INET6, input, ip6) == 0);
input = "1.2.3.4";
assert(inet_pton(AF_INET6, input, ip6) == 0);
#endif
return 0;
}