From 68bac84fefa9a1d1bb5da105fadfaedf33e7967e Mon Sep 17 00:00:00 2001 From: Nathaniel Date: Sun, 8 Sep 2024 07:32:12 -0400 Subject: [PATCH] Fix initial CI breaks --- .github/workflows/full_ci.yml | 6 +- Cargo.toml | 6 +- src/lib.rs | 27 ++- src/linux/tun.rs | 8 +- src/macos.rs | 68 ++++++- src/macos/feth.rs | 357 +++++++++++++++++++++++----------- src/macos/utun.rs | 64 ++++-- src/openbsd.rs | 1 - src/tap.rs | 1 - src/wintun/adapter.rs | 11 +- src/wintun/dll/link.rs | 6 +- 11 files changed, 397 insertions(+), 158 deletions(-) diff --git a/.github/workflows/full_ci.yml b/.github/workflows/full_ci.yml index 9027faa..bffa683 100644 --- a/.github/workflows/full_ci.yml +++ b/.github/workflows/full_ci.yml @@ -27,7 +27,7 @@ jobs: matrix: toolchain: - stable - - 1.66.0 + - 1.74.0 steps: - uses: actions/checkout@v4 - name: Setup Rust toolchain @@ -45,7 +45,7 @@ jobs: matrix: target: - { toolchain: stable, os: macos-14 } - - { toolchain: 1.66.0, os: macos-14 } + - { toolchain: 1.74.0, os: macos-14 } runs-on: ${{ matrix.target.os }} steps: - uses: actions/checkout@v4 @@ -63,7 +63,7 @@ jobs: fail-fast: true matrix: toolchain: - - 1.66.0 + - 1.74.0 - stable target: - x86_64-pc-windows-msvc diff --git a/Cargo.toml b/Cargo.toml index 7a786e5..bce2184 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,9 @@ name = "tappers" authors = ["Nathaniel Bennett "] description = "Cross-platform TUN and TAP interfaces" -rust-version = "1.66" # `std::os::fd` stabilized +# 1.66 - `std::os::fd` stabilized +# 1.74 - `OsStr::as_encoded_bytes` stabilized +rust-version = "1.74" version = "0.1.0" license = "MIT OR Apache-2.0" edition = "2021" @@ -21,7 +23,7 @@ wintun-runtime = ["wintun"] # Enables fallible run-time loading of tap-windows6 (default is load-time) tapwin6-runtime = ["tapwin6"] -[target.'cfg(unix)'.dependencies] +[dependencies] libc = { version = "0.2" } [target.'cfg(windows)'.dependencies] diff --git a/src/lib.rs b/src/lib.rs index b02cf66..faa4670 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,16 +17,32 @@ pub mod tapwin6; #[cfg(all(target_os = "windows", feature = "wintun"))] pub mod wintun; +#[cfg(not(target_os = "windows"))] mod tap; +#[cfg(any( + not(target_os = "windows"), + all(target_os = "windows", feature = "wintun") +))] mod tun; +#[cfg(not(target_os = "windows"))] pub use tap::Tap; +#[cfg(any( + not(target_os = "windows"), + all(target_os = "windows", feature = "wintun") +))] pub use tun::Tun; -use std::ffi::{CStr, OsStr, OsString}; +#[cfg(not(target_os = "windows"))] +use std::ffi::CStr; +use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Display}; +#[cfg(not(target_os = "windows"))] +use std::mem; +#[cfg(target_os = "windows")] +use std::ptr; use std::str::FromStr; -use std::{array, io, mem}; +use std::{array, io}; #[cfg(target_os = "windows")] use windows_sys::Win32::Foundation::{ERROR_DEV_NOT_EXIST, ERROR_NO_DATA}; @@ -80,12 +96,11 @@ impl Interface { pub const MAX_INTERFACE_NAME_LEN: usize = INTERNAL_MAX_INTERFACE_NAME_LEN; /// A special catch-all interface identifier that specifies all operational interfaces. + #[cfg(not(target_os = "windows"))] pub fn any() -> io::Result { - #[cfg(not(target_os = "windows"))] let name = [0; Self::MAX_INTERFACE_NAME_LEN + 1]; // Leave the interface name blank since this is the catch-all identifier - Ok(Self { name, is_catchall: true, @@ -115,7 +130,7 @@ impl Interface { #[cfg(target_os = "windows")] #[inline] fn new_inner(if_name: &impl AsRef) -> io::Result { - let utf16 = if_name.as_ref().encode_wide(); + let mut utf16 = if_name.as_ref().encode_wide(); let name = array::from_fn(|_| utf16.next().unwrap_or(0)); let interface = Interface { @@ -129,6 +144,7 @@ impl Interface { #[cfg(not(target_os = "windows"))] #[inline] fn new_inner(if_name: &impl AsRef) -> io::Result { + // Note: `as_encoded_bytes()` is the only think keeping MSRV as high as 1.74 Self::new_raw(if_name.as_ref().as_encoded_bytes()) } @@ -182,7 +198,6 @@ impl Interface { } /// Retrieves the associated index of the network interface. - #[cfg(not(target_os = "windows"))] #[inline] pub fn index(&self) -> io::Result { self.index_impl() diff --git a/src/linux/tun.rs b/src/linux/tun.rs index 9e35ea5..d5c00f6 100644 --- a/src/linux/tun.rs +++ b/src/linux/tun.rs @@ -300,9 +300,9 @@ impl Tun { } /// Reads a single packet from the TUN device. - pub fn recv(&self, data: &mut [u8]) -> io::Result { + pub fn recv(&self, buf: &mut [u8]) -> io::Result { unsafe { - match libc::read(self.fd, data.as_mut_ptr() as *mut libc::c_void, data.len()) { + match libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) { r @ 0.. => Ok(r as usize), _ => Err(io::Error::last_os_error()), } @@ -310,9 +310,9 @@ impl Tun { } /// Writes a single packet to the TUN device. - pub fn send(&self, data: &[u8]) -> io::Result { + pub fn send(&self, buf: &[u8]) -> io::Result { unsafe { - match libc::write(self.fd, data.as_ptr() as *const libc::c_void, data.len()) { + match libc::write(self.fd, buf.as_ptr() as *const libc::c_void, buf.len()) { r @ 0.. => Ok(r as usize), _ => Err(io::Error::last_os_error()), } diff --git a/src/macos.rs b/src/macos.rs index b2824d5..4f3bc51 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -64,4 +64,70 @@ impl TunImpl { } } -pub(crate) struct TapImpl {} +pub(crate) struct TapImpl { + tap: FethTap, +} + +impl TapImpl { + /// Creates a new, unique TUN device. + #[inline] + pub fn new() -> io::Result { + Ok(Self { + tap: FethTap::new()?, + }) + } + + /// Opens or creates a TUN device of the given name. + #[inline] + pub fn new_named(if_name: Interface) -> io::Result { + Ok(Self { + tap: FethTap::new_named(Some(if_name), None)?, + }) + } + + /// Retrieves the interface name of the TUN device. + #[inline] + pub fn name(&self) -> io::Result { + self.tap.name() + } + + #[inline] + pub fn set_state(&mut self, state: DeviceState) -> io::Result<()> { + self.tap.set_state(state) + } + + #[inline] + pub fn set_up(&mut self) -> io::Result<()> { + self.tap.set_state(DeviceState::Up) + } + + #[inline] + pub fn set_down(&mut self) -> io::Result<()> { + self.tap.set_state(DeviceState::Down) + } + + #[inline] + pub fn mtu(&self) -> io::Result { + self.tap.mtu() + } + + #[inline] + pub fn set_nonblocking(&mut self, nonblocking: bool) -> io::Result<()> { + self.tap.set_nonblocking(nonblocking) + } + + #[inline] + pub fn nonblocking(&self) -> io::Result { + self.tap.nonblocking() + } + + #[inline] + pub fn send(&mut self, buf: &[u8]) -> io::Result { + self.tap.send(buf) + } + + #[inline] + pub fn recv(&mut self, buf: &mut [u8]) -> io::Result { + self.tap.recv(buf) + } +} diff --git a/src/macos/feth.rs b/src/macos/feth.rs index 1695a47..26c99a2 100644 --- a/src/macos/feth.rs +++ b/src/macos/feth.rs @@ -12,6 +12,9 @@ const NET_LINK_FAKE_LRO: *const i8 = b"net.link.fake.lro\0".as_ptr() as *const i const BPF_CREATE_ATTEMPTS: u32 = 1024; const BPF_BUFFER_LEN: i32 = 131072; +#[allow(non_camel_case_types)] +type u_quad_t = u64; + // struct/const values to be removed once libc supports const SIOCAIFADDR: libc::c_ulong = 0x8040691a; const SIOCGIFCAP: libc::c_ulong = 0xc020695b; @@ -79,6 +82,8 @@ const AF_SYS_CONTROL: libc::c_int = 2; const AF_LINK: libc::c_int = 18; const AF_NDRV: libc::c_int = 27; +const SCOPE6_ID_MAX: libc::size_t = 16; + #[repr(C)] #[allow(non_camel_case_types)] pub struct ifdrv { @@ -94,7 +99,111 @@ pub struct ifdrv { struct sockaddr_ndrv { pub snd_len: libc::c_uchar, pub snd_family: libc::c_uchar, - pub snd_name: [libc::c_uchar; libc::IFNAMSIZ], + pub snd_name: [libc::c_uchar; libc::IFNAMSIZ as usize], +} + +#[repr(C)] +#[allow(non_camel_case_types)] +struct in6_ifreq { + pub ifr_name: [libc::c_char; libc::IFNAMSIZ as usize], + pub ifr_ifru: __c_anonymous_ifr_ifru6, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +union __c_anonymous_ifr_ifru6 { + pub ifru_addr: libc::sockaddr_in6, + pub ifru_dstaddr: libc::sockaddr_in6, + pub ifru_flags: libc::c_int, + pub ifru_flags6: libc::c_int, + pub ifru_metrics: libc::c_int, + pub ifru_intval: libc::c_int, + pub ifru_data: *mut libc::c_char, + pub ifru_lifetime: in6_addrlifetime, + pub ifru_stat: in6_ifstat, + pub ifru_icmp6stat: icmp6_ifstat, + pub ifru_scope_id: [u32; SCOPE6_ID_MAX], +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +struct in6_addrlifetime { + pub ia6t_expire: libc::time_t, + pub ia6t_preferred: libc::time_t, + pub ia6t_vltime: u32, + pub ia6t_pltime: u32, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +struct icmp6_ifstat { + pub ifs6_in_msg: u_quad_t, + pub ifs6_in_error: u_quad_t, + pub ifs6_in_dstunreach: u_quad_t, + pub ifs6_in_adminprohib: u_quad_t, + pub ifs6_in_timeexceed: u_quad_t, + pub ifs6_in_paramprob: u_quad_t, + pub ifs6_in_pkttoobig: u_quad_t, + pub ifs6_in_echo: u_quad_t, + pub ifs6_in_echoreply: u_quad_t, + pub ifs6_in_routersolicit: u_quad_t, + pub ifs6_in_routeradvert: u_quad_t, + pub ifs6_in_neighborsolicit: u_quad_t, + pub ifs6_in_neighboradvert: u_quad_t, + pub ifs6_in_redirect: u_quad_t, + pub ifs6_in_mldquery: u_quad_t, + pub ifs6_in_mldreport: u_quad_t, + pub ifs6_in_mlddone: u_quad_t, + pub ifs6_out_msg: u_quad_t, + pub ifs6_out_error: u_quad_t, + pub ifs6_out_dstunreach: u_quad_t, + pub ifs6_out_adminprohib: u_quad_t, + pub ifs6_out_timeexceed: u_quad_t, + pub ifs6_out_paramprob: u_quad_t, + pub ifs6_out_pkttoobig: u_quad_t, + pub ifs6_out_echo: u_quad_t, + pub ifs6_out_echoreply: u_quad_t, + pub ifs6_out_routersolicit: u_quad_t, + pub ifs6_out_routeradvert: u_quad_t, + pub ifs6_out_neighborsolicit: u_quad_t, + pub ifs6_out_neighboradvert: u_quad_t, + pub ifs6_out_redirect: u_quad_t, + pub ifs6_out_mldquery: u_quad_t, + pub ifs6_out_mldreport: u_quad_t, + pub ifs6_out_mlddone: u_quad_t, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +struct in6_ifstat { + pub ifs6_in_receive: u_quad_t, + pub ifs6_in_hdrerr: u_quad_t, + pub ifs6_in_toobig: u_quad_t, + pub ifs6_in_noroute: u_quad_t, + pub ifs6_in_addrerr: u_quad_t, + pub ifs6_in_protounknown: u_quad_t, + pub ifs6_in_truncated: u_quad_t, + pub ifs6_in_discard: u_quad_t, + pub ifs6_in_deliver: u_quad_t, + pub ifs6_out_forward: u_quad_t, + pub ifs6_out_request: u_quad_t, + pub ifs6_out_discard: u_quad_t, + pub ifs6_out_fragok: u_quad_t, + pub ifs6_out_fragfail: u_quad_t, + pub ifs6_out_fragcreat: u_quad_t, + pub ifs6_reass_reqd: u_quad_t, + pub ifs6_reass_ok: u_quad_t, + pub ifs6_atmfrag_rcvd: u_quad_t, + pub ifs6_reass_fail: u_quad_t, + pub ifs6_in_mcast: u_quad_t, + pub ifs6_out_mcast: u_quad_t, + pub ifs6_cantfoward_icmp6: u_quad_t, + pub ifs6_addr_expiry_cnt: u_quad_t, + pub ifs6_pfx_expiry_cnt: u_quad_t, + pub ifs6_defrtr_expiry_cnt: u_quad_t, } /// Fake Ethernet ("feth") TAP device interface. @@ -194,11 +303,13 @@ impl FethTap { )); } - let ndrv_fd = unsafe { libc::socket(AF_NDRV, libc::SOCK_RAW | libc::SOCK_CLOEXEC, 0) }; + let ndrv_fd = unsafe { libc::socket(AF_NDRV, libc::SOCK_RAW, 0) }; if ndrv_fd < 0 { return Err(io::Error::last_os_error()); } + // TODO: set O_CLOEXEC on this and all other sockets + // Create the primary `feth` device let mut req = libc::ifreq { @@ -283,7 +394,7 @@ impl FethTap { let mut bpf_fd = unsafe { libc::open(DEV_BPF, libc::O_RDWR | libc::O_CLOEXEC) }; if bpf_fd < 0 { - let errno = unsafe { *libc::__errno_location() }; + let errno = unsafe { *libc::__error() }; if errno != libc::ENOENT { // `/dev/bpf` device existed, but some other error occurred let err = io::Error::last_os_error(); @@ -302,7 +413,7 @@ impl FethTap { break; } - let errno = unsafe { *libc::__errno_location() }; + let errno = unsafe { *libc::__error() }; if errno != libc::EBUSY { // Device wasn't in use, but some other error occurred let err = io::Error::last_os_error(); @@ -380,7 +491,8 @@ impl FethTap { } // Do sniff packets even if they're not addressed specifically to us - if unsafe { libc::ioctl(bpf_fd, libc::BIOCPROMISC, ptr::addr_of_mut!(enable)) } != 0 { + if unsafe { libc::ioctl(bpf_fd, libc::BIOCPROMISC as u64, ptr::addr_of_mut!(enable)) } != 0 + { let err = io::Error::last_os_error(); Self::close_fd(bpf_fd); Self::destroy_iface(ndrv_fd, peer_iface); @@ -418,7 +530,7 @@ impl FethTap { unsafe { match libc::sysctlbyname( NET_LINK_FAKE_LRO, - ptr::addr_of_mut!(lro), + ptr::addr_of_mut!(lro) as *mut libc::c_void, ptr::addr_of_mut!(lro_len), ptr::null_mut(), 0, @@ -441,7 +553,7 @@ impl FethTap { NET_LINK_FAKE_LRO, ptr::null_mut(), ptr::null_mut(), - ptr::addr_of_mut!(lro), + ptr::addr_of_mut!(lro) as *mut libc::c_void, mem::size_of_val(&lro), ) { 0 => Ok(()), @@ -492,7 +604,7 @@ impl FethTap { unsafe { match libc::ioctl(self.ndrv_fd, SIOCGIFDEVMTU, ptr::addr_of_mut!(req)) { - 0 => Ok(unsafe { req.ifr_ifru.ifru_devmtu.ifdm_current as usize }), + 0 => Ok(req.ifr_ifru.ifru_devmtu.ifdm_current as usize), _ => Err(io::Error::last_os_error()), } } @@ -512,7 +624,7 @@ impl FethTap { unsafe { match libc::ioctl(self.ndrv_fd, SIOCGIFDEVMTU, ptr::addr_of_mut!(req)) { - 0 => Ok(unsafe { req.ifr_ifru.ifru_devmtu.ifdm_min as usize }), + 0 => Ok(req.ifr_ifru.ifru_devmtu.ifdm_min as usize), _ => Err(io::Error::last_os_error()), } } @@ -532,7 +644,7 @@ impl FethTap { unsafe { match libc::ioctl(self.ndrv_fd, SIOCGIFDEVMTU, ptr::addr_of_mut!(req)) { - 0 => Ok(unsafe { req.ifr_ifru.ifru_devmtu.ifdm_max as usize }), + 0 => Ok(req.ifr_ifru.ifru_devmtu.ifdm_max as usize), _ => Err(io::Error::last_os_error()), } } @@ -566,7 +678,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -589,12 +701,11 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { libc::ioctl(self.bpf_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 - { + if unsafe { libc::ioctl(self.bpf_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -611,12 +722,11 @@ impl FethTap { } } - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { libc::ioctl(self.bpf_fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 - { + if unsafe { libc::ioctl(self.bpf_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -629,7 +739,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -651,18 +761,11 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { - libc::ioctl( - self.ndrv_fd, - libc::SIOCGIFFLAGS, - ptr::addr_of_mut!(peer_req), - ) - } != 0 - { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -679,18 +782,11 @@ impl FethTap { } } - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { - libc::ioctl( - self.ndrv_fd, - libc::SIOCSIFFLAGS, - ptr::addr_of_mut!(peer_req), - ) - } != 0 - { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -703,7 +799,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -725,18 +821,11 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { - libc::ioctl( - self.ndrv_fd, - libc::SIOCGIFFLAGS, - ptr::addr_of_mut!(peer_req), - ) - } != 0 - { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -753,18 +842,11 @@ impl FethTap { } } - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } - if unsafe { - libc::ioctl( - self.ndrv_fd, - libc::SIOCSIFFLAGS, - ptr::addr_of_mut!(peer_req), - ) - } != 0 - { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(peer_req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -777,7 +859,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -797,7 +879,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -808,7 +890,7 @@ impl FethTap { } } - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -884,6 +966,16 @@ impl FethTap { } */ + /// Indicates whether nonblocking is enabled for `read` and `write` operations on the TAP device. + pub fn nonblocking(&self) -> io::Result { + let flags = unsafe { libc::fcntl(self.ndrv_fd, libc::F_GETFL) }; + if flags < 0 { + return Err(io::Error::last_os_error()); + } + + Ok(flags & libc::O_NONBLOCK > 0) + } + /// Sets nonblocking mode for `read` and `write` operations on the TUN device. pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let nonblocking = match nonblocking { @@ -947,6 +1039,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -995,6 +1088,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1013,7 +1107,7 @@ impl FethTap { } unsafe { - match libc::ioctl(self.ndrv_fd, libc::SIOCADDMULTI, ptr::addr_of_mut!(req)) { + match libc::ioctl(self.ndrv_fd, SIOCADDMULTI, ptr::addr_of_mut!(req)) { 0 => Ok(()), _ => Err(io::Error::last_os_error()), } @@ -1043,6 +1137,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1061,7 +1156,7 @@ impl FethTap { } unsafe { - match libc::ioctl(self.ndrv_fd, libc::SIOCDELMULTI, ptr::addr_of_mut!(req)) { + match libc::ioctl(self.ndrv_fd, SIOCDELMULTI, ptr::addr_of_mut!(req)) { 0 => Ok(()), _ => Err(io::Error::last_os_error()), } @@ -1077,10 +1172,11 @@ impl FethTap { } let addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { - s_addr: v4_addr.to_bits(), + s_addr: v4_addr.into(), }, sin_zero: [0; 8], }; @@ -1090,6 +1186,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1113,16 +1210,12 @@ impl FethTap { unsafe { match libc::ioctl(self.ndrv_fd, SIOCAIFADDR, ptr::addr_of_mut!(req)) { 0 => { - unsafe { - libc::close(inet_fd); - } + libc::close(inet_fd); Ok(()) } _ => { let err = io::Error::last_os_error(); - unsafe { - libc::close(inet_fd); - } + libc::close(inet_fd); Err(err) } } @@ -1135,11 +1228,12 @@ impl FethTap { } // TODO: do flowinfo or scope_id have any significance? - let mut req = libc::in6_ifreq { + let mut req = in6_ifreq { ifr_name: self.iface.name_raw_i8(), - ifr_ifru: libc::__c_anonymous_ifr_ifru6 { + ifr_ifru: __c_anonymous_ifr_ifru6 { ifru_addr: libc::sockaddr_in6 { - sin6_family: libc::AF_INET6 as u16, + sin6_family: libc::AF_INET6 as u8, + sin6_len: mem::size_of::() as u8, sin6_port: 0, sin6_flowinfo: 0, sin6_addr: libc::in6_addr { @@ -1176,10 +1270,11 @@ impl FethTap { } let addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { - s_addr: v4_addr.to_bits(), + s_addr: v4_addr.into(), }, sin_zero: [0; 8], }; @@ -1189,6 +1284,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1212,16 +1308,12 @@ impl FethTap { unsafe { match libc::ioctl(inet_fd, SIOCDIFADDR, ptr::addr_of_mut!(req)) { 0 => { - unsafe { - libc::close(inet_fd); - } + libc::close(inet_fd); Ok(()) } _ => { let err = io::Error::last_os_error(); - unsafe { - libc::close(inet_fd); - } + libc::close(inet_fd); Err(err) } } @@ -1234,11 +1326,12 @@ impl FethTap { } // TODO: do flowinfo or scope_id have any significance? - let mut req = libc::in6_ifreq { + let mut req = in6_ifreq { ifr_name: self.iface.name_raw_i8(), - ifr_ifru: libc::__c_anonymous_ifr_ifru6 { + ifr_ifru: __c_anonymous_ifr_ifru6 { ifru_addr: libc::sockaddr_in6 { - sin6_family: libc::AF_INET6 as u16, + sin6_family: libc::AF_INET6 as u8, + sin6_len: mem::size_of::() as u8, sin6_port: 0, sin6_flowinfo: 0, sin6_addr: libc::in6_addr { @@ -1277,12 +1370,13 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFNETMASK, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFNETMASK, ptr::addr_of_mut!(req)) } != 0 { let err = io::Error::last_os_error(); unsafe { libc::close(inet_fd) }; return Err(err); @@ -1294,7 +1388,8 @@ impl FethTap { ); let mut addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { s_addr: 0 }, sin_zero: [0; 8], @@ -1310,7 +1405,7 @@ impl FethTap { libc::close(inet_fd); } - Ok(Ipv4Addr::from_bits(addr.sin_addr.s_addr)) + Ok(Ipv4Addr::from(addr.sin_addr.s_addr.to_be_bytes())) // TODO: verify correct endianness } pub fn set_v4_netmask(&self, addr: Ipv4Addr) -> io::Result<()> { @@ -1320,10 +1415,11 @@ impl FethTap { } let addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { - s_addr: addr.to_bits(), + s_addr: addr.into(), }, sin_zero: [0; 8], }; @@ -1333,6 +1429,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1350,7 +1447,7 @@ impl FethTap { } unsafe { - match libc::ioctl(self.ndrv_fd, libc::SIOCSIFNETMASK, ptr::addr_of_mut!(req)) { + match libc::ioctl(self.ndrv_fd, SIOCSIFNETMASK, ptr::addr_of_mut!(req)) { 0 => { libc::close(inet_fd); Ok(()) @@ -1375,12 +1472,13 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, }; - if unsafe { libc::ioctl(self.ndrv_fd, libc::SIOCGIFDSTADDR, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.ndrv_fd, SIOCGIFDSTADDR, ptr::addr_of_mut!(req)) } != 0 { let err = io::Error::last_os_error(); unsafe { libc::close(inet_fd) }; return Err(err); @@ -1392,7 +1490,8 @@ impl FethTap { ); let mut addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { s_addr: 0 }, sin_zero: [0; 8], @@ -1408,7 +1507,7 @@ impl FethTap { libc::close(inet_fd); } - Ok(Ipv4Addr::from_bits(addr.sin_addr.s_addr)) + Ok(Ipv4Addr::from(addr.sin_addr.s_addr.to_be_bytes())) // TODO: verify correct endianness } pub fn set_dst_addr(&self, addr: Ipv4Addr) -> io::Result<()> { @@ -1418,10 +1517,11 @@ impl FethTap { } let addr = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, + sin_family: libc::AF_INET as u8, + sin_len: mem::size_of::() as u8, sin_port: 0, sin_addr: libc::in_addr { - s_addr: addr.to_bits(), + s_addr: addr.into(), }, sin_zero: [0; 8], }; @@ -1431,6 +1531,7 @@ impl FethTap { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_addr: libc::sockaddr { sa_family: 0, + sa_len: mem::size_of::() as u8, sa_data: [0i8; 14], }, }, @@ -1448,7 +1549,7 @@ impl FethTap { } unsafe { - match libc::ioctl(self.ndrv_fd, libc::SIOCSIFDSTADDR, ptr::addr_of_mut!(req)) { + match libc::ioctl(self.ndrv_fd, SIOCSIFDSTADDR, ptr::addr_of_mut!(req)) { 0 => { libc::close(inet_fd); Ok(()) @@ -1528,20 +1629,16 @@ impl FethTap { let mut addrs = Vec::new(); while !data.is_empty() { - let Some((hdr_slice, rem_data)) = - data.split_at_checked(mem::size_of::()) - else { - debug_assert!(false); - break; - }; + let (hdr_slice, rem_data) = data.split_at(mem::size_of::()); let (_, hdr_slice, _) = unsafe { hdr_slice.align_to::() }; let msghdr = hdr_slice.first().unwrap(); // TODO: handle alignment errors gracefully? let addr_end = msghdr.ifam_msglen as usize - mem::size_of::(); - let Some((mut addr_data, rem_data)) = rem_data.split_at_checked(addr_end) else { + if addr_end > rem_data.len() { break; - }; + } + let (mut addr_data, rem_data) = rem_data.split_at(addr_end); data = rem_data; if msghdr.ifam_version != Self::RTM_VERSION @@ -1573,28 +1670,58 @@ impl FethTap { match addr_family { libc::AF_INET => { - let offset = mem::offset_of!(libc::sockaddr_in, sin_addr) - + mem::offset_of!(libc::in_addr, s_addr); - let addr_bytes = addr_data[offset..offset + 4].try_into().unwrap(); - addrs.push(IpAddr::V4(Ipv4Addr::from_bits(u32::from_be_bytes( - addr_bytes, - )))); + // TODO: alignment *seems* like it would work out here... + let in_addr_bytes: [u8; mem::size_of::()] = addr_data + [2..2 + mem::size_of::()] + .try_into() + .unwrap(); + unsafe { + let in_addr: libc::sockaddr_in = mem::transmute(in_addr_bytes); + addrs.push(IpAddr::V4(Ipv4Addr::from(in_addr.sin_addr.s_addr))); + } } libc::AF_INET6 => { - let offset = mem::offset_of!(libc::sockaddr_in6, sin6_addr) - + mem::offset_of!(libc::in6_addr, s6_addr); - let addr_bytes = addr_data[offset..offset + 16].try_into().unwrap(); - addrs.push(IpAddr::V6(Ipv6Addr::from_bits(u128::from_be_bytes( - addr_bytes, - )))); + let in6_addr_bytes: [u8; mem::size_of::()] = addr_data + [2..2 + mem::size_of::()] + .try_into() + .unwrap(); + unsafe { + let in6_addr: libc::sockaddr_in6 = + unsafe { mem::transmute(in6_addr_bytes) }; + addrs.push(IpAddr::V6(Ipv6Addr::from(in6_addr.sin6_addr.s6_addr))); + } } - _ => (), // We exclude link-layer (and other) addresses here + _ => (), // We ignore link-layer (and other) addresses here } } Ok(addrs) } + #[inline] + pub fn send(&mut self, buf: &[u8]) -> io::Result { + unsafe { + match libc::write(self.ndrv_fd, buf.as_ptr() as *mut libc::c_void, buf.len()) { + s @ 0.. => Ok(s as usize), + _ => Err(io::Error::last_os_error()), + } + } + } + + #[inline] + pub fn recv(&mut self, buf: &mut [u8]) -> io::Result { + unsafe { + match libc::read( + self.ndrv_fd, + buf.as_mut_ptr() as *mut libc::c_void, + buf.len(), + ) { + r @ 0.. => Ok(r as usize), + _ => Err(io::Error::last_os_error()), + } + } + } + pub fn destroy(self) -> io::Result<()> { let mut err = None; diff --git a/src/macos/utun.rs b/src/macos/utun.rs index f7c47fe..87fcc07 100644 --- a/src/macos/utun.rs +++ b/src/macos/utun.rs @@ -9,13 +9,21 @@ const UTUN_CONTROL_NAME: &[u8] = b"com.apple.net.utun_control\0"; const SIOCGIFDEVMTU: libc::c_ulong = 0xc0206944; const SIOCIFDESTROY: libc::c_ulong = 0x80206979; +const SIOCGIFFLAGS: libc::c_ulong = 0xc0206911; +const SIOCSIFFLAGS: libc::c_ulong = 0x80206910; + +const SIOCGIFDSTADDR: libc::c_ulong = 0xc0206922; +const SIOCSIFDSTADDR: libc::c_ulong = 0x8020690e; + +const SIOCGIFNETMASK: libc::c_ulong = 0xc0206925; +const SIOCSIFNETMASK: libc::c_ulong = 0x80206916; // We use a custom `iovec` struct here because we don't want to do a *const to *mut conversion #[repr(C)] #[allow(non_camel_case_types)] -pub struct iovec_send { - pub iov_base: *const ::c_void, - pub iov_len: ::size_t, +pub struct iovec_const { + pub iov_base: *const libc::c_void, + pub iov_len: libc::size_t, } pub struct Utun { @@ -71,16 +79,23 @@ impl Utun { let mut utun_ctrl_iter = UTUN_CONTROL_NAME.iter(); let mut info = libc::ctl_info { ctl_id: 0u32, - ctl_name: array::from_fn(|_| utun_ctrl_iter.next().cloned().unwrap_or(0)), + ctl_name: array::from_fn(|_| utun_ctrl_iter.next().map(|b| *b as i8).unwrap_or(0)), }; - if unsafe { libc::ioctl(fd, libc::CTLIOCGINFO, &info) } != 0 { + if unsafe { + libc::ioctl( + fd, + libc::CTLIOCGINFO, + ptr::addr_of_mut!(info) as *mut libc::c_void, + ) + } != 0 + { Self::close_fd(fd); return Err(io::Error::last_os_error()); } let addrlen = mem::size_of::(); - let mut addr = libc::sockaddr_ctl { + let addr = libc::sockaddr_ctl { sc_len: addrlen as libc::c_uchar, sc_family: libc::AF_SYSTEM as libc::c_uchar, ss_sysaddr: libc::AF_SYS_CONTROL as u16, @@ -107,7 +122,7 @@ impl Utun { pub fn name(&self) -> io::Result { let mut name_buf = [0u8; Interface::MAX_INTERFACE_NAME_LEN + 1]; let name_ptr = ptr::addr_of_mut!(name_buf) as *mut libc::c_void; - let mut name_len = Interface::MAX_INTERFACE_NAME_LEN + 1; + let mut name_len: u32 = Interface::MAX_INTERFACE_NAME_LEN as u32 + 1; match unsafe { libc::getsockopt( @@ -115,7 +130,7 @@ impl Utun { libc::SYSPROTO_CONTROL, libc::UTUN_OPT_IFNAME, name_ptr, - name_len, + ptr::addr_of_mut!(name_len), ) } { 0 => Ok(Interface { @@ -142,7 +157,7 @@ impl Utun { unsafe { match libc::ioctl(self.fd, SIOCGIFDEVMTU, ptr::addr_of_mut!(req)) { - 0 => Ok(unsafe { req.ifr_ifru.ifru_devmtu.ifdm_current as usize }), + 0 => Ok(req.ifr_ifru.ifru_devmtu.ifdm_current as usize), _ => Err(io::Error::last_os_error()), } } @@ -157,7 +172,7 @@ impl Utun { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -177,7 +192,7 @@ impl Utun { ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 }, }; - if unsafe { libc::ioctl(self.fd, libc::SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.fd, SIOCGIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -188,7 +203,7 @@ impl Utun { } } - if unsafe { libc::ioctl(self.fd, libc::SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { + if unsafe { libc::ioctl(self.fd, SIOCSIFFLAGS, ptr::addr_of_mut!(req)) } != 0 { return Err(io::Error::last_os_error()); } @@ -197,13 +212,21 @@ impl Utun { pub fn send(&self, buf: &[u8]) -> io::Result { if buf.len() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "packet must not be empty")) + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "packet must not be empty", + )); } let family_prefix = match buf[0] & 0x0f { 0x04 => [0u8, 0, 0, 2], 0x06 => [0u8, 0, 0, 10], - _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "only IPv4 and IPv6 packets are supported over utun")), + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "only IPv4 and IPv6 packets are supported over utun", + )) + } }; let iov = [ @@ -218,7 +241,7 @@ impl Utun { ]; unsafe { - match libc::writev(self.fd, iov.as_ptr() as *const libc::iovec, iov.len()) { + match libc::writev(self.fd, iov.as_ptr() as *const libc::iovec, 2) { r @ 0.. => Ok((r as usize).saturating_sub(family_prefix.len())), _ => Err(io::Error::last_os_error()), } @@ -230,7 +253,7 @@ impl Utun { let mut iov = [ libc::iovec { iov_base: family_prefix.as_mut_ptr() as *mut libc::c_void, - iov_len: family_previx.len(), + iov_len: family_prefix.len(), }, libc::iovec { iov_base: buf.as_mut_ptr() as *mut libc::c_void, @@ -239,9 +262,12 @@ impl Utun { ]; unsafe { - match libc::readv(self.fd, iov.as_mut_ptr(), iov.len()) { + match libc::readv(self.fd, iov.as_mut_ptr(), 2) { + 0..=3 => Err(io::Error::new( + io::ErrorKind::InvalidData, + "insufficient bytes received from utun to form packet", + )), r @ 4.. => Ok((r - 4) as usize), - 0..4 => Err(io::Error::new(io::ErrorKind::InvalidData, "insufficient bytes received from utun to form packet")), _ => Err(io::Error::last_os_error()), } } @@ -286,7 +312,7 @@ impl Utun { let res = match unsafe { libc::ioctl(self.fd, SIOCIFDESTROY, ptr::addr_of_mut!(req)) } { 0 => Ok(()), - _ => Err(io::Error::last_os_error()) + _ => Err(io::Error::last_os_error()), }; Self::close_fd(self.fd); diff --git a/src/openbsd.rs b/src/openbsd.rs index 76772bf..35ae996 100644 --- a/src/openbsd.rs +++ b/src/openbsd.rs @@ -1,2 +1 @@ - // TODO: use readv/writev to overcome 4-byte header prefix diff --git a/src/tap.rs b/src/tap.rs index 721d2ce..b10fc1e 100644 --- a/src/tap.rs +++ b/src/tap.rs @@ -8,7 +8,6 @@ use crate::linux::TapImpl; use crate::macos::TapImpl; /// A cross-platform TaP interface. -#[cfg(not(target_os = "windows"))] pub struct Tap { inner: TapImpl, } diff --git a/src/wintun/adapter.rs b/src/wintun/adapter.rs index 393f1c3..665a798 100644 --- a/src/wintun/adapter.rs +++ b/src/wintun/adapter.rs @@ -1,4 +1,5 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; use std::os::windows::ffi::OsStrExt; use std::ptr::NonNull; use std::{io, ptr}; @@ -8,6 +9,7 @@ use windows_sys::core::GUID; use windows_sys::Win32::NetworkManagement::IpHelper::{ GetIfEntry, SetIfEntry, MIB_IFROW, MIB_IF_ADMIN_STATUS_DOWN, MIB_IF_ADMIN_STATUS_UP, }; +use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH; use crate::{DeviceState, Interface}; @@ -51,7 +53,7 @@ impl TunAdapter { /// Opens an existing TUN adapter. pub fn open(if_name: Interface) -> Result { let wintun = WINTUN_API.get_or_try_init(Wintun::new)?; - let name_utf16: Vec = if_name.encode_wide().collect(); + let name_utf16: Vec = if_name.name().encode_wide().collect(); let adapter = wintun.open_adapter(&name_utf16)?; Ok(Self { @@ -83,6 +85,11 @@ impl TunAdapter { self.if_name } + pub fn luid(&mut self) -> NET_LUID_LH { + self.wintun + .get_adapter_luid(unsafe { self.adapter.as_mut() }) + } + #[inline] pub fn state(&self) -> io::Result { let mut row = MIB_IFROW { diff --git a/src/wintun/dll/link.rs b/src/wintun/dll/link.rs index b6e700b..5481d7c 100644 --- a/src/wintun/dll/link.rs +++ b/src/wintun/dll/link.rs @@ -9,11 +9,9 @@ use windows_sys::core::{GUID, PCWSTR}; use windows_sys::Win32::Foundation::{BOOL, HANDLE}; use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH; -use crate::wintun::WintunError; - use super::{WintunAdapter, WintunLoggerCallback, WintunPacket, WintunSession}; -#[link(name = "wintun", kind = "dylib")] +#[link(name = "wintun", kind = "raw-dylib")] extern "C" { /// Creates a new Wintun adapter. /// @@ -146,7 +144,7 @@ extern "C" { pub struct Wintun; impl Wintun { - pub fn new() -> Result { + pub fn new() -> io::Result { Ok(Self) }