pub struct BorrowedAsyncDevice<'dev> { /* private fields */ }async_io or async_tokio only.Expand description
A borrowed asynchronous TUN/TAP device.
This type wraps an AsyncDevice but does not take ownership of the underlying file descriptor.
It’s designed for scenarios where the file descriptor is managed externally, such as:
- iOS PacketTunnelProvider (NetworkExtension framework)
- Android VpnService
- Other FFI scenarios where file descriptor ownership is managed by foreign code
§Ownership and Lifetime
Unlike AsyncDevice, BorrowedAsyncDevice:
- Does NOT close the file descriptor when dropped
- Requires the caller to manage the file descriptor’s lifetime
- Must not outlive the actual file descriptor
§Example
use tun_rs::BorrowedAsyncDevice;
// SAFETY: Caller must ensure fd is valid and remains open
let device = unsafe { BorrowedAsyncDevice::borrow_raw(fd)? };
let mut buffer = vec![0u8; 1500];
let n = device.recv(&mut buffer).await?;
println!("Received {} bytes", n);
// fd is still valid after device is dropped§Safety
When using borrow_raw, you must ensure:
- The file descriptor is valid and open
- The file descriptor is a TUN/TAP device
- The file descriptor outlives the
BorrowedAsyncDevice - No other code closes the file descriptor while in use
Implementations§
Source§impl BorrowedAsyncDevice<'_>
impl BorrowedAsyncDevice<'_>
Sourcepub unsafe fn borrow_raw(fd: RawFd) -> Result<Self>
pub unsafe fn borrow_raw(fd: RawFd) -> Result<Self>
Borrows an existing file descriptor without taking ownership.
Creates a BorrowedAsyncDevice from a raw file descriptor. The file descriptor
will not be closed when this device is dropped - the caller retains ownership
and is responsible for closing it.
§Safety
The caller must ensure that:
fdis a valid, open file descriptorfdrefers to a TUN/TAP device (not a regular file, socket, etc.)fdremains open for the lifetime of the returnedBorrowedAsyncDevice- No other code attempts to close
fdwhile the device is in use - The file descriptor is not used in conflicting ways (e.g., both blocking and non-blocking)
Violating these requirements may result in:
- I/O errors
- Undefined behavior (in case of use-after-close)
- Resource leaks (if the original fd is never closed)
§Example
use std::os::fd::RawFd;
use tun_rs::BorrowedAsyncDevice;
// Obtain fd from iOS PacketTunnelProvider or Android VpnService
let fd: RawFd = get_vpn_fd(); // exposition-only
// SAFETY: fd is valid and managed by the OS framework
let device = unsafe { BorrowedAsyncDevice::borrow_raw(fd)? };
// Use the device...
let mut buf = vec![0u8; 1500];
let n = device.recv(&mut buf).await?;
// device is dropped here, but fd remains valid
// Caller must close fd when done
§Errors
Returns an error if the file descriptor cannot be configured for async I/O. Common causes:
- Invalid file descriptor
- File descriptor does not refer to a TUN/TAP device
- Platform-specific configuration failures
Methods from Deref<Target = AsyncDevice>§
Sourcepub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll<Result<()>>
pub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll<Result<()>>
Polls the I/O handle for readability.
§Caveats
Note that on multiple calls to a poll_* method in the recv direction, only the
Waker from the Context passed to the most recent call will be scheduled to
receive a wakeup.
§Return value
The function returns:
Poll::Pendingif the device is not ready for reading.Poll::Ready(Ok(()))if the device is ready for reading.Poll::Ready(Err(e))if an error is encountered.
§Errors
This function may encounter any standard I/O error except WouldBlock.
Sourcepub fn poll_recv(
&self,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize>>
pub fn poll_recv( &self, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll<Result<usize>>
Attempts to receive a single packet from the device
§Caveats
Note that on multiple calls to a poll_* method in the recv direction, only the
Waker from the Context passed to the most recent call will be scheduled to
receive a wakeup.
§Return value
The function returns:
Poll::Pendingif the device is not ready to readPoll::Ready(Ok(()))reads databufif the device is readyPoll::Ready(Err(e))if an error is encountered.
§Errors
This function may encounter any standard I/O error except WouldBlock.
Sourcepub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll<Result<()>>
pub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll<Result<()>>
Polls the I/O handle for writability.
§Caveats
Note that on multiple calls to a poll_* method in the send direction,
only the Waker from the Context passed to the most recent call will
be scheduled to receive a wakeup.
§Return value
The function returns:
Poll::Pendingif the device is not ready for writing.Poll::Ready(Ok(()))if the device is ready for writing.Poll::Ready(Err(e))if an error is encountered.
§Errors
This function may encounter any standard I/O error except WouldBlock.
Sourcepub fn poll_send(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>
pub fn poll_send(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>
Attempts to send packet to the device
§Caveats
Note that on multiple calls to a poll_* method in the send direction,
only the Waker from the Context passed to the most recent call will
be scheduled to receive a wakeup.
§Return value
The function returns:
Poll::Pendingif the device is not available to writePoll::Ready(Ok(n))nis the number of bytes sentPoll::Ready(Err(e))if an error is encountered.
§Errors
This function may encounter any standard I/O error except WouldBlock.
Sourcepub async fn readable(&self) -> Result<()>
pub async fn readable(&self) -> Result<()>
Waits for the device to become readable.
This function is usually paired with try_recv() for manual readiness-based I/O.
The function may complete without the device being readable. This is a
false-positive and attempting a try_recv() will return with
io::ErrorKind::WouldBlock.
§Cancel safety
This method is cancel safe. Once a readiness event occurs, the method
will continue to return immediately until the readiness event is
consumed by an attempt to read that fails with WouldBlock or
Poll::Pending.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_async()?;
// Wait for the device to be readable
dev.readable().await?;
// Try to read (may still return WouldBlock)
let mut buf = vec![0u8; 1500];
match dev.try_recv(&mut buf) {
Ok(n) => println!("Read {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
println!("False positive readiness");
}
Err(e) => return Err(e),
}Sourcepub async fn writable(&self) -> Result<()>
pub async fn writable(&self) -> Result<()>
Waits for the device to become writable.
This function is usually paired with try_send() for manual readiness-based I/O.
The function may complete without the device being writable. This is a
false-positive and attempting a try_send() will return with
io::ErrorKind::WouldBlock.
§Cancel safety
This method is cancel safe. Once a readiness event occurs, the method
will continue to return immediately until the readiness event is
consumed by an attempt to write that fails with WouldBlock or
Poll::Pending.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_async()?;
// Prepare a packet
let packet = b"Hello, TUN!";
// Wait for the device to be writable
dev.writable().await?;
// Try to send (may still return WouldBlock)
match dev.try_send(packet) {
Ok(n) => println!("Sent {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
println!("False positive writability");
}
Err(e) => return Err(e),
}Sourcepub async fn recv(&self, buf: &mut [u8]) -> Result<usize>
pub async fn recv(&self, buf: &mut [u8]) -> Result<usize>
Receives a single packet from the device. On success, returns the number of bytes read.
The function must be called with valid byte array buf of sufficient
size to hold the message bytes. If a message is too long to fit in the
supplied buffer, excess bytes may be discarded.
Sourcepub fn try_recv(&self, buf: &mut [u8]) -> Result<usize>
pub fn try_recv(&self, buf: &mut [u8]) -> Result<usize>
Tries to receive a single packet from the device. On success, returns the number of bytes read.
This method must be called with valid byte array buf of sufficient size
to hold the message bytes. If a message is too long to fit in the
supplied buffer, excess bytes may be discarded.
When there is no pending data, Err(io::ErrorKind::WouldBlock) is
returned. This function is usually paired with readable().
Sourcepub async fn send(&self, buf: &[u8]) -> Result<usize>
pub async fn send(&self, buf: &[u8]) -> Result<usize>
Send a packet to the device
§Return
On success, the number of bytes sent is returned, otherwise, the encountered error is returned.
Sourcepub fn try_send(&self, buf: &[u8]) -> Result<usize>
pub fn try_send(&self, buf: &[u8]) -> Result<usize>
Tries to send packet to the device.
When the device buffer is full, Err(io::ErrorKind::WouldBlock) is
returned. This function is usually paired with writable().
§Returns
If successful, Ok(n) is returned, where n is the number of bytes
sent. If the device is not ready to send data,
Err(ErrorKind::WouldBlock) is returned.
Sourcepub async fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
pub async fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
Receives a packet into multiple buffers (scatter read). Processes single packet per call.
Sourcepub fn try_recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
pub fn try_recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
Non-blocking version of recv_vectored.
Sourcepub async fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
pub async fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
Sends multiple buffers as a single packet (gather write).
Sourcepub fn try_send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
pub fn try_send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
Non-blocking version of send_vectored.