Skip to main content

SyncDevice

Struct SyncDevice 

Source
pub struct SyncDevice(/* private fields */);
Expand description

A transparent wrapper around DeviceImpl, providing synchronous I/O operations.

§Examples

Basic read/write operation:

use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;

fn main() -> std::io::Result<()> {
    // Create a TUN device using the builder
    let mut tun = DeviceBuilder::new()
        .name("my-tun")
        .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
        .build_sync()?;

    // Send a packet
    // Example IP packet (Replace with real IP message)
    let packet = b"[IP Packet: 10.0.0.1 -> 10.0.0.2] Hello, TUN!";
    tun.send(packet)?;
    println!("Sent {} bytes IP packet", packet.len());

    // Receive a packet
    let mut buf = [0u8; 1500];
    let n = tun.recv(&mut buf)?;
    println!("Received {} bytes: {:?}", n, &buf[..n]);

    Ok(())
}

Implementations§

Source§

impl SyncDevice

Source

pub unsafe fn from_fd(fd: RawFd) -> Result<Self>

Available on Unix only.

Creates a SyncDevice from a raw file descriptor.

§Safety
  • The file descriptor (fd) must be an owned file descriptor.
  • It must be valid and open.
  • The file descriptor must refer to a TUN/TAP device.
  • After calling this function, the SyncDevice takes ownership of the fd and will close it when dropped.

This function is only available on Unix platforms.

§Example

On iOS using PacketTunnelProvider:

use std::os::fd::RawFd;
use tun_rs::SyncDevice;

// On iOS, obtain fd from PacketTunnelProvider.packetFlow
// let fd: RawFd = packet_flow.value(forKeyPath: "socket.fileDescriptor") as! Int32
let fd: RawFd = 10; // Example value - obtain from platform VPN APIs

// SAFETY: fd must be a valid, open file descriptor to a TUN device
let dev = unsafe { SyncDevice::from_fd(fd)? };

// Device now owns the file descriptor
let mut buf = [0u8; 1500];
let n = dev.recv(&mut buf)?;
println!("Received {} bytes", n);

On Android using VpnService:

use tun_rs::SyncDevice;

// On Android, obtain fd from VpnService.Builder.establish()
// ParcelFileDescriptor vpnInterface = builder.establish();
// int fd = vpnInterface.getFd();
let fd = 10; // Example value - obtain from VpnService

// SAFETY: fd must be valid and open
let dev = unsafe { SyncDevice::from_fd(fd)? };

let mut buf = [0u8; 1500];
loop {
    let n = dev.recv(&mut buf)?;
    // Process packet...
}
Source

pub fn recv(&self, buf: &mut [u8]) -> Result<usize>

Receives data from the device into the provided buffer.

Returns the number of bytes read, or an I/O error.

§Example
use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;
let mut tun = DeviceBuilder::new()
    .name("my-tun")
    .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
    .build_sync()
    .unwrap();
let mut buf = [0u8; 1500];
tun.recv(&mut buf).unwrap();
§Note

Blocking the current thread if no packet is available

Source

pub fn send(&self, buf: &[u8]) -> Result<usize>

Sends data from the provided buffer to the device.

Returns the number of bytes written, or an I/O error.

§Example
use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;
let mut tun = DeviceBuilder::new()
    .name("my-tun")
    .ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
    .build_sync()
    .unwrap();
tun.send(b"hello").unwrap();
Source

pub fn shutdown(&self) -> Result<()>

Available on Unix and crate feature experimental only.
Source

pub fn recv_intr(&self, buf: &mut [u8], event: &InterruptEvent) -> Result<usize>

Available on crate feature interruptible only.

Reads data into the provided buffer, with support for interruption.

This function attempts to read from the underlying file descriptor into buf, and can be interrupted using the given InterruptEvent. If the event is triggered while the read operation is blocked, the function will return early with an error of kind std::io::ErrorKind::Interrupted.

§Arguments
  • buf - The buffer to store the read data.
  • event - An InterruptEvent used to interrupt the blocking read.
§Returns

On success, returns the number of bytes read. On failure, returns an std::io::Error.

§Platform-specific Behavior

On Unix platforms, it is recommended to use this together with set_nonblocking(true). Without setting non-blocking mode, concurrent reads may not respond properly to interrupt signals.

§Feature

This method is only available when the interruptible feature is enabled.

Source

pub fn recv_intr_timeout( &self, buf: &mut [u8], event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<usize>

Available on crate feature interruptible only.

Like recv_intr, but with an optional timeout.

This function reads data from the device into the provided buffer, but can be interrupted by the given event or by the timeout expiring.

§Arguments
  • buf - The buffer to store the read data
  • event - The interrupt event that can cancel the operation
  • timeout - Optional duration to wait before returning with a timeout error
§Returns
  • Ok(n) - Successfully read n bytes
  • Err(e) with ErrorKind::Interrupted - Operation was interrupted by the event
  • Err(e) with ErrorKind::TimedOut - Timeout expired before data was available
§Example
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

let event = InterruptEvent::new()?;
let mut buf = vec![0u8; 1500];

// Read with a 5-second timeout
match dev.recv_intr_timeout(&mut buf, &event, Some(Duration::from_secs(5))) {
    Ok(n) => println!("Received {} bytes", n),
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        println!("Timed out waiting for data");
    }
    Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
        println!("Interrupted by event");
    }
    Err(e) => return Err(e),
}
§Feature

This method is only available when the interruptible feature is enabled.

Source

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

Available on Unix and crate feature interruptible only.

Like recv_intr, but reads into multiple buffers.

This function behaves the same as recv_intr, but uses readv to fill the provided set of non-contiguous buffers.

§Feature

This method is only available when the interruptible feature is enabled.

Source

pub fn recv_vectored_intr_timeout( &self, bufs: &mut [IoSliceMut<'_>], event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<usize>

Available on Unix and crate feature interruptible only.

Like recv_vectored_intr, but with an optional timeout.

This function reads data from the device into multiple buffers using vectored I/O, but can be interrupted by the given event or by the timeout expiring.

§Arguments
  • bufs - Multiple buffers to store the read data
  • event - The interrupt event that can cancel the operation
  • timeout - Optional duration to wait before returning with a timeout error
§Returns
  • Ok(n) - Successfully read n bytes total across all buffers
  • Err(e) with ErrorKind::Interrupted - Operation was interrupted by the event
  • Err(e) with ErrorKind::TimedOut - Timeout expired before data was available
§Example
use std::io::IoSliceMut;
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

let event = InterruptEvent::new()?;
let mut header = [0u8; 20];
let mut payload = [0u8; 1480];
let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];

// Read with timeout into multiple buffers
match dev.recv_vectored_intr_timeout(&mut bufs, &event, Some(Duration::from_secs(5))) {
    Ok(n) => println!("Received {} bytes", n),
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        println!("Timed out");
    }
    Err(e) => return Err(e),
}
§Feature

This method is only available when the interruptible feature is enabled.

Source

pub fn wait_readable_intr(&self, event: &InterruptEvent) -> Result<()>

Available on crate feature interruptible only.
Source

pub fn wait_readable_intr_timeout( &self, event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<()>

Available on crate feature interruptible only.

Like wait_readable_intr, but with an optional timeout.

This function waits until the device becomes readable, but can be interrupted by the given event or by the timeout expiring.

§Arguments
  • event - The interrupt event that can cancel the wait
  • timeout - Optional duration to wait before returning with a timeout error
§Returns
  • Ok(()) - Device is now readable
  • Err(e) with ErrorKind::Interrupted - Wait was interrupted by the event
  • Err(e) with ErrorKind::TimedOut - Timeout expired
§Example
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

let event = InterruptEvent::new()?;

// Wait for readability with timeout
match dev.wait_readable_intr_timeout(&event, Some(Duration::from_secs(10))) {
    Ok(()) => {
        println!("Device is readable");
        // Now try to read...
    }
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        println!("Timed out waiting for data");
    }
    Err(e) => return Err(e),
}
§Feature

This method is only available when the interruptible feature is enabled.

Source

pub fn send_intr(&self, buf: &[u8], event: &InterruptEvent) -> Result<usize>

Available on crate feature interruptible only.
Source

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

Available on Unix and crate feature interruptible only.

Sends data to the device from multiple buffers using vectored I/O, with interruption support.

Like send_intr, but uses writev to send from multiple non-contiguous buffers in a single operation.

§Arguments
  • bufs - Multiple buffers containing the data to send
  • event - The interrupt event that can cancel the operation
§Returns
  • Ok(n) - Successfully sent n bytes total from all buffers
  • Err(e) with ErrorKind::Interrupted - Operation was interrupted by the event
§Example
use std::io::IoSlice;
use tun_rs::{DeviceBuilder, InterruptEvent};

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

let event = InterruptEvent::new()?;
let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
let payload = b"Hello, TUN!";
let bufs = [IoSlice::new(&header), IoSlice::new(payload)];

match dev.send_vectored_intr(&bufs, &event) {
    Ok(n) => println!("Sent {} bytes", n),
    Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
        println!("Send was interrupted");
    }
    Err(e) => return Err(e),
}
§Feature

This method is only available when the interruptible feature is enabled.

Source

pub fn wait_writable_intr(&self, event: &InterruptEvent) -> Result<()>

Available on Unix and crate feature interruptible only.

Waits for the device to become writable, with interruption support.

This function waits until the device is ready to accept data for sending, but can be interrupted by the given event.

§Arguments
  • event - The interrupt event that can cancel the wait
§Returns
  • Ok(()) - Device is now writable
  • Err(e) with ErrorKind::Interrupted - Wait was interrupted by the event
§Example
use tun_rs::{DeviceBuilder, InterruptEvent};

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

let event = InterruptEvent::new()?;

// Wait for device to be writable
match dev.wait_writable_intr(&event) {
    Ok(()) => {
        println!("Device is writable");
        // Now send data...
    }
    Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
        println!("Wait was interrupted");
    }
    Err(e) => return Err(e),
}
§Feature

This method is only available when the interruptible feature is enabled.

Source

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

Available on Unix only.

Receives data from the device into multiple buffers using vectored I/O.

Note: This method operates on a single packet only. It will only read data from one packet, even if multiple buffers are provided.

Returns the total number of bytes read from the packet, or an error.

§Example
use std::io::IoSliceMut;
use tun_rs::DeviceBuilder;

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

// Prepare multiple buffers for receiving data
let mut header = [0u8; 20];
let mut payload = [0u8; 1480];
let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];

// Read one packet into the buffers
let n = dev.recv_vectored(&mut bufs)?;
println!("Received {} bytes total", n);
Source

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

Available on Unix only.

Sends data to the device from multiple buffers using vectored I/O.

Note: This method operates on a single packet only. It will only send the data contained in the provided buffers as one packet.

Returns the total number of bytes written for the packet, or an error.

§Example
use std::io::IoSlice;
use tun_rs::DeviceBuilder;

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

// Send a packet with header and payload in separate buffers
let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
let payload = b"Hello, TUN!";
let bufs = [IoSlice::new(&header), IoSlice::new(payload)];

let n = dev.send_vectored(&bufs)?;
println!("Sent {} bytes", n);
Source

pub fn is_nonblocking(&self) -> Result<bool>

Available on Unix only.

Checks whether the device is currently operating in nonblocking mode.

Returns true if nonblocking mode is enabled, false otherwise, or an error.

§Example
use tun_rs::DeviceBuilder;

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

// Check current nonblocking mode
if dev.is_nonblocking()? {
    println!("Device is in nonblocking mode");
} else {
    println!("Device is in blocking mode");
}
Source

pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()>

Available on Unix only.

Sets the nonblocking mode for the device.

  • nonblocking: Pass true to enable nonblocking mode, false to disable.

Returns an empty result on success or an I/O error.

§Example
use tun_rs::DeviceBuilder;

let dev = DeviceBuilder::new()
    .ipv4("10.0.0.1", 24, None)
    .build_sync()?;

// Enable nonblocking mode for non-blocking I/O
dev.set_nonblocking(true)?;

// Now recv() will return WouldBlock if no data is available
let mut buf = [0u8; 1500];
match dev.recv(&mut buf) {
    Ok(n) => println!("Received {} bytes", n),
    Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
        println!("No data available");
    }
    Err(e) => return Err(e),
}

Trait Implementations§

Source§

impl AsFd for SyncDevice

Available on Unix only.
Source§

fn as_fd(&self) -> BorrowedFd<'_>

Borrows the file descriptor. Read more
Source§

impl AsRawFd for SyncDevice

Available on Unix only.
Source§

fn as_raw_fd(&self) -> RawFd

Extracts the raw file descriptor. Read more
Source§

impl Deref for SyncDevice

Source§

type Target = DeviceImpl

The resulting type after dereferencing.
Source§

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

Dereferences the value.
Source§

impl FromRawFd for SyncDevice

Available on Unix only.
Source§

unsafe fn from_raw_fd(fd: RawFd) -> Self

Constructs a new instance of Self from the given raw file descriptor. Read more
Source§

impl IntoRawFd for SyncDevice

Available on Unix only.
Source§

fn into_raw_fd(self) -> RawFd

Consumes this object, returning the raw underlying file descriptor. Read more

Auto Trait Implementations§

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.