Skip to content

Commit acaa1a0

Browse files
committed
[#190] Supports TCP Transparent Proxy on BSD-like systems
1 parent cb89ec7 commit acaa1a0

File tree

1 file changed

+70
-72
lines changed

1 file changed

+70
-72
lines changed

src/relay/tcprelay/redir_local.rs

Lines changed: 70 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{
1010
},
1111
};
1212

13+
use cfg_if::cfg_if;
1314
use futures::future::{self, Either};
1415
use log::{debug, error, info, trace};
1516
use tokio::net::{TcpListener, TcpStream};
@@ -23,78 +24,79 @@ use crate::{
2324
},
2425
};
2526

26-
#[cfg(not(target_os = "linux"))]
27-
pub fn get_original_destination_addr(_: &mut TcpStream) -> io::Result<SocketAddr> {
28-
unimplemented!("Transparent Proxy (redir) doesn't work on this platform");
29-
}
30-
31-
#[cfg(target_os = "linux")]
32-
pub fn get_original_destination_addr(s: &mut TcpStream) -> io::Result<SocketAddr> {
33-
use std::{
34-
io::Error,
35-
mem,
36-
net::{SocketAddrV4, SocketAddrV6},
37-
os::unix::io::AsRawFd,
38-
};
39-
40-
let fd = s.as_raw_fd();
41-
42-
unsafe {
43-
let mut target_addr: libc::sockaddr_storage = mem::zeroed();
44-
let mut target_addr_len = mem::size_of_val(&target_addr) as libc::socklen_t;
45-
46-
// Check if it is IPv6 address
47-
let ret = libc::getsockopt(
48-
fd,
49-
libc::SOL_IPV6,
50-
libc::SO_ORIGINAL_DST, // FIXME: Should use IP6T_SO_ORIGINAL_DST
51-
&mut target_addr as *mut _ as *mut _,
52-
&mut target_addr_len,
53-
);
54-
55-
if ret != 0 {
56-
let err = Error::last_os_error();
57-
match err.raw_os_error() {
58-
Some(libc::ENOPROTOOPT) | Some(libc::EOPNOTSUPP) => {
59-
// The option is unknown at the level indicated.
60-
//
61-
// - The current system doesn't support IPv6 netfilter
62-
// - This is not an IPv6 connection
63-
//
64-
// Continue with IPv4
27+
cfg_if! {
28+
if #[cfg(any(target_os = "linux", target_os = "android"))] {
29+
fn get_original_destination_addr(s: &mut TcpStream) -> io::Result<SocketAddr> {
30+
use std::{
31+
io::Error,
32+
mem,
33+
net::{SocketAddrV4, SocketAddrV6},
34+
os::unix::io::AsRawFd,
35+
};
36+
let fd = s.as_raw_fd();
37+
unsafe {
38+
let mut target_addr: libc::sockaddr_storage = mem::zeroed();
39+
let mut target_addr_len = mem::size_of_val(&target_addr) as libc::socklen_t;
40+
// Check if it is IPv6 address
41+
let ret = libc::getsockopt(
42+
fd,
43+
libc::SOL_IPV6,
44+
libc::SO_ORIGINAL_DST, // FIXME: Should use IP6T_SO_ORIGINAL_DST
45+
&mut target_addr as *mut _ as *mut _,
46+
&mut target_addr_len,
47+
);
48+
if ret != 0 {
49+
let err = Error::last_os_error();
50+
match err.raw_os_error() {
51+
Some(libc::ENOPROTOOPT) | Some(libc::EOPNOTSUPP) => {
52+
// The option is unknown at the level indicated.
53+
//
54+
// - The current system doesn't support IPv6 netfilter
55+
// - This is not an IPv6 connection
56+
//
57+
// Continue with IPv4
58+
}
59+
_ => {
60+
return Err(err);
61+
}
62+
}
63+
let ret = libc::getsockopt(
64+
fd,
65+
libc::SOL_IP,
66+
libc::SO_ORIGINAL_DST,
67+
&mut target_addr as *mut _ as *mut _,
68+
&mut target_addr_len,
69+
);
70+
if ret != 0 {
71+
return Err(Error::last_os_error());
72+
}
6573
}
66-
_ => {
67-
return Err(err);
74+
// Convert sockaddr_storage to SocketAddr
75+
match target_addr.ss_family as libc::c_int {
76+
libc::AF_INET => {
77+
let addr: SocketAddrV4 = mem::transmute_copy(&target_addr);
78+
Ok(SocketAddr::V4(addr))
79+
}
80+
libc::AF_INET6 => {
81+
let addr: SocketAddrV6 = mem::transmute_copy(&target_addr);
82+
Ok(SocketAddr::V6(addr))
83+
}
84+
_ => {
85+
let err = Error::new(ErrorKind::InvalidData, "family must be either AF_INET or AF_INET6");
86+
return Err(err);
87+
}
6888
}
6989
}
70-
71-
let ret = libc::getsockopt(
72-
fd,
73-
libc::SOL_IP,
74-
libc::SO_ORIGINAL_DST,
75-
&mut target_addr as *mut _ as *mut _,
76-
&mut target_addr_len,
77-
);
78-
79-
if ret != 0 {
80-
return Err(Error::last_os_error());
81-
}
8290
}
83-
84-
// Convert sockaddr_storage to SocketAddr
85-
match target_addr.ss_family as libc::c_int {
86-
libc::AF_INET => {
87-
let addr: SocketAddrV4 = mem::transmute_copy(&target_addr);
88-
Ok(SocketAddr::V4(addr))
89-
}
90-
libc::AF_INET6 => {
91-
let addr: SocketAddrV6 = mem::transmute_copy(&target_addr);
92-
Ok(SocketAddr::V6(addr))
93-
}
94-
_ => {
95-
let err = Error::new(ErrorKind::InvalidData, "family must be either AF_INET or AF_INET6");
96-
return Err(err);
97-
}
91+
} else if #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))] {
92+
fn get_original_destination_addr(s: &mut TcpStream) -> io::Result<SocketAddr> {
93+
// On FreeBSD, Mac OS X, OpenBSD, ... use fwd action in IPFW or PF
94+
// Retrieve original destination address with getsockname()
95+
s.local_addr()
96+
}
97+
} else {
98+
fn get_original_destination_addr(_: &mut TcpStream) -> io::Result<SocketAddr> {
99+
unimplemented!("Transparent Proxy (redir) doesn't work on this platform");
98100
}
99101
}
100102
}
@@ -239,10 +241,6 @@ impl PingServer for ServerScore {
239241
}
240242

241243
pub async fn run(context: SharedContext) -> io::Result<()> {
242-
if cfg!(not(target_os = "linux")) {
243-
unimplemented!("Transparent Proxy (redir) doesn't work on this platform");
244-
}
245-
246244
assert!(
247245
context.config().mode.enable_tcp(),
248246
"You must enable TCP relay for transparent proxy"

0 commit comments

Comments
 (0)