use std::net::SocketAddr;
use tokio::net::TcpStream;
use std::collections::BTreeSet;
use std::io;
use async_trait::async_trait;
#[async_trait]
pub trait OriginalDstProvider: Send + Sync {
fn get_original_dst(&self, stream: &TcpStream) -> io::Result<Option<SocketAddr>>;
fn get_listen_addrs(&self) -> BTreeSet<SocketAddr>;
}
#[cfg(all(target_os = "macos", feature = "transparent-macos"))]
pub use crate::capture::macos_pf::MacOsOriginalDstProvider;
#[cfg(target_os = "windows")]
pub use crate::capture::windows::WindowsOriginalDstProvider;
#[cfg(all(target_os = "linux", feature = "transparent-linux"))]
pub struct LinuxOriginalDstProvider {
listen_addrs: BTreeSet<SocketAddr>,
}
#[cfg(all(target_os = "linux", feature = "transparent-linux"))]
impl LinuxOriginalDstProvider {
pub fn new(listen_addrs: BTreeSet<SocketAddr>) -> Self {
Self { listen_addrs }
}
}
#[cfg(all(target_os = "linux", feature = "transparent-linux"))]
#[async_trait]
impl OriginalDstProvider for LinuxOriginalDstProvider {
fn get_original_dst(&self, stream: &TcpStream) -> io::Result<Option<SocketAddr>> {
use std::os::unix::io::AsRawFd;
let fd = stream.as_raw_fd();
unsafe {
let mut addr: libc::sockaddr_storage = std::mem::zeroed();
let mut len = std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
if libc::getsockopt(
fd,
libc::SOL_IP,
80,
&mut addr as *mut _ as *mut libc::c_void,
&mut len,
) != 0 {
return Ok(None);
}
if addr.ss_family as i32 == libc::AF_INET {
let addr_in = *(&addr as *const _ as *const libc::sockaddr_in);
let ip = std::net::Ipv4Addr::from(u32::from_be(addr_in.sin_addr.s_addr));
let port = u16::from_be(addr_in.sin_port);
Ok(Some(SocketAddr::new(std::net::IpAddr::V4(ip), port)))
} else {
Err(io::Error::new(io::ErrorKind::Unsupported, "IPv6 transparent proxy not implemented"))
}
}
}
fn get_listen_addrs(&self) -> BTreeSet<SocketAddr> {
self.listen_addrs.clone()
}
}
pub struct NoOpOriginalDstProvider {
listen_addrs: BTreeSet<SocketAddr>,
}
impl NoOpOriginalDstProvider {
pub fn new(listen_addrs: BTreeSet<SocketAddr>) -> Self {
Self { listen_addrs }
}
}
#[async_trait]
impl OriginalDstProvider for NoOpOriginalDstProvider {
fn get_original_dst(&self, _stream: &TcpStream) -> io::Result<Option<SocketAddr>> {
Ok(None)
}
fn get_listen_addrs(&self) -> BTreeSet<SocketAddr> {
self.listen_addrs.clone()
}
}