Skip to content

Commit

Permalink
Add Socket::{header_included, set_header_included}
Browse files Browse the repository at this point in the history
Sets or gets the IP_HDRINCL socket option, only available on raw
sockets.
  • Loading branch information
Thomasdezeeuw committed Dec 17, 2021
1 parent dc6e996 commit f9e882e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 2 deletions.
40 changes: 39 additions & 1 deletion src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,44 @@ fn into_linger(duration: Option<Duration>) -> sys::linger {
/// * Linux: <https://man7.org/linux/man-pages/man7/ip.7.html>
/// * Windows: <https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options>
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<bool> {
unsafe {
getsockopt::<c_int>(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`].
Expand All @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/sys/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 25 additions & 1 deletion tests/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
};
}
Expand Down Expand Up @@ -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");
}

0 comments on commit f9e882e

Please sign in to comment.