diff --git a/Cargo.toml b/Cargo.toml index 1c5c87bc13..b9ce5d59c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ rtl8139 = ["tcp", "pci"] smp = [] tcp = ["smoltcp", "smoltcp/socket-tcp"] udp = ["smoltcp", "smoltcp/socket-udp"] +dns = ["smoltcp", "smoltcp/socket-dns"] trace = [] vga = [] common-os = [] diff --git a/src/executor/device.rs b/src/executor/device.rs index 7e689e67e5..42390014c9 100644 --- a/src/executor/device.rs +++ b/src/executor/device.rs @@ -7,10 +7,14 @@ use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{self, ChecksumCapabilities, Device, DeviceCapabilities, Medium}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; +#[cfg(all(feature = "dns", not(feature = "dhcpv4")))] +use smoltcp::socket::dns; use smoltcp::time::Instant; +#[cfg(any(feature = "dns", not(feature = "dhcpv4")))] +use smoltcp::wire::Ipv4Address; use smoltcp::wire::{EthernetAddress, HardwareAddress}; #[cfg(not(feature = "dhcpv4"))] -use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{IpAddress, IpCidr}; use super::network::{NetworkInterface, NetworkState}; use crate::arch; @@ -74,6 +78,8 @@ impl<'a> NetworkInterface<'a> { sockets, device, dhcp_handle, + #[cfg(feature = "dns")] + dns_handle: None, })) } @@ -150,10 +156,26 @@ impl<'a> NetworkInterface<'a> { }); iface.routes_mut().add_default_ipv4_route(mygw).unwrap(); + #[allow(unused_mut)] + let mut sockets = SocketSet::new(vec![]); + + #[cfg(feature = "dns")] + let dns_handle = { + // use Google's DNS servers + let servers = &[ + Ipv4Address::new(8, 8, 4, 4).into(), + Ipv4Address::new(8, 8, 8, 8).into(), + ]; + let dns_socket = dns::Socket::new(servers, vec![]); + sockets.add(dns_socket) + }; + NetworkState::Initialized(Box::new(Self { iface, - sockets: SocketSet::new(vec![]), + sockets, device, + #[cfg(feature = "dns")] + dns_handle: Some(dns_handle), })) } } diff --git a/src/executor/network.rs b/src/executor/network.rs index ed8fa1fa19..1f7952b8e2 100644 --- a/src/executor/network.rs +++ b/src/executor/network.rs @@ -1,4 +1,6 @@ use alloc::boxed::Box; +#[cfg(feature = "dns")] +use alloc::vec::Vec; use core::future; use core::ops::DerefMut; use core::sync::atomic::{AtomicU16, Ordering}; @@ -8,18 +10,24 @@ use hermit_sync::InterruptTicketMutex; use smoltcp::iface::{SocketHandle, SocketSet}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; +#[cfg(feature = "dns")] +use smoltcp::socket::dns::{self, GetQueryResultError, QueryHandle}; #[cfg(feature = "tcp")] use smoltcp::socket::tcp; #[cfg(feature = "udp")] use smoltcp::socket::udp; use smoltcp::socket::AnySocket; use smoltcp::time::{Duration, Instant}; +#[cfg(feature = "dns")] +use smoltcp::wire::{DnsQueryType, IpAddress}; #[cfg(feature = "dhcpv4")] use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; use crate::arch; use crate::executor::device::HermitNet; use crate::executor::spawn; +#[cfg(feature = "dns")] +use crate::fd::IoError; use crate::scheduler::PerCoreSchedulerExt; pub(crate) enum NetworkState<'a> { @@ -49,6 +57,8 @@ pub(crate) struct NetworkInterface<'a> { pub(super) device: HermitNet, #[cfg(feature = "dhcpv4")] pub(super) dhcp_handle: SocketHandle, + #[cfg(feature = "dns")] + pub(super) dns_handle: Option, } #[cfg(target_arch = "x86_64")] @@ -104,6 +114,34 @@ async fn network_run() { .await } +#[cfg(feature = "dns")] +pub(crate) async fn get_query_result(query: QueryHandle) -> Result, IoError> { + future::poll_fn(|cx| { + let mut guard = NIC.lock(); + let nic = guard.as_nic_mut().unwrap(); + let socket = nic.get_mut_dns_socket()?; + match socket.get_query_result(query) { + Ok(addrs) => { + let mut ips = Vec::new(); + for x in &addrs { + ips.push(*x); + } + + Poll::Ready(Ok(ips)) + } + Err(GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => { + warn!("DNS query failed: {e:?}"); + Poll::Ready(Err(IoError::ENOENT)) + } + } + }) + .await +} + pub(crate) fn init() { info!("Try to initialize network!"); @@ -183,8 +221,18 @@ impl<'a> NetworkInterface<'a> { self.iface.routes_mut().remove_default_ipv4_route(); } + #[cfg(feature = "dns")] + let mut dns_servers: Vec = Vec::new(); for (i, s) in config.dns_servers.iter().enumerate() { info!("DNS server {}: {}", i, s); + #[cfg(feature = "dns")] + dns_servers.push(IpAddress::Ipv4(*s)); + } + + #[cfg(feature = "dns")] + if dns_servers.len() > 0 { + let dns_socket = dns::Socket::new(dns_servers.as_slice(), vec![]); + self.dns_handle = Some(self.sockets.add(dns_socket)); } } Some(dhcpv4::Event::Deconfigured) => { @@ -196,6 +244,15 @@ impl<'a> NetworkInterface<'a> { } }); self.iface.routes_mut().remove_default_ipv4_route(); + + #[cfg(feature = "dns")] + { + if let Some(dns_handle) = self.dns_handle { + self.sockets.remove(dns_handle); + } + + self.dns_handle = None; + } } }; } @@ -224,6 +281,32 @@ impl<'a> NetworkInterface<'a> { // This deallocates the socket's buffers self.sockets.remove(handle); } + + #[cfg(feature = "dns")] + pub(crate) fn start_query( + &mut self, + name: &str, + query_type: DnsQueryType, + ) -> Result { + let dns_handle = self.dns_handle.ok_or(IoError::EINVAL)?; + let socket: &mut dns::Socket<'a> = self.sockets.get_mut(dns_handle); + socket + .start_query(self.iface.context(), name, query_type) + .map_err(|_| IoError::EIO) + } + + #[allow(dead_code)] + #[cfg(feature = "dns")] + pub(crate) fn get_dns_socket(&self) -> Result<&dns::Socket<'a>, IoError> { + let dns_handle = self.dns_handle.ok_or(IoError::EINVAL)?; + Ok(self.sockets.get(dns_handle)) + } + + #[cfg(feature = "dns")] + pub(crate) fn get_mut_dns_socket(&mut self) -> Result<&mut dns::Socket<'a>, IoError> { + let dns_handle = self.dns_handle.ok_or(IoError::EINVAL)?; + Ok(self.sockets.get_mut(dns_handle)) + } } #[inline] diff --git a/src/syscalls/socket.rs b/src/syscalls/socket.rs index 5975a4ab70..5d937d601b 100644 --- a/src/syscalls/socket.rs +++ b/src/syscalls/socket.rs @@ -257,6 +257,98 @@ pub struct linger { pub l_linger: i32, } +#[cfg(not(feature = "dns"))] +#[hermit_macro::system] +#[no_mangle] +pub unsafe extern "C" fn sys_getaddrbyname( + _name: *const c_char, + _inaddr: *mut u8, + _len: usize, +) -> i32 { + error!("Please enable the feature 'dns' to determine the network ip by name."); + -ENOSYS +} + +/// The system call `sys_getaddrbyname` determine the network host entry. +/// It expects an array of u8 with a size of in_addr or of in6_addr. +/// The result of the DNS request will be stored in this array. +/// +/// # Example +/// +/// ``` +/// use hermit_abi::in_addr; +/// let c_string = std::ffi::CString::new("rust-lang.org").expect("CString::new failed"); +/// let name = c_string.into_raw(); +/// let mut inaddr: in_addr = Default::default(); +/// let _ = unsafe { +/// hermit_abi::getaddrbyname( +/// name, +/// &mut inaddr as *mut _ as *mut u8, +/// std::mem::size_of::(), +/// ) +/// }; +/// +/// // retake pointer to free memory +/// let _ = CString::from_raw(name); +/// ``` +#[cfg(feature = "dns")] +#[hermit_macro::system] +#[no_mangle] +pub unsafe extern "C" fn sys_getaddrbyname( + name: *const c_char, + inaddr: *mut u8, + len: usize, +) -> i32 { + use alloc::borrow::ToOwned; + + use smoltcp::wire::DnsQueryType; + + use crate::executor::block_on; + use crate::executor::network::get_query_result; + + if len != size_of::().try_into().unwrap() + && len != size_of::().try_into().unwrap() + { + return -EINVAL; + } + + if inaddr.is_null() { + return -EINVAL; + } + + let query_type = if len == size_of::().try_into().unwrap() { + DnsQueryType::Aaaa + } else { + DnsQueryType::A + }; + + let name = unsafe { core::ffi::CStr::from_ptr(name) }; + let name = if let Ok(name) = name.to_str() { + name.to_owned() + } else { + return -EINVAL; + }; + + let query = { + let mut guard = NIC.lock(); + let nic = guard.as_nic_mut().unwrap(); + let query = nic.start_query(&name, query_type).unwrap(); + nic.poll_common(crate::executor::network::now()); + + query + }; + + match block_on(get_query_result(query), None) { + Ok(addr_vec) => { + let slice = unsafe { core::slice::from_raw_parts_mut(inaddr, len) }; + slice.copy_from_slice(addr_vec[0].as_bytes()); + + 0 + } + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } +} + #[hermit_macro::system] #[no_mangle] pub extern "C" fn sys_socket(domain: i32, type_: SockType, protocol: i32) -> i32 {