Skip to content

Commit e29f621

Browse files
committed
Implement original_dst for windows
Signed-off-by: Keith Mattix II <[email protected]>
1 parent 3a93893 commit e29f621

File tree

3 files changed

+104
-4
lines changed

3 files changed

+104
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ include = [
2929
[package.metadata.docs.rs]
3030
all-features = true
3131
rustdoc-args = ["--cfg", "docsrs"]
32-
targets = ["aarch64-apple-ios", "aarch64-linux-android", "x86_64-apple-darwin", "x86_64-unknown-fuchsia", "x86_64-pc-windows-msvc", "x86_64-pc-solaris", "x86_64-unknown-freebsd", "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-unknown-netbsd", "x86_64-unknown-redox", "armv7-linux-androideabi", "i686-linux-android"]
32+
targets = ["aarch64-apple-ios", "aarch64-linux-android", "x86_64-apple-darwin", "x86_64-unknown-fuchsia", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnu", "x86_64-pc-solaris", "x86_64-unknown-freebsd", "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-unknown-netbsd", "x86_64-unknown-redox", "armv7-linux-androideabi", "i686-linux-android"]
3333

3434
[package.metadata.playground]
3535
features = ["all"]

src/sys/windows.rs

Lines changed: 62 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};
@@ -927,6 +929,64 @@ impl crate::Socket {
927929
}
928930
}
929931

932+
/// Get the value for the `SO_ORIGINAL_DST` option on this socket.
933+
/// Only valid for sockets in accepting mode.
934+
///
935+
/// Note: if using this function in a proxy context, you must query the
936+
/// redirect records for this socket and set them on the outbound socket
937+
/// created by your proxy in order for any OS level firewall rules to be
938+
/// applied. Read more in the Windows bind and connect redirection
939+
/// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection).
940+
#[cfg(feature = "all")]
941+
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))]
942+
pub fn original_dst(&self) -> io::Result<SockAddr> {
943+
unsafe {
944+
SockAddr::try_init(|storage, len| {
945+
syscall!(
946+
getsockopt(
947+
self.as_raw(),
948+
SOL_IP as i32,
949+
SO_ORIGINAL_DST as i32,
950+
storage.cast(),
951+
len,
952+
),
953+
PartialEq::eq,
954+
SOCKET_ERROR
955+
)
956+
})
957+
}
958+
.map(|(_, addr)| addr)
959+
}
960+
961+
/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
962+
/// Only valid for sockets in accepting mode.
963+
///
964+
/// Note: if using this function in a proxy context, you must query the
965+
/// redirect records for this socket and set them on the outbound socket
966+
/// created by your proxy in order for any OS level firewall rules to be
967+
/// applied. Read more in the Windows bind and connect redirection
968+
/// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection).
969+
#[cfg(feature = "all")]
970+
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))]
971+
pub fn original_dst_ipv6(&self) -> io::Result<SockAddr> {
972+
unsafe {
973+
SockAddr::try_init(|storage, len| {
974+
syscall!(
975+
getsockopt(
976+
self.as_raw(),
977+
SOL_IP as i32,
978+
IP6T_SO_ORIGINAL_DST as i32,
979+
storage.cast(),
980+
len,
981+
),
982+
PartialEq::eq,
983+
SOCKET_ERROR
984+
)
985+
})
986+
}
987+
.map(|(_, addr)| addr)
988+
}
989+
930990
/// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL_INFOW`
931991
/// option on this socket.
932992
///

tests/socket.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ use std::num::NonZeroUsize;
4242
use std::os::unix::io::AsRawFd;
4343
#[cfg(windows)]
4444
use std::os::windows::io::AsRawSocket;
45+
#[cfg(windows)]
46+
use windows_sys::Win32::Networking::WinSock::WSAEINVAL;
47+
4548
#[cfg(unix)]
4649
use std::path::Path;
4750
use std::str;
@@ -1607,7 +1610,27 @@ fn original_dst() {
16071610
}
16081611

16091612
#[test]
1610-
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1613+
#[cfg(all(feature = "all", target_os = "windows"))]
1614+
fn original_dst() {
1615+
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
1616+
match socket.original_dst() {
1617+
Ok(_) => panic!("original_dst on non-redirected socket should fail"),
1618+
Err(err) => assert_eq!(err.raw_os_error(), Some(WSAEINVAL)),
1619+
}
1620+
1621+
// Not supported on IPv6 socket.
1622+
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
1623+
match socket.original_dst_ipv6() {
1624+
Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"),
1625+
Err(err) => assert_eq!(err.raw_os_error(), Some(WSAEINVAL)),
1626+
}
1627+
}
1628+
1629+
#[test]
1630+
#[cfg(all(
1631+
feature = "all",
1632+
any(target_os = "android", target_os = "fuchsia", target_os = "linux")
1633+
))]
16111634
fn original_dst_ipv6() {
16121635
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
16131636
match socket.original_dst_ipv6() {
@@ -1623,6 +1646,23 @@ fn original_dst_ipv6() {
16231646
}
16241647
}
16251648

1649+
#[test]
1650+
#[cfg(all(feature = "all", target_os = "windows"))]
1651+
fn original_dst_ipv6() {
1652+
let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap();
1653+
match socket.original_dst_ipv6() {
1654+
Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"),
1655+
Err(err) => assert_eq!(err.raw_os_error(), Some(WSAEINVAL)),
1656+
}
1657+
1658+
// Not supported on IPv4 socket.
1659+
let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
1660+
match socket.original_dst_ipv6() {
1661+
Ok(_) => panic!("original_dst_ipv6 on non-redirected socket should fail"),
1662+
Err(err) => assert_eq!(err.raw_os_error(), Some(WSAEINVAL)),
1663+
}
1664+
}
1665+
16261666
#[test]
16271667
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
16281668
fn tcp_congestion() {

0 commit comments

Comments
 (0)