fix: implement IPv6 for arpa_inet/inet_pton()
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user