Migrate dnsd to redox-scheme.

This commit is contained in:
4lDO2
2025-04-05 18:47:01 +02:00
parent ea6e864a5c
commit dda88e3a3b
5 changed files with 270 additions and 139 deletions
+3 -1
View File
@@ -19,12 +19,14 @@ path = "src/lib/lib.rs"
netutils = { git = "https://gitlab.redox-os.org/redox-os/netutils.git" }
redox_event = "0.4.1"
redox-daemon = "0.1.2"
redox_syscall = "0.5"
redox_syscall = { version = "0.5", features = ["std"] }
redox-log = "0.1"
dns-parser = "0.7.1"
libredox = { version = "0.1.3", features = ["mkns"] }
anyhow = "1.0.81"
ioslice = "0.6.0"
#redox-scheme = "0.5.0"
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
[dependencies.log]
version = "0.4"
+5 -10
View File
@@ -6,6 +6,7 @@ use event::EventQueue;
use ioslice::IoSlice;
use libredox::Fd;
use redox_netstack::logger;
use redox_scheme::Socket;
use scheme::Dnsd;
use std::fs::File;
use std::os::unix::io::{FromRawFd, RawFd};
@@ -16,8 +17,7 @@ mod scheme;
fn run(daemon: redox_daemon::Daemon) -> Result<()> {
use libredox::flag::*;
let dns_fd =
Fd::open(":dns", O_RDWR | O_CREAT | O_NONBLOCK, 0).context("failed to open :dns")?;
let dns_socket = Socket::nonblock("dns").context("failed to open :dns")?;
let time_path = format!("/scheme/time/{}", CLOCK_MONOTONIC);
let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open time")?;
@@ -33,7 +33,7 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> {
event_queue
.subscribe(
dns_fd.raw(),
dns_socket.inner().raw(),
EventSource::DnsScheme,
event::EventFlags::READ,
)
@@ -49,14 +49,9 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> {
.subscribe(time_fd.raw(), EventSource::Timer, event::EventFlags::READ)
.context("failed to listen to timer events")?;
let (dns_file, time_file) = unsafe {
(
File::from_raw_fd(dns_fd.into_raw() as RawFd),
File::from_raw_fd(time_fd.into_raw() as RawFd),
)
};
let time_file = unsafe { File::from_raw_fd(time_fd.into_raw() as RawFd) };
let mut dnsd = Dnsd::new(dns_file, time_file, &event_queue);
let mut dnsd = Dnsd::new(dns_socket, time_file, &event_queue);
let new_ns =
libredox::call::mkns(&[IoSlice::new(b"dns")]).expect("dnsd: failed to create namespace");
+127 -67
View File
@@ -1,9 +1,9 @@
use std::borrow::ToOwned;
use std::collections::btree_map::Entry;
use std::collections::VecDeque;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::{HashMap, VecDeque};
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::io::{Read, Write};
use std::mem;
use std::net::Ipv4Addr;
use std::os::unix::io::RawFd;
@@ -12,15 +12,15 @@ use std::str;
use std::str::FromStr;
use libredox::flag;
use syscall;
use redox_scheme::scheme::{IntoTag, Op, OpRead, SchemeSync};
use redox_scheme::{CallerCtx, Id, OpenResult, RequestKind, Response, SignalBehavior};
use syscall::data::TimeSpec;
use syscall::{
Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket,
Result as SyscallResult, SchemeMut,
};
use syscall::schemev2::NewFdFlags;
use syscall::{self, EAGAIN, ECANCELED, ENOSYS, EWOULDBLOCK};
use syscall::{Error as SyscallError, EventFlags as SyscallEventFlags, Result as SyscallResult};
use event::EventQueue;
use redox_netstack::error::{Error, Result};
use redox_netstack::error::{Error, Result, ResultExt};
use dns_parser::{Builder, Packet as DNSPacket, RRData, ResponseCode};
use dns_parser::{QueryClass, QueryType};
@@ -304,12 +304,15 @@ impl Domains {
}
pub struct Dnsd<'q> {
dns_file: File,
scheme_socket: redox_scheme::Socket,
time_file: File,
queue: &'q EventQueue<EventSource>,
files: BTreeMap<usize, DnsFile>,
domains: Domains,
wait_map: BTreeMap<usize, SyscallPacket>,
blocked_ids: HashMap<Id, (CallerCtx, OpRead)>,
blocked_fds: HashMap<usize, Vec<Id>>,
next_fd: usize,
}
@@ -318,14 +321,19 @@ impl<'q> Dnsd<'q> {
const REQUEST_TIMEOUT_S: i64 = 30;
const TIME_EVENT_TIMEOUT_S: i64 = 5;
pub fn new(dns_file: File, time_file: File, queue: &'q EventQueue<EventSource>) -> Self {
pub fn new(
scheme_socket: redox_scheme::Socket,
time_file: File,
queue: &'q EventQueue<EventSource>,
) -> Self {
Dnsd {
dns_file,
scheme_socket,
time_file,
queue,
files: BTreeMap::new(),
domains: Domains::new(),
wait_map: BTreeMap::new(),
blocked_ids: HashMap::new(),
blocked_fds: HashMap::new(),
next_fd: 1,
}
}
@@ -358,30 +366,58 @@ impl<'q> Dnsd<'q> {
pub fn on_dns_file_event(&mut self) -> Result<bool> {
loop {
let mut packet = SyscallPacket::default();
match self.dns_file.read(&mut packet) {
Ok(0) => {
let req = match self.scheme_socket.next_request(SignalBehavior::Restart) {
Ok(None) => {
//TODO: Cleanup must occur
return Ok(false);
}
Ok(_) => (),
Err(err) => {
if err.kind() == ErrorKind::WouldBlock {
return Ok(true);
Ok(Some(req)) => req,
Err(err) if err.errno == EWOULDBLOCK || err.errno == EAGAIN => {
return Ok(true);
}
Err(err) => return Err(err).context("scheme read error"),
};
let req = match req.kind() {
RequestKind::Call(call) => call,
RequestKind::OnClose { id } => {
self.on_close(id);
continue;
}
RequestKind::Cancellation(req) => {
if let Some((_, op)) = self.blocked_ids.remove(&req.id) {
if let Some(blocked) = self.blocked_fds.get_mut(&op.fd) {
blocked.retain(|id| *id != req.id);
}
self.scheme_socket
.write_response(Response::err(ECANCELED, op), SignalBehavior::Restart)
.context("scheme write fail")?;
}
continue;
}
_ => continue,
};
let ctx = req.caller();
let resp = match req.op() {
Err(req) => Response::err(ENOSYS, req),
Ok(Op::Read(mut op)) => {
let fd = op.fd;
// TODO: nonblock read flag to bypass blocking
let res = self.read(fd, op.buf(), 0, 0, &ctx);
if res == Err(SyscallError::new(EWOULDBLOCK)) {
let id = op.req_id();
self.blocked_ids.insert(id, (ctx, op));
self.blocked_fds.entry(fd).or_default().push(id);
continue;
} else {
return Err(Error::from(err));
Response::new(res, op)
}
}
}
// TODO: implement cancellation
let a = packet.a;
self.handle(&mut packet);
if packet.a != (-syscall::EWOULDBLOCK) as usize {
self.dns_file.write_all(&packet)?;
} else {
packet.a = a;
self.handle_block(packet)?;
}
Ok(op) => op.handle_sync(ctx, self),
};
self.scheme_socket
.write_response(resp, SignalBehavior::Restart)
.context("scheme write fail")?;
}
}
@@ -418,23 +454,29 @@ impl<'q> Dnsd<'q> {
}
fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet<usize>) {
let mut syscall_packets = vec![];
for fd in fds_to_wakeup {
if let Some(packet) = self.wait_map.remove(fd) {
syscall_packets.push(packet);
let Some(affected) = self.blocked_fds.remove(&fd) else {
continue;
};
for id in affected {
let Some((ctx, mut op)) = self.blocked_ids.remove(&id) else {
continue;
};
// TODO: deduplicate code
let fd = op.fd;
// TODO: nonblock read flag to bypass blocking
let res = self.read(fd, op.buf(), 0, 0, &ctx);
let resp = if res == Err(SyscallError::new(EWOULDBLOCK)) {
self.blocked_ids.insert(id, (ctx, op));
self.blocked_fds.entry(fd).or_default().push(id);
continue;
} else {
Response::new(res, op)
};
self.scheme_socket
.write_response(resp, SignalBehavior::Restart);
}
}
for mut packet in syscall_packets.drain(..) {
self.handle(&mut packet);
let _ = self.dns_file.write_all(&packet);
}
}
fn handle_block(&mut self, packet: SyscallPacket) -> Result<()> {
let fd = packet.b;
self.wait_map.insert(fd, packet);
Ok(())
}
fn validate_domain(domain: &str) -> bool {
@@ -452,8 +494,23 @@ impl<'q> Dnsd<'q> {
}
}
impl SchemeMut for Dnsd<'_> {
fn open(&mut self, url: &str, _flags: usize, _uid: u32, _gid: u32) -> SyscallResult<usize> {
impl Dnsd<'_> {
fn on_close(&mut self, fd: usize) {
trace!("Close {}", fd);
let Some(file) = self.files.get_mut(&fd) else {
return;
};
if let DnsFile::Waiting { ref domain } = *file {
self.domains.unwait_fd(domain, fd);
}
self.files.remove(&fd);
}
}
impl SchemeSync for Dnsd<'_> {
fn open(&mut self, url: &str, _flags: usize, _ctx: &CallerCtx) -> SyscallResult<OpenResult> {
let domain = url.to_lowercase();
if domain.is_empty() || !Dnsd::validate_domain(&domain) {
return Err(SyscallError::new(syscall::EINVAL));
@@ -472,29 +529,31 @@ impl SchemeMut for Dnsd<'_> {
);
self.files.insert(fd, dns_file);
trace!("Open {} {}", &domain, fd);
Ok(fd)
Ok(OpenResult::ThisScheme {
number: fd,
flags: NewFdFlags::empty(),
})
}
fn close(&mut self, fd: usize) -> SyscallResult<usize> {
trace!("Close {}", fd);
let file = self
.files
.get_mut(&fd)
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
if let DnsFile::Waiting { ref domain } = *file {
self.domains.unwait_fd(domain, fd);
}
self.files.remove(&fd);
Ok(0)
}
fn write(&mut self, _fd: usize, _buf: &[u8]) -> SyscallResult<usize> {
fn write(
&mut self,
_fd: usize,
_buf: &[u8],
_off: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> SyscallResult<usize> {
Err(SyscallError::new(syscall::EINVAL))
}
fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult<usize> {
fn read(
&mut self,
fd: usize,
buf: &mut [u8],
_off: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> SyscallResult<usize> {
trace!("Read {}", fd);
let file = self
.files
@@ -538,11 +597,12 @@ impl SchemeMut for Dnsd<'_> {
&mut self,
_fd: usize,
_events: SyscallEventFlags,
_ctx: &CallerCtx,
) -> SyscallResult<SyscallEventFlags> {
Ok(SyscallEventFlags::empty())
}
fn fsync(&mut self, _fd: usize) -> SyscallResult<usize> {
Ok(0)
fn fsync(&mut self, _fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> {
Ok(())
}
}
+11
View File
@@ -17,6 +17,17 @@ pub struct Error {
descr: String,
}
pub trait ResultExt {
type T;
fn context(self, ctxt: impl Into<String>) -> std::result::Result<Self::T, Error>;
}
impl<T> ResultExt for std::result::Result<T, SyscallError> {
type T = T;
fn context(self, ctxt: impl Into<String>) -> std::result::Result<T, Error> {
self.map_err(|err| Error::from_syscall_error(err, ctxt))
}
}
impl Error {
pub fn from_syscall_error<S: Into<String>>(syscall_error: SyscallError, descr: S) -> Error {
Error {