Enable IPv6 foundation in relibc: inet_pton/ntop, TCP socket options, DNS AAAA
Add three relibc patches (42 total) to close QtNetwork-critical socket gaps: - P3-inet6-pton-ntop: AF_INET6 address parsing/formatting with RFC 5952 shorthand, IPv4-mapped suffix support - P3-tcp-sockopt-forward: forward IPPROTO_TCP getsockopt/setsockopt to scheme daemon instead of hitting todo_skip - P3-dns-aaaa-getaddrinfo-ipv6: AAAA DNS record queries, lookup_host_v6, dual-stack getaddrinfo with sockaddr_in6 entries Also fix P3-tcp-nodelay to use sys_call_wo + from_raw_parts (const) in the SOL_SOCKET setsockopt fallback — setsockopt sends data to the kernel, not reads from it.
This commit is contained in:
@@ -0,0 +1,396 @@
|
|||||||
|
diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs
|
||||||
|
index aaaaaaa..bbbbbbb 100644
|
||||||
|
--- a/src/header/netdb/lookup.rs
|
||||||
|
+++ b/src/header/netdb/lookup.rs
|
||||||
|
@@ -14,7 +14,7 @@ use crate::header::{
|
||||||
|
bits_socklen_t::socklen_t,
|
||||||
|
bits_timespec::timespec,
|
||||||
|
errno::*,
|
||||||
|
- netinet_in::{IPPROTO_UDP, in_addr, sockaddr_in},
|
||||||
|
+ netinet_in::{IPPROTO_UDP, in6_addr, in_addr, sockaddr_in},
|
||||||
|
sys_socket::{
|
||||||
|
self,
|
||||||
|
constants::{AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_RCVTIMEO},
|
||||||
|
@@ -30,6 +30,7 @@ use super::{
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type LookupHost = Vec<in_addr>;
|
||||||
|
+pub type LookupHostV6 = Vec<in6_addr>;
|
||||||
|
|
||||||
|
pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||||
|
if let Some(host_direct_addr) = parse_ipv4_string(host) {
|
||||||
|
@@ -157,6 +158,123 @@ pub fn lookup_host(host: &str) -> Result<LookupHost, c_int> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+/// Look up IPv6 (AAAA) addresses for a host via DNS.
|
||||||
|
+pub fn lookup_host_v6(host: &str) -> Result<LookupHostV6, c_int> {
|
||||||
|
+ if let Some(addr6) = parse_ipv6_string(host) {
|
||||||
|
+ return Ok(vec![addr6]);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let dns_string = get_dns_server().map_err(|e| e.0)?;
|
||||||
|
+
|
||||||
|
+ if let Some(dns_addr) = parse_ipv4_string(&dns_string) {
|
||||||
|
+ let mut timespec = timespec::default();
|
||||||
|
+ if let Ok(()) = Sys::clock_gettime(
|
||||||
|
+ time::constants::CLOCK_REALTIME,
|
||||||
|
+ Out::from_mut(&mut timespec),
|
||||||
|
+ ) {};
|
||||||
|
+ let tid = (timespec.tv_nsec >> 16) as u16;
|
||||||
|
+
|
||||||
|
+ let packet = Dns {
|
||||||
|
+ transaction_id: tid,
|
||||||
|
+ flags: 0x0100,
|
||||||
|
+ queries: vec![DnsQuery {
|
||||||
|
+ name: host.to_string(),
|
||||||
|
+ q_type: 0x001c,
|
||||||
|
+ q_class: 0x0001,
|
||||||
|
+ }],
|
||||||
|
+ answers: vec![],
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let packet_data = packet.compile();
|
||||||
|
+ let packet_data_len = packet_data.len();
|
||||||
|
+
|
||||||
|
+ let packet_data_box = packet_data.into_boxed_slice();
|
||||||
|
+ let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void;
|
||||||
|
+
|
||||||
|
+ let dest = sockaddr_in {
|
||||||
|
+ sin_family: AF_INET as u16,
|
||||||
|
+ sin_port: htons(53),
|
||||||
|
+ sin_addr: in_addr { s_addr: dns_addr },
|
||||||
|
+ ..Default::default()
|
||||||
|
+ };
|
||||||
|
+ let dest_ptr = ptr::from_ref(&dest).cast::<sockaddr>();
|
||||||
|
+
|
||||||
|
+ let sock = unsafe {
|
||||||
|
+ let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP));
|
||||||
|
+ if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 {
|
||||||
|
+ return Err(EIO);
|
||||||
|
+ }
|
||||||
|
+ if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 {
|
||||||
|
+ drop(Box::from_raw(packet_data_ptr));
|
||||||
|
+ return Err(EIO);
|
||||||
|
+ }
|
||||||
|
+ sock
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ unsafe {
|
||||||
|
+ drop(Box::from_raw(packet_data_ptr));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let mut buf = vec![0u8; 65536];
|
||||||
|
+ let buf_ptr = buf.as_mut_ptr().cast::<c_void>();
|
||||||
|
+
|
||||||
|
+ // Set 5s recv timeout (best-effort; if this fails, recv may block longer).
|
||||||
|
+ let tv = timeval {
|
||||||
|
+ tv_sec: 5,
|
||||||
|
+ tv_usec: 0,
|
||||||
|
+ };
|
||||||
|
+ unsafe {
|
||||||
|
+ sys_socket::setsockopt(
|
||||||
|
+ sock,
|
||||||
|
+ SOL_SOCKET,
|
||||||
|
+ SO_RCVTIMEO,
|
||||||
|
+ &tv as *const timeval as *const c_void,
|
||||||
|
+ core::mem::size_of::<timeval>() as socklen_t,
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let mut count: isize = -1;
|
||||||
|
+ for _attempt in 0..2 {
|
||||||
|
+ count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) };
|
||||||
|
+ if count >= 0 {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ if unsafe { sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) } < 0 {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if count < 0 {
|
||||||
|
+ return Err(EIO);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ match Dns::parse(&buf[..count as usize]) {
|
||||||
|
+ Ok(response) => {
|
||||||
|
+ let addrs: Vec<_> = response
|
||||||
|
+ .answers
|
||||||
|
+ .into_iter()
|
||||||
|
+ .filter_map(|answer| {
|
||||||
|
+ if answer.a_type == 0x001c
|
||||||
|
+ && answer.a_class == 0x0001
|
||||||
|
+ && answer.data.len() == 16
|
||||||
|
+ {
|
||||||
|
+ let mut s6_addr = [0u8; 16];
|
||||||
|
+ s6_addr.copy_from_slice(&answer.data[..16]);
|
||||||
|
+ Some(in6_addr { s6_addr })
|
||||||
|
+ } else {
|
||||||
|
+ None
|
||||||
|
+ }
|
||||||
|
+ })
|
||||||
|
+ .collect();
|
||||||
|
+
|
||||||
|
+ Ok(addrs)
|
||||||
|
+ }
|
||||||
|
+ Err(_err) => Err(EINVAL),
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ Err(EINVAL)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
pub fn lookup_addr(addr: in_addr) -> Result<Vec<Vec<u8>>, c_int> {
|
||||||
|
let dns_string = get_dns_server().map_err(|e| e.0)?;
|
||||||
|
|
||||||
|
@@ -282,6 +400,23 @@ pub fn parse_ipv4_string(ip_string: &str) -> Option<u32> {
|
||||||
|
Some(u32::from_ne_bytes(dns_arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
+pub fn parse_ipv6_string(ip_string: &str) -> Option<in6_addr> {
|
||||||
|
+ let trimmed = ip_string.trim();
|
||||||
|
+
|
||||||
|
+ let s = if trimmed.starts_with('[') && trimmed.ends_with(']') {
|
||||||
|
+ &trimmed[1..trimmed.len() - 1]
|
||||||
|
+ } else {
|
||||||
|
+ trimmed
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let ip: core::net::Ipv6Addr = s.parse().ok()?;
|
||||||
|
+ let mut addr = in6_addr {
|
||||||
|
+ s6_addr: [0u8; 16],
|
||||||
|
+ };
|
||||||
|
+ addr.s6_addr.copy_from_slice(&ip.octets());
|
||||||
|
+ Some(addr)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use alloc::str;
|
||||||
|
diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs
|
||||||
|
index ccccccc..ddddddd 100644
|
||||||
|
--- a/src/header/netdb/mod.rs
|
||||||
|
+++ b/src/header/netdb/mod.rs
|
||||||
|
@@ -4,7 +4,7 @@
|
||||||
|
|
||||||
|
mod dns;
|
||||||
|
|
||||||
|
-use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, ptr, str};
|
||||||
|
+use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, net::Ipv6Addr, ptr, str};
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec};
|
||||||
|
|
||||||
|
@@ -18,10 +18,10 @@ use crate::{
|
||||||
|
bits_socklen_t::socklen_t,
|
||||||
|
errno::*,
|
||||||
|
fcntl::O_RDONLY,
|
||||||
|
- netinet_in::{in_addr, sockaddr_in, sockaddr_in6},
|
||||||
|
+ netinet_in::{in6_addr, in_addr, sockaddr_in, sockaddr_in6},
|
||||||
|
stdlib::atoi,
|
||||||
|
strings::strcasecmp,
|
||||||
|
- sys_socket::{constants::AF_INET, sockaddr},
|
||||||
|
+ sys_socket::{constants::{AF_INET, AF_INET6, AF_UNSPEC}, sockaddr},
|
||||||
|
unistd::SEEK_SET,
|
||||||
|
},
|
||||||
|
platform::{
|
||||||
|
@@ -871,11 +871,16 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||||
|
hints_opt
|
||||||
|
);
|
||||||
|
|
||||||
|
+ let requested_family = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
|
||||||
|
+
|
||||||
|
+ if requested_family != AF_INET && requested_family != AF_INET6 && requested_family != AF_UNSPEC
|
||||||
|
+ {
|
||||||
|
+ return EAI_FAMILY;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
//TODO: Use hints
|
||||||
|
let mut ai_flags = hints_opt.map_or(0, |hints| hints.ai_flags);
|
||||||
|
- let mut ai_family; // = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family);
|
||||||
|
let ai_socktype = hints_opt.map_or(0, |hints| hints.ai_socktype);
|
||||||
|
- let mut ai_protocol; // = hints_opt.map_or(0, |hints| hints.ai_protocol);
|
||||||
|
|
||||||
|
unsafe { *res = ptr::null_mut() };
|
||||||
|
|
||||||
|
@@ -896,31 +901,52 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
- let lookuphost = if ai_flags & AI_NUMERICHOST > 0 {
|
||||||
|
- match parse_ipv4_string(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
|
||||||
|
- Some(s_addr) => vec![in_addr { s_addr }],
|
||||||
|
- None => {
|
||||||
|
- return EAI_NONAME;
|
||||||
|
+ let node_str = unsafe { str::from_utf8_unchecked(node.to_bytes()) };
|
||||||
|
+
|
||||||
|
+ let want_inet4 = requested_family == AF_INET || requested_family == AF_UNSPEC;
|
||||||
|
+ let want_inet6 = requested_family == AF_INET6 || requested_family == AF_UNSPEC;
|
||||||
|
+
|
||||||
|
+ let lookuphost_v4: Vec<in_addr> = if want_inet4 {
|
||||||
|
+ if ai_flags & AI_NUMERICHOST > 0 {
|
||||||
|
+ match parse_ipv4_string(node_str) {
|
||||||
|
+ Some(s_addr) => vec![in_addr { s_addr }],
|
||||||
|
+ None => vec![],
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ match lookup_host(node_str) {
|
||||||
|
+ Ok(addrs) => addrs,
|
||||||
|
+ Err(_) => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
- match lookup_host(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) {
|
||||||
|
- Ok(lookuphost) => lookuphost,
|
||||||
|
- Err(e) => {
|
||||||
|
- platform::ERRNO.set(e);
|
||||||
|
- return EAI_SYSTEM;
|
||||||
|
+ vec![]
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let lookuphost_v6: Vec<in6_addr> = if want_inet6 {
|
||||||
|
+ if ai_flags & AI_NUMERICHOST > 0 {
|
||||||
|
+ match parse_ipv6_string(node_str) {
|
||||||
|
+ Some(addr) => vec![addr],
|
||||||
|
+ None => vec![],
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ match lookup_host_v6(node_str) {
|
||||||
|
+ Ok(addrs) => addrs,
|
||||||
|
+ Err(_) => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ } else {
|
||||||
|
+ vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
- for in_addr in lookuphost {
|
||||||
|
- ai_family = AF_INET;
|
||||||
|
- ai_protocol = 0;
|
||||||
|
+ if lookuphost_v4.is_empty() && lookuphost_v6.is_empty() {
|
||||||
|
+ return EAI_NONAME;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
+ for addr in lookuphost_v4 {
|
||||||
|
let ai_addr = Box::into_raw(Box::new(sockaddr_in {
|
||||||
|
- sin_family: ai_family as sa_family_t,
|
||||||
|
+ sin_family: AF_INET as sa_family_t,
|
||||||
|
sin_port: htons(port),
|
||||||
|
- sin_addr: in_addr,
|
||||||
|
+ sin_addr: addr,
|
||||||
|
sin_zero: [0; 8],
|
||||||
|
}))
|
||||||
|
.cast::<sockaddr>();
|
||||||
|
@@ -939,9 +965,53 @@ pub unsafe extern "C" fn getaddrinfo(
|
||||||
|
|
||||||
|
let addrinfo = Box::new(addrinfo {
|
||||||
|
ai_flags: 0,
|
||||||
|
- ai_family,
|
||||||
|
+ ai_family: AF_INET,
|
||||||
|
ai_socktype,
|
||||||
|
- ai_protocol,
|
||||||
|
+ ai_protocol: 0,
|
||||||
|
+ ai_addrlen,
|
||||||
|
+ ai_canonname,
|
||||||
|
+ ai_addr,
|
||||||
|
+ ai_next: ptr::null_mut(),
|
||||||
|
+ });
|
||||||
|
+ unsafe {
|
||||||
|
+ let mut indirect = res;
|
||||||
|
+ while !(*indirect).is_null() {
|
||||||
|
+ indirect = &raw mut (**indirect).ai_next;
|
||||||
|
+ }
|
||||||
|
+ *indirect = Box::into_raw(addrinfo)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for addr in lookuphost_v6 {
|
||||||
|
+ let mut s6_addr = [0u8; 16];
|
||||||
|
+ s6_addr.copy_from_slice(&addr.s6_addr);
|
||||||
|
+
|
||||||
|
+ let ai_addr = Box::into_raw(Box::new(sockaddr_in6 {
|
||||||
|
+ sin6_family: AF_INET6 as sa_family_t,
|
||||||
|
+ sin6_port: htons(port),
|
||||||
|
+ sin6_flowinfo: 0,
|
||||||
|
+ sin6_addr: in6_addr { s6_addr },
|
||||||
|
+ sin6_scope_id: 0,
|
||||||
|
+ }))
|
||||||
|
+ .cast::<sockaddr>();
|
||||||
|
+
|
||||||
|
+ let ai_addrlen = mem::size_of::<sockaddr_in6>() as socklen_t;
|
||||||
|
+
|
||||||
|
+ let ai_canonname = if ai_flags & AI_CANONNAME > 0 {
|
||||||
|
+ if node_opt.is_none() {
|
||||||
|
+ return EAI_BADFLAGS;
|
||||||
|
+ }
|
||||||
|
+ ai_flags &= !AI_CANONNAME;
|
||||||
|
+ node.to_owned_cstring().into_raw()
|
||||||
|
+ } else {
|
||||||
|
+ ptr::null_mut()
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let addrinfo = Box::new(addrinfo {
|
||||||
|
+ ai_flags: 0,
|
||||||
|
+ ai_family: AF_INET6,
|
||||||
|
+ ai_socktype,
|
||||||
|
+ ai_protocol: 0,
|
||||||
|
ai_addrlen,
|
||||||
|
ai_canonname,
|
||||||
|
ai_addr,
|
||||||
|
@@ -970,10 +1040,56 @@ pub unsafe extern "C" fn getnameinfo(
|
||||||
|
servlen: socklen_t,
|
||||||
|
flags: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
- if addr.is_null() || addrlen as usize != mem::size_of::<sockaddr_in>() {
|
||||||
|
+ if addr.is_null() {
|
||||||
|
+ return EAI_FAMILY;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let addrlen_usize = addrlen as usize;
|
||||||
|
+ if addrlen_usize != mem::size_of::<sockaddr_in>()
|
||||||
|
+ && addrlen_usize != mem::size_of::<sockaddr_in6>()
|
||||||
|
+ {
|
||||||
|
return EAI_FAMILY;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if addrlen_usize == mem::size_of::<sockaddr_in6>() {
|
||||||
|
+ let sa = unsafe { &*(addr.cast::<sockaddr_in6>()) };
|
||||||
|
+
|
||||||
|
+ if !serv.is_null() && servlen > 0 {
|
||||||
|
+ if flags & NI_NUMERICSERV != 0 {
|
||||||
|
+ let port_str = sa.sin6_port.to_be().to_string();
|
||||||
|
+ let port_bytes = port_str.as_bytes();
|
||||||
|
+ if (servlen as usize) <= port_bytes.len() {
|
||||||
|
+ return EAI_MEMORY;
|
||||||
|
+ }
|
||||||
|
+ unsafe {
|
||||||
|
+ ptr::copy_nonoverlapping(
|
||||||
|
+ port_bytes.as_ptr().cast::<c_char>(),
|
||||||
|
+ serv,
|
||||||
|
+ port_bytes.len(),
|
||||||
|
+ )
|
||||||
|
+ };
|
||||||
|
+ unsafe { *serv.add(port_bytes.len()) = 0 };
|
||||||
|
+ } else {
|
||||||
|
+ unsafe { *serv = 0 };
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if !host.is_null() && hostlen > 0 {
|
||||||
|
+ let ip_addr = Ipv6Addr::from(sa.sin6_addr.s6_addr);
|
||||||
|
+ let ip_str = ip_addr.to_string();
|
||||||
|
+ let ip_bytes = ip_str.as_bytes();
|
||||||
|
+ if (hostlen as usize) <= ip_bytes.len() {
|
||||||
|
+ return EAI_MEMORY;
|
||||||
|
+ }
|
||||||
|
+ unsafe {
|
||||||
|
+ ptr::copy_nonoverlapping(ip_bytes.as_ptr().cast::<c_char>(), host, ip_bytes.len())
|
||||||
|
+ };
|
||||||
|
+ unsafe { *host.add(ip_bytes.len()) = 0 };
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
let sa = unsafe { &*(addr.cast::<sockaddr_in>()) };
|
||||||
|
|
||||||
|
if !serv.is_null() && servlen > 0 {
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
diff --git a/src/header/arpa_inet/mod.rs b/src/header/arpa_inet/mod.rs
|
||||||
|
index e982353f..56806d4a 100644
|
||||||
|
--- a/src/header/arpa_inet/mod.rs
|
||||||
|
+++ b/src/header/arpa_inet/mod.rs
|
||||||
|
@@ -2,6 +2,7 @@
|
||||||
|
//!
|
||||||
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
|
||||||
|
|
||||||
|
+use alloc::{string::String, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
ptr, slice,
|
||||||
|
str::{self, FromStr},
|
||||||
|
@@ -13,8 +14,8 @@ use crate::{
|
||||||
|
bits_arpainet::ntohl,
|
||||||
|
bits_socklen_t::socklen_t,
|
||||||
|
errno::{EAFNOSUPPORT, ENOSPC},
|
||||||
|
- netinet_in::{INADDR_NONE, in_addr, in_addr_t},
|
||||||
|
- sys_socket::constants::AF_INET,
|
||||||
|
+ netinet_in::{INADDR_NONE, INET6_ADDRSTRLEN, in6_addr, in_addr, in_addr_t},
|
||||||
|
+ sys_socket::constants::{AF_INET, AF_INET6},
|
||||||
|
},
|
||||||
|
platform::{
|
||||||
|
self,
|
||||||
|
@@ -181,34 +182,111 @@ pub unsafe extern "C" fn inet_ntop(
|
||||||
|
dst: *mut c_char,
|
||||||
|
size: socklen_t,
|
||||||
|
) -> *const c_char {
|
||||||
|
- if af != AF_INET {
|
||||||
|
- platform::ERRNO.set(EAFNOSUPPORT);
|
||||||
|
- ptr::null()
|
||||||
|
- } else if size < 16 {
|
||||||
|
- platform::ERRNO.set(ENOSPC);
|
||||||
|
- ptr::null()
|
||||||
|
- } else {
|
||||||
|
- let s_addr = unsafe {
|
||||||
|
- slice::from_raw_parts(
|
||||||
|
- ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
|
||||||
|
- 4,
|
||||||
|
- )
|
||||||
|
- };
|
||||||
|
- let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
|
||||||
|
+ if af == AF_INET6 {
|
||||||
|
+ if size < INET6_ADDRSTRLEN as socklen_t {
|
||||||
|
+ platform::ERRNO.set(ENOSPC);
|
||||||
|
+ return ptr::null();
|
||||||
|
+ }
|
||||||
|
+ let s6_addr = unsafe { &(*(src.cast::<in6_addr>())).s6_addr };
|
||||||
|
+ let output = inet_ntop6(s6_addr);
|
||||||
|
+ let bytes = output.as_bytes();
|
||||||
|
unsafe {
|
||||||
|
- ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
|
||||||
|
+ ptr::copy(bytes.as_ptr().cast::<c_char>(), dst, bytes.len());
|
||||||
|
+ *dst.add(bytes.len()) = 0;
|
||||||
|
}
|
||||||
|
dst
|
||||||
|
+ } else if af == AF_INET {
|
||||||
|
+ if size < 16 {
|
||||||
|
+ platform::ERRNO.set(ENOSPC);
|
||||||
|
+ ptr::null()
|
||||||
|
+ } else {
|
||||||
|
+ let s_addr = unsafe {
|
||||||
|
+ slice::from_raw_parts(
|
||||||
|
+ ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
|
||||||
|
+ 4,
|
||||||
|
+ )
|
||||||
|
+ };
|
||||||
|
+ let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
|
||||||
|
+ unsafe {
|
||||||
|
+ ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
|
||||||
|
+ }
|
||||||
|
+ dst
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ platform::ERRNO.set(EAFNOSUPPORT);
|
||||||
|
+ ptr::null()
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn inet_ntop6(addr: &[u8; 16]) -> String {
|
||||||
|
+ let groups: [u16; 8] = core::array::from_fn(|i| {
|
||||||
|
+ u16::from_be_bytes([addr[i * 2], addr[i * 2 + 1]])
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ let mut best_start = 8usize;
|
||||||
|
+ let mut best_len = 1usize;
|
||||||
|
+ let mut cur_start = 8usize;
|
||||||
|
+ let mut cur_len = 0usize;
|
||||||
|
+
|
||||||
|
+ for i in 0..8 {
|
||||||
|
+ if groups[i] == 0 {
|
||||||
|
+ if cur_len == 0 {
|
||||||
|
+ cur_start = i;
|
||||||
|
+ }
|
||||||
|
+ cur_len += 1;
|
||||||
|
+ } else {
|
||||||
|
+ if cur_len > best_len {
|
||||||
|
+ best_start = cur_start;
|
||||||
|
+ best_len = cur_len;
|
||||||
|
+ }
|
||||||
|
+ cur_len = 0;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if cur_len > best_len {
|
||||||
|
+ best_start = cur_start;
|
||||||
|
+ best_len = cur_len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let mut parts = Vec::new();
|
||||||
|
+ let mut i = 0usize;
|
||||||
|
+ while i < 8 {
|
||||||
|
+ if i == best_start && best_len > 1 {
|
||||||
|
+ if i == 0 {
|
||||||
|
+ parts.push(String::new());
|
||||||
|
+ }
|
||||||
|
+ if i + best_len == 8 {
|
||||||
|
+ parts.push(String::new());
|
||||||
|
+ }
|
||||||
|
+ i += best_len;
|
||||||
|
+ } else {
|
||||||
|
+ parts.push(format!("{:x}", groups[i]));
|
||||||
|
+ i += 1;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if best_len == 8 {
|
||||||
|
+ return String::from("::");
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ parts.join(":")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
|
||||||
|
#[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_INET6 {
|
||||||
|
+ let src_cstr = unsafe { CStr::from_ptr(src) };
|
||||||
|
+ let src_str = match src_cstr.to_str() {
|
||||||
|
+ Ok(s) => s,
|
||||||
|
+ Err(_) => return 0,
|
||||||
|
+ };
|
||||||
|
+ let out = unsafe { &mut *(dst.cast::<in6_addr>()) };
|
||||||
|
+ if inet_pton6(src_str, &mut out.s6_addr) {
|
||||||
|
+ 1
|
||||||
|
+ } else {
|
||||||
|
+ 0
|
||||||
|
+ }
|
||||||
|
+ } 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>(),
|
||||||
|
@@ -233,5 +311,137 @@ pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_vo
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
+ } else {
|
||||||
|
+ platform::ERRNO.set(EAFNOSUPPORT);
|
||||||
|
+ -1
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn inet_pton6(src: &str, dst: &mut [u8; 16]) -> bool {
|
||||||
|
+ dst.fill(0);
|
||||||
|
+
|
||||||
|
+ let double_colon_pos = src.find("::");
|
||||||
|
+ let second_double = if let Some(pos) = double_colon_pos {
|
||||||
|
+ src[pos + 2..].find("::").map(|p| p + pos + 2)
|
||||||
|
+ } else {
|
||||||
|
+ None
|
||||||
|
+ };
|
||||||
|
+ if second_double.is_some() {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let (left_str, right_str) = match double_colon_pos {
|
||||||
|
+ Some(pos) => (&src[..pos], &src[pos + 2..]),
|
||||||
|
+ None => (src, ""),
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let left_groups: Vec<&str> = if left_str.is_empty() {
|
||||||
|
+ Vec::new()
|
||||||
|
+ } else {
|
||||||
|
+ left_str.split(':').collect()
|
||||||
|
+ };
|
||||||
|
+ let right_groups: Vec<&str> = if right_str.is_empty() {
|
||||||
|
+ Vec::new()
|
||||||
|
+ } else {
|
||||||
|
+ right_str.split(':').collect()
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ let right_has_ipv4 = right_groups.last().is_some_and(|g| g.contains('.'));
|
||||||
|
+ let mut left_count = left_groups.len();
|
||||||
|
+ let mut right_count = right_groups.len();
|
||||||
|
+ if right_has_ipv4 {
|
||||||
|
+ right_count -= 1;
|
||||||
|
+ left_count += 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let gap = 8 - left_count - right_count;
|
||||||
|
+ if double_colon_pos.is_none() && gap != 0 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ if double_colon_pos.is_some() && gap < 0 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ if double_colon_pos.is_none() && left_groups.len() + right_groups.len() != 8 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let mut idx = 0usize;
|
||||||
|
+
|
||||||
|
+ for group in &left_groups {
|
||||||
|
+ if idx >= 16 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ let val = match parse_hex_group(group) {
|
||||||
|
+ Some(v) => v,
|
||||||
|
+ None => return false,
|
||||||
|
+ };
|
||||||
|
+ dst[idx] = (val >> 8) as u8;
|
||||||
|
+ dst[idx + 1] = val as u8;
|
||||||
|
+ idx += 2;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if double_colon_pos.is_some() {
|
||||||
|
+ for _ in 0..gap {
|
||||||
|
+ if idx >= 16 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ dst[idx] = 0;
|
||||||
|
+ dst[idx + 1] = 0;
|
||||||
|
+ idx += 2;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let right_hex_count = if right_has_ipv4 {
|
||||||
|
+ right_groups.len().saturating_sub(1)
|
||||||
|
+ } else {
|
||||||
|
+ right_groups.len()
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ for group in &right_groups[..right_hex_count] {
|
||||||
|
+ if idx >= 16 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ let val = match parse_hex_group(group) {
|
||||||
|
+ Some(v) => v,
|
||||||
|
+ None => return false,
|
||||||
|
+ };
|
||||||
|
+ dst[idx] = (val >> 8) as u8;
|
||||||
|
+ dst[idx + 1] = val as u8;
|
||||||
|
+ idx += 2;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if right_has_ipv4 {
|
||||||
|
+ if idx != 12 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ let ipv4_str = right_groups[right_groups.len() - 1];
|
||||||
|
+ let parts: Vec<&str> = ipv4_str.split('.').collect();
|
||||||
|
+ if parts.len() != 4 {
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ for (i, part) in parts.iter().enumerate() {
|
||||||
|
+ match u8::from_str(part) {
|
||||||
|
+ Ok(v) => dst[12 + i] = v,
|
||||||
|
+ Err(_) => return false,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ idx += 4;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ idx == 16
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn parse_hex_group(s: &str) -> Option<u16> {
|
||||||
|
+ if s.is_empty() || s.len() > 4 {
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+ let mut val: u16 = 0;
|
||||||
|
+ for c in s.chars() {
|
||||||
|
+ val = val.checked_mul(16)?;
|
||||||
|
+ match c.to_digit(16) {
|
||||||
|
+ Some(d) => val = val.checked_add(d as u16)?,
|
||||||
|
+ None => return None,
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+ Some(val)
|
||||||
|
}
|
||||||
@@ -10,9 +10,22 @@ index ec42889b..c91ffb1a 100644
|
|||||||
+pub const IPPROTO_TCP: c_int = 6;
|
+pub const IPPROTO_TCP: c_int = 6;
|
||||||
+pub const TCP_NODELAY: c_int = 1;
|
+pub const TCP_NODELAY: c_int = 1;
|
||||||
diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs
|
diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs
|
||||||
index d223c36f..5e17a2e5 100644
|
index d223c36f..2faae4f5 100644
|
||||||
--- a/src/platform/redox/socket.rs
|
--- a/src/platform/redox/socket.rs
|
||||||
+++ b/src/platform/redox/socket.rs
|
+++ b/src/platform/redox/socket.rs
|
||||||
|
@@ -1051,10 +1051,10 @@ impl PalSocket for Sys {
|
||||||
|
_ => {
|
||||||
|
let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||||
|
let payload = unsafe {
|
||||||
|
- slice::from_raw_parts_mut(option_value as *mut u8, option_len as usize)
|
||||||
|
+ slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||||
|
};
|
||||||
|
let call_flags = CallFlags::empty();
|
||||||
|
- redox_rt::sys::sys_call_rw(
|
||||||
|
+ redox_rt::sys::sys_call_wo(
|
||||||
|
socket as usize,
|
||||||
|
payload,
|
||||||
|
CallFlags::empty(),
|
||||||
@@ -1063,6 +1063,24 @@ impl PalSocket for Sys {
|
@@ -1063,6 +1063,24 @@ impl PalSocket for Sys {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -22,9 +35,9 @@ index d223c36f..5e17a2e5 100644
|
|||||||
+ crate::header::sys_socket::constants::TCP_NODELAY => {
|
+ crate::header::sys_socket::constants::TCP_NODELAY => {
|
||||||
+ let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
+ let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||||
+ let payload = unsafe {
|
+ let payload = unsafe {
|
||||||
+ slice::from_raw_parts_mut(option_value as *mut u8, option_len as usize)
|
+ slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||||
+ };
|
+ };
|
||||||
+ redox_rt::sys::sys_call_rw(
|
+ redox_rt::sys::sys_call_wo(
|
||||||
+ socket as usize,
|
+ socket as usize,
|
||||||
+ payload,
|
+ payload,
|
||||||
+ CallFlags::empty(),
|
+ CallFlags::empty(),
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs
|
||||||
|
index d223c36f..f8a1c2e0 100644
|
||||||
|
--- a/src/platform/redox/socket.rs
|
||||||
|
+++ b/src/platform/redox/socket.rs
|
||||||
|
@@ -774,6 +774,21 @@ impl PalSocket for Sys {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
+ crate::header::sys_socket::constants::IPPROTO_TCP => {
|
||||||
|
+ let metadata = [SocketCall::GetSockOpt as u64, option_name as u64];
|
||||||
|
+ let payload =
|
||||||
|
+ unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len) };
|
||||||
|
+ let call_flags = CallFlags::empty();
|
||||||
|
+ unsafe {
|
||||||
|
+ *option_len_ptr = redox_rt::sys::sys_call_ro(
|
||||||
|
+ socket as usize,
|
||||||
|
+ payload,
|
||||||
|
+ CallFlags::empty(),
|
||||||
|
+ &metadata,
|
||||||
|
+ )? as socklen_t;
|
||||||
|
+ }
|
||||||
|
+ return Ok(());
|
||||||
|
+ }
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1069,18 +1069,13 @@ impl PalSocket for Sys {
|
||||||
|
crate::header::sys_socket::constants::IPPROTO_TCP => {
|
||||||
|
- match option_name {
|
||||||
|
- crate::header::sys_socket::constants::TCP_NODELAY => {
|
||||||
|
- let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||||
|
- let payload = unsafe {
|
||||||
|
- slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||||
|
- };
|
||||||
|
- redox_rt::sys::sys_call_wo(
|
||||||
|
- socket as usize,
|
||||||
|
- payload,
|
||||||
|
- CallFlags::empty(),
|
||||||
|
- &metadata,
|
||||||
|
- )?;
|
||||||
|
- return Ok(());
|
||||||
|
- }
|
||||||
|
- _ => (),
|
||||||
|
- }
|
||||||
|
+ let metadata = [SocketCall::SetSockOpt as u64, option_name as u64];
|
||||||
|
+ let payload = unsafe {
|
||||||
|
+ slice::from_raw_parts(option_value as *const u8, option_len as usize)
|
||||||
|
+ };
|
||||||
|
+ redox_rt::sys::sys_call_wo(
|
||||||
|
+ socket as usize,
|
||||||
|
+ payload,
|
||||||
|
+ CallFlags::empty(),
|
||||||
|
+ &metadata,
|
||||||
|
+ )?;
|
||||||
|
+ return Ok(());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
@@ -37,6 +37,9 @@ patches = [
|
|||||||
"../../../local/patches/relibc/P3-fenv.patch",
|
"../../../local/patches/relibc/P3-fenv.patch",
|
||||||
"../../../local/patches/relibc/P3-sched.patch",
|
"../../../local/patches/relibc/P3-sched.patch",
|
||||||
"../../../local/patches/relibc/P3-aio.patch",
|
"../../../local/patches/relibc/P3-aio.patch",
|
||||||
|
"../../../local/patches/relibc/P3-inet6-pton-ntop.patch",
|
||||||
|
"../../../local/patches/relibc/P3-tcp-sockopt-forward.patch",
|
||||||
|
"../../../local/patches/relibc/P3-dns-aaaa-getaddrinfo-ipv6.patch",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
|
|||||||
Reference in New Issue
Block a user