use smoltcp::iface::SocketHandle; use smoltcp::socket::udp::{ PacketBuffer as UdpSocketBuffer, PacketMetadata as UdpPacketMetadata, Socket as UdpSocket, }; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use std::str; use syscall; use syscall::{Error as SyscallError, Result as SyscallResult}; use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile}; use super::{parse_endpoint, SchemeWrapper, Smolnetd, SocketSet}; use crate::port_set::PortSet; use crate::router::Router; use std::fmt::Write; pub type UdpScheme = SchemeWrapper>; impl<'a> SchemeSocket for UdpSocket<'a> { type SchemeDataT = PortSet; type DataT = IpListenEndpoint; type SettingT = (); fn new_scheme_data() -> Self::SchemeDataT { PortSet::new(49_152u16, 65_535u16).expect("Wrong UDP port numbers") } fn can_send(&self) -> bool { self.can_send() } fn can_recv(&self) -> bool { self.can_recv() } fn may_recv(&self) -> bool { true } fn hop_limit(&self) -> u8 { self.hop_limit().unwrap_or(64) } fn set_hop_limit(&mut self, hop_limit: u8) { self.set_hop_limit(Some(hop_limit)); } fn get_setting( _file: &SocketFile, _setting: Self::SettingT, _buf: &mut [u8], ) -> SyscallResult { Ok(0) } fn set_setting( _file: &mut SocketFile, _setting: Self::SettingT, _buf: &[u8], ) -> SyscallResult { Ok(0) } fn new_socket( socket_set: &mut SocketSet, path: &str, uid: u32, port_set: &mut Self::SchemeDataT, context: &Context, ) -> SyscallResult<(SocketHandle, Self::DataT)> { let mut parts = path.split('/'); let remote_endpoint = parse_endpoint(parts.next().unwrap_or("")); let mut local_endpoint = parse_endpoint(parts.next().unwrap_or("")); if local_endpoint.port > 0 && local_endpoint.port <= 1024 && uid != 0 { return Err(SyscallError::new(syscall::EACCES)); } let rx_buffer = UdpSocketBuffer::new( vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], ); let tx_buffer = UdpSocketBuffer::new( vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE], vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE], ); let udp_socket = UdpSocket::new(rx_buffer, tx_buffer); // TODO: claim port with ethernet ip address if local_endpoint.port == 0 { local_endpoint.port = port_set .get_port() .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; } else if !port_set.claim_port(local_endpoint.port) { return Err(SyscallError::new(syscall::EADDRINUSE)); } let socket_handle = socket_set.add(udp_socket); let udp_socket = socket_set.get_mut::(socket_handle); if remote_endpoint.is_specified() { let local_endpoint_addr = match local_endpoint.addr { Some(addr) if addr.is_unspecified() => Some(addr), _ => { // local ip is 0.0.0.0, resolve it let route_table = context.route_table.borrow(); let addr = route_table .lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified")); if matches!(addr, None) { error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint); } addr } }; local_endpoint = IpListenEndpoint { addr: local_endpoint_addr, port: local_endpoint.port, }; } udp_socket .bind(local_endpoint) .expect("Can't bind udp socket to local endpoint"); Ok((socket_handle, remote_endpoint)) } fn close_file( &self, file: &SchemeFile, port_set: &mut Self::SchemeDataT, ) -> SyscallResult<()> { if let SchemeFile::Socket(_) = *file { port_set.release_port(self.endpoint().port); } Ok(()) } fn write_buf( &mut self, file: &mut SocketFile, buf: &[u8], ) -> SyscallResult { if !file.data.is_specified() { return Err(SyscallError::new(syscall::EADDRNOTAVAIL)); } if self.can_send() { let endpoint = file.data; let endpoint = IpEndpoint::new( endpoint .addr .expect("If we can send, this should be specified"), endpoint.port, ); self.send_slice(buf, endpoint).expect("Can't send slice"); Ok(buf.len()) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { Err(SyscallError::new(syscall::EAGAIN)) } else { Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read } } fn read_buf( &mut self, file: &mut SocketFile, buf: &mut [u8], ) -> SyscallResult { if self.can_recv() { let (length, _) = self.recv_slice(buf).expect("Can't receive slice"); Ok(length) } else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { Err(SyscallError::new(syscall::EAGAIN)) } else { Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read } } fn dup( socket_set: &mut SocketSet, file: &mut SchemeFile, path: &str, port_set: &mut Self::SchemeDataT, ) -> SyscallResult> { let socket_handle = file.socket_handle(); let file = match path { "listen" => { // there's no accept() for UDP return Err(SyscallError::new(syscall::EAFNOSUPPORT)); } _ => { let remote_endpoint = parse_endpoint(path); if let SchemeFile::Socket(ref udp_handle) = *file { SchemeFile::Socket(udp_handle.clone_with_data( if remote_endpoint.is_specified() { remote_endpoint } else { udp_handle.data }, )) } else { SchemeFile::Socket(SocketFile::new_with_data(socket_handle, remote_endpoint)) } } }; let endpoint = { let socket = socket_set.get::(socket_handle); socket.endpoint() }; if let SchemeFile::Socket(_) = file { port_set.acquire_port(endpoint.port); } Ok(Some((file, None))) } fn fpath(&self, file: &SchemeFile, buf: &mut [u8]) -> SyscallResult { let unspecified = "0.0.0.0:0"; let mut path = String::from("/scheme/udp/"); // remote match file { SchemeFile::Socket(SocketFile { data: endpoint, .. }) => { if endpoint.is_specified() { write!(&mut path, "{}", endpoint).unwrap() } else { write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap() } } _ => path.push_str(unspecified), } path.push('/'); // local let endpoint = self.endpoint(); if endpoint.is_specified() { write!(&mut path, "{}", endpoint).unwrap() } else { write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap() } let path = path.as_bytes(); let mut i = 0; while i < buf.len() && i < path.len() { buf[i] = path[i]; i += 1; } Ok(i) } }