diff --git a/src/adapters/tcp.rs b/src/adapters/tcp.rs index 9e9fc5a..66aec3b 100644 --- a/src/adapters/tcp.rs +++ b/src/adapters/tcp.rs @@ -10,7 +10,11 @@ use mio::event::{Source}; use socket2::{Socket, Domain, TcpKeepalive, Type, Protocol}; use std::net::{SocketAddr}; +#[cfg(unix)] +use std::ffi::{CString}; use std::io::{self, ErrorKind, Read, Write}; +#[cfg(target_os = "macos")] +use std::num::NonZeroU32; use std::ops::{Deref}; use std::mem::{forget, MaybeUninit}; #[cfg(target_os = "windows")] @@ -25,11 +29,19 @@ pub const INPUT_BUFFER_SIZE: usize = u16::MAX as usize; // 2^16 - 1 #[derive(Clone, Debug, Default)] pub struct TcpConnectConfig { + bind_device: Option, source_address: Option, keepalive: Option, } impl TcpConnectConfig { + /// Bind the TCP connection to a specific interface, identified by its name. On other systems + /// this opion will be ignored. + pub fn with_bind_device(mut self, device: String) -> Self { + self.bind_device = Some(device); + self + } + /// Enables TCP keepalive settings on the socket. pub fn with_keepalive(mut self, keepalive: TcpKeepalive) -> Self { self.keepalive = Some(keepalive); @@ -98,6 +110,25 @@ impl Remote for RemoteResource { socket.bind(&source_address.into())?; } + #[cfg(unix)] + if let Some(bind_device) = config.bind_device { + let device = CString::new(bind_device)?; + + #[cfg(not(target_os = "macos"))] + socket.bind_device(Some(device.as_bytes()))?; + + #[cfg(target_os = "macos")] + match NonZeroU32::new(unsafe { libc::if_nametoindex(device.as_ptr()) }) { + Some(index) => socket.bind_device_by_index(Some(index))?, + None => { + return Err(io::Error::new( + ErrorKind::NotFound, + "Bind device interface not found", + )) + } + } + } + match socket.connect(&peer_addr.into()) { #[cfg(unix)] Err(e) if e.raw_os_error() != Some(libc::EINPROGRESS) => return Err(e),