diff --git a/Cargo.lock b/Cargo.lock index f553cfd180a..9d4fe9e05fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7175,6 +7175,7 @@ name = "yggdrasil-abi" version = "0.1.0" dependencies = [ "compiler_builtins", + "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] diff --git a/library/std/src/os/yggdrasil/io/mod.rs b/library/std/src/os/yggdrasil/io/mod.rs index a736ec823a4..3798b25e45e 100644 --- a/library/std/src/os/yggdrasil/io/mod.rs +++ b/library/std/src/os/yggdrasil/io/mod.rs @@ -12,6 +12,7 @@ pub mod pipe; pub mod poll; pub mod shared_memory; pub mod terminal; +pub mod timer; // Public exports diff --git a/library/std/src/os/yggdrasil/io/timer.rs b/library/std/src/os/yggdrasil/io/timer.rs new file mode 100644 index 00000000000..d8e4962ce10 --- /dev/null +++ b/library/std/src/os/yggdrasil/io/timer.rs @@ -0,0 +1,34 @@ +#![unstable(feature = "yggdrasil_os", issue = "none")] + +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::cvt_io; +use crate::sys::fd::FileDesc; +use crate::time::Duration; + +use yggdrasil_rt::sys as syscall; + +#[unstable(feature = "yggdrasil_os", issue = "none")] +pub struct TimerFd(FileDesc); + +impl TimerFd { + #[unstable(feature = "yggdrasil_os", issue = "none")] + pub fn new(repeat: bool) -> io::Result { + let raw = cvt_io(unsafe { syscall::create_timer(repeat) })?; + let fd = unsafe { FileDesc::from_raw_fd(raw) }; + Ok(Self(fd)) + } + + pub fn start(&mut self, timeout: Duration) -> io::Result<()> { + let tval = timeout.as_micros(); + cvt_io(unsafe { syscall::write(self.0.as_raw_fd(), &tval.to_ne_bytes()) })?; + Ok(()) + } +} + +#[unstable(feature = "yggdrasil_os", issue = "none")] +impl AsRawFd for TimerFd { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/sys/yggdrasil/net/dns.rs b/library/std/src/sys/yggdrasil/net/dns.rs new file mode 100644 index 00000000000..12ab9e14c2c --- /dev/null +++ b/library/std/src/sys/yggdrasil/net/dns.rs @@ -0,0 +1,145 @@ +use yggdrasil_rt::net::dns::{ + self, DnsClass, DnsMessage, DnsRecordData, DnsReplyCode, DnsType, UdpRequester, +}; + +use crate::io; +use crate::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; +use crate::os::{ + fd::AsRawFd, + yggdrasil::io::{poll::PollChannel, timer::TimerFd}, +}; +use crate::time::Duration; + +pub struct LookupHost { + addresses: Vec, + port: u16, +} + +struct DnsRequester { + nameserver: SocketAddr, + + poll: PollChannel, + + timer: TimerFd, + socket: UdpSocket, +} + +// TODO remove hardcoded nameserver +// Maybe use local resolver? +const NAMESERVER: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(11, 0, 0, 1)), 53); + +impl DnsRequester { + pub fn new(nameserver: SocketAddr) -> io::Result { + let mut poll = PollChannel::new()?; + let timer = TimerFd::new(false)?; + let socket = UdpSocket::bind("0.0.0.0:0")?; + + poll.add(timer.as_raw_fd())?; + poll.add(socket.as_raw_fd())?; + + Ok(Self { nameserver, poll, timer, socket }) + } +} + +impl UdpRequester for DnsRequester { + type Error = io::Error; + + fn send_message(&mut self, message: &[u8]) -> io::Result<()> { + self.socket.send_to(message, &self.nameserver)?; + Ok(()) + } + + fn receive_message Option>( + &mut self, + map: F, + ) -> io::Result { + let mut buffer = [0; 2048]; + + self.timer.start(Duration::from_millis(500)); + + loop { + let (poll_fd, result) = self.poll.wait(None)?.unwrap(); + result?; + + match poll_fd { + fd if fd == self.socket.as_raw_fd() => { + let (len, _) = self.socket.recv_from(&mut buffer)?; + + if let Some(message) = map(&buffer[..len]) { + return Ok(message); + } + } + fd if fd == self.timer.as_raw_fd() => { + return Err(io::Error::new(io::ErrorKind::TimedOut, "DNS query timed out")); + } + _ => unreachable!(), + } + } + } +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + + fn next(&mut self) -> Option { + let ip = self.addresses.pop()?; + Some(SocketAddr::new(ip, self.port)) + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + use crate::str::FromStr; + + let (hostname, port) = s + .rsplit_once(':') + .ok_or(io::Error::new(io::ErrorKind::InvalidData, "Invalid host:port combination"))?; + let port = + u16::from_str(port).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + Self::try_from((hostname, port)) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((hostname, port): (&'a str, u16)) -> io::Result { + // TODO randomize xid/cookie + let mut requester = DnsRequester::new(NAMESERVER)?; + let message = dns::perform_query(&mut requester, hostname, DnsType::A, 12341, 0x12313121)?; + + if message.reply_code != DnsReplyCode::NO_ERROR { + return Err(io::Error::new(io::ErrorKind::Other, "DNS server returned error")); + } + + let mut addresses = vec![]; + + for answer in message.answers { + let Some(name) = answer.name.0.as_ref() else { + continue; + }; + let name = name.trim_end_matches('.'); + if name != hostname { + continue; + } + + match (answer.ty, answer.class, &answer.rdata) { + (DnsType::A, DnsClass::IN, DnsRecordData::A(address)) => { + addresses.push(IpAddr::V4(u32::from(*address).into())); + } + _ => (), + } + } + + Ok(Self { addresses, port }) + } +} diff --git a/library/std/src/sys/yggdrasil/net/mod.rs b/library/std/src/sys/yggdrasil/net/mod.rs index 3e54cf2aa71..f11db2720f1 100644 --- a/library/std/src/sys/yggdrasil/net/mod.rs +++ b/library/std/src/sys/yggdrasil/net/mod.rs @@ -1,58 +1,15 @@ #![allow(dead_code)] -// use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -// use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -// use crate::os::yggdrasil::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -// use crate::sys::io::FileDesc; -// use crate::sys_common::{AsInner, FromInner, IntoInner}; -// use crate::time::Duration; -// -// pub use crate::sys::{cvt, cvt_r}; - -// pub use super::yggdrasil_rt::netc; - +mod dns; mod tcp_listener; mod tcp_stream; mod udp; +pub use dns::LookupHost; pub use tcp_listener::TcpListener; pub use tcp_stream::TcpStream; pub use udp::UdpSocket; -use crate::net::SocketAddr; - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - todo!() - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - - fn next(&mut self) -> Option { - todo!() - } -} - -impl TryFrom<&str> for LookupHost { - type Error = crate::io::Error; - - fn try_from(_s: &str) -> crate::io::Result { - todo!() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = crate::io::Error; - - fn try_from(_s: (&'a str, u16)) -> crate::io::Result { - todo!() - } -} - // pub type wrlen_t = usize; // #[derive(Debug)]