diff --git a/src/dnsd/main.rs b/src/dnsd/main.rs index 73babd05fd..6a1b67c9ef 100644 --- a/src/dnsd/main.rs +++ b/src/dnsd/main.rs @@ -31,7 +31,18 @@ fn run() -> Result<()> { .map_err(|e| Error::from_syscall_error(e, "failed to open time:"))? as RawFd; - let (dns_file, time_file) = unsafe { (File::from_raw_fd(dns_fd), File::from_raw_fd(time_fd)) }; + let nameserver_fd = syscall::open( + "netcfg:resolv/nameserver", + syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ).map_err(|e| Error::from_syscall_error(e, "failed to open nameserver:"))? + as RawFd; + + let (dns_file, time_file) = unsafe { + ( + File::from_raw_fd(dns_fd), + File::from_raw_fd(time_fd), + ) + }; let dnsd = Rc::new(RefCell::new(Dnsd::new(dns_file, time_file))); @@ -46,6 +57,12 @@ fn run() -> Result<()> { let dnsd_ = Rc::clone(&dnsd); + event_queue + .add(nameserver_fd, move |_| dnsd_.borrow_mut().on_nameserver_event()) + .map_err(|e| Error::from_io_error(e, "failed to listen to nameserver"))?; + + let dnsd_ = Rc::clone(&dnsd); + event_queue.set_default_callback(move |fd, _| dnsd_.borrow_mut().on_unknown_fd_event(fd)); event_queue diff --git a/src/dnsd/scheme.rs b/src/dnsd/scheme.rs index 3be32c1d8d..d32e562736 100644 --- a/src/dnsd/scheme.rs +++ b/src/dnsd/scheme.rs @@ -9,7 +9,9 @@ use std::io::{Read, Write}; use std::mem; use std::os::unix::io::RawFd; use std::str; +use std::str::FromStr; use std::rc::Rc; +use std::net::Ipv4Addr; use syscall::data::TimeSpec; use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; use syscall; @@ -40,6 +42,7 @@ enum DnsParsingResult { } struct Domains { + nameserver: Ipv4Addr, domains: BTreeMap, Domain>, requests: BTreeMap>, resolved_timeouts: VecDeque<(TimeSpec, Rc)>, @@ -48,11 +51,28 @@ struct Domains { impl Domains { fn new() -> Domains { - 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("netcfg:resolv/nameserver") { + let mut nameserver = String::new(); + if let Ok(_) = file.read_to_string(&mut nameserver) { + if let Some(line) = nameserver.lines().next() { + if let Ok(ip) = Ipv4Addr::from_str(&line) { + trace!("Changing nameserver to {}", ip); + self.nameserver = ip; + } + } + } } } @@ -62,7 +82,7 @@ impl Domains { builder.add_question(domain, QueryType::A, QueryClass::IN); let packet = builder.build().ok()?; let udp_fd = syscall::open( - "udp:8.8.8.8:53", + &format!("udp:{}:53", self.nameserver), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, ).ok()? as RawFd; if syscall::write(udp_fd as usize, &packet) != Ok(packet.len()) { @@ -131,13 +151,13 @@ impl Domains { } Entry::Occupied(e) => e, }; - let mut buf = [0u8; 4096]; + let mut buf = [0u8; 0x1000]; let readed = syscall::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.len() == 0 { + 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() { unsubscribe_from_fd(fd).ok()?; @@ -290,11 +310,11 @@ impl Dnsd { let fds_to_wakeup = self.domains.on_time_event(&time); if !fds_to_wakeup.is_empty() { for fd in &fds_to_wakeup { - if let Some(file) = self.files.get_mut(&fd) { + if let Some(file) = self.files.get_mut(fd) { *file = DnsFile::Timeout; } } - self.wakeup_fds(fds_to_wakeup); + self.wakeup_fds(&fds_to_wakeup); } time.tv_sec += Dnsd::TIME_EVENT_TIMEOUT_S; @@ -323,6 +343,7 @@ impl Dnsd { } pub fn on_unknown_fd_event(&mut self, fd: RawFd) -> Result> { + trace!("Unknown fd event {}", fd); let mut cur_time = TimeSpec::default(); syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut cur_time) .map_err(|e| Error::from_syscall_error(e, "Can't get time"))?; @@ -330,24 +351,29 @@ impl Dnsd { match self.domains.on_fd_event(fd, &cur_time) { Some(DnsParsingResult::FailFiles(fds_to_fail)) => { for fd in &fds_to_fail { - if let Some(file) = self.files.get_mut(&fd) { + if let Some(file) = self.files.get_mut(fd) { *file = DnsFile::Failed; } } - self.wakeup_fds(fds_to_fail); + self.wakeup_fds(&fds_to_fail); } Some(DnsParsingResult::WakeUpFiles(fds_to_wakeup)) => { - self.wakeup_fds(fds_to_wakeup); + self.wakeup_fds(&fds_to_wakeup); } None => {} } Ok(None) } - fn wakeup_fds(&mut self, fds_to_wakeup: BTreeSet) { + pub fn on_nameserver_event(&mut self) -> Result> { + self.domains.update_nameserver(); + Ok(None) + } + + fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet) { let mut syscall_packets = vec![]; - for fd in &fds_to_wakeup { - if let Some(packet) = self.wait_map.remove(&fd) { + for fd in fds_to_wakeup { + if let Some(packet) = self.wait_map.remove(fd) { syscall_packets.push(packet); } } @@ -375,7 +401,7 @@ impl Dnsd { } } - return true; + true } } diff --git a/src/lib/logger.rs b/src/lib/logger.rs index 0fd36d9b1b..90ed1f8306 100644 --- a/src/lib/logger.rs +++ b/src/lib/logger.rs @@ -8,7 +8,7 @@ impl Log for Logger { } fn log(&self, record: &LogRecord) { - println!("{}: {}", record.level(), record.args()); + println!("{}: {}", record.target(), record.args()); } } diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index 673a1b02cc..62233cf90d 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -52,12 +52,17 @@ fn run() -> Result<()> { .map_err(|e| Error::from_syscall_error(e, "failed to open :icmp"))? as RawFd; + trace!("opening :netcfg"); + let netcfg_fd = syscall::open(":netcfg", O_RDWR | O_CREAT | O_NONBLOCK) + .map_err(|e| Error::from_syscall_error(e, "failed to open :netcfg"))? + as RawFd; + let time_path = format!("time:{}", syscall::CLOCK_MONOTONIC); let time_fd = syscall::open(&time_path, syscall::O_RDWR) .map_err(|e| Error::from_syscall_error(e, "failed to open time:"))? as RawFd; - let (network_file, ip_file, time_file, udp_file, tcp_file, icmp_file) = unsafe { + let (network_file, ip_file, time_file, udp_file, tcp_file, icmp_file, netcfg_file) = unsafe { ( File::from_raw_fd(network_fd), File::from_raw_fd(ip_fd), @@ -65,6 +70,7 @@ fn run() -> Result<()> { File::from_raw_fd(udp_fd), File::from_raw_fd(tcp_fd), File::from_raw_fd(icmp_fd), + File::from_raw_fd(netcfg_fd), ) }; @@ -75,6 +81,7 @@ fn run() -> Result<()> { tcp_file, icmp_file, time_file, + netcfg_file, ))); let mut event_queue = EventQueue::<(), Error>::new() @@ -120,10 +127,18 @@ fn run() -> Result<()> { }) .map_err(|e| Error::from_io_error(e, "failed to listen to icmp events"))?; + let smolnetd_ = Rc::clone(&smolnetd); + event_queue - .add(time_fd, move |_| smolnetd.borrow_mut().on_time_event()) + .add(time_fd, move |_| smolnetd_.borrow_mut().on_time_event()) .map_err(|e| Error::from_io_error(e, "failed to listen to time events"))?; + event_queue + .add(netcfg_fd, move |_| { + smolnetd.borrow_mut().on_netcfg_scheme_event() + }) + .map_err(|e| Error::from_io_error(e, "failed to listen to netcfg events"))?; + event_queue.trigger_all(0)?; event_queue.run() diff --git a/src/smolnetd/scheme/icmp.rs b/src/smolnetd/scheme/icmp.rs index bccb75a3b0..cacb9f6605 100644 --- a/src/smolnetd/scheme/icmp.rs +++ b/src/smolnetd/scheme/icmp.rs @@ -92,8 +92,10 @@ impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { tx_packets.push(IcmpPacketBuffer::new(vec![0; NetworkDevice::MTU])); } - let socket = IcmpSocket::new(IcmpSocketBuffer::new(rx_packets), - IcmpSocketBuffer::new(tx_packets)); + let socket = IcmpSocket::new( + IcmpSocketBuffer::new(rx_packets), + IcmpSocketBuffer::new(tx_packets), + ); let handle = socket_set.add(socket); let mut icmp_socket = socket_set.get::(handle); let ident = ident_set diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index 6ba011acaf..22ad873c48 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -1,10 +1,10 @@ use netutils::getcfg; use smoltcp; -use smoltcp::iface::{NeighborCache, EthernetInterface, EthernetInterfaceBuilder}; +use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache}; use smoltcp::socket::SocketSet as SmoltcpSocketSet; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address}; use std::cell::RefCell; -use std::collections::{VecDeque, BTreeMap}; +use std::collections::{BTreeMap, VecDeque}; use std::fs::File; use std::io::{Read, Write}; use std::mem::size_of; @@ -21,20 +21,23 @@ use self::ip::IpScheme; use self::tcp::TcpScheme; use self::udp::UdpScheme; use self::icmp::IcmpScheme; +use self::netcfg::NetCfgScheme; mod ip; mod socket; mod tcp; mod udp; mod icmp; +mod netcfg; type SocketSet = SmoltcpSocketSet<'static, 'static, 'static>; +type Interface = Rc>>; pub struct Smolnetd { network_file: Rc>, time_file: File, - iface: EthernetInterface<'static, 'static, NetworkDevice>, + iface: Interface, socket_set: Rc>, startup_time: Instant, @@ -43,6 +46,7 @@ pub struct Smolnetd { udp_scheme: UdpScheme, tcp_scheme: TcpScheme, icmp_scheme: IcmpScheme, + netcfg_scheme: NetCfgScheme, input_queue: Rc>>, buffer_pool: Rc>, @@ -61,12 +65,13 @@ impl Smolnetd { tcp_file: File, icmp_file: File, time_file: File, + netcfg_file: File, ) -> Smolnetd { let hardware_addr = EthernetAddress::from_str(getcfg("mac").unwrap().trim()) .expect("Can't parse the 'mac' cfg"); let local_ip = IpAddress::from_str(getcfg("ip").unwrap().trim()).expect("Can't parse the 'ip' cfg."); - let protocol_addrs = [ + let protocol_addrs = vec![ IpCidr::new(local_ip, 24), IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), ]; @@ -83,14 +88,15 @@ impl Smolnetd { Rc::clone(&buffer_pool), ); let iface = EthernetInterfaceBuilder::new(network_device) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .ethernet_addr(hardware_addr) - .ip_addrs(protocol_addrs) - .ipv4_gateway(default_gw) - .finalize(); + .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .ethernet_addr(hardware_addr) + .ip_addrs(protocol_addrs) + .ipv4_gateway(default_gw) + .finalize(); + let iface = Rc::new(RefCell::new(iface)); let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![]))); Smolnetd { - iface, + iface: Rc::clone(&iface), socket_set: Rc::clone(&socket_set), startup_time: Instant::now(), time_file, @@ -98,6 +104,7 @@ impl Smolnetd { udp_scheme: UdpScheme::new(Rc::clone(&socket_set), udp_file), tcp_scheme: TcpScheme::new(Rc::clone(&socket_set), tcp_file), icmp_scheme: IcmpScheme::new(Rc::clone(&socket_set), icmp_file), + netcfg_scheme: NetCfgScheme::new(Rc::clone(&iface), netcfg_file), input_queue, network_file, buffer_pool, @@ -141,6 +148,11 @@ impl Smolnetd { Ok(None) } + pub fn on_netcfg_scheme_event(&mut self) -> Result> { + self.netcfg_scheme.on_scheme_event()?; + Ok(None) + } + fn schedule_time_event(&mut self, timeout: i64) -> Result<()> { let mut time = TimeSpec::default(); if self.time_file.read(&mut time)? < size_of::() { @@ -160,30 +172,33 @@ impl Smolnetd { } fn poll(&mut self) -> Result { - let mut iter_limit = 10usize; - let timeout = loop { - iter_limit -= 1; - if iter_limit == 0 { - break 0; - } + let timeout = { + let mut iter_limit = 10usize; + let mut iface = self.iface.borrow_mut(); + let mut socket_set = self.socket_set.borrow_mut(); let timestamp = self.get_timestamp(); - match self.iface.poll(&mut *self.socket_set.borrow_mut(), timestamp) { - Ok(_) => (), - Err(smoltcp::Error::Unrecognized) => (), - Err(e) => { - error!("poll error: {}", e); - break 0 + loop { + if iter_limit == 0 { + break 0; + } + iter_limit -= 1; + match iface.poll(&mut socket_set, timestamp) { + Ok(_) | Err(smoltcp::Error::Unrecognized) => (), + Err(e) => { + error!("poll error: {}", e); + break 0; + } + } + match iface.poll_at(&socket_set, timestamp) { + Some(n) if n > timestamp => { + break ::std::cmp::min(::std::i64::MAX as u64, n - timestamp) as i64 + } + Some(_) => {} + None => break ::std::i64::MAX, } } - self.notify_sockets()?; - match self.iface.poll_at(&*self.socket_set.borrow(), timestamp) { - Some(n) if n > timestamp => { - break ::std::cmp::min(::std::i64::MAX as u64, n - timestamp) as i64 - }, - Some(_) => {}, - None => break ::std::i64::MAX - } }; + self.notify_sockets()?; Ok(::std::cmp::min( ::std::cmp::max(Smolnetd::MIN_CHECK_TIMEOUT_MS, timeout), Smolnetd::MAX_CHECK_TIMEOUT_MS, @@ -197,9 +212,7 @@ impl Smolnetd { let count = self.network_file .borrow_mut() .read(&mut buffer) - .map_err(|e| { - Error::from_io_error(e, "Failed to read from network file") - })?; + .map_err(|e| Error::from_io_error(e, "Failed to read from network file"))?; if count == 0 { break; } diff --git a/src/smolnetd/scheme/netcfg/mod.rs b/src/smolnetd/scheme/netcfg/mod.rs new file mode 100644 index 0000000000..a77a1a5d20 --- /dev/null +++ b/src/smolnetd/scheme/netcfg/mod.rs @@ -0,0 +1,346 @@ +#[macro_use] +mod nodes; +mod notifier; + +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::fs::File; +use std::io::{Read, Write}; +use std::rc::Rc; +use std::str::FromStr; +use std::str; +use syscall::data::Stat; +use syscall::flag::{MODE_DIR, MODE_FILE}; +use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; +use syscall; + +use self::nodes::*; +use self::notifier::*; +use redox_netstack::error::Result; +use super::{post_fevent, Interface}; + +const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; + +fn parse_default_gw(value: &str) -> SyscallResult { + let mut routes = value.lines(); + if let Some(route) = routes.next() { + if !routes.next().is_none() { + return Err(SyscallError::new(syscall::EINVAL)); + } + let mut words = route.split_whitespace(); + if let Some("default") = words.next() { + if let Some("via") = words.next() { + if let Some(ip) = words.next() { + return Ipv4Address::from_str(ip) + .map_err(|_| SyscallError::new(syscall::EINVAL)); + } + } + } + } + Err(SyscallError::new(syscall::EINVAL)) +} + +fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRef) -> CfgNodeRef { + cfg_node!{ + "resolv" => { + "nameserver" => { + rw [dns_config, notifier] + || { + format!("{}\n", dns_config.borrow().name_server) + } + |name_server| { + let ip = Ipv4Address::from_str(name_server.trim()) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !ip.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + dns_config.borrow_mut().name_server = ip; + notifier.borrow_mut().schedule_notify("resolv/nameserver"); + Ok(0) + } + } + }, + "route" => { + "list" => { + ro [iface] || { + if let Some(ip) = iface.borrow().ipv4_gateway() { + format!("default via {}\n", ip) + } else { + String::new() + } + } + }, + "add" => { + wo [iface, notifier] |routes| { + let default_gw = parse_default_gw(routes)?; + iface.borrow_mut().set_ipv4_gateway(Some(default_gw)); + notifier.borrow_mut().schedule_notify("route/list"); + Ok(0) + } + }, + "rm" => { + wo [iface, notifier] |routes| { + let default_gw = parse_default_gw(routes)?; + let mut iface = iface.borrow_mut(); + if iface.ipv4_gateway() != Some(default_gw) { + return Err(SyscallError::new(syscall::EINVAL)); + } + iface.set_ipv4_gateway(None); + notifier.borrow_mut().schedule_notify("route/list"); + Ok(0) + } + } + }, + "ifaces" => { + "eth0" => { + "mac" => { + rw [iface, notifier] + || { + format!("{}\n", iface.borrow().ethernet_addr()) + } + |mac| { + let mac = mac.lines().next() + .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; + let mac = EthernetAddress::from_str(mac). + map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !mac.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + iface.borrow_mut().set_ethernet_addr(mac); + notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); + Ok(0) + } + }, + "addr" => { + "list" => { + ro [iface] || { + let mut ips = String::new(); + for cidr in iface.borrow().ip_addrs() { + ips += &format!("{}\n", cidr); + } + ips + } + }, + "add" => { + wo [iface, notifier] |input| { + let mut iface = iface.borrow_mut(); + let mut cidrs = iface.ip_addrs().to_vec(); + for cidr in input.lines() { + let cidr = IpCidr::from_str(cidr) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + cidrs.insert(0, cidr); + } + iface.update_ip_addrs(|s| { + *s = From::from(cidrs); + }); + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); + Ok(0) + } + }, + "rm" => { + wo [iface, notifier] |input| { + let mut iface = iface.borrow_mut(); + let mut cidrs = iface.ip_addrs().to_vec(); + for cidr in input.lines() { + let cidr = IpCidr::from_str(cidr) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + let pre_retain_len = cidrs.len(); + cidrs.retain(|&c| c != cidr); + if pre_retain_len == cidrs.len() { + return Err(SyscallError::new(syscall::EINVAL)); + } + } + iface.update_ip_addrs(|s| { + *s = From::from(cidrs); + }); + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); + Ok(0) + } + }, + } + } + } + } +} + +struct DNSConfig { + name_server: Ipv4Address, +} + +type DNSConfigRef = Rc>; + +struct NetCfgFile { + path: String, + cfg_node: CfgNodeRef, + read_buf: Vec, + write_buf: Vec, + pos: usize, + uid: u32, +} + +pub struct NetCfgScheme { + scheme_file: File, + next_fd: usize, + files: BTreeMap, + root_node: CfgNodeRef, + notifier: NotifierRef, +} + +impl NetCfgScheme { + pub fn new(iface: Interface, scheme_file: File) -> NetCfgScheme { + let notifier = Notifier::new_ref(); + let dns_config = Rc::new(RefCell::new(DNSConfig { + name_server: Ipv4Address::new(8, 8, 8, 8), + })); + NetCfgScheme { + scheme_file, + next_fd: 1, + files: BTreeMap::new(), + root_node: mk_root_node(iface, Rc::clone(¬ifier), dns_config), + notifier, + } + } + + pub fn on_scheme_event(&mut self) -> Result> { + loop { + let mut packet = SyscallPacket::default(); + if self.scheme_file.read(&mut packet)? == 0 { + break; + } + self.handle(&mut packet); + self.scheme_file.write_all(&packet)?; + } + self.notify_scheduled_fds(); + Ok(None) + } + + fn notify_scheduled_fds(&mut self) { + let fds_to_notify = self.notifier.borrow_mut().get_notified_fds(); + for fd in fds_to_notify { + let _ = post_fevent(&mut self.scheme_file, fd, syscall::EVENT_READ, 1); + } + } +} + +impl SchemeMut for NetCfgScheme { + fn open(&mut self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> SyscallResult { + let path = str::from_utf8(url).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + let mut current_node = Rc::clone(&self.root_node); + for part in path.split('/') { + if part.is_empty() { + continue; + } + let next_node = current_node + .borrow_mut() + .open(part) + .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; + current_node = next_node; + } + let read_buf = Vec::from(current_node.borrow().read()); + let fd = self.next_fd; + trace!("open {} {}", fd, path); + self.next_fd += 1; + self.files.insert( + fd, + NetCfgFile { + path: path.to_owned(), + cfg_node: current_node, + uid, + pos: 0, + read_buf, + write_buf: vec![], + }, + ); + Ok(fd) + } + + fn close(&mut self, fd: usize) -> SyscallResult { + trace!("close {}", fd); + if let Some(file) = self.files.remove(&fd) { + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + let node = file.cfg_node.borrow(); + if node.is_writable() { + let value = str::from_utf8(&file.write_buf) + .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + node.write(value) + } else { + Ok(0) + } + } else { + Err(SyscallError::new(syscall::EBADF)) + } + } + + fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + if file.uid != 0 { + return Err(SyscallError::new(syscall::EACCES)); + } + + if (WRITE_BUFFER_MAX_SIZE - file.write_buf.len()) < buf.len() { + return Err(SyscallError::new(syscall::EMSGSIZE)); + } + file.write_buf.extend_from_slice(buf); + Ok(buf.len()) + } + + fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + let mut i = 0; + while i < buf.len() && file.pos < file.read_buf.len() { + buf[i] = file.read_buf[file.pos]; + i += 1; + file.pos += 1; + } + Ok(i) + } + + fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let cfg_node = file.cfg_node.borrow(); + + stat.st_mode = if cfg_node.is_dir() { + MODE_DIR + } else { + MODE_FILE + }; + if cfg_node.is_writable() { + stat.st_mode |= 0o222; + } + if cfg_node.is_readable() { + stat.st_mode |= 0o444; + } + stat.st_uid = 0; + stat.st_gid = 0; + stat.st_size = file.read_buf.len() as u64; + + Ok(0) + } + + fn fevent(&mut self, fd: usize, events: usize) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + if events & syscall::EVENT_READ == syscall::EVENT_READ { + self.notifier.borrow_mut().subscribe(&file.path, fd); + } else { + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + } + Ok(fd) + } +} diff --git a/src/smolnetd/scheme/netcfg/nodes.rs b/src/smolnetd/scheme/netcfg/nodes.rs new file mode 100644 index 0000000000..4b9fe17401 --- /dev/null +++ b/src/smolnetd/scheme/netcfg/nodes.rs @@ -0,0 +1,199 @@ +use std::cell::RefCell; +use std::rc::Rc; +use std::collections::BTreeMap; +use syscall::Result as SyscallResult; + +pub type CfgNodeRef = Rc>; + +pub trait CfgNode { + fn is_dir(&self) -> bool { + false + } + + fn is_writable(&self) -> bool { + false + } + + fn is_readable(&self) -> bool { + true + } + + fn read(&self) -> String { + String::new() + } + + fn write(&self, _buf: &str) -> SyscallResult { + Ok(0) + } + + fn open(&self, _file: &str) -> Option { + None + } +} + +pub struct RONode +where + F: Fn() -> String, +{ + read_fun: F, +} + +impl CfgNode for RONode +where + F: Fn() -> String, +{ + fn read(&self) -> String { + (self.read_fun)() + } +} + +impl RONode +where + F: 'static + Fn() -> String, +{ + pub fn new_ref(read_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(RONode { read_fun })) + } +} + +pub struct WONode +where + F: Fn(&str) -> SyscallResult, +{ + write_fun: F, +} + +impl CfgNode for WONode +where + F: Fn(&str) -> SyscallResult, +{ + fn write(&self, buf: &str) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_readable(&self) -> bool { + false + } + + fn is_writable(&self) -> bool { + true + } +} + +impl WONode +where + F: 'static + Fn(&str) -> SyscallResult, +{ + pub fn new_ref(write_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(WONode { write_fun })) + } +} + +pub struct RWNode +where + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, +{ + read_fun: F, + write_fun: G, +} + +impl CfgNode for RWNode +where + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, +{ + fn read(&self) -> String { + (self.read_fun)() + } + + fn write(&self, buf: &str) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_writable(&self) -> bool { + true + } +} + +impl RWNode +where + F: 'static + Fn() -> String, + G: 'static + Fn(&str) -> SyscallResult, +{ + pub fn new_ref(read_fun: F, write_fun: G) -> CfgNodeRef { + Rc::new(RefCell::new(RWNode { + read_fun, + write_fun, + })) + } +} + +pub struct StaticDirNode { + child_nodes: BTreeMap, +} + +impl CfgNode for StaticDirNode { + fn is_dir(&self) -> bool { + true + } + + fn read(&self) -> String { + let mut files = String::new(); + for child in self.child_nodes.keys() { + if !files.is_empty() { + files.push('\n'); + } + files += child; + } + files + } + + fn open(&self, file: &str) -> Option { + self.child_nodes.get(file).map(|node| Rc::clone(node)) + } +} + +impl StaticDirNode { + pub fn new_ref(child_nodes: BTreeMap) -> CfgNodeRef { + Rc::new(RefCell::new(StaticDirNode { child_nodes })) + } +} + +macro_rules! cfg_node { + (val $e:expr) => { + $e + }; + (ro [ $($c:ident),* ] || $b:block ) => { + { + $(let $c = $c.clone();)* + RONode::new_ref(move|| $b) + } + }; + (wo [ $($c:ident),* ] |$i:ident| $b:block ) => { + { + $(let $c = $c.clone();)* + WONode::new_ref(move |$i: &str| $b) + } + }; + (rw [ $($c:ident),* ] || $rb:block |$i:ident| $wb:block ) => { + { + let read_fun = { + $(#[allow(unused_variables)] let $c = $c.clone();)* + move || $rb + }; + let write_fun = { + $(#[allow(unused_variables)] let $c = $c.clone();)* + move |$i: &str| $wb + }; + RWNode::new_ref(read_fun, write_fun) + } + }; + ($($e:expr => { $($t:tt)* }),* $(,)*) => { + { + let mut children = BTreeMap::new(); + $(children.insert($e.into(), cfg_node!($($t)*));)* + StaticDirNode::new_ref(children) + } + }; +} diff --git a/src/smolnetd/scheme/netcfg/notifier.rs b/src/smolnetd/scheme/netcfg/notifier.rs new file mode 100644 index 0000000000..24b0a66fcb --- /dev/null +++ b/src/smolnetd/scheme/netcfg/notifier.rs @@ -0,0 +1,62 @@ +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::{BTreeMap, BTreeSet}; +use std::collections::btree_map::Entry; + +pub struct Notifier { + listeners: BTreeMap>, + notified: BTreeSet, +} + +pub type NotifierRef = Rc>; + +impl Notifier { + pub fn new_ref() -> NotifierRef { + Rc::new(RefCell::new(Notifier { + listeners: BTreeMap::new(), + notified: BTreeSet::new(), + })) + } + + pub fn subscribe(&mut self, path: &str, fd: usize) { + trace!("Sub fd {} to {}", fd, path); + match self.listeners.entry(path.to_owned()) { + Entry::Occupied(mut e) => { + e.get_mut().insert(fd); + } + Entry::Vacant(e) => { + let mut fds = BTreeSet::new(); + fds.insert(fd); + e.insert(fds); + } + } + } + + pub fn unsubscribe(&mut self, path: &str, fd: usize) { + let empty = if let Some(fds) = self.listeners.get_mut(path) { + if fds.remove(&fd) { + trace!("Unsub fd {} from {}", fd, path); + } + fds.is_empty() + } else { + false + }; + if empty { + self.listeners.remove(path); + } + } + + pub fn schedule_notify(&mut self, path: &str) { + trace!("Notifying {}", path); + if let Some(fds) = self.listeners.get(path) { + self.notified.extend(fds); + } + } + + pub fn get_notified_fds(&mut self) -> BTreeSet { + use std::mem::swap; + let mut notified = BTreeSet::new(); + swap(&mut self.notified, &mut notified); + notified + } +} diff --git a/src/smolnetd/scheme/socket.rs b/src/smolnetd/scheme/socket.rs index a94f16ac24..d6723964ab 100644 --- a/src/smolnetd/scheme/socket.rs +++ b/src/smolnetd/scheme/socket.rs @@ -84,8 +84,8 @@ where { pub fn socket_handle(&self) -> SocketHandle { match *self { - SchemeFile::Socket(SocketFile { socket_handle, .. }) | - SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle, + SchemeFile::Socket(SocketFile { socket_handle, .. }) + | SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle, } } } @@ -580,9 +580,10 @@ where } fn dup(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { - if let Some((flags, uid, gid)) = self.nulls.get(&fd).map(|null| { - (null.flags, null.uid, null.gid) - }) { + if let Some((flags, uid, gid)) = self.nulls + .get(&fd) + .map(|null| (null.flags, null.uid, null.gid)) + { return self.open(buf, flags, uid, gid); } diff --git a/src/smolnetd/scheme/tcp.rs b/src/smolnetd/scheme/tcp.rs index 7896c22948..a7cbf99c35 100644 --- a/src/smolnetd/scheme/tcp.rs +++ b/src/smolnetd/scheme/tcp.rs @@ -65,8 +65,8 @@ impl<'a> SchemeSocket for TcpSocket<'a> { return Err(SyscallError::new(syscall::EACCES)); } - let rx_packets = vec![0; 65_535]; - let tx_packets = vec![0; 65_535]; + let rx_packets = vec![0; 0xffff]; + let tx_packets = vec![0; 0xffff]; let rx_buffer = TcpSocketBuffer::new(rx_packets); let tx_buffer = TcpSocketBuffer::new(tx_packets); let socket = TcpSocket::new(rx_buffer, tx_buffer); @@ -164,8 +164,8 @@ impl<'a> SchemeSocket for TcpSocket<'a> { trace!("TCP creating new listening socket"); let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(())); - let rx_packets = vec![0; 65_535]; - let tx_packets = vec![0; 65_535]; + let rx_packets = vec![0; 0xffff]; + let tx_packets = vec![0; 0xffff]; let rx_buffer = TcpSocketBuffer::new(rx_packets); let tx_buffer = TcpSocketBuffer::new(tx_packets); let socket = TcpSocket::new(rx_buffer, tx_buffer);