#![allow(clippy::module_name_repetitions)]
use std::net::UdpSocket;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use tokio::net::TcpListener;
use crate::error::{Error, ErrorKind};
#[cfg(unix)]
fn bind_device(socket_fd: RawFd, device: &str) -> Result<(), Error> {
if !device.is_empty() {
unsafe {
#[allow(clippy::cast_possible_truncation)]
let socket_len = device.len() as nc::socklen_t;
nc::setsockopt(
socket_fd,
nc::SOL_SOCKET,
nc::SO_BINDTODEVICE,
device.as_ptr() as usize,
socket_len,
)
.map_err(|errno| {
Error::from_string(
ErrorKind::KernelError,
format!(
"Failed to bind device: {}, err: {}",
device,
nc::strerror(errno)
),
)
})?;
}
}
Ok(())
}
#[cfg(unix)]
fn enable_fast_open(socket_fd: RawFd) -> Result<(), Error> {
#[cfg(unix)]
let queue_len: i32 = 5;
#[cfg(not(unix))]
let queue_len: i32 = 1;
let queue_len_ptr = std::ptr::addr_of!(queue_len) as usize;
unsafe {
#[allow(clippy::cast_possible_truncation)]
let len = std::mem::size_of_val(&queue_len) as u32;
nc::setsockopt(
socket_fd,
nc::IPPROTO_TCP,
nc::TCP_FASTOPEN,
queue_len_ptr,
len,
)
.map_err(|errno| {
Error::from_string(
ErrorKind::KernelError,
format!(
"Failed to enable socket fast open, got err: {}",
nc::strerror(errno)
),
)
})
}
}
#[cfg(unix)]
pub async fn new_tcp_listener(address: &str, device: &str) -> Result<TcpListener, Error> {
let listener = TcpListener::bind(address).await?;
let socket_fd: RawFd = listener.as_raw_fd();
bind_device(socket_fd, device)?;
enable_fast_open(socket_fd)?;
Ok(listener)
}
#[cfg(not(unix))]
pub async fn new_tcp_listener(address: &str, _device: &str) -> Result<TcpListener, Error> {
let listener = TcpListener::bind(address).await?;
Ok(listener)
}
#[cfg(unix)]
pub fn new_udp_socket(address: &str, device: &str) -> Result<UdpSocket, Error> {
let socket = UdpSocket::bind(address)?;
let socket_fd: RawFd = socket.as_raw_fd();
bind_device(socket_fd, device)?;
Ok(socket)
}
#[cfg(not(unix))]
pub fn new_udp_socket(address: &str, _device: &str) -> Result<UdpSocket, Error> {
let socket = UdpSocket::bind(address)?;
Ok(socket)
}