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 TCP_NODELAY: c_int = 1;
|
||||
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
|
||||
+++ 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 {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -22,9 +35,9 @@ index d223c36f..5e17a2e5 100644
|
||||
+ crate::header::sys_socket::constants::TCP_NODELAY => {
|
||||
+ 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)
|
||||
+ };
|
||||
+ redox_rt::sys::sys_call_rw(
|
||||
+ redox_rt::sys::sys_call_wo(
|
||||
+ socket as usize,
|
||||
+ payload,
|
||||
+ 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-sched.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]
|
||||
|
||||
Reference in New Issue
Block a user