alnyan/yggdrasil: Add LookupHost and TimerFd

This commit is contained in:
2024-01-28 01:01:54 +02:00
parent d822648ddd
commit 3e7f518a1d
5 changed files with 183 additions and 45 deletions
+1
View File
@@ -12,6 +12,7 @@ pub mod pipe;
pub mod poll;
pub mod shared_memory;
pub mod terminal;
pub mod timer;
// Public exports
+34
View File
@@ -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<Self> {
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()
}
}
+145
View File
@@ -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<IpAddr>,
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<Self> {
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<F: Fn(&[u8]) -> Option<DnsMessage>>(
&mut self,
map: F,
) -> io::Result<DnsMessage> {
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<SocketAddr> {
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<Self> {
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<Self> {
// 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 })
}
}
+2 -45
View File
@@ -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<SocketAddr> {
todo!()
}
}
impl TryFrom<&str> for LookupHost {
type Error = crate::io::Error;
fn try_from(_s: &str) -> crate::io::Result<Self> {
todo!()
}
}
impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
type Error = crate::io::Error;
fn try_from(_s: (&'a str, u16)) -> crate::io::Result<Self> {
todo!()
}
}
// pub type wrlen_t = usize;
// #[derive(Debug)]