diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs index 22c7b1eb18..ac76fe85c1 100644 --- a/src/header/netdb/lookup.rs +++ b/src/header/netdb/lookup.rs @@ -33,21 +33,16 @@ impl Iterator for LookupHost { } pub fn lookup_host(host: &str) -> Result { + if let Some(host_direct_addr) = parse_ipv4_string(host) { + // already an ip address + return Ok(LookupHost(vec![in_addr { + s_addr: host_direct_addr, + }].into_iter())); + } + let dns_string = get_dns_server().map_err(|e| e.0)?; - let dns_vec: Vec = dns_string - .trim() - .split('.') - .map(|octet| octet.parse::().unwrap_or(0)) - .collect(); - - if dns_vec.len() == 4 { - let mut dns_arr = [0u8; 4]; - for (i, octet) in dns_vec.iter().enumerate() { - dns_arr[i] = *octet; - } - let dns_addr = u32::from_ne_bytes(dns_arr); - + if let Some(dns_addr) = parse_ipv4_string(&dns_string) { let mut timespec = timespec::default(); unsafe { Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec); @@ -140,26 +135,14 @@ pub fn lookup_host(host: &str) -> Result { pub fn lookup_addr(addr: in_addr) -> Result>, c_int> { let dns_string = get_dns_server().map_err(|e| e.0)?; - let dns_vec: Vec = dns_string - .trim() - .split('.') - .map(|octet| octet.parse::().unwrap_or(0)) - .collect(); + if let Some(dns_addr) = parse_ipv4_string(&dns_string) { + let addr: [u8; 4] = addr.s_addr.to_ne_bytes(); + // Address intentionally backwards for reverse lookup + let name = format!( + "{}.{}.{}.{}.in-addr.arpa", + addr[3], addr[2], addr[1], addr[0] + ); - let mut dns_arr = [0u8; 4]; - - for (i, octet) in dns_vec.iter().enumerate() { - dns_arr[i] = *octet; - } - - let addr: [u8; 4] = addr.s_addr.to_ne_bytes(); - // Address intentionally backwards for reverse lookup - let name = format!( - "{}.{}.{}.{}.in-addr.arpa", - addr[3], addr[2], addr[1], addr[0] - ); - - if dns_vec.len() == 4 { let mut timespec = timespec::default(); unsafe { Sys::clock_gettime(time::constants::CLOCK_REALTIME, &mut timespec) }; let tid = (timespec.tv_nsec >> 16) as u16; @@ -183,9 +166,7 @@ pub fn lookup_addr(addr: in_addr) -> Result>, c_int> { let dest = sockaddr_in { sin_family: AF_INET as u16, sin_port: htons(53), - sin_addr: in_addr { - s_addr: u32::from_ne_bytes(dns_arr), - }, + sin_addr: in_addr { s_addr: dns_addr }, ..Default::default() }; @@ -278,6 +259,26 @@ fn parse_revdns_answer(data: &[u8]) -> Vec { output } +pub fn parse_ipv4_string(ip_string: &str) -> Option { + let dns_vec: Vec = ip_string + .trim() + .split('.') + .map(|octet| octet.parse::().unwrap_or(0)) + .collect(); + + if dns_vec.len() != 4 { + return None; + } + + let mut dns_arr = [0u8; 4]; + + for (i, octet) in dns_vec.iter().enumerate() { + dns_arr[i] = *octet; + } + + Some(u32::from_ne_bytes(dns_arr)) +} + #[cfg(test)] mod tests { use alloc::str; diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs index 6cf1d16246..92f7975231 100644 --- a/src/header/netdb/mod.rs +++ b/src/header/netdb/mod.rs @@ -5,11 +5,13 @@ mod dns; use core::{ cell::Cell, fmt::Write, - mem, ptr, slice, + mem, + net::Ipv4Addr, + ptr, slice, str::{self, FromStr}, }; -use alloc::{borrow::ToOwned, boxed::Box, str::SplitWhitespace, vec::Vec}; +use alloc::{borrow::ToOwned, boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec}; use crate::{ c_str::{CStr, CString}, @@ -323,23 +325,7 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { // Addresses and hostnames are both valid, so we'll check addresses first // The standard doesn't define what to do when called with addresses // Some implementations just skip resolution and copy the address to h_name - let mut octets = name_str.split('.'); - let mut s_addr = [0u8; 4]; - let mut is_addr = true; - for item in &mut s_addr { - if let Some(n) = octets.next().and_then(|x| u8::from_str(x).ok()) { - *item = n; - } else { - is_addr = false; - break; - } - } - if octets.next() != None { - is_addr = false; - } - - if is_addr { - let s_addr = u32::from_ne_bytes(s_addr); + if let Some(s_addr) = parse_ipv4_string(name_str) { let addr = in_addr { s_addr }; return gethostbyaddr(&addr as *const _ as *const c_void, 4, AF_INET); } @@ -878,29 +864,81 @@ pub unsafe extern "C" fn getnameinfo( servlen: socklen_t, flags: c_int, ) -> c_int { - //TODO: getnameinfo - if addrlen as usize != mem::size_of::() { + if addr.is_null() || addrlen as usize != mem::size_of::() { return EAI_FAMILY; } - let addr = &*(addr as *const sockaddr_in); + let sa = &*(addr as *const sockaddr_in); - let host_opt = if host.is_null() { - None - } else { - Some(slice::from_raw_parts_mut(host, hostlen as usize)) - }; + if !serv.is_null() && servlen > 0 { + if flags & NI_NUMERICSERV != 0 { + let port_str = sa.sin_port.to_be().to_string(); + let port_bytes = port_str.as_bytes(); + if (servlen as usize) <= port_bytes.len() { + return EAI_MEMORY; // Buffer too small + } + ptr::copy_nonoverlapping(port_bytes.as_ptr() as *const c_char, serv, port_bytes.len()); + *serv.add(port_bytes.len()) = 0; + } else { + // TODO: Implement service name lookup (e.g., from /etc/services) + *serv = 0; + } + } - let serv_opt = if serv.is_null() { - None - } else { - Some(slice::from_raw_parts_mut(serv, servlen as usize)) - }; + if !host.is_null() && hostlen > 0 { + if flags & NI_NUMERICHOST != 0 { + let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be()); + let ip_str = ip_addr.to_string(); + let ip_bytes = ip_str.as_bytes(); + if (hostlen as usize) <= ip_bytes.len() { + return EAI_MEMORY; // Buffer too small + } + ptr::copy_nonoverlapping(ip_bytes.as_ptr() as *const c_char, host, ip_bytes.len()); + *host.add(ip_bytes.len()) = 0; + } else { + match lookup_addr(sa.sin_addr.clone()).map(|host_names| host_names.into_iter().next()) { + Ok(Some(hostname)) => { + if (hostlen as usize) <= hostname.len() { + return EAI_MEMORY; // Buffer too small + } + unsafe { + ptr::copy_nonoverlapping( + hostname.as_ptr() as *const c_char, + host, + hostname.len(), + ); + *host.add(hostname.len()) = 0; + } + } + Ok(None) => { + if flags & NI_NAMEREQD != 0 { + return EAI_NONAME; + } + } + Err(_) => { + if flags & NI_NAMEREQD != 0 { + return EAI_NONAME; + } + let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be()); + 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() as *const c_char, + host, + ip_bytes.len(), + ); + *host.add(ip_bytes.len()) = 0; + } + } + } + } + } - eprintln!("getnameinfo({:p}, {}, {:#x})", addr, addrlen, flags); - - platform::ERRNO.set(ENOSYS); - EAI_SYSTEM + 0 } #[no_mangle]