From d93f2b40bc843767e5f995bb32506a4456f19cb8 Mon Sep 17 00:00:00 2001 From: Clayton Slaughter Date: Thu, 13 Aug 2026 15:41:34 -0500 Subject: [PATCH] adds support for AF_IUCV sockets --- src/lib.rs | 8 ++++++++ src/sockaddr.rs | 43 +++++++++++++++++++++++++++++++++++++++ src/sys/unix.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 30eedd62..b915b15e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,6 +224,14 @@ impl Domain { /// Domain for IPv6 communication, corresponding to `AF_INET6`. pub const IPV6: Domain = Domain(sys::AF_INET6); + #[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" + ))] + /// Domain for IUCV socket communication, corresponding to `AF_IUCV`. + pub const IUCV: Domain = Domain(sys::AF_IUCV); + /// Domain for Unix socket communication, corresponding to `AF_UNIX`. #[cfg(not(target_os = "wasi"))] pub const UNIX: Domain = Domain(sys::AF_UNIX); diff --git a/src/sockaddr.rs b/src/sockaddr.rs index 120a5466..4e5b3a27 100644 --- a/src/sockaddr.rs +++ b/src/sockaddr.rs @@ -223,6 +223,19 @@ impl SockAddr { crate::sys::unix_sockaddr(path.as_ref()) } + #[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" + ))] + /// Constructs a `SockAddr` with the family `AF_IUCV` and the provided + /// components. + /// + /// Returns an error if the path is too long. + pub fn iucv(userid: &str, name: &str) -> io::Result { + crate::sys::iucv_sockaddr(userid, name) + } + /// Set the length of the address. /// /// # Safety @@ -278,6 +291,16 @@ impl SockAddr { self.storage.ss_family == AF_UNIX as sa_family_t } + #[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" + ))] + /// Returns true if this address is of an IUCV socket, false otherwise + pub const fn is_iucv(&self) -> bool { + self.storage.ss_family == libc::AF_IUCV as sa_family_t + } + /// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4) /// or `AF_INET6` (IPv6) family, otherwise returns `None`. pub fn as_socket(&self) -> Option { @@ -335,6 +358,26 @@ impl SockAddr { // bytes by using `self.len`. unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) } } + + #[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" + ))] + /// Returns this address as a [`sockaddr_iucv`](https://docs.rs/libc/latest/libc/struct.sockaddr_iucv.html) if it is in the 'AF_IUCV' family + pub fn as_socket_iucv(&self) -> Option { + use libc::sockaddr_iucv; + + if self.storage.ss_family == libc::AF_IUCV as sa_family_t + && self.len == (mem::size_of::() as u32) + { + let s_iucv_ptr = &self.storage as *const _ as *const libc::sockaddr_iucv; + // SAFETY: We checked that the Address Family and size were correct + let s_iucv = unsafe { *s_iucv_ptr }; + return Some(s_iucv); + } + None + } } impl From for SockAddr { diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 65e494bc..1e9aa681 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -79,6 +79,14 @@ pub(crate) use std::ffi::c_int; #[cfg(not(target_os = "wasi"))] pub(crate) use libc::AF_UNIX; pub(crate) use libc::{AF_INET, AF_INET6}; + +#[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" +))] +pub(crate) use libc::AF_IUCV; + // Used in `Type`. #[cfg(all(feature = "all", target_os = "linux"))] pub(crate) use libc::SOCK_DCCP; @@ -765,6 +773,52 @@ pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { Ok(unsafe { SockAddr::new(storage, len as socklen_t) }) } +#[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" +))] +fn copy_and_pad_with_spaces(src: &str, target: &mut [libc::c_char; 8]) -> io::Result<()> { + let bytes = src.as_bytes(); + if bytes.len() > target.len() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "IUCV address component is too long", + )); + } + let mut bytes_iter = bytes.iter(); + for c in target { + if let Some(b) = bytes_iter.next() { + *c = *b as libc::c_char; + } else { + *c = b' ' as libc::c_char; + } + } + Ok(()) +} + +#[cfg(all( + feature = "all", + any(target_arch = "x86_64", target_arch = "s390x"), + target_env = "gnu" +))] +#[allow(unsafe_op_in_unsafe_fn)] +pub(crate) fn iucv_sockaddr(userid: &str, name: &str) -> io::Result { + let mut storage = SockAddrStorage::zeroed(); + { + // SAFETY: sockaddr_iucv is one of the sockaddr_* types defined by this platform. + let storage = unsafe { storage.view_as::() }; + + storage.siucv_family = libc::AF_IUCV as sa_family_t; + // Note that storage.siucv_port is initialized to zero above, which is required for the reserved field + // Note that storage.siucv_addr is initialized to zero above, which is required for the reserved field + storage.siucv_nodeid = [b' ' as libc::c_char; 8]; // Required for the reserved field + copy_and_pad_with_spaces(userid, &mut storage.siucv_user_id)?; + copy_and_pad_with_spaces(name, &mut storage.siucv_name)?; + } + Ok(unsafe { SockAddr::new(storage, mem::size_of::() as socklen_t) }) +} + // Used in `MsgHdr`. #[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))] pub(crate) use libc::msghdr;