From 16abb5d3421cfee54a76d93af0cd34a5eb4fd050 Mon Sep 17 00:00:00 2001 From: sourceturner <126549-sourceturner@users.noreply.gitlab.redox-os.org> Date: Wed, 10 Jun 2026 21:46:43 +0200 Subject: [PATCH] fix: implement IPv6 for arpa_inet/inet_pton() --- src/header/arpa_inet/mod.rs | 99 ++++++++++++++++- tests/arpa_inet/inet_pton.c | 212 ++++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 tests/arpa_inet/inet_pton.c diff --git a/src/header/arpa_inet/mod.rs b/src/header/arpa_inet/mod.rs index 5d35b6e8ce..0b3e276cdf 100644 --- a/src/header/arpa_inet/mod.rs +++ b/src/header/arpa_inet/mod.rs @@ -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::()).s_addr).cast::(), @@ -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::()).s6_addr).cast::(), + 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::(), 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 } } diff --git a/tests/arpa_inet/inet_pton.c b/tests/arpa_inet/inet_pton.c new file mode 100644 index 0000000000..e699f298bc --- /dev/null +++ b/tests/arpa_inet/inet_pton.c @@ -0,0 +1,212 @@ +#include +#include + + +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; +}