diff --git a/src/socket.rs b/src/socket.rs index 30dcbc40..9c83475d 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -991,6 +991,44 @@ fn into_linger(duration: Option) -> sys::linger { /// * Linux: /// * Windows: impl Socket { + /// Get the value of the `IP_HDRINCL` option on this socket. + /// + /// For more information about this option, see [`set_header_included`]. + /// + /// [`set_header_included`]: Socket::set_header_included + #[cfg(all(feature = "all", not(target_os = "redox")))] + #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))] + pub fn header_included(&self) -> io::Result { + unsafe { + getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_HDRINCL) + .map(|included| included != 0) + } + } + + /// Set the value of the `IP_HDRINCL` option on this socket. + /// + /// If enabled, the user supplies an IP header in front of the user data. + /// Valid only for [`SOCK_RAW`] sockets; see [raw(7)] for more information. + /// When this flag is enabled, the values set by `IP_OPTIONS`, [`IP_TTL`], + /// and [`IP_TOS`] are ignored. + /// + /// [`SOCK_RAW`]: Type::RAW + /// [raw(7)]: https://man7.org/linux/man-pages/man7/raw.7.html + /// [`IP_TTL`]: Socket::set_ttl + /// [`IP_TOS`]: Socket::set_tos + #[cfg(all(feature = "all", not(target_os = "redox")))] + #[cfg_attr(docsrs, doc(all(feature = "all", not(target_os = "redox"))))] + pub fn set_header_included(&self, included: bool) -> io::Result<()> { + unsafe { + setsockopt( + self.as_raw(), + sys::IPPROTO_IP, + sys::IP_HDRINCL, + included as c_int, + ) + } + } + /// Get the value of the `IP_TRANSPARENT` option on this socket. /// /// For more information about this option, see [`set_ip_transparent`]. @@ -1016,7 +1054,7 @@ impl Socket { /// are routed through the TProxy box (i.e., the system /// hosting the application that employs the IP_TRANSPARENT /// socket option). Enabling this socket option requires - /// superuser privileges (the CAP_NET_ADMIN capability). + /// superuser privileges (the `CAP_NET_ADMIN` capability). /// /// TProxy redirection with the iptables TPROXY target also /// requires that this option be set on the redirected socket. diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 7e3250d6..0514ee2d 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -75,6 +75,8 @@ pub(crate) use libc::{ #[cfg(not(target_os = "redox"))] pub(crate) use libc::{MSG_TRUNC, SO_OOBINLINE}; // Used in `Socket`. +#[cfg(all(feature = "all", not(target_os = "redox")))] +pub(crate) use libc::IP_HDRINCL; #[cfg(not(any( target_os = "fuschia", target_os = "redox", diff --git a/src/sys/windows.rs b/src/sys/windows.rs index 941cb630..6652105c 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -66,6 +66,8 @@ pub(crate) use winapi::shared::ws2def::{ IPPROTO_IP, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY, }; +#[cfg(feature = "all")] +pub(crate) use winapi::shared::ws2ipdef::IP_HDRINCL; pub(crate) use winapi::shared::ws2ipdef::{ IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, diff --git a/tests/socket.rs b/tests/socket.rs index c2d8c195..4ab875e0 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1082,11 +1082,11 @@ macro_rules! test { let initial = socket.$get_fn().expect("failed to get initial value"); let arg = $arg; - let expected = $expected; assert_ne!(initial, arg, "initial value and argument are the same"); socket.$set_fn(arg).expect("failed to set option"); let got = socket.$get_fn().expect("failed to get value"); + let expected = $expected; assert_eq!(got, expected, "set and get values differ"); }; } @@ -1174,3 +1174,27 @@ test!( tcp_user_timeout, set_tcp_user_timeout(Some(Duration::from_secs(10))) ); + +#[test] +#[cfg(all(feature = "all", not(target_os = "redox")))] +fn header_included() { + let socket = match Socket::new(Domain::IPV4, Type::RAW, None) { + Ok(socket) => socket, + // Need certain permissions to create a raw sockets. + Err(ref err) if err.kind() == io::ErrorKind::PermissionDenied => return, + #[cfg(unix)] + Err(ref err) if err.raw_os_error() == Some(libc::EPROTONOSUPPORT) => return, + Err(err) => panic!("unexpected error creating socket: {}", err), + }; + + let initial = socket + .header_included() + .expect("failed to get initial value"); + assert_eq!(initial, false, "initial value and argument are the same"); + + socket + .set_header_included(true) + .expect("failed to set option"); + let got = socket.header_included().expect("failed to get value"); + assert_eq!(got, true, "set and get values differ"); +}