@@ -18,6 +18,10 @@ use std::net::{self, Ipv4Addr, Shutdown};
18
18
use std:: os:: unix:: io:: { FromRawFd , IntoRawFd } ;
19
19
#[ cfg( windows) ]
20
20
use std:: os:: windows:: io:: { FromRawSocket , IntoRawSocket } ;
21
+ #[ cfg( windows) ]
22
+ use windows_sys:: Win32 :: Networking :: WinSock :: {
23
+ IP6T_SO_ORIGINAL_DST , SOL_IP , SO_ORIGINAL_DST , SOCKET_ERROR
24
+ } ;
21
25
use std:: time:: Duration ;
22
26
23
27
use crate :: sys:: { self , c_int, getsockopt, setsockopt, Bool } ;
@@ -27,6 +31,47 @@ use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
27
31
#[ cfg( not( target_os = "redox" ) ) ]
28
32
use crate :: { MaybeUninitSlice , MsgHdr , RecvFlags } ;
29
33
34
+ #[ cfg( all( feature = "all" , windows) ) ]
35
+ #[ cfg_attr( docsrs, doc( cfg( all( windows, feature = "all" ) ) ) ) ]
36
+ /// Helper macro to execute a system call that returns an `io::Result`.
37
+ macro_rules! syscall {
38
+ ( $fn: ident ( $( $arg: expr) ,* $( , ) * ) , $err_test: path, $err_value: expr) => { {
39
+ #[ allow( unused_unsafe) ]
40
+ let res = unsafe { windows_sys:: Win32 :: Networking :: WinSock :: $fn( $( $arg, ) * ) } ;
41
+ if $err_test( & res, & $err_value) {
42
+ Err ( io:: Error :: last_os_error( ) )
43
+ } else {
44
+ Ok ( res)
45
+ }
46
+ } } ;
47
+ }
48
+
49
+ #[ cfg( all(
50
+ feature = "all" ,
51
+ any(
52
+ target_os = "android" ,
53
+ target_os = "dragonfly" ,
54
+ target_os = "freebsd" ,
55
+ target_os = "fuchsia" ,
56
+ target_os = "hurd" ,
57
+ target_os = "illumos" ,
58
+ target_os = "linux" ,
59
+ target_os = "netbsd" ,
60
+ target_os = "openbsd" ,
61
+ ) ) ) ]
62
+ /// Helper macro to execute a system call that returns an `io::Result`.
63
+ macro_rules! syscall {
64
+ ( $fn: ident ( $( $arg: expr) ,* $( , ) * ) ) => { {
65
+ #[ allow( unused_unsafe) ]
66
+ let res = unsafe { libc:: $fn( $( $arg, ) * ) } ;
67
+ if res == -1 {
68
+ Err ( std:: io:: Error :: last_os_error( ) )
69
+ } else {
70
+ Ok ( res)
71
+ }
72
+ } } ;
73
+ }
74
+
30
75
/// Owned wrapper around a system socket.
31
76
///
32
77
/// This type simply wraps an instance of a file descriptor (`c_int`) on Unix
@@ -2205,6 +2250,120 @@ impl Socket {
2205
2250
)
2206
2251
}
2207
2252
}
2253
+
2254
+ /// Get the value for the `SO_ORIGINAL_DST` option on this socket.
2255
+ ///
2256
+ /// This value contains the original destination IPv4 address of the connection
2257
+ /// redirected using `iptables` `REDIRECT` or `TPROXY`.
2258
+ #[ cfg( all(
2259
+ feature = "all" ,
2260
+ any( target_os = "android" , target_os = "fuchsia" , target_os = "linux" )
2261
+ ) ) ]
2262
+ #[ cfg_attr(
2263
+ docsrs,
2264
+ doc( cfg( all(
2265
+ feature = "all" ,
2266
+ any( target_os = "android" , target_os = "fuchsia" , target_os = "linux" )
2267
+ ) ) )
2268
+ ) ]
2269
+ pub fn original_dst ( & self ) -> io:: Result < SockAddr > {
2270
+ // Safety: `getsockopt` initialises the `SockAddr` for us.
2271
+ unsafe {
2272
+ SockAddr :: try_init ( |storage, len| {
2273
+ syscall ! ( getsockopt(
2274
+ self . as_raw( ) ,
2275
+ libc:: SOL_IP ,
2276
+ libc:: SO_ORIGINAL_DST ,
2277
+ storage. cast( ) ,
2278
+ len
2279
+ ) )
2280
+ } )
2281
+ }
2282
+ . map ( |( _, addr) | addr)
2283
+ }
2284
+
2285
+ /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
2286
+ ///
2287
+ /// This value contains the original destination IPv6 address of the connection
2288
+ /// redirected using `ip6tables` `REDIRECT` or `TPROXY`.
2289
+ #[ cfg( all( feature = "all" , any( target_os = "android" , target_os = "linux" ) ) ) ]
2290
+ #[ cfg_attr(
2291
+ docsrs,
2292
+ doc( cfg( all( feature = "all" , any( target_os = "android" , target_os = "linux" ) ) ) )
2293
+ ) ]
2294
+ pub fn original_dst_ipv6 ( & self ) -> io:: Result < SockAddr > {
2295
+ // Safety: `getsockopt` initialises the `SockAddr` for us.
2296
+ unsafe {
2297
+ SockAddr :: try_init ( |storage, len| {
2298
+ syscall ! ( getsockopt(
2299
+ self . as_raw( ) ,
2300
+ libc:: SOL_IPV6 ,
2301
+ libc:: IP6T_SO_ORIGINAL_DST ,
2302
+ storage. cast( ) ,
2303
+ len
2304
+ ) )
2305
+ } )
2306
+ }
2307
+ . map ( |( _, addr) | addr)
2308
+ }
2309
+
2310
+ /// Get the value for the `SO_ORIGINAL_DST` option on this socket.
2311
+ /// Only valid for sockets in accepting mode.
2312
+ ///
2313
+ /// Note: if using this function in a proxy context, you must query the
2314
+ /// redirect records for this socket and set them on the outbound socket
2315
+ /// created by your proxy in order for any OS level firewall rules to be
2316
+ /// applied. Read more in the Windows bind and connect redirection
2317
+ /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection).
2318
+ #[ cfg( all( feature = "all" , target_os = "windows" ) ) ]
2319
+ #[ cfg_attr( docsrs, doc( cfg( all( windows, feature = "all" ) ) ) ) ]
2320
+ pub fn original_dst ( & self ) -> io:: Result < SockAddr > {
2321
+ unsafe {
2322
+ SockAddr :: try_init ( |storage, len| {
2323
+ syscall ! (
2324
+ getsockopt(
2325
+ self . as_raw( ) ,
2326
+ SOL_IP as i32 ,
2327
+ SO_ORIGINAL_DST as i32 ,
2328
+ storage. cast( ) ,
2329
+ len,
2330
+ ) ,
2331
+ PartialEq :: eq,
2332
+ SOCKET_ERROR
2333
+ )
2334
+ } )
2335
+ }
2336
+ . map ( |( _, addr) | addr)
2337
+ }
2338
+
2339
+ /// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket.
2340
+ /// Only valid for sockets in accepting mode.
2341
+ ///
2342
+ /// Note: if using this function in a proxy context, you must query the
2343
+ /// redirect records for this socket and set them on the outbound socket
2344
+ /// created by your proxy in order for any OS level firewall rules to be
2345
+ /// applied. Read more in the Windows bind and connect redirection
2346
+ /// [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/using-bind-or-connect-redirection).
2347
+ #[ cfg( all( feature = "all" , target_os = "windows" ) ) ]
2348
+ #[ cfg_attr( docsrs, doc( cfg( all( windows, feature = "all" ) ) ) ) ]
2349
+ pub fn original_dst_ipv6 ( & self ) -> io:: Result < SockAddr > {
2350
+ unsafe {
2351
+ SockAddr :: try_init ( |storage, len| {
2352
+ syscall ! (
2353
+ getsockopt(
2354
+ self . as_raw( ) ,
2355
+ SOL_IP as i32 ,
2356
+ IP6T_SO_ORIGINAL_DST as i32 ,
2357
+ storage. cast( ) ,
2358
+ len,
2359
+ ) ,
2360
+ PartialEq :: eq,
2361
+ SOCKET_ERROR
2362
+ )
2363
+ } )
2364
+ }
2365
+ . map ( |( _, addr) | addr)
2366
+ }
2208
2367
}
2209
2368
2210
2369
impl Read for Socket {
0 commit comments