Skip to content

Commit

Permalink
Merge #1482
Browse files Browse the repository at this point in the history
1482: Add support for LOCAL_PEER_CRED r=asomers a=asomers

On FreeBSD and its derivatives, this socket option gets the credentials
of the connected peer.

Co-authored-by: Alan Somers <asomers@gmail.com>
  • Loading branch information
bors[bot] and asomers committed Aug 11, 2021
2 parents a7e86b2 + 0d3bc08 commit 06b5d33
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased] - ReleaseDate
### Added

- Added the `LocalPeerCred` sockopt.
(#[1482](https://github.com/nix-rust/nix/pull/1482))
- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
(#[1465](https://github.com/nix-rust/nix/pull/1465))
- Added `IPV6_V6ONLY` sockopt.
Expand Down
32 changes: 32 additions & 0 deletions src/sys/socket/mod.rs
Expand Up @@ -358,6 +358,38 @@ cfg_if! {
}
}

cfg_if!{
if #[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))] {
/// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct XuCred(libc::xucred);

impl XuCred {
/// Structure layout version
pub fn version(&self) -> u32 {
self.0.cr_version
}

/// Effective user ID
pub fn uid(&self) -> libc::uid_t {
self.0.cr_uid
}

/// Returns a list of group identifiers (the first one being the
/// effective GID)
pub fn groups(&self) -> &[libc::gid_t] {
&self.0.cr_groups
}
}
}
}

/// Request for multicast socket operations
///
/// This is a wrapper type around `ip_mreq`.
Expand Down
48 changes: 28 additions & 20 deletions src/sys/socket/sockopt.rs
Expand Up @@ -30,7 +30,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// # Arguments
///
/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
Expand All @@ -41,7 +41,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
macro_rules! setsockopt_impl {
($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
type Val = $ty;

Expand Down Expand Up @@ -82,7 +82,7 @@ macro_rules! setsockopt_impl {
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
macro_rules! getsockopt_impl {
($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
type Val = $ty;

Expand Down Expand Up @@ -117,7 +117,7 @@ macro_rules! getsockopt_impl {
/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
/// both of them.
/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
Expand All @@ -128,81 +128,81 @@ macro_rules! getsockopt_impl {
/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
macro_rules! sockopt_impl {
(GetOnly, $name:ident, $level:path, $flag:path, bool) => {
(GetOnly, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool);
};

(GetOnly, $name:ident, $level:path, $flag:path, u8) => {
(GetOnly, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8);
};

(GetOnly, $name:ident, $level:path, $flag:path, usize) => {
(GetOnly, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize);
};

(SetOnly, $name:ident, $level:path, $flag:path, bool) => {
(SetOnly, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool);
};

(SetOnly, $name:ident, $level:path, $flag:path, u8) => {
(SetOnly, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8);
};

(SetOnly, $name:ident, $level:path, $flag:path, usize) => {
(SetOnly, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize);
};

(Both, $name:ident, $level:path, $flag:path, bool) => {
(Both, $name:ident, $level:expr, $flag:path, bool) => {
sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool);
};

(Both, $name:ident, $level:path, $flag:path, u8) => {
(Both, $name:ident, $level:expr, $flag:path, u8) => {
sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8);
};

(Both, $name:ident, $level:path, $flag:path, usize) => {
(Both, $name:ident, $level:expr, $flag:path, usize) => {
sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize);
};

(Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => {
(Both, $name:ident, $level:expr, $flag:path, OsString<$array:ty>) => {
sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString);
};

/*
* Matchers with generic getter types must be placed at the end, so
* they'll only match _after_ specialized matchers fail
*/
(GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
(GetOnly, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>);
};

(GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
(GetOnly, $name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;

getsockopt_impl!($name, $level, $flag, $ty, $getter);
};

(SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
(SetOnly, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>);
};

(SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
(SetOnly, $name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;

setsockopt_impl!($name, $level, $flag, $ty, $setter);
};

(Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
(Both, $name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;

setsockopt_impl!($name, $level, $flag, $ty, $setter);
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};

(Both, $name:ident, $level:path, $flag:path, $ty:ty) => {
(Both, $name:ident, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>);
};
}
Expand Down Expand Up @@ -246,6 +246,14 @@ sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32);
sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))]
// Get the credentials of the peer process of a connected unix domain socket.
sockopt_impl!(GetOnly, LocalPeerCred, 0, libc::LOCAL_PEERCRED, super::XuCred);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
#[cfg(any(target_os = "ios",
Expand Down
41 changes: 41 additions & 0 deletions test/sys/test_sockopt.rs
Expand Up @@ -3,6 +3,47 @@ use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, S
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::*;

// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
))]
#[test]
pub fn test_local_peercred_seqpacket() {
use nix::{
unistd::{Gid, Uid},
sys::socket::socketpair
};

let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::SeqPacket, None,
SockFlag::empty()).unwrap();
let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
assert_eq!(xucred.version(), 0);
assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
}

#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))]
#[test]
pub fn test_local_peercred_stream() {
use nix::{
unistd::{Gid, Uid},
sys::socket::socketpair
};

let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
SockFlag::empty()).unwrap();
let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
assert_eq!(xucred.version(), 0);
assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
}

#[cfg(target_os = "linux")]
#[test]
fn is_so_mark_functional() {
Expand Down

0 comments on commit 06b5d33

Please sign in to comment.