diff --git a/src/socket.rs b/src/socket.rs index 8d517b47..b0c2ad75 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -18,6 +18,10 @@ use std::net::{self, Ipv4Addr, Shutdown}; use std::os::unix::io::{FromRawFd, IntoRawFd}; #[cfg(windows)] use std::os::windows::io::{FromRawSocket, IntoRawSocket}; +#[cfg(windows)] +use windows_sys::Win32::Networking::WinSock::{ + IP6T_SO_ORIGINAL_DST, SOL_IP, SO_ORIGINAL_DST, SOCKET_ERROR +}; use std::time::Duration; use crate::sys::{self, c_int, getsockopt, setsockopt, Bool}; @@ -27,6 +31,47 @@ use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type}; #[cfg(not(target_os = "redox"))] use crate::{MaybeUninitSlice, MsgHdr, RecvFlags}; +#[cfg(all(feature = "all", windows))] +#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] +/// Helper macro to execute a system call that returns an `io::Result`. +macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{ + #[allow(unused_unsafe)] + let res = unsafe { windows_sys::Win32::Networking::WinSock::$fn($($arg, )*) }; + if $err_test(&res, &$err_value) { + Err(io::Error::last_os_error()) + } else { + Ok(res) + } + }}; +} + +#[cfg(all( + feature = "all", + any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", +)))] +/// Helper macro to execute a system call that returns an `io::Result`. +macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ + #[allow(unused_unsafe)] + let res = unsafe { libc::$fn($($arg, )*) }; + if res == -1 { + Err(std::io::Error::last_os_error()) + } else { + Ok(res) + } + }}; +} + /// Owned wrapper around a system socket. /// /// This type simply wraps an instance of a file descriptor (`c_int`) on Unix @@ -2205,6 +2250,120 @@ impl Socket { ) } } + + /// Get the value for the `SO_ORIGINAL_DST` option on this socket. + /// + /// This value contains the original destination IPv4 address of the connection + /// redirected using `iptables` `REDIRECT` or `TPROXY`. + #[cfg(all( + feature = "all", + any(target_os = "android", target_os = "fuchsia", target_os = "linux") + ))] + #[cfg_attr( + docsrs, + doc(cfg(all( + feature = "all", + any(target_os = "android", target_os = "fuchsia", target_os = "linux") + ))) + )] + pub fn original_dst(&self) -> io::Result { + // Safety: `getsockopt` initialises the `SockAddr` for us. + unsafe { + SockAddr::try_init(|storage, len| { + syscall!(getsockopt( + self.as_raw(), + libc::SOL_IP, + libc::SO_ORIGINAL_DST, + storage.cast(), + len + )) + }) + } + .map(|(_, addr)| addr) + } + + /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket. + /// + /// This value contains the original destination IPv6 address of the connection + /// redirected using `ip6tables` `REDIRECT` or `TPROXY`. + #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) + )] + pub fn original_dst_ipv6(&self) -> io::Result { + // Safety: `getsockopt` initialises the `SockAddr` for us. + unsafe { + SockAddr::try_init(|storage, len| { + syscall!(getsockopt( + self.as_raw(), + libc::SOL_IPV6, + libc::IP6T_SO_ORIGINAL_DST, + storage.cast(), + len + )) + }) + } + .map(|(_, addr)| addr) + } + + /// Get the value for the `SO_ORIGINAL_DST` option on this socket. + /// Only valid for sockets in accepting mode. + /// + /// Note: if using this function in a proxy context, you must query the + /// redirect records for this socket and set them on the outbound socket + /// created by your proxy in order for any OS level firewall rules to be + /// applied. Read more in the Windows bind and connect redirection + /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection). + #[cfg(all(feature = "all", target_os = "windows"))] + #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] + pub fn original_dst(&self) -> io::Result { + unsafe { + SockAddr::try_init(|storage, len| { + syscall!( + getsockopt( + self.as_raw(), + SOL_IP as i32, + SO_ORIGINAL_DST as i32, + storage.cast(), + len, + ), + PartialEq::eq, + SOCKET_ERROR + ) + }) + } + .map(|(_, addr)| addr) + } + + /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket. + /// Only valid for sockets in accepting mode. + /// + /// Note: if using this function in a proxy context, you must query the + /// redirect records for this socket and set them on the outbound socket + /// created by your proxy in order for any OS level firewall rules to be + /// applied. Read more in the Windows bind and connect redirection + /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection). + #[cfg(all(feature = "all", target_os = "windows"))] + #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] + pub fn original_dst_ipv6(&self) -> io::Result { + unsafe { + SockAddr::try_init(|storage, len| { + syscall!( + getsockopt( + self.as_raw(), + SOL_IP as i32, + IP6T_SO_ORIGINAL_DST as i32, + storage.cast(), + len, + ), + PartialEq::eq, + SOCKET_ERROR + ) + }) + } + .map(|(_, addr)| addr) + } } impl Read for Socket { diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 3a898bc3..01c62b9f 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -2402,62 +2402,6 @@ impl crate::Socket { } } - /// Get the value for the `SO_ORIGINAL_DST` option on this socket. - /// - /// This value contains the original destination IPv4 address of the connection - /// redirected using `iptables` `REDIRECT` or `TPROXY`. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn original_dst(&self) -> io::Result { - // Safety: `getsockopt` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!(getsockopt( - self.as_raw(), - libc::SOL_IP, - libc::SO_ORIGINAL_DST, - storage.cast(), - len - )) - }) - } - .map(|(_, addr)| addr) - } - - /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket. - /// - /// This value contains the original destination IPv6 address of the connection - /// redirected using `ip6tables` `REDIRECT` or `TPROXY`. - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub fn original_dst_ipv6(&self) -> io::Result { - // Safety: `getsockopt` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!(getsockopt( - self.as_raw(), - libc::SOL_IPV6, - libc::IP6T_SO_ORIGINAL_DST, - storage.cast(), - len - )) - }) - } - .map(|(_, addr)| addr) - } - /// Copies data between a `file` and this socket using the `sendfile(2)` /// system call. Because this copying is done within the kernel, /// `sendfile()` is more efficient than the combination of `read(2)` and diff --git a/src/sys/windows.rs b/src/sys/windows.rs index 3db91483..a1839e0e 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -27,9 +27,7 @@ use windows_sys::Win32::Networking::WinSock::{ WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, }; #[cfg(feature = "all")] -use windows_sys::Win32::Networking::WinSock::{ - IP6T_SO_ORIGINAL_DST, SOL_IP, SO_ORIGINAL_DST, SO_PROTOCOL_INFOW, -}; +use windows_sys::Win32::Networking::WinSock::SO_PROTOCOL_INFOW; use windows_sys::Win32::System::Threading::INFINITE; use crate::{MsgHdr, RecvFlags, SockAddr, TcpKeepalive, Type}; @@ -929,64 +927,6 @@ impl crate::Socket { } } - /// Get the value for the `SO_ORIGINAL_DST` option on this socket. - /// Only valid for sockets in accepting mode. - /// - /// Note: if using this function in a proxy context, you must query the - /// redirect records for this socket and set them on the outbound socket - /// created by your proxy in order for any OS level firewall rules to be - /// applied. Read more in the Windows bind and connect redirection - /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection). - #[cfg(feature = "all")] - #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] - pub fn original_dst(&self) -> io::Result { - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getsockopt( - self.as_raw(), - SOL_IP as i32, - SO_ORIGINAL_DST as i32, - storage.cast(), - len, - ), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) - } - - /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket. - /// Only valid for sockets in accepting mode. - /// - /// Note: if using this function in a proxy context, you must query the - /// redirect records for this socket and set them on the outbound socket - /// created by your proxy in order for any OS level firewall rules to be - /// applied. Read more in the Windows bind and connect redirection - /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection). - #[cfg(feature = "all")] - #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] - pub fn original_dst_ipv6(&self) -> io::Result { - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getsockopt( - self.as_raw(), - SOL_IP as i32, - IP6T_SO_ORIGINAL_DST as i32, - storage.cast(), - len, - ), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) - } - /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL_INFOW` /// option on this socket. /// diff --git a/tests/socket.rs b/tests/socket.rs index 69df1186..66436ab2 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -542,7 +542,7 @@ fn unix_sockets_supported() -> bool { Ok(_) => {} Err(err) if err.raw_os_error() - == Some(windows_sys::Win32::Networking::WinSock::WSAEAFNOSUPPORT as i32) => + == Some(windows_sys::Win32::Networking::WinSock::WSAEAFNOSUPPORT) => { return false; }