From 2947f02293d1efe06dfe182945aa54c437d9d327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls=20Pi=C5=86=C4=B7is?= Date: Thu, 22 Apr 2021 11:04:54 +0100 Subject: [PATCH 1/5] Unify UAPI implementation for unixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emīls --- src/platform/linux/mod.rs | 3 +-- src/platform/linux/uapi.rs | 2 +- src/platform/unix/mod.rs | 1 + src/platform/unix/uapi.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/platform/unix/mod.rs create mode 100644 src/platform/unix/uapi.rs diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index d28391e..4962f04 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,7 +1,6 @@ mod tun; -mod uapi; mod udp; +pub use crate::platform::unix::uapi::UnixUAPI as UAPI; pub use tun::LinuxTun as Tun; -pub use uapi::LinuxUAPI as UAPI; pub use udp::LinuxUDP as UDP; diff --git a/src/platform/linux/uapi.rs b/src/platform/linux/uapi.rs index 107745a..99dd457 100644 --- a/src/platform/linux/uapi.rs +++ b/src/platform/linux/uapi.rs @@ -1,4 +1,4 @@ -use super::super::uapi::*; +use crate::platform::unix::UnixAPI; use std::fs; use std::io; diff --git a/src/platform/unix/mod.rs b/src/platform/unix/mod.rs new file mode 100644 index 0000000..9c87073 --- /dev/null +++ b/src/platform/unix/mod.rs @@ -0,0 +1 @@ +pub mod uapi; diff --git a/src/platform/unix/uapi.rs b/src/platform/unix/uapi.rs new file mode 100644 index 0000000..1210f7b --- /dev/null +++ b/src/platform/unix/uapi.rs @@ -0,0 +1,31 @@ +use crate::platform::uapi::*; + +use std::fs; +use std::io; +use std::os::unix::net::{UnixListener, UnixStream}; + +const SOCK_DIR: &str = "/var/run/wireguard/"; + +pub struct UnixUAPI {} + +impl PlatformUAPI for UnixUAPI { + type Error = io::Error; + type Bind = UnixListener; + + fn bind(name: &str) -> Result { + let socket_path = format!("{}{}.sock", SOCK_DIR, name); + let _ = fs::create_dir_all(SOCK_DIR); + let _ = fs::remove_file(&socket_path); + UnixListener::bind(socket_path) + } +} + +impl BindUAPI for UnixListener { + type Stream = UnixStream; + type Error = io::Error; + + fn connect(&self) -> Result { + let (stream, _) = self.accept()?; + Ok(stream) + } +} From 8bddba9483fce3e17717f1bbc38b117f3d463b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls=20Pi=C5=86=C4=B7is?= Date: Thu, 22 Apr 2021 11:47:55 +0100 Subject: [PATCH 2/5] Add macOS platform module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emīls --- src/platform/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 6b8fa0e..d77e85f 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -7,10 +7,14 @@ pub mod udp; pub use endpoint::Endpoint; #[cfg(target_os = "linux")] -pub mod linux; +#[path = "linux/mod.rs"] +pub use linux as plt; + +#[cfg(target_os = "macos")] +#[path = "macos/mod.rs"] +pub mod plt; + +pub(crate) mod unix; #[cfg(test)] pub mod dummy; - -#[cfg(target_os = "linux")] -pub use linux as plt; From 16cdf181e5d23b53264217756c06ca81f83aff3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls=20Pi=C5=86=C4=B7is?= Date: Tue, 13 Apr 2021 11:22:42 +0100 Subject: [PATCH 3/5] Add macOS utun support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emīls --- Cargo.lock | 22 +- Cargo.toml | 4 + src/main.rs | 5 + src/platform/macos/fd.rs | 44 +++ src/platform/macos/mod.rs | 6 + src/platform/macos/sys.rs | 38 +++ src/platform/macos/tun.rs | 288 ++++++++++++++++ src/platform/macos/udp.rs | 695 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 1100 insertions(+), 2 deletions(-) create mode 100644 src/platform/macos/fd.rs create mode 100644 src/platform/macos/mod.rs create mode 100644 src/platform/macos/sys.rs create mode 100644 src/platform/macos/tun.rs create mode 100644 src/platform/macos/udp.rs diff --git a/Cargo.lock b/Cargo.lock index 2941071..a3a8374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -398,6 +398,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "ioctl-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c429fffa658f288669529fc26565f728489a2e39bc7b24a428aaaf51355182e" + [[package]] name = "iovec" version = "0.1.4" @@ -455,9 +461,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "lock_api" @@ -593,6 +599,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -1231,10 +1247,12 @@ dependencies = [ "hex", "hjul", "hmac", + "ioctl-sys", "ip_network_table-deps-treebitmap", "libc", "log 0.4.11", "num_cpus", + "page_size", "parking_lot", "pnet", "proptest", diff --git a/Cargo.toml b/Cargo.toml index 640d8cb..cf99926 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,10 @@ version = "0.5.0" [target.'cfg(unix)'.dependencies] libc = "^0.2" +[target.'cfg(target_os = "macos")'.dependencies] +ioctl-sys = "0.6" +page_size = "0.4" + [dependencies.x25519-dalek] version = "^1.1" diff --git a/src/main.rs b/src/main.rs index 7e752bd..1a6967b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,11 @@ extern crate alloc; +#[cfg(target_os = "macos")] +extern crate ioctl_sys; +#[cfg(target_os = "macos")] +extern crate page_size; + #[cfg(feature = "profiler")] extern crate cpuprofiler; diff --git a/src/platform/macos/fd.rs b/src/platform/macos/fd.rs new file mode 100644 index 0000000..94cef98 --- /dev/null +++ b/src/platform/macos/fd.rs @@ -0,0 +1,44 @@ +use std::{io, os::unix::io::RawFd, sync::Arc}; + +struct FdInner { + fd: RawFd, +} + +impl Drop for FdInner { + fn drop(&mut self) { + unsafe { libc::close(self.fd) }; + } +} + +#[derive(Clone)] +pub(super) struct Fd { + fd: Arc, +} + +impl Fd { + pub fn new(fd: RawFd) -> Self { + Self { + fd: Arc::new(FdInner { fd }), + } + } + + pub unsafe fn raw_fd(&self) -> RawFd { + self.fd.fd + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let bytes_read = unsafe { libc::write(self.raw_fd(), buf.as_ptr() as _, buf.len()) }; + if bytes_read < 0 { + return Err(io::Error::from_raw_os_error(-bytes_read as i32)); + } + Ok(bytes_read as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let bytes_written = unsafe { libc::read(self.raw_fd(), buf.as_mut_ptr() as _, buf.len()) }; + if bytes_written < 0 { + return Err(io::Error::last_os_error()); + } + Ok(bytes_written as usize) + } +} diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs new file mode 100644 index 0000000..753fcc3 --- /dev/null +++ b/src/platform/macos/mod.rs @@ -0,0 +1,6 @@ +mod fd; +mod sys; +mod tun; + +pub use crate::platform::unix::uapi::UnixUAPI as UAPI; +pub use tun::MacosTun as Tun; diff --git a/src/platform/macos/sys.rs b/src/platform/macos/sys.rs new file mode 100644 index 0000000..de4d10d --- /dev/null +++ b/src/platform/macos/sys.rs @@ -0,0 +1,38 @@ +#![allow(non_camel_case_types)] +#[repr(C)] +pub struct ctl_info { + pub ctl_id: u32, + pub ctl_name: [u8; 96], +} +ioctl_sys::ioctl!(readwrite ctliocginfo with 'N', 3; ctl_info); + +#[repr(C)] +pub struct rt_msghdr { + pub rtm_msglen: u16, + pub rtm_version: u8, + pub rtm_type: u8, + pub rtm_index: u16, + pub rtm_flags: i32, + pub rtm_addrs: i32, + pub rtm_pid: libc::pid_t, + pub rtm_seq: i32, + pub rtm_errno: i32, + pub rtm_use: i32, + pub rtm_inits: u32, + pub rtm_rmx: rt_metrics, +} + +#[repr(C)] +pub struct rt_metrics { + pub rmx_locks: u32, /* Kernel must leave these values alone */ + pub rmx_mtu: u32, /* MTU for this path */ + pub rmx_hopcount: u32, /* max hops expected */ + pub rmx_expire: i32, /* lifetime for route, e.g. redirect */ + pub rmx_recvpipe: u32, /* inbound delay-bandwidth product */ + pub rmx_sendpipe: u32, /* outbound delay-bandwidth product */ + pub rmx_ssthresh: u32, /* outbound gateway buffer limit */ + pub rmx_rtt: u32, /* estimated round trip time */ + pub rmx_rttvar: u32, /* estimated rtt variance */ + pub rmx_pksent: u32, /* packets sent using this route */ + pub rmx_filler: [u32; 4], /* will be used for T/TCP later */ +} diff --git a/src/platform/macos/tun.rs b/src/platform/macos/tun.rs new file mode 100644 index 0000000..243e1e9 --- /dev/null +++ b/src/platform/macos/tun.rs @@ -0,0 +1,288 @@ +use crate::{ + plt::{fd::Fd, sys::*}, + tun::*, +}; +use std::{ + fmt, + fs::File, + io::{self, Read, Write}, + mem, + os::unix::io::FromRawFd, +}; + +pub const UTUN_CONTROL_NAME: &[u8] = b"com.apple.net.utun_control"; + +use libc::{socklen_t, IFNAMSIZ}; + +#[derive(Debug)] +pub enum MacosTunError { + InvalidName, + Open(io::Error), + CtliocginfoError(io::Error), + Connect(io::Error), + GetName(io::Error), + TunStatusError(StatusError), +} + +impl MacosTunError { + fn last_os_error(kind: impl Fn(io::Error) -> Self) -> Self { + kind(io::Error::last_os_error()) + } +} + +impl fmt::Display for MacosTunError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use MacosTunError::*; + match self { + InvalidName => write!(f, "Interface name must be utun[0-9]*"), + Open(err) => write!(f, "failed to open tunnel socket: {}", err), + CtliocginfoError(err) => write!(f, "failed configure socket for utun: {}", err), + Connect(err) => write!(f, "failed to connect tunnel socket: {}", err), + GetName(err) => write!(f, "failed to get tunnel name: {}", err), + TunStatusError(err) => write!(f, "failed to create tunnel status reader: {}", err), + } + } +} + +impl std::error::Error for MacosTunError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use MacosTunError::*; + match self { + Open(err) | CtliocginfoError(err) | Connect(err) | GetName(err) => Some(err), + TunStatusError(err) => Some(err), + InvalidName => None, + } + } +} + +impl From for MacosTunError { + fn from(err: StatusError) -> Self { + Self::TunStatusError(err) + } +} + +#[derive(Debug)] +pub enum StatusError { + GetInterfaceIndex(io::Error), + Open(io::Error), + Read(io::Error), +} + +impl StatusError { + fn last_os_error(kind: impl Fn(io::Error) -> Self) -> Self { + kind(io::Error::last_os_error()) + } +} + +impl fmt::Display for StatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use StatusError::*; + match self { + GetInterfaceIndex(err) => { + write!(f, "failed to get interface index: {}", err) + } + Open(err) => { + write!(f, "failed to open route socket: {}", err) + } + Read(err) => { + write!(f, "failed to read from route socket: {}", err) + } + } + } +} + +impl std::error::Error for StatusError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use StatusError::*; + match self { + GetInterfaceIndex(err) | Open(err) | Read(err) => Some(err), + } + } +} + +pub struct MacosTun {} + +pub struct MacosTunStatus { + interface_index: u32, + route_socket: File, +} + +impl MacosTunStatus { + fn new(interface_name: [u8; libc::IFNAMSIZ]) -> Result { + let interface_index = unsafe { libc::if_nametoindex(interface_name.as_ptr() as _) }; + if interface_index == 0 { + return Err(StatusError::last_os_error(StatusError::GetInterfaceIndex)); + } + let route_socket_fd = + unsafe { libc::socket(libc::AF_ROUTE, libc::SOCK_RAW, libc::AF_UNSPEC) }; + if route_socket_fd < 0 { + return Err(StatusError::last_os_error(StatusError::Open)); + } + let route_socket = unsafe { File::from_raw_fd(route_socket_fd) }; + + Ok(Self { + interface_index, + route_socket, + }) + } +} +impl Status for MacosTunStatus { + type Error = StatusError; + + fn event(&mut self) -> Result { + let mut buffer = vec![0u8; page_size::get()]; + loop { + let route_read = self.route_socket.read(buffer.as_mut_slice()); + let bytes_read = match route_read { + Ok(bytes_read) => bytes_read, + Err(err) => { + if err.kind() == io::ErrorKind::Interrupted { + continue; + } + return Err(StatusError::Read(err)); + } + }; + + if bytes_read < mem::size_of::() + || bytes_read < mem::size_of::() + { + continue; + } + let msg_header: &rt_msghdr = unsafe { &*(buffer.as_ptr() as *const rt_msghdr) }; + if msg_header.rtm_type != libc::RTM_IFINFO as u8 { + continue; + } + + let if_msg: &libc::if_msghdr = unsafe { &*(buffer.as_ptr() as *const libc::if_msghdr) }; + if if_msg.ifm_index as u32 != self.interface_index { + continue; + } + if if_msg.ifm_flags & libc::IFF_UP == 0 { + return Ok(TunEvent::Down); + } + let mtu = if_msg.ifm_data.ifi_mtu; + return Ok(TunEvent::Up(mtu as usize)); + } + } +} +pub struct MacosTunWriter { + tun: Fd, +} + +impl Writer for MacosTunWriter { + type Error = io::Error; + fn write(&self, src: &[u8]) -> Result<(), Self::Error> { + let mut buf = vec![0u8; src.len() + 4]; + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + + if src[0] >> 4 == 6 { + buf[3] = libc::AF_INET6 as u8; + } else { + buf[3] = libc::AF_INET as u8; + } + buf[4..].copy_from_slice(src); + + let _ = self.tun.write(&buf)?; + Ok(()) + } +} +pub struct MacosTunReader { + tun: Fd, +} + +impl Reader for MacosTunReader { + type Error = io::Error; + + fn read(&self, buf: &mut [u8], offset: usize) -> Result { + let bytes_read = self.tun.read(&mut buf[(offset.saturating_sub(4))..])?; + if bytes_read < 4 { + return Ok(0); + } + Ok(bytes_read - 4) + } +} + +impl Tun for MacosTun { + type Writer = MacosTunWriter; + type Reader = MacosTunReader; + type Error = MacosTunError; +} + +impl PlatformTun for MacosTun { + type Status = MacosTunStatus; + + fn create(name: &str) -> Result<(Vec, Self::Writer, Self::Status), Self::Error> { + if name.as_bytes().len() > IFNAMSIZ { + return Err(MacosTunError::InvalidName); + } + let name_index: u32 = name + .strip_prefix("utun") + .and_then(|index| index.parse().ok()) + .ok_or(MacosTunError::InvalidName)?; + + let tun_fd = + unsafe { libc::socket(libc::PF_SYSTEM, libc::SOCK_DGRAM, libc::SYSPROTO_CONTROL) }; + if tun_fd < 0 { + return Err(MacosTunError::last_os_error(MacosTunError::Open)); + } + let tun = Fd::new(tun_fd); + + let mut info = ctl_info { + ctl_id: 0, + ctl_name: [0; 96], + }; + (&mut info.ctl_name[..]) + .write(UTUN_CONTROL_NAME) + .expect("failed to control name into ctl_info buffer"); + + if unsafe { ctliocginfo(tun.raw_fd(), &mut info as *mut _ as *mut _) } < 0 { + return Err(MacosTunError::last_os_error( + MacosTunError::CtliocginfoError, + )); + } + + let addr = libc::sockaddr_ctl { + sc_id: info.ctl_id, + sc_len: mem::size_of::() as _, + sc_family: libc::AF_SYSTEM as u8, + ss_sysaddr: libc::AF_SYS_CONTROL as u16, + sc_unit: name_index + 1, + sc_reserved: [0; 5], + }; + + if unsafe { + libc::connect( + tun.raw_fd(), + &addr as *const libc::sockaddr_ctl as *const libc::sockaddr, + mem::size_of::() as socklen_t, + ) + } < 0 + { + return Err(MacosTunError::last_os_error(MacosTunError::Connect)); + } + + let mut interface_name = [0u8; libc::IFNAMSIZ]; + let mut name_len: socklen_t = libc::IFNAMSIZ as u32; + + if unsafe { + libc::getsockopt( + tun.raw_fd(), + libc::SYSPROTO_CONTROL, + libc::UTUN_OPT_IFNAME, + interface_name.as_mut_ptr() as *mut libc::c_void, + &mut name_len as *mut socklen_t, + ) + } < 0 + { + return Err(MacosTunError::last_os_error(MacosTunError::GetName)); + } + + Ok(( + vec![MacosTunReader { tun: tun.clone() }], + MacosTunWriter { tun }, + MacosTunStatus::new(interface_name)?, + )) + } +} diff --git a/src/platform/macos/udp.rs b/src/platform/macos/udp.rs new file mode 100644 index 0000000..2114877 --- /dev/null +++ b/src/platform/macos/udp.rs @@ -0,0 +1,695 @@ +use super::super::udp::*; +use super::super::Endpoint; + +use std::convert::TryInto; +use std::io; +use std::mem; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::os::unix::io::RawFd; +use std::ptr; +use std::sync::Arc; + +pub struct FD(RawFd); + +impl Drop for FD { + fn drop(&mut self) { + if self.0 != -1 { + log::debug!("linux udp, release fd (fd = {})", self.0); + unsafe { + libc::close(self.0); + }; + } + } +} + +#[repr(C, align(1))] +struct ControlHeaderV4 { + hdr: libc::cmsghdr, + info: libc::in_pktinfo, +} + +#[repr(C, align(1))] +struct ControlHeaderV6 { + hdr: libc::cmsghdr, + info: libc::in6_pktinfo, +} + +pub struct EndpointV4 { + dst: libc::sockaddr_in, // destination IP + info: libc::in_pktinfo, // src & ifindex +} + +pub struct EndpointV6 { + dst: libc::sockaddr_in6, // destination IP + info: libc::in6_pktinfo, // src & zone id +} + +pub struct MacosUDP(); + +pub struct MacosOwner { + port: u16, + sock4: Option>, + sock6: Option>, +} + +pub enum MacosUDPReader { + V4(Arc), + V6(Arc), +} + +#[derive(Clone)] +pub struct MacosUDPWriter { + sock4: Arc, + sock6: Arc, +} + +pub enum MacosEndpoint { + V4(EndpointV4), + V6(EndpointV6), +} + +fn errno() -> libc::c_int { + io::Error::last_os_error().raw_os_error().unwrap_or(0) +} + +fn setsockopt( + fd: RawFd, + level: libc::c_int, + name: libc::c_int, + value: &V, +) -> Result<(), io::Error> { + let res = unsafe { + libc::setsockopt( + fd, + level, + name, + mem::transmute(value), + mem::size_of_val(value).try_into().unwrap(), + ) + }; + if res == 0 { + Ok(()) + } else { + Err(io::Error::new( + io::Error::last_os_error().kind(), + format!("Failed to set sockopt (res = {}, errno = {})", res, errno()), + )) + } +} + +#[inline(always)] +fn setsockopt_int( + fd: RawFd, + level: libc::c_int, + name: libc::c_int, + value: libc::c_int, +) -> Result<(), io::Error> { + setsockopt(fd, level, name, &value) +} + +#[allow(non_snake_case)] +const fn CMSG_ALIGN(len: usize) -> u32 { + let size_of_u32 = mem::size_of::() as u32; + ((len as u32) + size_of_u32 - 1) & !(size_of_u32 - 1) +} + +#[allow(non_snake_case)] +const fn CMSG_LEN(len: usize) -> u32 { + CMSG_ALIGN(len + mem::size_of::()) +} + +#[inline(always)] +fn safe_cast(v: &mut T) -> *mut D { + (v as *mut T) as *mut D +} + +impl Endpoint for MacosEndpoint { + fn from_address(addr: SocketAddr) -> Self { + match addr { + SocketAddr::V4(addr) => MacosEndpoint::V4(EndpointV4 { + dst: libc::sockaddr_in { + sin_family: libc::AF_INET as libc::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr: libc::in_addr { + s_addr: u32::from(*addr.ip()).to_be(), + }, + sin_zero: [0; 8], + }, + info: libc::in_pktinfo { + ipi_ifindex: 0, // interface (0 is via routing table) + ipi_spec_dst: libc::in_addr { s_addr: 0 }, // src IP (dst of incoming packet) + ipi_addr: libc::in_addr { s_addr: 0 }, + }, + }), + SocketAddr::V6(addr) => MacosEndpoint::V6(EndpointV6 { + dst: libc::sockaddr_in6 { + sin6_family: libc::AF_INET6 as libc::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_flowinfo: addr.flowinfo(), + sin6_addr: libc::in6_addr { + s6_addr: addr.ip().octets(), + }, + sin6_scope_id: addr.scope_id(), + }, + info: libc::in6_pktinfo { + ipi6_addr: libc::in6_addr { s6_addr: [0; 16] }, // src IP + ipi6_ifindex: 0, // zone id + }, + }), + } + } + + fn into_address(&self) -> SocketAddr { + match self { + MacosEndpoint::V4(EndpointV4 { ref dst, .. }) => { + SocketAddr::V4(SocketAddrV4::new( + u32::from_be(dst.sin_addr.s_addr).into(), // IPv4 addr + u16::from_be(dst.sin_port), // convert back to native byte-order + )) + } + MacosEndpoint::V6(EndpointV6 { ref dst, .. }) => SocketAddr::V6(SocketAddrV6::new( + u128::from_ne_bytes(dst.sin6_addr.s6_addr).into(), // IPv6 addr + u16::from_be(dst.sin6_port), // convert back to native byte-order + dst.sin6_flowinfo, + dst.sin6_scope_id, + )), + } + } + + fn clear_src(&mut self) { + match self { + MacosEndpoint::V4(EndpointV4 { ref mut info, .. }) => { + info.ipi_ifindex = 0; + info.ipi_spec_dst = libc::in_addr { s_addr: 0 }; + } + MacosEndpoint::V6(EndpointV6 { ref mut info, .. }) => { + info.ipi6_addr = libc::in6_addr { s6_addr: [0; 16] }; + info.ipi6_ifindex = 0; + } + }; + } +} + +impl MacosUDPReader { + fn read6(fd: RawFd, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), io::Error> { + log::trace!( + "receive IPv6 packet (block), (fd {}, max-len {})", + fd, + buf.len() + ); + + debug_assert!(!buf.is_empty(), "reading into empty buffer (will fail)"); + + let mut iovs: [libc::iovec; 1] = [libc::iovec { + iov_base: buf.as_mut_ptr() as *mut core::ffi::c_void, + iov_len: buf.len(), + }]; + let mut src: libc::sockaddr_in6 = unsafe { mem::MaybeUninit::uninit().assume_init() }; + let mut control: ControlHeaderV6 = unsafe { mem::MaybeUninit::uninit().assume_init() }; + let mut hdr = libc::msghdr { + msg_name: safe_cast(&mut src), + msg_namelen: mem::size_of_val(&src) as u32, + msg_iov: iovs.as_mut_ptr(), + msg_iovlen: iovs.len(), + msg_control: safe_cast(&mut control), + msg_controllen: mem::size_of_val(&control), + msg_flags: 0, + }; + + debug_assert!( + hdr.msg_controllen + >= mem::size_of::() + mem::size_of::(), + ); + + let len = unsafe { libc::recvmsg(fd, &mut hdr as *mut libc::msghdr, 0) }; + + if len <= 0 { + // TODO: FIX! + return Err(io::Error::new( + io::ErrorKind::NotConnected, + format!( + "failed to receive (len = {}, fd = {}, errno = {})", + len, + fd, + errno() + ), + )); + } + + Ok(( + len.try_into().unwrap(), + MacosEndpoint::V6(EndpointV6 { + info: control.info, // save pktinfo (sticky source) + dst: src, // our future destination is the source address + }), + )) + } + + fn read4(fd: RawFd, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), io::Error> { + log::trace!( + "receive IPv4 packet (block), (fd {}, max-len {})", + fd, + buf.len() + ); + + debug_assert!(!buf.is_empty(), "reading into empty buffer (will fail)"); + + let mut iovs: [libc::iovec; 1] = [libc::iovec { + iov_base: buf.as_mut_ptr() as *mut core::ffi::c_void, + iov_len: buf.len(), + }]; + let mut src: libc::sockaddr_in = unsafe { mem::MaybeUninit::uninit().assume_init() }; + let mut control: ControlHeaderV4 = unsafe { mem::MaybeUninit::uninit().assume_init() }; + let mut hdr = libc::msghdr { + msg_name: safe_cast(&mut src), + msg_namelen: mem::size_of_val(&src) as u32, + msg_iov: iovs.as_mut_ptr(), + msg_iovlen: iovs.len(), + msg_control: safe_cast(&mut control), + msg_controllen: mem::size_of_val(&control), + msg_flags: 0, + }; + + debug_assert!( + hdr.msg_controllen + >= mem::size_of::() + mem::size_of::(), + ); + + let len = unsafe { libc::recvmsg(fd, &mut hdr as *mut libc::msghdr, 0) }; + + if len <= 0 { + return Err(io::Error::new( + io::ErrorKind::NotConnected, + format!( + "failed to receive (len = {}, fd = {}, errno = {})", + len, + fd, + errno() + ), + )); + } + + Ok(( + len.try_into().unwrap(), + MacosEndpoint::V4(EndpointV4 { + info: control.info, // save pktinfo (sticky source) + dst: src, // our future destination is the source address + }), + )) + } +} + +impl Reader for MacosUDPReader { + type Error = io::Error; + + fn read(&self, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), Self::Error> { + match self { + Self::V4(fd) => Self::read4(fd.0, buf), + Self::V6(fd) => Self::read6(fd.0, buf), + } + } +} + +impl MacosUDPWriter { + fn write6(fd: RawFd, buf: &[u8], dst: &mut EndpointV6) -> Result<(), io::Error> { + log::debug!("sending IPv6 packet ({} fd, {} bytes)", fd, buf.len()); + + let mut iovs: [libc::iovec; 1] = [libc::iovec { + iov_base: buf.as_ptr() as *mut core::ffi::c_void, + iov_len: buf.len(), + }]; + + let mut control = ControlHeaderV6 { + hdr: libc::cmsghdr { + cmsg_len: CMSG_LEN(mem::size_of::()), + cmsg_level: libc::IPPROTO_IPV6, + cmsg_type: libc::IPV6_PKTINFO, + }, + info: dst.info, + }; + + debug_assert_eq!( + control.hdr.cmsg_len % mem::size_of::() as u32, + 0, + "cmsg_len must be aligned to a long" + ); + + debug_assert_eq!( + dst.dst.sin6_family, + libc::AF_INET6 as libc::sa_family_t, + "this method only handles IPv6 destinations" + ); + + let mut hdr = libc::msghdr { + msg_name: safe_cast(&mut dst.dst), + msg_namelen: mem::size_of_val(&dst.dst) as u32, + msg_iov: iovs.as_mut_ptr(), + msg_iovlen: iovs.len() as i32, + msg_control: safe_cast(&mut control), + msg_controllen: mem::size_of_val(&control) as u32, + msg_flags: 0, + }; + + let ret = unsafe { libc::sendmsg(fd, &hdr, 0) }; + + if ret < 0 { + if errno() == libc::EINVAL { + log::trace!("clear source and retry"); + hdr.msg_control = ptr::null_mut(); + hdr.msg_controllen = 0; + dst.info = unsafe { mem::zeroed() }; + return if unsafe { libc::sendmsg(fd, &hdr, 0) } < 0 { + Err(io::Error::new( + io::ErrorKind::NotConnected, + "failed to send IPv6 packet", + )) + } else { + Ok(()) + }; + } + return Err(io::Error::new( + io::ErrorKind::NotConnected, + "failed to send IPv6 packet", + )); + } + + Ok(()) + } + + fn write4(fd: RawFd, buf: &[u8], dst: &mut EndpointV4) -> Result<(), io::Error> { + log::debug!("sending IPv4 packet ({} fd, {} bytes)", fd, buf.len()); + + let mut iovs: [libc::iovec; 1] = [libc::iovec { + iov_base: buf.as_ptr() as *mut core::ffi::c_void, + iov_len: buf.len(), + }]; + + let mut control = ControlHeaderV4 { + hdr: libc::cmsghdr { + cmsg_len: CMSG_LEN(mem::size_of::()), + cmsg_level: libc::IPPROTO_IP, + cmsg_type: libc::IP_PKTINFO, + }, + info: dst.info, + }; + + debug_assert_eq!( + control.hdr.cmsg_len % mem::size_of::() as u32, + 0, + "cmsg_len must be aligned to a long" + ); + + debug_assert_eq!( + dst.dst.sin_family, + libc::AF_INET as libc::sa_family_t, + "this method only handles IPv4 destinations" + ); + + let mut hdr = libc::msghdr { + msg_name: safe_cast(&mut dst.dst), + msg_namelen: mem::size_of_val(&dst.dst) as u32, + msg_iov: iovs.as_mut_ptr(), + msg_control: &mut control as *mut _ as *mut _, + msg_iovlen: iovs.len() as i32, + msg_controllen: mem::size_of_val(&control) as u32, + msg_flags: 0, + }; + + let ret = unsafe { libc::sendmsg(fd, &hdr, 0) }; + + if ret < 0 { + if errno() == libc::EINVAL { + log::trace!("clear source and retry"); + hdr.msg_control = ptr::null_mut(); + hdr.msg_controllen = 0; + dst.info = unsafe { mem::zeroed() }; + return if unsafe { libc::sendmsg(fd, &hdr, 0) } < 0 { + Err(io::Error::new( + io::ErrorKind::NotConnected, + "failed to send IPv4 packet", + )) + } else { + Ok(()) + }; + } + return Err(io::Error::new( + io::ErrorKind::NotConnected, + "failed to send IPv4 packet", + )); + } + + Ok(()) + } +} + +impl Writer for MacosUDPWriter { + type Error = io::Error; + + fn write(&self, buf: &[u8], dst: &mut MacosEndpoint) -> Result<(), Self::Error> { + match dst { + MacosEndpoint::V4(ref mut end) => Self::write4(self.sock4.0, buf, end), + MacosEndpoint::V6(ref mut end) => Self::write6(self.sock6.0, buf, end), + } + } +} + +impl Drop for MacosOwner { + fn drop(&mut self) { + log::debug!("closing the bind (port = {})", self.port); + if let Some(fd) = &self.sock4 { + log::debug!("shutdown IPv4 (fd = {})", fd.0); + unsafe { + libc::shutdown(fd.0, libc::SHUT_RDWR); + } + }; + if let Some(fd) = &self.sock6 { + log::debug!("shutdown IPv6 (fd = {})", fd.0); + unsafe { + libc::shutdown(fd.0, libc::SHUT_RDWR); + } + }; + } +} + +impl UDP for MacosUDP { + type Error = io::Error; + type Endpoint = MacosEndpoint; + type Writer = MacosUDPWriter; + type Reader = MacosUDPReader; +} + +impl MacosUDP { + /* Bind on all IPv6 interfaces + * + * Arguments: + * + * - 'port', port to bind to (0 = any) + * + * Returns: + * + * Returns a tuple of the resulting port and socket. + */ + fn bind6(port: u16) -> Result<(u16, RawFd), io::Error> { + log::trace!("attempting to bind on IPv6 (port {})", port); + + // create socket fd + let fd: RawFd = unsafe { libc::socket(libc::AF_INET6, libc::SOCK_DGRAM, 0) }; + if fd < 0 { + log::debug!("failed to create IPv6 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + setsockopt_int(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1)?; + setsockopt_int(fd, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, 1)?; + setsockopt_int(fd, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, 1)?; + + const INADDR_ANY: libc::in6_addr = libc::in6_addr { s6_addr: [0; 16] }; + + // bind + let mut sockaddr = libc::sockaddr_in6 { + sin6_addr: INADDR_ANY, + sin6_family: libc::AF_INET6 as libc::sa_family_t, + sin6_port: port.to_be(), // convert to network (big-endian) byte-order + sin6_scope_id: 0, + sin6_flowinfo: 0, + sin6_len: mem::size_of::() as u8, + }; + + let err = unsafe { + libc::bind( + fd, + safe_cast(&mut sockaddr), + mem::size_of_val(&sockaddr).try_into().unwrap(), + ) + }; + if err != 0 { + log::debug!("failed to bind IPv6 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + // get the assigned port + let mut socklen: libc::socklen_t = mem::size_of_val(&sockaddr).try_into().unwrap(); + let err = unsafe { + libc::getsockname( + fd, + safe_cast(&mut sockaddr), + &mut socklen as *mut libc::socklen_t, + ) + }; + if err != 0 { + log::debug!("failed to get port of IPv6 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + // basic sanity checks + let new_port = u16::from_be(sockaddr.sin6_port); + debug_assert_eq!(socklen, mem::size_of::() as u32); + debug_assert_eq!(sockaddr.sin6_family, libc::AF_INET6 as libc::sa_family_t); + debug_assert_eq!(new_port, if port != 0 { port } else { new_port }); + log::trace!("bound IPv6 socket (port {}, fd {})", new_port, fd); + Ok((new_port, fd)) + } + + /* Bind on all IPv4 interfaces. + * + * Arguments: + * + * - 'port', port to bind to (0 = any) + * + * Returns: + * + * Returns a tuple of the resulting port and socket. + */ + fn bind4(port: u16) -> Result<(u16, RawFd), io::Error> { + log::trace!("attempting to bind on IPv4 (port {})", port); + + // create socket fd + let fd: RawFd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; + if fd < 0 { + log::debug!("failed to create IPv4 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + setsockopt_int(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1)?; + setsockopt_int(fd, libc::IPPROTO_IP, libc::IP_PKTINFO, 1)?; + + const INADDR_ANY: libc::in_addr = libc::in_addr { s_addr: 0 }; + + // bind + let mut sockaddr = libc::sockaddr_in { + sin_addr: INADDR_ANY, + sin_family: libc::AF_INET as libc::sa_family_t, + sin_port: port.to_be(), + sin_zero: [0; 8], + sin_len: mem::size_of::() as u8, + }; + + let err = unsafe { + libc::bind( + fd, + safe_cast(&mut sockaddr), + mem::size_of_val(&sockaddr).try_into().unwrap(), + ) + }; + if err != 0 { + log::debug!("failed to bind IPv4 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + // get the assigned port + let mut socklen: libc::socklen_t = mem::size_of_val(&sockaddr).try_into().unwrap(); + let err = unsafe { + libc::getsockname( + fd, + safe_cast(&mut sockaddr), + &mut socklen as *mut libc::socklen_t, + ) + }; + if err != 0 { + log::debug!("failed to get port of IPv4 socket (errno = {})", errno()); + return Err(io::Error::new( + io::ErrorKind::Other, + "failed to create socket", + )); + } + + // basic sanity checks + let new_port = u16::from_be(sockaddr.sin_port); + debug_assert_eq!(socklen, mem::size_of::() as u32); + debug_assert_eq!(sockaddr.sin_family, libc::AF_INET as libc::sa_family_t); + debug_assert_eq!(new_port, if port != 0 { port } else { new_port }); + log::trace!("bound IPv4 socket (port {}, fd {})", new_port, fd); + Ok((new_port, fd)) + } +} + +impl PlatformUDP for MacosUDP { + type Owner = MacosOwner; + + #[allow(clippy::type_complexity)] + #[allow(clippy::unnecessary_unwrap)] + fn bind(mut port: u16) -> Result<(Vec, Self::Writer, Self::Owner), Self::Error> { + log::debug!("bind to port {}", port); + + // attempt to bind on ipv6 + let bind6 = Self::bind6(port); + if let Ok((new_port, _)) = bind6 { + port = new_port; + } + + // attempt to bind on ipv4 on the same port + let bind4 = Self::bind4(port); + if let Ok((new_port, _)) = bind4 { + port = new_port; + } + + // check if failed to bind on both + if bind4.is_err() && bind6.is_err() { + log::trace!("failed to bind for either IP version"); + return Err(bind6.unwrap_err()); + } + + let sock6 = bind6.ok().map(|(_, fd)| Arc::new(FD(fd))); + let sock4 = bind4.ok().map(|(_, fd)| Arc::new(FD(fd))); + + // create owner + let owner = MacosOwner { + port, + sock6: sock6.clone(), + sock4: sock4.clone(), + }; + + // create readers + let mut readers: Vec = Vec::with_capacity(2); + if let Some(sock) = sock6.clone() { + readers.push(MacosUDPReader::V6(sock)) + } + if let Some(sock) = sock4.clone() { + readers.push(MacosUDPReader::V4(sock)) + } + debug_assert!(!readers.is_empty()); + + // create writer + let writer = MacosUDPWriter { + sock4: sock4.unwrap_or_else(|| Arc::new(FD(-1))), + sock6: sock6.unwrap_or_else(|| Arc::new(FD(-1))), + }; + + Ok((readers, writer, owner)) + } +} From 8c8ed09bc86636ec7cf77ab891daf362b1d1eab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls=20Pi=C5=86=C4=B7is?= Date: Thu, 22 Apr 2021 11:22:24 +0100 Subject: [PATCH 4/5] Add macOS UDP interface. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emīls --- Cargo.lock | 361 +++++++++------ Cargo.toml | 1 + src/platform/macos/mod.rs | 2 + src/platform/macos/udp.rs | 953 +++++++++++++++----------------------- 4 files changed, 591 insertions(+), 726 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3a8374..21b2714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,18 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.13.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -58,12 +58,12 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.50" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ "addr2line", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -81,9 +81,9 @@ dependencies = [ [[package]] name = "bit-vec" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" @@ -110,21 +110,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.60" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg", "cfg-if 1.0.0", @@ -239,22 +239,22 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8492de420e9e60bc9a1d66e2dbb91825390b738a388606600663fc529b4b307" +checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3" dependencies = [ "byteorder", "digest", - "rand_core", + "rand_core 0.5.1", "subtle", "zeroize", ] [[package]] name = "dashmap" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b937cd1fbd1f194ac842196bd2529f21618088ee6d8bff6a46ece611451c96b" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" dependencies = [ "cfg-if 1.0.0", "num_cpus", @@ -271,13 +271,13 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", - "log 0.4.11", + "log 0.4.14", "regex", "termcolor", ] @@ -326,20 +326,31 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] name = "gimli" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "glob" @@ -349,18 +360,18 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] [[package]] name = "hex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hjul" @@ -385,9 +396,9 @@ dependencies = [ [[package]] name = "humantime" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "instant" @@ -430,9 +441,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.45" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -467,9 +478,9 @@ checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -480,29 +491,29 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.11", + "log 0.4.14", ] [[package]] name = "log" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "miniz_oxide" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg", @@ -510,9 +521,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.22" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", @@ -520,7 +531,7 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log 0.4.11", + "log 0.4.14", "miow", "net2", "slab", @@ -534,16 +545,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ "lazycell", - "log 0.4.11", + "log 0.4.14", "mio", "slab", ] [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", @@ -553,20 +564,32 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags 1.2.1", + "cc", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] @@ -583,15 +606,15 @@ dependencies = [ [[package]] name = "object" -version = "0.20.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.4.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "opaque-debug" @@ -622,9 +645,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ "cfg-if 1.0.0", "instant", @@ -636,9 +659,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "pnet" @@ -740,15 +763,15 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid 0.2.1", ] @@ -765,8 +788,8 @@ dependencies = [ "lazy_static", "num-traits", "quick-error", - "rand", - "rand_chacha", + "rand 0.7.3", + "rand_chacha 0.2.2", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -781,9 +804,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -794,11 +817,23 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", ] [[package]] @@ -808,7 +843,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", ] [[package]] @@ -817,7 +862,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", ] [[package]] @@ -826,7 +880,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", ] [[package]] @@ -835,32 +898,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" +dependencies = [ + "bitflags 1.2.1", +] [[package]] name = "regex" -version = "1.3.9" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "remove_dir_all" @@ -873,9 +938,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.15" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -888,9 +953,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" [[package]] name = "rustc-serialize" @@ -918,9 +983,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.116" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" [[package]] name = "slab" @@ -930,9 +995,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.4.2" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "spin" @@ -942,9 +1007,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "652ac3743312871a5fb703f0337e68ffa3cdc28c863efad0b8dc858fa10c991b" +checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" [[package]] name = "subtle" @@ -954,9 +1019,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.41" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2", "quote", @@ -1026,13 +1091,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "rand", + "rand 0.8.3", "redox_syscall", "remove_dir_all", "winapi 0.3.9", @@ -1050,27 +1115,18 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "unicode-xid" @@ -1102,9 +1158,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wait-timeout" @@ -1121,25 +1177,31 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" -version = "0.2.68" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.68" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.11", + "log 0.4.14", "proc-macro2", "quote", "syn", @@ -1148,9 +1210,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.68" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1158,9 +1220,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.68" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1171,15 +1233,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.68" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.45" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -1250,17 +1312,18 @@ dependencies = [ "ioctl-sys", "ip_network_table-deps-treebitmap", "libc", - "log 0.4.11", + "log 0.4.14", + "nix", "num_cpus", "page_size", "parking_lot", "pnet", "proptest", - "rand", - "rand_chacha", - "rand_core", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rand_core 0.5.1", "ring", - "spin 0.7.0", + "spin 0.7.1", "subtle", "x25519-dalek", "zerocopy", @@ -1278,12 +1341,12 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc614d95359fd7afc321b66d2107ede58b246b844cf5d8a0adcca413e439f088" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.5.1", "zeroize", ] @@ -1310,9 +1373,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a" +checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index cf99926..e4d387d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ libc = "^0.2" [target.'cfg(target_os = "macos")'.dependencies] ioctl-sys = "0.6" page_size = "0.4" +nix = "0.20" [dependencies.x25519-dalek] version = "^1.1" diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 753fcc3..32e67f9 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,6 +1,8 @@ mod fd; mod sys; mod tun; +mod udp; pub use crate::platform::unix::uapi::UnixUAPI as UAPI; pub use tun::MacosTun as Tun; +pub use udp::MacosUDP as UDP; diff --git a/src/platform/macos/udp.rs b/src/platform/macos/udp.rs index 2114877..5ceafc4 100644 --- a/src/platform/macos/udp.rs +++ b/src/platform/macos/udp.rs @@ -1,680 +1,483 @@ use super::super::udp::*; use super::super::Endpoint; -use std::convert::TryInto; -use std::io; -use std::mem; -use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::fmt; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; use std::os::unix::io::RawFd; -use std::ptr; -use std::sync::Arc; - -pub struct FD(RawFd); -impl Drop for FD { - fn drop(&mut self) { - if self.0 != -1 { - log::debug!("linux udp, release fd (fd = {})", self.0); - unsafe { - libc::close(self.0); - }; - } - } -} +use nix::sys::{ + socket::{ + bind, getsockname, recvmsg, sendmsg, setsockopt, socket, + sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf, Ipv6RecvPacketInfo, ReuseAddr, ReusePort}, + AddressFamily, ControlMessage, ControlMessageOwned, InetAddr, IpAddr, MsgFlags, SockAddr, + SockFlag, SockProtocol, SockType, + }, + uio::IoVec, +}; +use std::sync::Arc; -#[repr(C, align(1))] -struct ControlHeaderV4 { - hdr: libc::cmsghdr, - info: libc::in_pktinfo, +#[derive(Debug)] +pub struct UdpSocket { + socket: RawFd, + is_ipv4: bool, } -#[repr(C, align(1))] -struct ControlHeaderV6 { - hdr: libc::cmsghdr, - info: libc::in6_pktinfo, +#[derive(Debug)] +pub enum UdpError { + OpenSocket(nix::Error), + SetSocketOpt(nix::Error), + GetSockName(nix::Error), + BindSocket(nix::Error), + SendMsg(nix::Error), + RecvMsg(nix::Error), + UnexpectedControlMessage(ControlMessageOwned), + NoControlMessage, + InvalidAddress(Option), + UnsupportedProtocol(&'static str), + InsufficientSourceInfo(Option, Option), } -pub struct EndpointV4 { - dst: libc::sockaddr_in, // destination IP - info: libc::in_pktinfo, // src & ifindex +impl std::fmt::Display for UdpError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UdpError::*; + match self { + OpenSocket(err) => { + write!(f, "failed to open socket: {}", err) + } + SetSocketOpt(err) => { + write!(f, "failed to set socket option: {}", err) + } + GetSockName(err) => { + write!(f, "failed to get socket name: {}", err) + } + BindSocket(err) => { + write!(f, "failed to bind socket: {}", err) + } + SendMsg(err) => { + write!(f, "failed to send message: {}", err) + } + RecvMsg(err) => { + write!(f, "failed to receive message: {}", err) + } + InvalidAddress(Some(invalid_addr)) => { + write!(f, "expected socket address, got {}", invalid_addr) + } + InvalidAddress(None) => { + write!(f, "expected socket address") + } + UnexpectedControlMessage(unexpected_message) => { + write!( + f, + "received unexpected control message: {:?}", + unexpected_message + ) + } + NoControlMessage => { + write!(f, "received no control message") + } + UnsupportedProtocol(protocol) => { + write!(f, "unsupported protocol {}", protocol) + } + InsufficientSourceInfo(in_addr, if_index) => { + let mut faults = Vec::with_capacity(2); + if in_addr.is_none() { + faults.push("no address") + } + if if_index.is_none() { + faults.push("no reciving interface index") + } + write!(f, "received packet with {}", faults.join(" and ")) + } + } + } } -pub struct EndpointV6 { - dst: libc::sockaddr_in6, // destination IP - info: libc::in6_pktinfo, // src & zone id +impl std::error::Error for UdpError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use UdpError::*; + match self { + OpenSocket(err) | SetSocketOpt(err) | GetSockName(err) | BindSocket(err) + | SendMsg(err) | RecvMsg(err) => Some(err), + UnexpectedControlMessage(_) + | NoControlMessage + | InvalidAddress(_) + | UnsupportedProtocol(_) + | InsufficientSourceInfo(_, _) => None, + } + } } -pub struct MacosUDP(); - -pub struct MacosOwner { - port: u16, - sock4: Option>, - sock6: Option>, -} +type Result = std::result::Result; -pub enum MacosUDPReader { - V4(Arc), - V6(Arc), -} +impl UdpSocket { + fn bind(addr: impl Into, port: u16) -> Result<(u16, Self)> { + let ip_addr = addr.into(); + let addr_family = if ip_addr.is_ipv4() { + AddressFamily::Inet + } else { + AddressFamily::Inet6 + }; + let inet_addr = InetAddr::new(IpAddr::from_std(&ip_addr), port); + let socket_addr = SockAddr::new_inet(inet_addr); + + let socket: RawFd = socket( + addr_family, + SockType::Datagram, + SockFlag::empty(), + SockProtocol::Udp, + ) + .map_err(UdpError::OpenSocket)?; -#[derive(Clone)] -pub struct MacosUDPWriter { - sock4: Arc, - sock6: Arc, -} + if ip_addr.is_ipv4() { + setsockopt(socket, Ipv4RecvDstAddr, &true).map_err(UdpError::SetSocketOpt)?; + setsockopt(socket, Ipv4RecvIf, &true).map_err(UdpError::SetSocketOpt)?; + } else { + setsockopt(socket, Ipv6RecvPacketInfo, &true).map_err(UdpError::SetSocketOpt)?; + } -pub enum MacosEndpoint { - V4(EndpointV4), - V6(EndpointV6), -} + setsockopt(socket, ReuseAddr, &true).map_err(UdpError::SetSocketOpt)?; + setsockopt(socket, ReusePort, &true).map_err(UdpError::SetSocketOpt)?; -fn errno() -> libc::c_int { - io::Error::last_os_error().raw_os_error().unwrap_or(0) -} + bind(socket, &socket_addr).map_err(UdpError::BindSocket)?; + let bound_port = if port == 0 { + let sockaddr = getsockname(socket).map_err(UdpError::GetSockName)?; + Self::validate_sockaddr(Some(sockaddr))?.port() + } else { + port + }; -fn setsockopt( - fd: RawFd, - level: libc::c_int, - name: libc::c_int, - value: &V, -) -> Result<(), io::Error> { - let res = unsafe { - libc::setsockopt( - fd, - level, - name, - mem::transmute(value), - mem::size_of_val(value).try_into().unwrap(), - ) - }; - if res == 0 { - Ok(()) - } else { - Err(io::Error::new( - io::Error::last_os_error().kind(), - format!("Failed to set sockopt (res = {}, errno = {})", res, errno()), + Ok(( + bound_port, + Self { + socket, + is_ipv4: ip_addr.is_ipv4(), + }, )) } -} - -#[inline(always)] -fn setsockopt_int( - fd: RawFd, - level: libc::c_int, - name: libc::c_int, - value: libc::c_int, -) -> Result<(), io::Error> { - setsockopt(fd, level, name, &value) -} - -#[allow(non_snake_case)] -const fn CMSG_ALIGN(len: usize) -> u32 { - let size_of_u32 = mem::size_of::() as u32; - ((len as u32) + size_of_u32 - 1) & !(size_of_u32 - 1) -} -#[allow(non_snake_case)] -const fn CMSG_LEN(len: usize) -> u32 { - CMSG_ALIGN(len + mem::size_of::()) -} - -#[inline(always)] -fn safe_cast(v: &mut T) -> *mut D { - (v as *mut T) as *mut D -} - -impl Endpoint for MacosEndpoint { - fn from_address(addr: SocketAddr) -> Self { + fn validate_sockaddr(addr: Option) -> Result { match addr { - SocketAddr::V4(addr) => MacosEndpoint::V4(EndpointV4 { - dst: libc::sockaddr_in { - sin_family: libc::AF_INET as libc::sa_family_t, - sin_port: addr.port().to_be(), - sin_addr: libc::in_addr { - s_addr: u32::from(*addr.ip()).to_be(), - }, - sin_zero: [0; 8], - }, - info: libc::in_pktinfo { - ipi_ifindex: 0, // interface (0 is via routing table) - ipi_spec_dst: libc::in_addr { s_addr: 0 }, // src IP (dst of incoming packet) - ipi_addr: libc::in_addr { s_addr: 0 }, - }, - }), - SocketAddr::V6(addr) => MacosEndpoint::V6(EndpointV6 { - dst: libc::sockaddr_in6 { - sin6_family: libc::AF_INET6 as libc::sa_family_t, - sin6_port: addr.port().to_be(), - sin6_flowinfo: addr.flowinfo(), - sin6_addr: libc::in6_addr { - s6_addr: addr.ip().octets(), - }, - sin6_scope_id: addr.scope_id(), - }, - info: libc::in6_pktinfo { - ipi6_addr: libc::in6_addr { s6_addr: [0; 16] }, // src IP - ipi6_ifindex: 0, // zone id - }, - }), + Some(SockAddr::Inet(inet)) => Ok(inet), + anything_else => Err(UdpError::InvalidAddress(anything_else)), } } - fn into_address(&self) -> SocketAddr { - match self { - MacosEndpoint::V4(EndpointV4 { ref dst, .. }) => { - SocketAddr::V4(SocketAddrV4::new( - u32::from_be(dst.sin_addr.s_addr).into(), // IPv4 addr - u16::from_be(dst.sin_port), // convert back to native byte-order - )) - } - MacosEndpoint::V6(EndpointV6 { ref dst, .. }) => SocketAddr::V6(SocketAddrV6::new( - u128::from_ne_bytes(dst.sin6_addr.s6_addr).into(), // IPv6 addr - u16::from_be(dst.sin6_port), // convert back to native byte-order - dst.sin6_flowinfo, - dst.sin6_scope_id, - )), - } + fn send_to(&self, buf: &[u8], endpoint: &MacosEndpoint) -> Result { + let iov = [IoVec::from_slice(buf)]; + let packet_info = PacketInfo::new(endpoint); + let control_messages = [packet_info.control_message()]; + sendmsg( + self.socket, + &iov, + &control_messages, + MsgFlags::empty(), + Some(&SockAddr::new_inet(endpoint.destination())), + ) + .map_err(UdpError::SendMsg) } - fn clear_src(&mut self) { - match self { - MacosEndpoint::V4(EndpointV4 { ref mut info, .. }) => { - info.ipi_ifindex = 0; - info.ipi_spec_dst = libc::in_addr { s_addr: 0 }; + fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, MacosEndpoint)> { + let iov = [IoVec::from_mut_slice(buf)]; + let mut control_messages_buffer = self.control_message_buffer(); + let msg = recvmsg( + self.socket, + &iov, + Some(&mut control_messages_buffer), + MsgFlags::empty(), + ) + .map_err(UdpError::RecvMsg)?; + + let endpoint = if self.is_ipv4 { + let mut destination_addr = None; + let mut if_index = None; + let src_addr_info = match msg.address { + Some(SockAddr::Inet(InetAddr::V4(sockaddr_in))) => sockaddr_in, + anything_else => { + return Err(UdpError::InvalidAddress(anything_else)); + } + }; + let control_messages = msg.cmsgs(); + for message in control_messages { + match message { + ControlMessageOwned::Ipv4RecvIf(if_sockaddr) => { + if_index = Some(if_sockaddr.sdl_index as u32); + } + ControlMessageOwned::Ipv4RecvDstAddr(in_addr) => { + destination_addr = Some(in_addr); + } + other => { + log::error!("received unexpected control message: {:?}", other); + continue; + } + } } - MacosEndpoint::V6(EndpointV6 { ref mut info, .. }) => { - info.ipi6_addr = libc::in6_addr { s6_addr: [0; 16] }; - info.ipi6_ifindex = 0; + match (destination_addr, if_index) { + (Some(incoming_destination), Some(src_if_index)) => MacosEndpoint::V4 { + destination: src_addr_info, + src_if_index: src_if_index, + src_addr: incoming_destination, + }, + (dest, if_index) => { + return Err(UdpError::InsufficientSourceInfo(dest, if_index)); + } } - }; - } -} + } else { + let src_addr_info = match msg.address { + Some(SockAddr::Inet(InetAddr::V6(inet_addr))) => inet_addr, + anything_else => { + return Err(UdpError::InvalidAddress(anything_else)); + } + }; -impl MacosUDPReader { - fn read6(fd: RawFd, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), io::Error> { - log::trace!( - "receive IPv6 packet (block), (fd {}, max-len {})", - fd, - buf.len() - ); - - debug_assert!(!buf.is_empty(), "reading into empty buffer (will fail)"); - - let mut iovs: [libc::iovec; 1] = [libc::iovec { - iov_base: buf.as_mut_ptr() as *mut core::ffi::c_void, - iov_len: buf.len(), - }]; - let mut src: libc::sockaddr_in6 = unsafe { mem::MaybeUninit::uninit().assume_init() }; - let mut control: ControlHeaderV6 = unsafe { mem::MaybeUninit::uninit().assume_init() }; - let mut hdr = libc::msghdr { - msg_name: safe_cast(&mut src), - msg_namelen: mem::size_of_val(&src) as u32, - msg_iov: iovs.as_mut_ptr(), - msg_iovlen: iovs.len(), - msg_control: safe_cast(&mut control), - msg_controllen: mem::size_of_val(&control), - msg_flags: 0, + let src_if_index = match msg.cmsgs().next() { + Some(ControlMessageOwned::Ipv6PacketInfo(packet_info)) => packet_info.ipi6_ifindex, + Some(any_other_cmsg) => { + return Err(UdpError::UnexpectedControlMessage(any_other_cmsg)); + } + None => { + return Err(UdpError::NoControlMessage); + } + }; + MacosEndpoint::V6 { + destination: src_addr_info, + src_if_index, + } }; + Ok((msg.bytes, endpoint)) + } - debug_assert!( - hdr.msg_controllen - >= mem::size_of::() + mem::size_of::(), - ); - - let len = unsafe { libc::recvmsg(fd, &mut hdr as *mut libc::msghdr, 0) }; - - if len <= 0 { - // TODO: FIX! - return Err(io::Error::new( - io::ErrorKind::NotConnected, - format!( - "failed to receive (len = {}, fd = {}, errno = {})", - len, - fd, - errno() - ), - )); + fn control_message_buffer(&self) -> Vec { + if self.is_ipv4 { + nix::cmsg_space![libc::in_addr, libc::sockaddr_dl] + } else { + nix::cmsg_space![libc::in6_pktinfo] } - - Ok(( - len.try_into().unwrap(), - MacosEndpoint::V6(EndpointV6 { - info: control.info, // save pktinfo (sticky source) - dst: src, // our future destination is the source address - }), - )) } +} - fn read4(fd: RawFd, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), io::Error> { - log::trace!( - "receive IPv4 packet (block), (fd {}, max-len {})", - fd, - buf.len() - ); - - debug_assert!(!buf.is_empty(), "reading into empty buffer (will fail)"); - - let mut iovs: [libc::iovec; 1] = [libc::iovec { - iov_base: buf.as_mut_ptr() as *mut core::ffi::c_void, - iov_len: buf.len(), - }]; - let mut src: libc::sockaddr_in = unsafe { mem::MaybeUninit::uninit().assume_init() }; - let mut control: ControlHeaderV4 = unsafe { mem::MaybeUninit::uninit().assume_init() }; - let mut hdr = libc::msghdr { - msg_name: safe_cast(&mut src), - msg_namelen: mem::size_of_val(&src) as u32, - msg_iov: iovs.as_mut_ptr(), - msg_iovlen: iovs.len(), - msg_control: safe_cast(&mut control), - msg_controllen: mem::size_of_val(&control), - msg_flags: 0, - }; - - debug_assert!( - hdr.msg_controllen - >= mem::size_of::() + mem::size_of::(), - ); - - let len = unsafe { libc::recvmsg(fd, &mut hdr as *mut libc::msghdr, 0) }; - - if len <= 0 { - return Err(io::Error::new( - io::ErrorKind::NotConnected, - format!( - "failed to receive (len = {}, fd = {}, errno = {})", - len, - fd, - errno() - ), - )); +impl Drop for UdpSocket { + fn drop(&mut self) { + log::debug!("macos udp, release fd (fd = {})", self.socket); + if let Err(err) = nix::unistd::close(self.socket) { + log::error!("failed to close UdpSocket {}", err); } - - Ok(( - len.try_into().unwrap(), - MacosEndpoint::V4(EndpointV4 { - info: control.info, // save pktinfo (sticky source) - dst: src, // our future destination is the source address - }), - )) } } +enum PacketInfo { + V4(libc::in_pktinfo), + V6(libc::in6_pktinfo), +} -impl Reader for MacosUDPReader { - type Error = io::Error; +impl PacketInfo { + fn new(endpoint: &MacosEndpoint) -> Self { + match endpoint { + MacosEndpoint::V4 { + destination, + src_if_index, + src_addr, + } => Self::V4(libc::in_pktinfo { + ipi_addr: destination.sin_addr, + ipi_ifindex: *src_if_index, + ipi_spec_dst: *src_addr, + }), + MacosEndpoint::V6 { + destination, + src_if_index, + } => Self::V6(libc::in6_pktinfo { + ipi6_addr: destination.sin6_addr, + ipi6_ifindex: *src_if_index, + }), + } + } - fn read(&self, buf: &mut [u8]) -> Result<(usize, MacosEndpoint), Self::Error> { + fn control_message<'a>(&'a self) -> ControlMessage<'a> { match self { - Self::V4(fd) => Self::read4(fd.0, buf), - Self::V6(fd) => Self::read6(fd.0, buf), + Self::V4(v4) => ControlMessage::Ipv4PacketInfo(v4), + Self::V6(v6) => ControlMessage::Ipv6PacketInfo(v6), } } } -impl MacosUDPWriter { - fn write6(fd: RawFd, buf: &[u8], dst: &mut EndpointV6) -> Result<(), io::Error> { - log::debug!("sending IPv6 packet ({} fd, {} bytes)", fd, buf.len()); - - let mut iovs: [libc::iovec; 1] = [libc::iovec { - iov_base: buf.as_ptr() as *mut core::ffi::c_void, - iov_len: buf.len(), - }]; - - let mut control = ControlHeaderV6 { - hdr: libc::cmsghdr { - cmsg_len: CMSG_LEN(mem::size_of::()), - cmsg_level: libc::IPPROTO_IPV6, - cmsg_type: libc::IPV6_PKTINFO, - }, - info: dst.info, - }; +pub struct MacosUDP(); - debug_assert_eq!( - control.hdr.cmsg_len % mem::size_of::() as u32, - 0, - "cmsg_len must be aligned to a long" - ); - - debug_assert_eq!( - dst.dst.sin6_family, - libc::AF_INET6 as libc::sa_family_t, - "this method only handles IPv6 destinations" - ); - - let mut hdr = libc::msghdr { - msg_name: safe_cast(&mut dst.dst), - msg_namelen: mem::size_of_val(&dst.dst) as u32, - msg_iov: iovs.as_mut_ptr(), - msg_iovlen: iovs.len() as i32, - msg_control: safe_cast(&mut control), - msg_controllen: mem::size_of_val(&control) as u32, - msg_flags: 0, - }; +pub struct MacosOwner { + port: u16, + _sock4: Option>, + _sock6: Option>, +} - let ret = unsafe { libc::sendmsg(fd, &hdr, 0) }; - - if ret < 0 { - if errno() == libc::EINVAL { - log::trace!("clear source and retry"); - hdr.msg_control = ptr::null_mut(); - hdr.msg_controllen = 0; - dst.info = unsafe { mem::zeroed() }; - return if unsafe { libc::sendmsg(fd, &hdr, 0) } < 0 { - Err(io::Error::new( - io::ErrorKind::NotConnected, - "failed to send IPv6 packet", - )) - } else { - Ok(()) - }; - } - return Err(io::Error::new( - io::ErrorKind::NotConnected, - "failed to send IPv6 packet", - )); - } +impl Owner for MacosOwner { + type Error = UdpError; - Ok(()) + fn get_port(&self) -> u16 { + self.port } - fn write4(fd: RawFd, buf: &[u8], dst: &mut EndpointV4) -> Result<(), io::Error> { - log::debug!("sending IPv4 packet ({} fd, {} bytes)", fd, buf.len()); - - let mut iovs: [libc::iovec; 1] = [libc::iovec { - iov_base: buf.as_ptr() as *mut core::ffi::c_void, - iov_len: buf.len(), - }]; - - let mut control = ControlHeaderV4 { - hdr: libc::cmsghdr { - cmsg_len: CMSG_LEN(mem::size_of::()), - cmsg_level: libc::IPPROTO_IP, - cmsg_type: libc::IP_PKTINFO, - }, - info: dst.info, - }; - - debug_assert_eq!( - control.hdr.cmsg_len % mem::size_of::() as u32, - 0, - "cmsg_len must be aligned to a long" - ); - - debug_assert_eq!( - dst.dst.sin_family, - libc::AF_INET as libc::sa_family_t, - "this method only handles IPv4 destinations" - ); - - let mut hdr = libc::msghdr { - msg_name: safe_cast(&mut dst.dst), - msg_namelen: mem::size_of_val(&dst.dst) as u32, - msg_iov: iovs.as_mut_ptr(), - msg_control: &mut control as *mut _ as *mut _, - msg_iovlen: iovs.len() as i32, - msg_controllen: mem::size_of_val(&control) as u32, - msg_flags: 0, - }; - - let ret = unsafe { libc::sendmsg(fd, &hdr, 0) }; - - if ret < 0 { - if errno() == libc::EINVAL { - log::trace!("clear source and retry"); - hdr.msg_control = ptr::null_mut(); - hdr.msg_controllen = 0; - dst.info = unsafe { mem::zeroed() }; - return if unsafe { libc::sendmsg(fd, &hdr, 0) } < 0 { - Err(io::Error::new( - io::ErrorKind::NotConnected, - "failed to send IPv4 packet", - )) - } else { - Ok(()) - }; - } - return Err(io::Error::new( - io::ErrorKind::NotConnected, - "failed to send IPv4 packet", - )); - } - + fn set_fwmark(&mut self, _value: Option) -> Result<()> { Ok(()) } } -impl Writer for MacosUDPWriter { - type Error = io::Error; +pub enum MacosUDPReader { + V4(Arc), + V6(Arc), +} - fn write(&self, buf: &[u8], dst: &mut MacosEndpoint) -> Result<(), Self::Error> { - match dst { - MacosEndpoint::V4(ref mut end) => Self::write4(self.sock4.0, buf, end), - MacosEndpoint::V6(ref mut end) => Self::write6(self.sock6.0, buf, end), +impl AsRef for MacosUDPReader { + fn as_ref(&self) -> &UdpSocket { + match self { + Self::V4(socket) | Self::V6(socket) => &*socket, } } } -impl Drop for MacosOwner { - fn drop(&mut self) { - log::debug!("closing the bind (port = {})", self.port); - if let Some(fd) = &self.sock4 { - log::debug!("shutdown IPv4 (fd = {})", fd.0); - unsafe { - libc::shutdown(fd.0, libc::SHUT_RDWR); - } - }; - if let Some(fd) = &self.sock6 { - log::debug!("shutdown IPv6 (fd = {})", fd.0); - unsafe { - libc::shutdown(fd.0, libc::SHUT_RDWR); - } - }; - } +#[derive(Clone)] +pub struct MacosUDPWriter { + sock4: Option>, + sock6: Option>, } -impl UDP for MacosUDP { - type Error = io::Error; - type Endpoint = MacosEndpoint; - type Writer = MacosUDPWriter; - type Reader = MacosUDPReader; +#[derive(Debug)] +pub enum MacosEndpoint { + V4 { + destination: libc::sockaddr_in, + src_if_index: u32, + src_addr: libc::in_addr, + }, + V6 { + destination: libc::sockaddr_in6, + src_if_index: u32, + }, } -impl MacosUDP { - /* Bind on all IPv6 interfaces - * - * Arguments: - * - * - 'port', port to bind to (0 = any) - * - * Returns: - * - * Returns a tuple of the resulting port and socket. - */ - fn bind6(port: u16) -> Result<(u16, RawFd), io::Error> { - log::trace!("attempting to bind on IPv6 (port {})", port); - - // create socket fd - let fd: RawFd = unsafe { libc::socket(libc::AF_INET6, libc::SOCK_DGRAM, 0) }; - if fd < 0 { - log::debug!("failed to create IPv6 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); +impl MacosEndpoint { + fn destination(&self) -> InetAddr { + match self { + Self::V4 { destination, .. } => InetAddr::V4(*destination), + Self::V6 { destination, .. } => InetAddr::V6(*destination), } - - setsockopt_int(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1)?; - setsockopt_int(fd, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, 1)?; - setsockopt_int(fd, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, 1)?; - - const INADDR_ANY: libc::in6_addr = libc::in6_addr { s6_addr: [0; 16] }; - - // bind - let mut sockaddr = libc::sockaddr_in6 { - sin6_addr: INADDR_ANY, - sin6_family: libc::AF_INET6 as libc::sa_family_t, - sin6_port: port.to_be(), // convert to network (big-endian) byte-order - sin6_scope_id: 0, - sin6_flowinfo: 0, - sin6_len: mem::size_of::() as u8, - }; - - let err = unsafe { - libc::bind( - fd, - safe_cast(&mut sockaddr), - mem::size_of_val(&sockaddr).try_into().unwrap(), - ) - }; - if err != 0 { - log::debug!("failed to bind IPv6 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); + } + fn is_ipv4(&self) -> bool { + match self { + Self::V4 { .. } => true, + Self::V6 { .. } => false, } + } +} - // get the assigned port - let mut socklen: libc::socklen_t = mem::size_of_val(&sockaddr).try_into().unwrap(); - let err = unsafe { - libc::getsockname( - fd, - safe_cast(&mut sockaddr), - &mut socklen as *mut libc::socklen_t, - ) - }; - if err != 0 { - log::debug!("failed to get port of IPv6 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); +impl Endpoint for MacosEndpoint { + fn from_address(addr: SocketAddr) -> Self { + let sock_addr = InetAddr::from_std(&addr); + match sock_addr { + InetAddr::V4(destination) => Self::V4 { + destination, + src_if_index: 0, + src_addr: libc::in_addr { s_addr: 0u32 }, + }, + InetAddr::V6(destination) => Self::V6 { + destination, + src_if_index: 0, + }, } - - // basic sanity checks - let new_port = u16::from_be(sockaddr.sin6_port); - debug_assert_eq!(socklen, mem::size_of::() as u32); - debug_assert_eq!(sockaddr.sin6_family, libc::AF_INET6 as libc::sa_family_t); - debug_assert_eq!(new_port, if port != 0 { port } else { new_port }); - log::trace!("bound IPv6 socket (port {}, fd {})", new_port, fd); - Ok((new_port, fd)) } - /* Bind on all IPv4 interfaces. - * - * Arguments: - * - * - 'port', port to bind to (0 = any) - * - * Returns: - * - * Returns a tuple of the resulting port and socket. - */ - fn bind4(port: u16) -> Result<(u16, RawFd), io::Error> { - log::trace!("attempting to bind on IPv4 (port {})", port); - - // create socket fd - let fd: RawFd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; - if fd < 0 { - log::debug!("failed to create IPv4 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); + fn clear_src(&mut self) { + match self { + Self::V4 { + ref mut src_if_index, + ref mut src_addr, + .. + } => { + *src_if_index = 0; + *src_addr = libc::in_addr { s_addr: 0u32 }; + } + Self::V6 { + ref mut src_if_index, + .. + } => { + *src_if_index = 0; + } } + } - setsockopt_int(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1)?; - setsockopt_int(fd, libc::IPPROTO_IP, libc::IP_PKTINFO, 1)?; + fn into_address(&self) -> SocketAddr { + self.destination().to_std() + } +} - const INADDR_ANY: libc::in_addr = libc::in_addr { s_addr: 0 }; +impl Reader for MacosUDPReader { + type Error = UdpError; - // bind - let mut sockaddr = libc::sockaddr_in { - sin_addr: INADDR_ANY, - sin_family: libc::AF_INET as libc::sa_family_t, - sin_port: port.to_be(), - sin_zero: [0; 8], - sin_len: mem::size_of::() as u8, - }; + fn read(&self, buf: &mut [u8]) -> Result<(usize, MacosEndpoint)> { + self.as_ref().recv_from(buf) + } +} - let err = unsafe { - libc::bind( - fd, - safe_cast(&mut sockaddr), - mem::size_of_val(&sockaddr).try_into().unwrap(), - ) - }; - if err != 0 { - log::debug!("failed to bind IPv4 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); - } +impl Writer for MacosUDPWriter { + type Error = UdpError; - // get the assigned port - let mut socklen: libc::socklen_t = mem::size_of_val(&sockaddr).try_into().unwrap(); - let err = unsafe { - libc::getsockname( - fd, - safe_cast(&mut sockaddr), - &mut socklen as *mut libc::socklen_t, - ) + fn write(&self, buf: &[u8], dst: &mut MacosEndpoint) -> Result<()> { + let maybe_socket = if dst.is_ipv4() { + &self.sock4 + } else { + &self.sock6 }; - if err != 0 { - log::debug!("failed to get port of IPv4 socket (errno = {})", errno()); - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to create socket", - )); - } - // basic sanity checks - let new_port = u16::from_be(sockaddr.sin_port); - debug_assert_eq!(socklen, mem::size_of::() as u32); - debug_assert_eq!(sockaddr.sin_family, libc::AF_INET as libc::sa_family_t); - debug_assert_eq!(new_port, if port != 0 { port } else { new_port }); - log::trace!("bound IPv4 socket (port {}, fd {})", new_port, fd); - Ok((new_port, fd)) + let socket = + maybe_socket + .as_ref() + .ok_or(UdpError::UnsupportedProtocol(if dst.is_ipv4() { + "ipv4" + } else { + "ipv6" + }))?; + + let _ = socket.send_to(buf, dst)?; + Ok(()) } } +impl UDP for MacosUDP { + type Error = UdpError; + type Endpoint = MacosEndpoint; + type Writer = MacosUDPWriter; + type Reader = MacosUDPReader; +} + +impl MacosUDP {} + impl PlatformUDP for MacosUDP { type Owner = MacosOwner; #[allow(clippy::type_complexity)] #[allow(clippy::unnecessary_unwrap)] - fn bind(mut port: u16) -> Result<(Vec, Self::Writer, Self::Owner), Self::Error> { - log::debug!("bind to port {}", port); + fn bind(mut port: u16) -> Result<(Vec, Self::Writer, Self::Owner)> { + log::trace!("binding to port {}", port); - // attempt to bind on ipv6 - let bind6 = Self::bind6(port); + let bind6 = UdpSocket::bind(Ipv6Addr::UNSPECIFIED, port); if let Ok((new_port, _)) = bind6 { port = new_port; } - // attempt to bind on ipv4 on the same port - let bind4 = Self::bind4(port); + let bind4 = UdpSocket::bind(Ipv4Addr::UNSPECIFIED, port); if let Ok((new_port, _)) = bind4 { port = new_port; } - // check if failed to bind on both if bind4.is_err() && bind6.is_err() { log::trace!("failed to bind for either IP version"); return Err(bind6.unwrap_err()); } - let sock6 = bind6.ok().map(|(_, fd)| Arc::new(FD(fd))); - let sock4 = bind4.ok().map(|(_, fd)| Arc::new(FD(fd))); + let sock6 = bind6.ok().map(|(_, socket)| Arc::new(socket)); + let sock4 = bind4.ok().map(|(_, socket)| Arc::new(socket)); - // create owner let owner = MacosOwner { port, - sock6: sock6.clone(), - sock4: sock4.clone(), + _sock6: sock6.clone(), + _sock4: sock4.clone(), }; - // create readers let mut readers: Vec = Vec::with_capacity(2); if let Some(sock) = sock6.clone() { readers.push(MacosUDPReader::V6(sock)) @@ -684,11 +487,7 @@ impl PlatformUDP for MacosUDP { } debug_assert!(!readers.is_empty()); - // create writer - let writer = MacosUDPWriter { - sock4: sock4.unwrap_or_else(|| Arc::new(FD(-1))), - sock6: sock6.unwrap_or_else(|| Arc::new(FD(-1))), - }; + let writer = MacosUDPWriter { sock4, sock6 }; Ok((readers, writer, owner)) } From 421e65549bf20dcd749fc5cf0aeaeda74c2ede49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Em=C4=ABls=20Pi=C5=86=C4=B7is?= Date: Thu, 22 Apr 2021 14:23:08 +0100 Subject: [PATCH 5/5] Fix undeflow in Validator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emīls --- src/wireguard/handshake/macs.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/wireguard/handshake/macs.rs b/src/wireguard/handshake/macs.rs index f4f5586..2eaffb3 100644 --- a/src/wireguard/handshake/macs.rs +++ b/src/wireguard/handshake/macs.rs @@ -181,7 +181,16 @@ impl Generator { struct Secret { value: [u8; 32], - birth: Instant, + birth: Option, +} + +impl Secret { + fn is_still_valid(&self) -> bool { + match self.birth { + Some(birth) => birth.elapsed() < COOKIE_UPDATE_INTERVAL, + None => false, + } + } } pub struct Validator { @@ -197,14 +206,15 @@ impl Validator { cookie_key: HASH!(LABEL_COOKIE, pk.as_bytes()).into(), secret: RwLock::new(Secret { value: [0u8; SIZE_SECRET], - birth: Instant::now() - Duration::new(86400, 0), + birth: None, }), } } fn get_tau(&self, src: &[u8]) -> Option<[u8; SIZE_COOKIE]> { let secret = self.secret.read(); - if secret.birth.elapsed() < COOKIE_UPDATE_INTERVAL { + if secret.is_still_valid() + { Some(MAC!(&secret.value, src)) } else { None @@ -215,7 +225,7 @@ impl Validator { // check if current value is still valid { let secret = self.secret.read(); - if secret.birth.elapsed() < COOKIE_UPDATE_INTERVAL { + if secret.is_still_valid() { return MAC!(&secret.value, src); }; } @@ -223,13 +233,13 @@ impl Validator { // take write lock, check again { let mut secret = self.secret.write(); - if secret.birth.elapsed() < COOKIE_UPDATE_INTERVAL { + if secret.is_still_valid() { return MAC!(&secret.value, src); }; // set new random cookie secret rng.fill_bytes(&mut secret.value); - secret.birth = Instant::now(); + secret.birth = Some(Instant::now()); MAC!(&secret.value, src) } }