Skip to content

Commit 33a98be

Browse files
authored
Add support for original_dst for windows (#529)
1 parent 68de29b commit 33a98be

File tree

4 files changed

+154
-64
lines changed

4 files changed

+154
-64
lines changed

src/socket.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,48 @@ impl Socket {
22252225
)
22262226
}
22272227
}
2228+
2229+
/// Get the value for the `SO_ORIGINAL_DST` option on this socket.
2230+
#[cfg(all(
2231+
feature = "all",
2232+
any(
2233+
target_os = "android",
2234+
target_os = "fuchsia",
2235+
target_os = "linux",
2236+
target_os = "windows",
2237+
)
2238+
))]
2239+
#[cfg_attr(
2240+
docsrs,
2241+
doc(cfg(all(
2242+
feature = "all",
2243+
any(
2244+
target_os = "android",
2245+
target_os = "fuchsia",
2246+
target_os = "linux",
2247+
target_os = "windows",
2248+
)
2249+
)))
2250+
)]
2251+
pub fn original_dst(&self) -> io::Result<SockAddr> {
2252+
sys::original_dst(self.as_raw())
2253+
}
2254+
2255+
/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
2256+
#[cfg(all(
2257+
feature = "all",
2258+
any(target_os = "android", target_os = "linux", target_os = "windows")
2259+
))]
2260+
#[cfg_attr(
2261+
docsrs,
2262+
doc(cfg(all(
2263+
feature = "all",
2264+
any(target_os = "android", target_os = "linux", target_os = "windows")
2265+
)))
2266+
)]
2267+
pub fn original_dst_ipv6(&self) -> io::Result<SockAddr> {
2268+
sys::original_dst_ipv6(self.as_raw())
2269+
}
22282270
}
22292271

22302272
impl Read for Socket {

src/sys/unix.rs

Lines changed: 41 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,47 @@ pub(crate) const fn to_mreqn(
14061406
}
14071407
}
14081408

1409+
#[cfg(all(
1410+
feature = "all",
1411+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1412+
))]
1413+
pub(crate) fn original_dst(fd: Socket) -> io::Result<SockAddr> {
1414+
// Safety: `getsockopt` initialises the `SockAddr` for us.
1415+
unsafe {
1416+
SockAddr::try_init(|storage, len| {
1417+
syscall!(getsockopt(
1418+
fd,
1419+
libc::SOL_IP,
1420+
libc::SO_ORIGINAL_DST,
1421+
storage.cast(),
1422+
len
1423+
))
1424+
})
1425+
}
1426+
.map(|(_, addr)| addr)
1427+
}
1428+
1429+
/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
1430+
///
1431+
/// This value contains the original destination IPv6 address of the connection
1432+
/// redirected using `ip6tables` `REDIRECT` or `TPROXY`.
1433+
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1434+
pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result<SockAddr> {
1435+
// Safety: `getsockopt` initialises the `SockAddr` for us.
1436+
unsafe {
1437+
SockAddr::try_init(|storage, len| {
1438+
syscall!(getsockopt(
1439+
fd,
1440+
libc::SOL_IPV6,
1441+
libc::IP6T_SO_ORIGINAL_DST,
1442+
storage.cast(),
1443+
len
1444+
))
1445+
})
1446+
}
1447+
.map(|(_, addr)| addr)
1448+
}
1449+
14091450
/// Unix only API.
14101451
impl crate::Socket {
14111452
/// Accept a new incoming connection from this listener.
@@ -2402,62 +2443,6 @@ impl crate::Socket {
24022443
}
24032444
}
24042445

2405-
/// Get the value for the `SO_ORIGINAL_DST` option on this socket.
2406-
///
2407-
/// This value contains the original destination IPv4 address of the connection
2408-
/// redirected using `iptables` `REDIRECT` or `TPROXY`.
2409-
#[cfg(all(
2410-
feature = "all",
2411-
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2412-
))]
2413-
#[cfg_attr(
2414-
docsrs,
2415-
doc(cfg(all(
2416-
feature = "all",
2417-
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
2418-
)))
2419-
)]
2420-
pub fn original_dst(&self) -> io::Result<SockAddr> {
2421-
// Safety: `getsockopt` initialises the `SockAddr` for us.
2422-
unsafe {
2423-
SockAddr::try_init(|storage, len| {
2424-
syscall!(getsockopt(
2425-
self.as_raw(),
2426-
libc::SOL_IP,
2427-
libc::SO_ORIGINAL_DST,
2428-
storage.cast(),
2429-
len
2430-
))
2431-
})
2432-
}
2433-
.map(|(_, addr)| addr)
2434-
}
2435-
2436-
/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
2437-
///
2438-
/// This value contains the original destination IPv6 address of the connection
2439-
/// redirected using `ip6tables` `REDIRECT` or `TPROXY`.
2440-
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
2441-
#[cfg_attr(
2442-
docsrs,
2443-
doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
2444-
)]
2445-
pub fn original_dst_ipv6(&self) -> io::Result<SockAddr> {
2446-
// Safety: `getsockopt` initialises the `SockAddr` for us.
2447-
unsafe {
2448-
SockAddr::try_init(|storage, len| {
2449-
syscall!(getsockopt(
2450-
self.as_raw(),
2451-
libc::SOL_IPV6,
2452-
libc::IP6T_SO_ORIGINAL_DST,
2453-
storage.cast(),
2454-
len
2455-
))
2456-
})
2457-
}
2458-
.map(|(_, addr)| addr)
2459-
}
2460-
24612446
/// Copies data between a `file` and this socket using the `sendfile(2)`
24622447
/// system call. Because this copying is done within the kernel,
24632448
/// `sendfile()` is more efficient than the combination of `read(2)` and

src/sys/windows.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ use std::time::{Duration, Instant};
2020
use std::{process, ptr, slice};
2121

2222
use windows_sys::Win32::Foundation::{SetHandleInformation, HANDLE, HANDLE_FLAG_INHERIT};
23-
#[cfg(feature = "all")]
24-
use windows_sys::Win32::Networking::WinSock::SO_PROTOCOL_INFOW;
2523
use windows_sys::Win32::Networking::WinSock::{
2624
self, tcp_keepalive, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0,
2725
POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, SD_BOTH, SD_RECEIVE, SD_SEND, SIO_KEEPALIVE_VALS,
2826
SOCKET_ERROR, WSABUF, WSAEMSGSIZE, WSAESHUTDOWN, WSAPOLLFD, WSAPROTOCOL_INFOW,
2927
WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED,
3028
};
29+
#[cfg(feature = "all")]
30+
use windows_sys::Win32::Networking::WinSock::{
31+
IP6T_SO_ORIGINAL_DST, SOL_IP, SO_ORIGINAL_DST, SO_PROTOCOL_INFOW,
32+
};
3133
use windows_sys::Win32::System::Threading::INFINITE;
3234

3335
use crate::{MsgHdr, RecvFlags, SockAddr, TcpKeepalive, Type};
@@ -857,6 +859,46 @@ pub(crate) fn to_mreqn(
857859
}
858860
}
859861

862+
#[cfg(feature = "all")]
863+
pub(crate) fn original_dst(socket: Socket) -> io::Result<SockAddr> {
864+
unsafe {
865+
SockAddr::try_init(|storage, len| {
866+
syscall!(
867+
getsockopt(
868+
socket,
869+
SOL_IP as i32,
870+
SO_ORIGINAL_DST as i32,
871+
storage.cast(),
872+
len,
873+
),
874+
PartialEq::eq,
875+
SOCKET_ERROR
876+
)
877+
})
878+
}
879+
.map(|(_, addr)| addr)
880+
}
881+
882+
#[cfg(feature = "all")]
883+
pub(crate) fn original_dst_ipv6(socket: Socket) -> io::Result<SockAddr> {
884+
unsafe {
885+
SockAddr::try_init(|storage, len| {
886+
syscall!(
887+
getsockopt(
888+
socket,
889+
SOL_IP as i32,
890+
IP6T_SO_ORIGINAL_DST as i32,
891+
storage.cast(),
892+
len,
893+
),
894+
PartialEq::eq,
895+
SOCKET_ERROR
896+
)
897+
})
898+
}
899+
.map(|(_, addr)| addr)
900+
}
901+
860902
#[allow(unsafe_op_in_unsafe_fn)]
861903
pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
862904
// SAFETY: a `sockaddr_storage` of all zeros is valid.

tests/socket.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,36 +1600,57 @@ fn header_included_ipv6() {
16001600
#[test]
16011601
#[cfg(all(
16021602
feature = "all",
1603-
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1603+
any(
1604+
target_os = "android",
1605+
target_os = "fuchsia",
1606+
target_os = "linux",
1607+
target_os = "windows"
1608+
)
16041609
))]
16051610
fn original_dst() {
16061611
let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
1612+
#[cfg(not(target_os = "windows"))]
1613+
let expected = Some(libc::ENOENT);
1614+
#[cfg(target_os = "windows")]
1615+
let expected = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL);
1616+
16071617
match socket.original_dst() {
16081618
Ok(_) => panic!("original_dst on non-redirected socket should fail"),
1609-
Err(err) => assert_eq!(err.raw_os_error(), Some(libc::ENOENT)),
1619+
Err(err) => assert_eq!(err.raw_os_error(), expected),
16101620
}
16111621

16121622
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
16131623
match socket.original_dst() {
16141624
Ok(_) => panic!("original_dst on non-redirected socket should fail"),
1615-
Err(err) => assert_eq!(err.raw_os_error(), Some(libc::ENOENT)),
1625+
Err(err) => assert_eq!(err.raw_os_error(), expected),
16161626
}
16171627
}
16181628

16191629
#[test]
1620-
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1630+
#[cfg(all(
1631+
feature = "all",
1632+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1633+
))]
16211634
fn original_dst_ipv6() {
16221635
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
1636+
#[cfg(not(target_os = "windows"))]
1637+
let expected = Some(libc::ENOENT);
1638+
#[cfg(target_os = "windows")]
1639+
let expected = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL);
1640+
#[cfg(not(target_os = "windows"))]
1641+
let expected_v4 = Some(libc::EOPNOTSUPP);
1642+
#[cfg(target_os = "windows")]
1643+
let expected_v4 = Some(windows_sys::Win32::Networking::WinSock::WSAEINVAL);
16231644
match socket.original_dst_ipv6() {
16241645
Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"),
1625-
Err(err) => assert_eq!(err.raw_os_error(), Some(libc::ENOENT)),
1646+
Err(err) => assert_eq!(err.raw_os_error(), expected),
16261647
}
16271648

16281649
// Not supported on IPv4 socket.
16291650
let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
16301651
match socket.original_dst_ipv6() {
16311652
Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"),
1632-
Err(err) => assert_eq!(err.raw_os_error(), Some(libc::EOPNOTSUPP)),
1653+
Err(err) => assert_eq!(err.raw_os_error(), expected_v4),
16331654
}
16341655
}
16351656

0 commit comments

Comments
 (0)