diff --git a/src/header/ifaddrs/mod.rs b/src/header/ifaddrs/mod.rs index 093bead221..0f9d490a9c 100644 --- a/src/header/ifaddrs/mod.rs +++ b/src/header/ifaddrs/mod.rs @@ -3,13 +3,39 @@ //! Non-POSIX, see . use crate::{ - header::{errno, stdlib, sys_socket::sockaddr}, + header::{ + errno, stdlib, + netinet_in::{sockaddr_in, sockaddr_in6, AF_INET}, + sys_socket::{sa_family_t, sockaddr, AF_PACKET}, + }, platform::{ self, types::{c_char, c_int, c_uint, c_void}, }, }; +use core::ptr; + +#[cfg(target_os = "redox")] +const O_RDONLY: usize = 0; +#[cfg(target_os = "redox")] +const O_DIRECTORY: usize = 0x10000; + +#[cfg(target_os = "redox")] +#[repr(C)] +struct Dirent { + d_ino: u64, + d_off: i64, + d_reclen: u16, + d_type: u8, + d_name: [c_char; 256], +} + +const IFF_UP: u32 = 0x1; +const IFF_LOOPBACK: u32 = 0x8; +const IFF_RUNNING: u32 = 0x40; +const IFF_MULTICAST: u32 = 0x1000; + /// Either the broadcast address associated with `ifa_addr` (if applicable /// for the address family) or the destination address of the /// point-to-point interface. @@ -60,7 +86,380 @@ pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) { /// be freed using `freeifaddrs()` when no longer needed. #[unsafe(no_mangle)] pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int { - //TODO: implement getifaddrs - platform::ERRNO.set(errno::ENOSYS); - -1 + if ifap.is_null() { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + let mut head: *mut ifaddrs = ptr::null_mut(); + let mut tail: *mut *mut ifaddrs = &mut head; + let Ok(interfaces) = enumerate_interfaces() else { + *ifap = ptr::null_mut(); + platform::ERRNO.set(errno::ENOSYS); + return -1; + }; + + for iface in interfaces { + let node = match build_ifa_node(&iface) { + Ok(n) => n, + Err(_) => { + *ifap = head; + freeifaddrs(head); + platform::ERRNO.set(errno::ENOMEM); + return -1; + } + }; + *tail = node; + tail = unsafe { &mut (*node).ifa_next }; + } + + *ifap = head; + platform::ERRNO.set(0); + 0 +} + +struct InterfaceInfo { + name: [c_char; 64], + name_len: usize, + flags: u32, + ifindex: u32, + addr: [u8; 16], + addr_len: u8, + addr_family: sa_family_t, + netmask: [u8; 16], + netmask_len: u8, + has_addr: bool, + has_netmask: bool, +} + +fn enumerate_interfaces() -> Result, ()> { + #[cfg(target_os = "redox")] + { + enumerate_interfaces_redox() + } + #[cfg(not(target_os = "redox"))] + { + let _ = (); // suppress unused + Err(()) + } +} + +#[cfg(target_os = "redox")] +fn parse_addr_string(s: &[u8]) -> Option<([u8; 16], u8, sa_family_t)> { + if s.is_empty() { + return None; + } + let mut split = s.splitn(2, |&b| b == b'/'); + let ip_part = split.next()?; + let prefix_part = split.next(); + let prefix: u8 = match prefix_part { + Some(p) => match core::str::from_utf8(p).ok()?.parse().ok()? { + 0..=128 => return None, + v => v, + }, + None => 64, + }; + let mut out = [0u8; 16]; + let len = if let Some(pos) = ip_part.iter().position(|&b| b == b':') { + if pos == 0 { + return None; + } + let mut groups = [0u16; 8]; + let mut count = 0; + for part in ip_part.split(|&b| b == b':') { + if part.is_empty() { + continue; + } + if count >= 8 { + return None; + } + let hex_str = core::str::from_utf8(part).ok()?; + groups[count] = u16::from_str_radix(hex_str, 16).ok()?; + count += 1; + } + for (i, g) in groups.iter().enumerate().take(count) { + let off = (i * 2) as usize; + out[off] = (g >> 8) as u8; + out[off + 1] = (g & 0xFF) as u8; + } + 16 + } else { + let mut parts = ip_part.split(|&b| b == b'.'); + let mut count = 0; + for part in parts.by_ref() { + if count >= 4 { + return None; + } + let s = core::str::from_utf8(part).ok()?; + let v: u8 = s.parse().ok()?; + out[count] = v; + count += 1; + } + if count != 4 { + return None; + } + 4 + }; + if prefix > (len as u8) * 8 { + return None; + } + Some((out, prefix, if len == 4 { AF_INET } else { AF_PACKET })) +} + +#[cfg(target_os = "redox")] +fn read_dir_entries(path: &[u8]) -> Result>, ()> { + let mut cpath = [0u8; 256]; + if path.len() + 1 >= cpath.len() { + return Err(()); + } + cpath[..path.len()].copy_from_slice(path); + cpath[path.len()] = 0; + let fd = unsafe { + sc::syscall3( + syscall::SYS_OPENAT, + 0, + cpath.as_ptr() as usize, + (O_RDONLY | O_DIRECTORY) as usize, + ) + } as i32; + if fd < 0 { + return Err(()); + } + let mut entries = Vec::new(); + let mut buf = [0u8; 4096]; + loop { + let n = unsafe { + sc::syscall3( + syscall::SYS_GETDENTS, + fd as usize, + buf.as_mut_ptr() as usize, + buf.len(), + ) + } as isize; + if n <= 0 { + break; + } + let mut off = 0; + while off < n as usize { + let dirent = unsafe { &*(buf.as_ptr().add(off) as *const Dirent) }; + let name_bytes: &[u8] = unsafe { + core::ffi::CStr::from_ptr(dirent.d_name.as_ptr()).to_bytes() + }; + if name_bytes != b"." && name_bytes != b".." { + entries.push(name_bytes.to_vec()); + } + off += dirent.d_reclen as usize; + } + } + let _ = unsafe { + sc::syscall1(syscall::SYS_CLOSE, fd as usize) + }; + Ok(entries) +} + +#[cfg(target_os = "redox")] +fn read_file(path: &[u8]) -> Result, ()> { + let mut cpath = [0u8; 256]; + if path.len() + 1 >= cpath.len() { + return Err(()); + } + cpath[..path.len()].copy_from_slice(path); + cpath[path.len()] = 0; + let fd = unsafe { + sc::syscall3( + syscall::SYS_OPENAT, + 0, + cpath.as_ptr() as usize, + O_RDONLY as usize, + ) + } as i32; + if fd < 0 { + return Err(()); + } + let mut out = Vec::new(); + let mut buf = [0u8; 4096]; + loop { + let n = unsafe { + sc::syscall3( + syscall::SYS_READ, + fd as usize, + buf.as_mut_ptr() as usize, + buf.len(), + ) + } as isize; + if n <= 0 { + break; + } + out.extend_from_slice(&buf[..n as usize]); + } + let _ = unsafe { + sc::syscall1(syscall::SYS_CLOSE, fd as usize) + }; + Ok(out) +} + +#[cfg(target_os = "redox")] +fn enumerate_interfaces_redox() -> Result, ()> { + let dir_entries = read_dir_entries(b"/scheme/net/ifs\0").or_else(|()| { + // Fallback: try /scheme/net (older paths) + read_dir_entries(b"/scheme/net\0") + })?; + let mut result = Vec::new(); + for name in dir_entries { + if name.is_empty() || name.len() > 63 { + continue; + } + let mut info = InterfaceInfo { + name: [0; 64], + name_len: name.len(), + flags: 0, + ifindex: 0, + addr: [0; 16], + addr_len: 0, + addr_family: 0, + netmask: [0; 16], + netmask_len: 0, + has_addr: false, + has_netmask: false, + }; + info.name[..name.len()].copy_from_slice(&name); + + let mut path = [0u8; 256]; + let path_str = b"/scheme/net/ifs/"; + let len = path_str.len(); + path[..len].copy_from_slice(path_str); + let max = 256 - len - 1; + let name_len = name.len().min(max); + path[len..len + name_len].copy_from_slice(&name[..name_len]); + let path_len = len + name_len; + path[path_len] = 0; + + let mut flags_path = path; + let len = flags_path.len(); + flags_path[len - 1] = b'/'; + flags_path[len] = 0; + let fl_str = b"flags"; + let pl = flags_path.len() - 1; + if pl + fl_str.len() < flags_path.len() { + flags_path[pl..pl + fl_str.len()].copy_from_slice(fl_str); + flags_path[pl + fl_str.len()] = 0; + } + if let Ok(flags_bytes) = read_file(&flags_path[..pl + fl_str.len() + 1]) { + if let Ok(s) = core::str::from_utf8(&flags_bytes) { + if let Ok(parsed) = s.trim().parse::() { + info.flags = parsed; + } + } + } + + let mut ip_path = path; + let len = ip_path.len(); + let ip_str = b"ip"; + let pl = len - 1; + if pl + ip_str.len() < ip_path.len() { + ip_path[pl..pl + ip_str.len()].copy_from_slice(ip_str); + ip_path[pl + ip_str.len()] = 0; + } + if let Ok(ip_bytes) = read_file(&ip_path[..pl + ip_str.len() + 1]) { + let ip_str = core::str::from_utf8(&ip_bytes).unwrap_or("").trim(); + if let Some((addr, prefix, family)) = parse_addr_string(ip_str.as_bytes()) { + info.has_addr = true; + info.addr = addr; + info.addr_len = prefix; + info.addr_family = family; + } + } + + let mut netmask_path = path; + let len = netmask_path.len(); + let nm_str = b"netmask"; + let pl = len - 1; + if pl + nm_str.len() < netmask_path.len() { + netmask_path[pl..pl + nm_str.len()].copy_from_slice(nm_str); + netmask_path[pl + nm_str.len()] = 0; + } + if let Ok(nm_bytes) = read_file(&netmask_path[..pl + nm_str.len() + 1]) { + let nm_str = core::str::from_utf8(&nm_bytes).unwrap_or("").trim(); + if let Some((addr, prefix, _)) = parse_addr_string(nm_str.as_bytes()) { + info.has_netmask = true; + info.netmask = addr; + info.netmask_len = prefix; + } + } + + result.push(info); + } + Ok(result) +} + +fn build_ifa_node(iface: &InterfaceInfo) -> Result<*mut ifaddrs, ()> { + let size = core::mem::size_of::() + + iface.name_len + 1 + + core::mem::size_of::() + + core::mem::size_of::(); + let raw = unsafe { stdlib::calloc(1, size) } as *mut u8; + if raw.is_null() { + return Err(()); + } + let node = raw as *mut ifaddrs; + + let name_ptr = unsafe { raw.add(core::mem::size_of::()) } as *mut c_char; + unsafe { + ptr::copy_nonoverlapping(iface.name.as_ptr(), name_ptr, iface.name_len); + *name_ptr.add(iface.name_len) = 0; + (*node).ifa_name = name_ptr; + (*node).ifa_flags = iface.flags; + (*node).ifa_data = core::ptr::null_mut(); + } + + if iface.has_addr && iface.addr_len > 0 { + let addr_offset = core::mem::size_of::() + iface.name_len + 1; + let addr_ptr = unsafe { raw.add(addr_offset) } as *mut sockaddr; + if iface.addr_family == AF_INET { + let addr = addr_ptr as *mut sockaddr_in; + unsafe { + (*addr).sin_family = AF_INET; + (*addr).sin_port = 0; + let addr_bytes = (*addr).sin_addr.s_addr.to_ne_bytes(); + core::ptr::copy_nonoverlapping( + addr_bytes.as_ptr(), + iface.addr.as_ptr(), + 4, + ); + (*node).ifa_addr = addr_ptr; + } + } else { + unsafe { + (*(addr_ptr as *mut sockaddr_in6)).sin6_family = iface.addr_family; + (*node).ifa_addr = addr_ptr; + } + } + } + + if iface.has_netmask && iface.netmask_len > 0 { + let netmask_offset = core::mem::size_of::() + + iface.name_len + 1 + + core::mem::size_of::(); + let netmask_ptr = unsafe { raw.add(netmask_offset) } as *mut sockaddr; + if iface.netmask_len as u32 * 8 <= 32 { + let addr = netmask_ptr as *mut sockaddr_in; + unsafe { + (*addr).sin_family = AF_INET; + let nm_bytes = (*addr).sin_addr.s_addr.to_ne_bytes(); + core::ptr::copy_nonoverlapping( + nm_bytes.as_ptr(), + iface.netmask.as_ptr(), + 4, + ); + (*node).ifa_netmask = netmask_ptr; + } + } else { + unsafe { + (*(netmask_ptr as *mut sockaddr_in6)).sin6_family = AF_INET as sa_family_t; + (*node).ifa_netmask = netmask_ptr; + } + } + } + + Ok(node) }