Skip to main content

BorrowedAsyncDevice

Struct BorrowedAsyncDevice 

Source
pub struct BorrowedAsyncDevice<'dev> { /* private fields */ }
Available on crate features 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:

  1. The file descriptor is valid and open
  2. The file descriptor is a TUN/TAP device
  3. The file descriptor outlives the BorrowedAsyncDevice
  4. No other code closes the file descriptor while in use

Implementations§

Source§

impl BorrowedAsyncDevice<'_>

Source

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:

  • fd is a valid, open file descriptor
  • fd refers to a TUN/TAP device (not a regular file, socket, etc.)
  • fd remains open for the lifetime of the returned BorrowedAsyncDevice
  • No other code attempts to close fd while 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>§

Source

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::Pending if 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.

Source

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::Pending if the device is not ready to read
  • Poll::Ready(Ok(())) reads data buf if the device is ready
  • Poll::Ready(Err(e)) if an error is encountered.
§Errors

This function may encounter any standard I/O error except WouldBlock.

Source

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::Pending if 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.

Source

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::Pending if the device is not available to write
  • Poll::Ready(Ok(n)) n is the number of bytes sent
  • Poll::Ready(Err(e)) if an error is encountered.
§Errors

This function may encounter any standard I/O error except WouldBlock.

Source

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),
}
Source

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),
}
Source

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.

Source

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().

Source

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.

Source

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.

Source

pub async fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>

Receives a packet into multiple buffers (scatter read). Processes single packet per call.

Source

pub fn try_recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>

Non-blocking version of recv_vectored.

Source

pub async fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>

Sends multiple buffers as a single packet (gather write).

Source

pub fn try_send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>

Non-blocking version of send_vectored.

Trait Implementations§

Source§

impl Deref for BorrowedAsyncDevice<'_>

Source§

type Target = AsyncDevice

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.

Auto Trait Implementations§

§

impl<'dev> !Freeze for BorrowedAsyncDevice<'dev>

§

impl<'dev> !RefUnwindSafe for BorrowedAsyncDevice<'dev>

§

impl<'dev> Send for BorrowedAsyncDevice<'dev>

§

impl<'dev> Sync for BorrowedAsyncDevice<'dev>

§

impl<'dev> Unpin for BorrowedAsyncDevice<'dev>

§

impl<'dev> !UnwindSafe for BorrowedAsyncDevice<'dev>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.