Skip to content

Commit

Permalink
Make sockopt_impl! public
Browse files Browse the repository at this point in the history
This allows users to define their own sockopts, instead of needing to
make a PR to Nix for every single one.

Fixes #577
  • Loading branch information
asomers committed Dec 4, 2024
1 parent f9ed9f0 commit 68a4726
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 51 deletions.
2 changes: 2 additions & 0 deletions changelog/2556.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added `sockopt_impl!` to the public API. It's now possible for users to define
their own sockopts without needing to make a PR to Nix.
149 changes: 98 additions & 51 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
//! Socket options as used by `setsockopt` and `getsockopt`.
use super::{GetSockOpt, SetSockOpt};
use crate::errno::Errno;
#[cfg(linux_android)]
use super::SetSockOpt;
#[cfg(linux_android)]
use crate::{errno::Errno, Result};
use crate::sys::time::TimeVal;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int, c_void, socklen_t};
#[cfg(apple_targets)]
use std::ffi::{CStr, CString};
#[cfg(any(target_os = "freebsd", linux_android))]
use std::ffi::{OsStr, OsString};
use std::mem::{self, MaybeUninit};
#[cfg(any(target_os = "freebsd", linux_android))]
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsFd, AsRawFd};
use std::os::unix::io::AsRawFd;
#[cfg(linux_android)]
use std::os::unix::io::AsFd;

// Constants
// TCP_CA_NAME_MAX isn't defined in user space include files
Expand All @@ -26,8 +28,8 @@ const TCP_CA_NAME_MAX: usize = 16;
/// This macro aims to help implementing `SetSockOpt` for different socket options that accept
/// different kinds of data to be used with `setsockopt`.
///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///
/// # Arguments
///
Expand All @@ -42,15 +44,22 @@ const TCP_CA_NAME_MAX: usize = 16;
/// * Type of the value that you are going to set.
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! setsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
#[allow(deprecated)] // to allow we have deprecated socket option
impl SetSockOpt for $name {
impl $crate::sys::socket::SetSockOpt for $name {
type Val = $ty;

fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> {
fn set<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
val: &$ty,
) -> $crate::Result<()> {
use $crate::sys::socket::sockopt::Set;
unsafe {
let setter: $setter = Set::new(val);
let setter: $setter =
$crate::sys::socket::sockopt::Set::new(val);

let res = libc::setsockopt(
fd.as_fd().as_raw_fd(),
Expand All @@ -59,7 +68,7 @@ macro_rules! setsockopt_impl {
setter.ffi_ptr(),
setter.ffi_len(),
);
Errno::result(res).map(drop)
$crate::errno::Errno::result(res).map(drop)
}
}
}
Expand All @@ -72,8 +81,8 @@ macro_rules! setsockopt_impl {
/// This macro aims to help implementing `GetSockOpt` for different socket options that accept
/// different kinds of data to be use with `getsockopt`.
///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///
/// # Arguments
///
Expand All @@ -88,15 +97,21 @@ macro_rules! setsockopt_impl {
/// * Type of the value that you are going to get.
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! getsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
#[allow(deprecated)] // to allow we have deprecated socket option
impl GetSockOpt for $name {
impl $crate::sys::socket::GetSockOpt for $name {
type Val = $ty;

fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> {
fn get<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
) -> $crate::Result<$ty> {
use $crate::sys::socket::sockopt::Get;
unsafe {
let mut getter: $getter = Get::uninit();
let mut getter: $getter =
$crate::sys::socket::sockopt::Get::uninit();

let res = libc::getsockopt(
fd.as_fd().as_raw_fd(),
Expand All @@ -105,7 +120,7 @@ macro_rules! getsockopt_impl {
getter.ffi_ptr(),
getter.ffi_len(),
);
Errno::result(res)?;
$crate::errno::Errno::result(res)?;

match <$ty>::try_from(getter.assume_init()) {
// In most `getsockopt_impl!` implementations, `assume_init()`
Expand All @@ -122,7 +137,7 @@ macro_rules! getsockopt_impl {
// buffer type for this socket option, see issue:
// https://github.com/nix-rust/nix/issues/1819
#[allow(unreachable_patterns)]
Err(_) => Err(Errno::EINVAL),
Err(_) => Err($crate::errno::Errno::EINVAL),
Ok(r) => Ok(r),
}
}
Expand Down Expand Up @@ -158,58 +173,59 @@ macro_rules! getsockopt_impl {
/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
// Some targets don't use all rules.
#[allow(unused_macro_rules)]
#[macro_export]
macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, bool, GetBool);
$name, GetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool);
};

($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8);
};

($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, usize, GetUsize);
$name, GetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize);
};

($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, bool, SetBool);
$name, SetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::SetBool);
};

($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::SetU8);
};

($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, usize, SetUsize);
$name, SetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::SetUsize);
};

($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, bool, GetBool, SetBool);
$name, Both, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool, $crate::sys::socket::sockopt::SetBool);
};

($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, u8, GetU8, SetU8);
$name, Both, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8, $crate::sys::socket::sockopt::SetU8);
};

($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, usize, GetUsize, SetUsize);
$name, Both, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize, $crate::sys::socket::sockopt::SetUsize);
};

($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
OsString<$array:ty>) =>
{
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OsString, GetOsString<$array>,
SetOsString);
$name, Both, $level, $flag, std::ffi::OsString, $crate::sys::socket::sockopt::GetOsString<$array>,
$crate::sys::socket::sockopt::SetOsString);
};

/*
Expand All @@ -219,7 +235,7 @@ macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
$name, GetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>);
};

($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
Expand All @@ -235,7 +251,7 @@ macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
$name, SetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::SetStruct<$ty>);
};

($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
Expand All @@ -261,8 +277,8 @@ macro_rules! sockopt_impl {

($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, $ty, GetStruct<$ty>,
SetStruct<$ty>);
$name, Both, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>,
$crate::sys::socket::sockopt::SetStruct<$ty>);
};
}

Expand Down Expand Up @@ -1437,7 +1453,9 @@ impl SetSockOpt for TcpTlsRx {
*/

/// Helper trait that describes what is expected from a `GetSockOpt` getter.
trait Get<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Get<T> {
/// Returns an uninitialized value.
fn uninit() -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
Expand All @@ -1451,7 +1469,9 @@ trait Get<T> {
}

/// Helper trait that describes what is expected from a `SetSockOpt` setter.
trait Set<'a, T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Set<'a, T> {
/// Initialize the setter with a given value.
fn new(val: &'a T) -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
Expand All @@ -1463,7 +1483,10 @@ trait Set<'a, T> {
}

/// Getter for an arbitrary `struct`.
struct GetStruct<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetStruct<T> {
len: socklen_t,
val: MaybeUninit<T>,
}
Expand Down Expand Up @@ -1495,7 +1518,10 @@ impl<T> Get<T> for GetStruct<T> {
}

/// Setter for an arbitrary `struct`.
struct SetStruct<'a, T: 'static> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct SetStruct<'a, T: 'static> {
ptr: &'a T,
}

Expand All @@ -1514,7 +1540,10 @@ impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
}

/// Getter for a boolean value.
struct GetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetBool {
len: socklen_t,
val: MaybeUninit<c_int>,
}
Expand Down Expand Up @@ -1546,7 +1575,10 @@ impl Get<bool> for GetBool {
}

/// Setter for a boolean value.
struct SetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetBool {
val: c_int,
}

Expand All @@ -1568,7 +1600,10 @@ impl<'a> Set<'a, bool> for SetBool {

/// Getter for an `u8` value.
#[cfg(feature = "net")]
struct GetU8 {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetU8 {
len: socklen_t,
val: MaybeUninit<u8>,
}
Expand Down Expand Up @@ -1601,7 +1636,9 @@ impl Get<u8> for GetU8 {
}

/// Setter for an `u8` value.
#[cfg(feature = "net")]
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct SetU8 {
val: u8,
}
Expand All @@ -1622,7 +1659,10 @@ impl<'a> Set<'a, u8> for SetU8 {
}

/// Getter for an `usize` value.
struct GetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetUsize {
len: socklen_t,
val: MaybeUninit<c_int>,
}
Expand Down Expand Up @@ -1654,7 +1694,10 @@ impl Get<usize> for GetUsize {
}

/// Setter for an `usize` value.
struct SetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetUsize {
val: c_int,
}

Expand All @@ -1673,8 +1716,10 @@ impl<'a> Set<'a, usize> for SetUsize {
}

/// Getter for a `OsString` value.
#[cfg(any(target_os = "freebsd", linux_android))]
struct GetOsString<T: AsMut<[u8]>> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetOsString<T: AsMut<[u8]>> {
len: socklen_t,
val: MaybeUninit<T>,
}
Expand Down Expand Up @@ -1704,8 +1749,10 @@ impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
}

/// Setter for a `OsString` value.
#[cfg(any(target_os = "freebsd", linux_android))]
struct SetOsString<'a> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetOsString<'a> {
val: &'a OsStr,
}

Expand All @@ -1727,14 +1774,14 @@ impl<'a> Set<'a, OsString> for SetOsString<'a> {
}

/// Getter for a `CString` value.
#[cfg(apple_targets)]
#[cfg(feature = "net")]
struct GetCString<T: AsMut<[u8]>> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetCString<T: AsMut<[u8]>> {
len: socklen_t,
val: MaybeUninit<T>,
}

#[cfg(apple_targets)]
#[cfg(feature = "net")]
impl<T: AsMut<[u8]>> Get<CString> for GetCString<T> {
fn uninit() -> Self {
Expand Down
Loading

0 comments on commit 68a4726

Please sign in to comment.