diff --git a/Cargo.lock b/Cargo.lock index 7333557cbf..da5612e345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,16 +324,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "dns-parser" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7020f6760aea312d43d23cb83bf6c0c49f162541db8b02bf95209ac51dc253d" -dependencies = [ - "byteorder 1.5.0", - "quick-error", -] - [[package]] name = "env_logger" version = "0.8.4" @@ -708,12 +698,6 @@ dependencies = [ "redox_termios", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.40" @@ -905,8 +889,6 @@ name = "redox_netstack" version = "0.1.0" dependencies = [ "anyhow", - "dns-parser", - "ioslice", "libredox", "log", "netutils", diff --git a/netstack/Cargo.toml b/netstack/Cargo.toml index 386a6e8466..ac485f4d45 100644 --- a/netstack/Cargo.toml +++ b/netstack/Cargo.toml @@ -3,10 +3,6 @@ name = "redox_netstack" version = "0.1.0" edition = "2021" -[[bin]] -name = "dnsd" -path = "src/dnsd/main.rs" - [[bin]] name = "smolnetd" path = "src/smolnetd/main.rs" @@ -21,10 +17,8 @@ redox_event = "0.4.1" redox-daemon = "0.1.2" 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" } diff --git a/netstack/src/dnsd/main.rs b/netstack/src/dnsd/main.rs deleted file mode 100644 index 994b45bc4f..0000000000 --- a/netstack/src/dnsd/main.rs +++ /dev/null @@ -1,97 +0,0 @@ -#[macro_use] -extern crate log; - -use anyhow::{Context, Result}; -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}; -use std::process; - -mod scheme; - -fn run(daemon: redox_daemon::Daemon) -> Result<()> { - use libredox::flag::*; - - 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")?; - - let nameserver_fd = Fd::open( - "/scheme/netcfg/resolv/nameserver", - O_RDWR | O_CREAT | O_NONBLOCK, - 0, - ) - .context("failed to open nameserver")?; - - let event_queue = EventQueue::::new().context("failed to create event queue")?; - - event_queue - .subscribe( - dns_socket.inner().raw(), - EventSource::DnsScheme, - event::EventFlags::READ, - ) - .context("failed to listen to time events")?; - event_queue - .subscribe( - nameserver_fd.raw(), - EventSource::NameserverScheme, - event::EventFlags::READ, - ) - .context("failed to listen to nameserver socket events")?; - event_queue - .subscribe(time_fd.raw(), EventSource::Timer, event::EventFlags::READ) - .context("failed to listen to timer events")?; - - let time_file = unsafe { File::from_raw_fd(time_fd.into_raw() as RawFd) }; - - 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"); - libredox::call::setrens(new_ns, new_ns).expect("dnsd: failed to enter namespace"); - - daemon.ready().expect("dnsd: failed to notify parent"); - - for event_res in event_queue.iter() { - let event = event_res.context("failed to read from event queue")?; - match event.user_data { - EventSource::DnsScheme => { - if !dnsd.on_dns_file_event()? { - break; - } - } - EventSource::NameserverScheme => dnsd.on_nameserver_event()?, - EventSource::Timer => dnsd.on_time_event()?, - EventSource::Other => dnsd.on_unknown_fd_event(event.fd as RawFd)?, - } - } - Ok(()) -} - -fn main() { - redox_daemon::Daemon::new(move |daemon| { - logger::init_logger("dnsd"); - if let Err(err) = run(daemon) { - error!("dnsd: {}", err); - process::exit(1); - } - process::exit(0); - }) - .expect("dnsd: failed to daemonize"); -} - -event::user_data! { - enum EventSource { - DnsScheme, - NameserverScheme, - Timer, - Other, - } -} diff --git a/netstack/src/dnsd/scheme.rs b/netstack/src/dnsd/scheme.rs deleted file mode 100644 index 12c2a93685..0000000000 --- a/netstack/src/dnsd/scheme.rs +++ /dev/null @@ -1,610 +0,0 @@ -use std::borrow::ToOwned; -use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, BTreeSet}; -use std::collections::{HashMap, VecDeque}; -use std::fs::File; -use std::io::{Read, Write}; -use std::mem; -use std::net::Ipv4Addr; -use std::os::unix::io::RawFd; -use std::rc::Rc; -use std::str; -use std::str::FromStr; - -use libredox::flag; -use redox_scheme::scheme::{IntoTag, Op, OpRead, SchemeSync}; -use redox_scheme::{CallerCtx, Id, OpenResult, RequestKind, Response, SignalBehavior}; -use syscall::data::TimeSpec; -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, ResultExt}; - -use dns_parser::{Builder, Packet as DNSPacket, RRData, ResponseCode}; -use dns_parser::{QueryClass, QueryType}; - -use crate::EventSource; - -enum DnsFile { - Resolved { data: Rc<[u8]>, pos: usize }, - Waiting { domain: String }, - Timeout, - Failed, -} - -enum Domain { - Resolved { - data: Rc<[u8]>, - }, - Requested { - waiting_fds: BTreeSet, - socket_fd: RawFd, - }, -} - -enum DnsParsingResult { - WakeUpFiles(BTreeSet), - FailFiles(BTreeSet), -} - -struct Domains { - nameserver: Ipv4Addr, - domains: BTreeMap, Domain>, - requests: BTreeMap>, - resolved_timeouts: VecDeque<(TimeSpec, Rc)>, - requested_timeouts: VecDeque<(TimeSpec, Rc)>, -} - -impl Domains { - fn new() -> Domains { - let mut domains = Domains { - nameserver: Ipv4Addr::new(8, 8, 8, 8), - domains: BTreeMap::new(), - requests: BTreeMap::new(), - resolved_timeouts: VecDeque::new(), - requested_timeouts: VecDeque::new(), - }; - domains.update_nameserver(); - domains - } - - pub fn update_nameserver(&mut self) { - if let Ok(mut file) = File::open("/scheme/netcfg/resolv/nameserver") { - let mut nameserver = String::new(); - if file.read_to_string(&mut nameserver).is_ok() { - if let Some(line) = nameserver.lines().next() { - if let Ok(ip) = Ipv4Addr::from_str(line) { - trace!("Changing nameserver to {}", ip); - self.nameserver = ip; - } - } - } - } - } - - fn request_domain(&mut self, domain: &str, queue: &EventQueue) -> Option { - trace!("Requesting domain {}", domain); - let mut builder = Builder::new_query(1, true); - builder.add_question(domain, QueryType::A, QueryClass::IN); - let packet = builder.build().ok()?; - let udp_fd = libredox::call::open( - &format!("udp:{}:53", self.nameserver), - libredox::flag::O_RDWR | libredox::flag::O_CREAT | libredox::flag::O_NONBLOCK, - 0, - ) - .ok()?; - if libredox::call::write(udp_fd, &packet) != Ok(packet.len()) { - libredox::call::close(udp_fd).ok()?; - return None; - } - queue - .subscribe(udp_fd, EventSource::Other, event::EventFlags::READ) - .ok()?; - self.requests - .insert(udp_fd as RawFd, domain.to_owned().into()); - Some(udp_fd as RawFd) - } - - fn on_time_event( - &mut self, - cur_time: &TimeSpec, - queue: &EventQueue, - ) -> Result> { - while let Some((timeout, domain)) = self.resolved_timeouts.pop_front() { - if timeout.tv_sec > cur_time.tv_sec - || (timeout.tv_sec == cur_time.tv_sec && timeout.tv_nsec > cur_time.tv_nsec) - { - self.resolved_timeouts.push_front((timeout, domain)); - break; - } - trace!("Timing out resolved domain {:?}", domain); - match self.domains.entry(domain) { - Entry::Vacant(_) => {} - Entry::Occupied(e) => { - if let Domain::Resolved { .. } = *e.get() { - e.remove(); - } - } - } - } - - let mut fds_to_wakeup = BTreeSet::new(); - - while let Some((timeout, domain)) = self.requested_timeouts.pop_front() { - if timeout.tv_sec > cur_time.tv_sec - || (timeout.tv_sec == cur_time.tv_sec && timeout.tv_nsec > cur_time.tv_nsec) - { - self.requested_timeouts.push_front((timeout, domain)); - break; - } - trace!("Timing out requested domain {:?}", domain); - match self.domains.entry(domain) { - Entry::Vacant(_) => {} - Entry::Occupied(e) => { - if let Domain::Requested { .. } = *e.get() { - if let Domain::Requested { - mut waiting_fds, - socket_fd, - } = e.remove() - { - fds_to_wakeup.append(&mut waiting_fds); - queue.unsubscribe(socket_fd as usize).map_err(|e| { - Error::from_syscall_error(e.into(), "unsubscribe failure") - })?; - let _ = libredox::call::close(socket_fd as usize); - } - } - } - } - } - - Ok(fds_to_wakeup) - } - - fn on_fd_event( - &mut self, - fd: RawFd, - cur_time: &TimeSpec, - queue: &EventQueue, - ) -> Option { - let e = match self.requests.entry(fd) { - Entry::Vacant(_) => { - return None; - } - Entry::Occupied(e) => e, - }; - let mut buf = [0u8; 0x1000]; - let readed = libredox::call::read(fd as usize, &mut buf).ok()?; - if readed == 0 { - return None; - } - let pkt = DNSPacket::parse(&buf).ok()?; - if pkt.header.response_code != ResponseCode::NoError || pkt.answers.is_empty() { - if let Some(query) = pkt.questions.iter().next() { - if query.qname.to_string().to_lowercase() == e.get().as_ref() { - queue.unsubscribe(fd as usize).ok()?; - libredox::call::close(fd as usize).ok()?; - let domain = e.remove(); - self.requested_timeouts - .retain(|&(_, ref d)| d.as_ref() != domain.as_ref()); - if let Entry::Occupied(e) = self.domains.entry(domain) { - let domain_data = e.remove(); - return if let Domain::Requested { waiting_fds, .. } = domain_data { - Some(DnsParsingResult::FailFiles(waiting_fds)) - } else { - None - }; - } - } - } - return None; - } - let mut result = String::new(); - for answer in pkt.answers { - if answer.name.to_string().to_lowercase() != e.get().as_ref() { - continue; - } - if let RRData::A(ip) = answer.data { - result += &format!("{}\n", ip); - } - } - if result.is_empty() { - return None; - } - let data = Rc::from(result.into_bytes()); - queue.unsubscribe(fd as usize).ok()?; - libredox::call::close(fd as usize).ok()?; - let domain = e.remove(); - let mut domain_data = Domain::Resolved { data }; - trace!("On FD event {} {} resolved", fd, domain); - - let mut resolved_timeout = *cur_time; - resolved_timeout.tv_sec += Dnsd::RESOLVED_TIMEOUT_S; - - self.resolved_timeouts - .push_back((resolved_timeout, Rc::clone(&domain))); - - self.requested_timeouts - .retain(|&(_, ref d)| d.as_ref() != domain.as_ref()); - - match self.domains.entry(domain) { - Entry::Vacant(e) => { - e.insert(domain_data); - None - } - Entry::Occupied(mut e) => { - mem::swap(e.get_mut(), &mut domain_data); - if let Domain::Requested { waiting_fds, .. } = domain_data { - Some(DnsParsingResult::WakeUpFiles(waiting_fds)) - } else { - None - } - } - } - } - - fn file_from_domain( - &mut self, - domain: &str, - fd: usize, - cur_time: &TimeSpec, - queue: &EventQueue, - ) -> DnsFile { - if let Some(domain_data) = self.domains.get_mut(domain) { - match *domain_data { - Domain::Resolved { ref data } => DnsFile::Resolved { - data: Rc::clone(data), - pos: 0, - }, - Domain::Requested { - ref mut waiting_fds, - .. - } => { - waiting_fds.insert(fd); - DnsFile::Waiting { - domain: domain.to_owned(), - } - } - } - } else { - if let Some(socket_fd) = self.request_domain(domain, queue) { - let mut waiting_fds = BTreeSet::new(); - let domain = domain.to_owned().into(); - waiting_fds.insert(fd); - self.domains.insert( - Rc::clone(&domain), - Domain::Requested { - waiting_fds, - socket_fd, - }, - ); - let mut timeout = *cur_time; - timeout.tv_sec += Dnsd::REQUEST_TIMEOUT_S; - self.requested_timeouts.push_back((timeout, domain)); - } - DnsFile::Waiting { - domain: domain.to_owned(), - } - } - } - - fn unwait_fd(&mut self, domain: &str, fd: usize) { - if let Some(domain_data) = self.domains.get_mut(domain) { - if let Domain::Requested { - ref mut waiting_fds, - .. - } = *domain_data - { - waiting_fds.remove(&fd); - } - } - } -} - -pub struct Dnsd<'q> { - scheme_socket: redox_scheme::Socket, - time_file: File, - queue: &'q EventQueue, - files: BTreeMap, - domains: Domains, - - blocked_ids: HashMap, - blocked_fds: HashMap>, - - next_fd: usize, -} - -impl<'q> Dnsd<'q> { - const RESOLVED_TIMEOUT_S: i64 = 5 * 60; - const REQUEST_TIMEOUT_S: i64 = 30; - const TIME_EVENT_TIMEOUT_S: i64 = 5; - - pub fn new( - scheme_socket: redox_scheme::Socket, - time_file: File, - queue: &'q EventQueue, - ) -> Self { - Dnsd { - scheme_socket, - time_file, - queue, - files: BTreeMap::new(), - domains: Domains::new(), - blocked_ids: HashMap::new(), - blocked_fds: HashMap::new(), - next_fd: 1, - } - } - - pub fn on_time_event(&mut self) -> Result<()> { - let mut time = TimeSpec::default(); - if self.time_file.read(&mut time)? < mem::size_of::() { - return Err(Error::from_syscall_error( - syscall::Error::new(syscall::EBADF), - "Can't read current time", - )); - } - - let fds_to_wakeup = self.domains.on_time_event(&time, self.queue)?; - if !fds_to_wakeup.is_empty() { - for fd in &fds_to_wakeup { - if let Some(file) = self.files.get_mut(fd) { - *file = DnsFile::Timeout; - } - } - self.wakeup_fds(&fds_to_wakeup)?; - } - - time.tv_sec += Dnsd::TIME_EVENT_TIMEOUT_S; - self.time_file - .write_all(&time) - .map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?; - Ok(()) - } - - pub fn on_dns_file_event(&mut self) -> Result { - loop { - let req = match self.scheme_socket.next_request(SignalBehavior::Restart) { - Ok(None) => { - //TODO: Cleanup must occur - return Ok(false); - } - 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 { - Response::new(res, op) - } - } - Ok(op) => op.handle_sync(ctx, self), - }; - self.scheme_socket - .write_response(resp, SignalBehavior::Restart) - .context("scheme write fail")?; - } - } - - pub fn on_unknown_fd_event(&mut self, fd: RawFd) -> Result<()> { - trace!("Unknown fd event {}", fd); - let cur_time = libredox::call::clock_gettime(libredox::flag::CLOCK_MONOTONIC) - .map_err(|e| Error::from_syscall_error(e.into(), "Can't get time"))?; - // TODO - let cur_time = TimeSpec { - tv_sec: cur_time.tv_sec, - tv_nsec: cur_time.tv_nsec as _, - }; - - match self.domains.on_fd_event(fd, &cur_time, self.queue) { - Some(DnsParsingResult::FailFiles(fds_to_fail)) => { - for fd in &fds_to_fail { - if let Some(file) = self.files.get_mut(fd) { - *file = DnsFile::Failed; - } - } - self.wakeup_fds(&fds_to_fail)?; - } - Some(DnsParsingResult::WakeUpFiles(fds_to_wakeup)) => { - self.wakeup_fds(&fds_to_wakeup)?; - } - None => {} - } - Ok(()) - } - - pub fn on_nameserver_event(&mut self) -> Result<()> { - self.domains.update_nameserver(); - Ok(()) - } - - fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet) -> Result<()> { - for fd in fds_to_wakeup { - 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) - .context("scheme write fail")?; - } - } - Ok(()) - } - - fn validate_domain(domain: &str) -> bool { - if domain.len() > 256 { - return false; - } - - for part in domain.split('.') { - if part.len() >= 63 { - return false; - } - } - - true - } -} - -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 { - let domain = url.to_lowercase(); - if domain.is_empty() || !Dnsd::validate_domain(&domain) { - return Err(SyscallError::new(syscall::EINVAL)); - } - let fd = self.next_fd; - self.next_fd += 1; - let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC)?; - let dns_file = self.domains.file_from_domain( - &domain, - fd, - &TimeSpec { - tv_sec: cur_time.tv_sec, - tv_nsec: cur_time.tv_nsec as i32, - }, - self.queue, - ); - self.files.insert(fd, dns_file); - trace!("Open {} {}", &domain, fd); - Ok(OpenResult::ThisScheme { - number: fd, - flags: NewFdFlags::empty(), - }) - } - - fn write( - &mut self, - _fd: usize, - _buf: &[u8], - _off: u64, - _flags: u32, - _ctx: &CallerCtx, - ) -> SyscallResult { - Err(SyscallError::new(syscall::EINVAL)) - } - - fn read( - &mut self, - fd: usize, - buf: &mut [u8], - _off: u64, - _flags: u32, - _ctx: &CallerCtx, - ) -> SyscallResult { - trace!("Read {}", fd); - let file = self - .files - .get_mut(&fd) - .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - - let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC)?; - - if let DnsFile::Waiting { ref domain } = *file { - *file = self.domains.file_from_domain( - domain, - fd, - &TimeSpec { - tv_sec: cur_time.tv_sec, - tv_nsec: cur_time.tv_nsec as i32, - }, - self.queue, - ); - } - - match *file { - DnsFile::Resolved { - ref data, - ref mut pos, - } => { - let mut i = 0; - while i < buf.len() && *pos < data.len() { - buf[i] = data[*pos]; - i += 1; - *pos += 1; - } - Ok(i) - } - DnsFile::Waiting { .. } => Err(SyscallError::new(syscall::EWOULDBLOCK)), - DnsFile::Timeout => Err(SyscallError::new(syscall::ETIMEDOUT)), - DnsFile::Failed => Err(SyscallError::new(syscall::ENODATA)), - } - } - - fn fevent( - &mut self, - _fd: usize, - _events: SyscallEventFlags, - _ctx: &CallerCtx, - ) -> SyscallResult { - Ok(SyscallEventFlags::empty()) - } - - fn fsync(&mut self, _fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> { - Ok(()) - } -}