diff --git a/src/async_io/tap.rs b/src/async_io/tap.rs index 3a2fc9b..50d76fe 100644 --- a/src/async_io/tap.rs +++ b/src/async_io/tap.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(target_os = "windows")] +use std::cmp; use std::io; #[cfg(target_os = "windows")] use std::mem::ManuallyDrop; @@ -85,6 +87,7 @@ impl AsSocket for TapWrapper { /// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets. pub struct AsyncTap { + #[cfg(not(target_os = "windows"))] tap: Async, #[cfg(target_os = "windows")] tap: Async, @@ -248,15 +251,3 @@ impl AsyncTap { self.tap.read_with(|inner| inner.0.recv(buf)).await } } - -impl Drop for AsyncTap { - fn drop(&mut self) { - #[cfg(target_os = "windows")] - { - // This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd. - // SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop - let io = unsafe { ManuallyDrop::take(&mut self.io) }; - io.into_raw_fd(); - } - } -} diff --git a/src/async_io/tun.rs b/src/async_io/tun.rs index 6771ab2..cbe8128 100644 --- a/src/async_io/tun.rs +++ b/src/async_io/tun.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io; #[cfg(target_os = "windows")] -use std::mem::ManuallyDrop; +use std::cmp; +use std::io; #[cfg(not(target_os = "windows"))] use std::net::IpAddr; #[cfg(target_os = "windows")] @@ -78,12 +78,13 @@ impl TunWrapper { #[cfg(target_os = "windows")] impl AsSocket for TunWrapper { fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw(self.inner.read_handle() as RawSocket) } + unsafe { BorrowedSocket::borrow_raw(self.0.read_handle() as RawSocket) } } } /// A cross-platform asynchronous TUN interface, suitable for tunnelling network-layer packets. pub struct AsyncTun { + #[cfg(not(target_os = "windows"))] tun: Async, #[cfg(target_os = "windows")] tun: Async, @@ -102,7 +103,7 @@ impl AsyncTun { tun.set_nonblocking(true)?; Ok(Self { - tun: Async::new(TunWrapper(tun)), + tun: Async::new(TunWrapper(tun))?, }) } @@ -247,15 +248,3 @@ impl AsyncTun { self.tun.read_with(|inner| inner.0.recv(buf)).await } } - -impl Drop for AsyncTun { - fn drop(&mut self) { - #[cfg(target_os = "windows")] - { - // This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd. - // SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop - let io = unsafe { ManuallyDrop::take(&mut self.io) }; - io.into_raw_fd(); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index adc79af..5d84b57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,7 @@ // Show required OS/features on docs.rs. #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#[cfg(any(doc, all(feature = "async-io", not(target_os = "windows"))))] +#[cfg(any(doc, feature = "async-io"))] pub mod async_io; #[cfg(any(doc, target_os = "linux"))] pub mod linux; @@ -141,7 +141,7 @@ pub mod linux; pub mod macos; #[cfg(any(doc, all(feature = "mio", not(target_os = "windows"))))] pub mod mio; -#[cfg(any(doc, all(feature = "tokio", not(target_os = "windows"))))] +#[cfg(any(doc, feature = "tokio"))] pub mod tokio; #[cfg(any( doc, @@ -220,6 +220,7 @@ use rtnetlink::{ ))] use sysctl::*; +#[cfg(target_os = "linux")] const NETLINK_MAX_RECV: usize = 65536; pub type Netmask = u8; @@ -336,32 +337,36 @@ pub enum AddAddress { } impl AddAddress { + /// The IP address associated with the interface. #[inline] - fn addr(&self) -> IpAddr { + pub fn addr(&self) -> IpAddr { match self { Self::V4(a) => a.addr.into(), Self::V6(a) => a.addr.into(), } } + /// The broadcast address associated with the interface. #[inline] - fn brd(&self) -> Option { + pub fn brd(&self) -> Option { match self { Self::V4(a) => Some(a.brd?.into()), Self::V6(a) => Some(a.brd?.into()), } } + /// The point-to-point destination address associated with the interface. #[inline] - fn dst(&self) -> Option { + pub fn dst(&self) -> Option { match self { Self::V4(a) => Some(a.dst?.into()), Self::V6(a) => Some(a.dst?.into()), } } + /// The netmask associated with the interface. #[inline] - fn netmask(&self) -> Option { + pub fn netmask(&self) -> Option { match self { Self::V4(a) => a.netmask, Self::V6(a) => a.netmask, @@ -1669,7 +1674,7 @@ impl Interface { } } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] #[inline] fn close_fd(fd: RawFd) { unsafe { diff --git a/src/libc_extra.rs b/src/libc_extra.rs index c203e94..2f286f7 100644 --- a/src/libc_extra.rs +++ b/src/libc_extra.rs @@ -856,14 +856,14 @@ pub const IF_FAKE_MEDIA_LIST_MAX: usize = 27; #[cfg(target_os = "macos")] pub const SIOCIFCREATE: libc::c_ulong = _IOWR::(b'i', 120); -#[cfg(target_os = "macos")] -pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::(b'i', 122); +//#[cfg(target_os = "macos")] +//pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::(b'i', 122); #[cfg(any(target_os = "openbsd", target_os = "netbsd"))] pub const SIOCIFCREATE: libc::c_ulong = _IOW::(b'i', 122); #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::(b'i', 124); -#[cfg(target_os = "macos")] -pub const SIOCGDRVSPEC: libc::c_ulong = _IOWR::(b'i', 123); +//#[cfg(target_os = "macos")] +//pub const SIOCGDRVSPEC: libc::c_ulong = _IOWR::(b'i', 123); #[cfg(target_os = "macos")] pub const SIOCSDRVSPEC: libc::c_ulong = _IOW::(b'i', 123); #[cfg(any( diff --git a/src/macos/utun.rs b/src/macos/utun.rs index 7ef0779..f3cf9d9 100644 --- a/src/macos/utun.rs +++ b/src/macos/utun.rs @@ -41,15 +41,6 @@ pub struct iovec_const { pub iov_len: libc::size_t, } -// MacOS `route` utility uses this buffer size -#[cfg(not(doc))] -#[repr(C)] -#[allow(non_camel_case_types)] -struct rtmsg { - m_rtm: libc::rt_msghdr, - m_space: [u8; 512], -} - pub struct Utun { fd: RawFd, } diff --git a/src/mio/tap.rs b/src/mio/tap.rs index c6d8358..822fce5 100644 --- a/src/mio/tap.rs +++ b/src/mio/tap.rs @@ -28,6 +28,7 @@ use mio::{Interest, Registry, Token}; /// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets. pub struct AsyncTap { tap: Tap, + /// SAFETY: file descriptor/handle is closed when `tap` goes out of scope, so this doesn't need to. io: ManuallyDrop, } diff --git a/src/tokio/tap.rs b/src/tokio/tap.rs index c725154..8b8c34c 100644 --- a/src/tokio/tap.rs +++ b/src/tokio/tap.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(target_os = "windows")] +use std::cmp; use std::io; #[cfg(target_os = "windows")] use std::mem::ManuallyDrop; @@ -29,12 +31,30 @@ use tokio::io::Interest; #[cfg(target_os = "windows")] use tokio::net::UdpSocket; +/// A convenience type used to make internal operations consistent between Windows and Unix. +#[cfg(target_os = "windows")] +struct TapWrapper(Tap); + +#[cfg(target_os = "windows")] +impl TapWrapper { + /// Returns a reference to the underlying `Tap` function. + pub fn get_ref(&self) -> &Tap { + &self.0 + } + + /// Returns a reference to the underlying `Tap` function. + pub fn get_mut(&mut self) -> &mut Tap { + &mut self.0 + } +} + /// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets. pub struct AsyncTap { #[cfg(not(target_os = "windows"))] tap: AsyncFd, #[cfg(target_os = "windows")] - tap: Tap, + tap: TapWrapper, + /// SAFETY: file descriptor/handle is closed when `tap` goes out of scope, so this doesn't need to. #[cfg(target_os = "windows")] io: ManuallyDrop, } @@ -46,28 +66,32 @@ impl AsyncTap { Self::new_impl() } - #[cfg(target_os = "windows")] + #[cfg(not(target_os = "windows"))] fn new_impl() -> io::Result { let mut tap = Tap::new()?; tap.set_nonblocking(true)?; - // SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop() - // implementation so that the descriptor isn't closed twice. - let io = unsafe { UdpSocket::from_raw_socket(tap.read_handle() as RawSocket) }; - Ok(Self { - tap, - io: ManuallyDrop::new(io), + tap: AsyncFd::new(tap)?, }) } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "windows")] fn new_impl() -> io::Result { let mut tap = Tap::new()?; tap.set_nonblocking(true)?; + // SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop() + // implementation so that the descriptor isn't closed twice. + let io = unsafe { + UdpSocket::from_std(std::net::UdpSocket::from_raw_socket( + tap.read_handle() as RawSocket + ))? + }; + Ok(Self { - tap: AsyncFd::new(tap)?, + tap: TapWrapper(tap), + io: ManuallyDrop::new(io), }) } @@ -95,10 +119,14 @@ impl AsyncTap { // SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop() // implementation so that the descriptor isn't closed twice. #[cfg(target_os = "windows")] - let io = unsafe { UdpSocket::from_raw_socket(tun.read_handle() as RawSocket) }; + let io = unsafe { + UdpSocket::from_std(std::net::UdpSocket::from_raw_socket( + tap.read_handle() as RawSocket + ))? + }; Ok(Self { - tap, + tap: TapWrapper(tap), io: ManuallyDrop::new(io), }) } @@ -189,7 +217,7 @@ impl AsyncTap { let mut timeout = 1; // Start with 1 millisecond timeout loop { - match self.tun.send(buf) { + match self.tap.get_ref().send(buf) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { tokio::time::sleep(Duration::from_millis(timeout)).await; timeout = cmp::min(timeout * 2, SEND_MAX_BLOCKING_INTERVAL); @@ -222,22 +250,10 @@ impl AsyncTap { loop { let mut guard = self.io.readable().await?; - match guard.try_io(Interest::READABLE, |inner| self.tap.recv(buf)) { - Ok(result) => return result, - Err(_would_block) => continue, + match guard.try_io(Interest::READABLE, || self.tap.get_ref().recv(buf)) { + Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue, + res => return res, } } } } - -impl Drop for AsyncTap { - fn drop(&mut self) { - #[cfg(target_os = "windows")] - { - // This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd. - // SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop - let io = unsafe { ManuallyDrop::take(&mut self.io) }; - io.into_raw_fd(); - } - } -} diff --git a/src/tokio/tun.rs b/src/tokio/tun.rs index ff53dcb..54d83f6 100644 --- a/src/tokio/tun.rs +++ b/src/tokio/tun.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(target_os = "windows")] +use std::cmp; use std::io; #[cfg(target_os = "windows")] use std::mem::ManuallyDrop; @@ -29,12 +31,30 @@ use tokio::io::Interest; #[cfg(target_os = "windows")] use tokio::net::UdpSocket; +/// A convenience type used to make internal operations consistent between Windows and Unix. +#[cfg(target_os = "windows")] +struct TunWrapper(Tun); + +#[cfg(target_os = "windows")] +impl TunWrapper { + /// Returns a reference to the underlying `Tun` function. + pub fn get_ref(&self) -> &Tun { + &self.0 + } + + /// Returns a reference to the underlying `Tap` function. + pub fn get_mut(&mut self) -> &mut Tun { + &mut self.0 + } +} + /// A cross-platform asynchronous TUN interface, suitable for tunnelling network-layer packets. pub struct AsyncTun { #[cfg(not(target_os = "windows"))] tun: AsyncFd, #[cfg(target_os = "windows")] - tun: Tun, + tun: TunWrapper, + /// SAFETY: file descriptor/handle is closed when `tun` goes out of scope, so this doesn't need to. #[cfg(target_os = "windows")] io: ManuallyDrop, } @@ -46,28 +66,32 @@ impl AsyncTun { Self::new_impl() } - #[cfg(target_os = "windows")] + #[cfg(not(target_os = "windows"))] fn new_impl() -> io::Result { let mut tun = Tun::new()?; tun.set_nonblocking(true)?; - // SAFETY: `AsyncTun` ensures that the RawFd is extracted from `io` in its drop() - // implementation so that the descriptor isn't closed twice. - let io = unsafe { UdpSocket::from_raw_socket(tun.read_handle() as RawSocket) }; - Ok(Self { - tun, - io: ManuallyDrop::new(io), + tun: AsyncFd::new(tun)?, }) } - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "windows")] fn new_impl() -> io::Result { let mut tun = Tun::new()?; tun.set_nonblocking(true)?; + // SAFETY: `AsyncTun` ensures that the RawFd is extracted from `io` in its drop() + // implementation so that the descriptor isn't closed twice. + let io = unsafe { + UdpSocket::from_std(std::net::UdpSocket::from_raw_socket( + tun.read_handle() as RawSocket + ))? + }; + Ok(Self { - tun: AsyncFd::new(tun)?, + tun: TunWrapper(tun), + io: ManuallyDrop::new(io), }) } @@ -95,10 +119,14 @@ impl AsyncTun { // SAFETY: `AsyncTun` ensures that the RawFd is extracted from `io` in its drop() // implementation so that the descriptor isn't closed twice. #[cfg(target_os = "windows")] - let io = unsafe { UdpSocket::from_raw_socket(tun.read_handle() as RawSocket) }; + let io = unsafe { + UdpSocket::from_std(std::net::UdpSocket::from_raw_socket( + tun.read_handle() as RawSocket + ))? + }; Ok(Self { - tun, + tun: TunWrapper(tun), io: ManuallyDrop::new(io), }) } @@ -189,7 +217,7 @@ impl AsyncTun { let mut timeout = 1; // Start with 1 millisecond timeout loop { - match self.tun.send(buf) { + match self.tun.get_ref().send(buf) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { tokio::time::sleep(Duration::from_millis(timeout)).await; timeout = cmp::min(timeout * 2, SEND_MAX_BLOCKING_INTERVAL); @@ -208,7 +236,7 @@ impl AsyncTun { #[cfg(not(target_os = "windows"))] pub async fn recv_impl(&self, buf: &mut [u8]) -> io::Result { loop { - let mut guard = self.tun.writable().await?; + let mut guard = self.tun.readable().await?; match guard.try_io(|inner| inner.get_ref().recv(buf)) { Ok(result) => return result, @@ -220,24 +248,15 @@ impl AsyncTun { #[cfg(target_os = "windows")] pub async fn recv_impl(&self, buf: &mut [u8]) -> io::Result { loop { - let mut guard = self.io.readable().await?; + self.io.readable().await?; - match guard.try_io(Interest::READABLE, |inner| self.tun.recv(buf)) { - Ok(result) => return result, - Err(_would_block) => continue, + match self + .io + .try_io(Interest::READABLE, || self.tun.get_ref().recv(buf)) + { + Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue, + res => return res, } } } } - -impl Drop for AsyncTun { - fn drop(&mut self) { - #[cfg(target_os = "windows")] - { - // This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd. - // SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop - let io = unsafe { ManuallyDrop::take(&mut self.io) }; - io.into_raw_fd(); - } - } -} diff --git a/src/tun.rs b/src/tun.rs index 864b12a..aee0381 100644 --- a/src/tun.rs +++ b/src/tun.rs @@ -167,10 +167,15 @@ impl Tun { self.inner.recv(buf) } - /// The HANDLE + /// The HANDLE used to poll for incoming packet events in Windows. + /// + /// # Safety + /// + /// The returned handle must only be used in thread-safe contexts, and should not be used after + /// the `Tun` device is dropped. #[cfg(target_os = "windows")] #[inline] - pub fn read_handle(&mut self) -> HANDLE { + pub unsafe fn read_handle(&self) -> HANDLE { self.inner.read_handle() } } diff --git a/src/wintun.rs b/src/wintun.rs index 8445d67..f0fe575 100644 --- a/src/wintun.rs +++ b/src/wintun.rs @@ -165,7 +165,7 @@ impl TunImpl { } #[inline] - pub fn read_handle(&mut self) -> HANDLE { + pub fn read_handle(&self) -> HANDLE { TunSession::read_handle_impl(&self.adapter, self.session.as_ptr()) } }