aboutsummaryrefslogtreecommitdiff
path: root/vendor/rustix/src/backend/libc/net/read_sockaddr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/backend/libc/net/read_sockaddr.rs')
-rw-r--r--vendor/rustix/src/backend/libc/net/read_sockaddr.rs306
1 files changed, 306 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/libc/net/read_sockaddr.rs b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs
new file mode 100644
index 0000000..6da7a50
--- /dev/null
+++ b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs
@@ -0,0 +1,306 @@
+//! The BSD sockets API requires us to read the `ss_family` field before we can
+//! interpret the rest of a `sockaddr` produced by the kernel.
+
+#[cfg(unix)]
+use super::addr::SocketAddrUnix;
+use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
+use crate::backend::c;
+#[cfg(not(windows))]
+use crate::ffi::CStr;
+use crate::io;
+use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
+use core::mem::size_of;
+
+// This must match the header of `sockaddr`.
+#[repr(C)]
+struct sockaddr_header {
+ #[cfg(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ ))]
+ sa_len: u8,
+ #[cfg(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ ))]
+ ss_family: u8,
+ #[cfg(not(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ )))]
+ ss_family: u16,
+}
+
+/// Read the `ss_family` field from a socket address returned from the OS.
+///
+/// # Safety
+///
+/// `storage` must point to a valid socket address returned from the OS.
+#[inline]
+unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 {
+ // Assert that we know the layout of `sockaddr`.
+ let _ = c::sockaddr {
+ #[cfg(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ ))]
+ sa_len: 0_u8,
+ #[cfg(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ ))]
+ sa_family: 0_u8,
+ #[cfg(not(any(
+ bsd,
+ target_os = "aix",
+ target_os = "espidf",
+ target_os = "haiku",
+ target_os = "nto",
+ target_os = "vita"
+ )))]
+ sa_family: 0_u16,
+ #[cfg(not(target_os = "haiku"))]
+ sa_data: [0; 14],
+ #[cfg(target_os = "haiku")]
+ sa_data: [0; 30],
+ };
+
+ (*storage.cast::<sockaddr_header>()).ss_family.into()
+}
+
+/// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we
+/// can test for `AF_UNSPEC` to test whether it was stored to.
+pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) {
+ (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _;
+}
+
+/// Read a socket address encoded in a platform-specific format.
+///
+/// # Safety
+///
+/// `storage` must point to valid socket address storage.
+pub(crate) unsafe fn read_sockaddr(
+ storage: *const c::sockaddr_storage,
+ len: usize,
+) -> io::Result<SocketAddrAny> {
+ #[cfg(unix)]
+ let offsetof_sun_path = super::addr::offsetof_sun_path();
+
+ if len < size_of::<c::sa_family_t>() {
+ return Err(io::Errno::INVAL);
+ }
+ match read_ss_family(storage).into() {
+ c::AF_INET => {
+ if len < size_of::<c::sockaddr_in>() {
+ return Err(io::Errno::INVAL);
+ }
+ let decode = &*storage.cast::<c::sockaddr_in>();
+ Ok(SocketAddrAny::V4(SocketAddrV4::new(
+ Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
+ u16::from_be(decode.sin_port),
+ )))
+ }
+ c::AF_INET6 => {
+ if len < size_of::<c::sockaddr_in6>() {
+ return Err(io::Errno::INVAL);
+ }
+ let decode = &*storage.cast::<c::sockaddr_in6>();
+ #[cfg(not(windows))]
+ let s6_addr = decode.sin6_addr.s6_addr;
+ #[cfg(windows)]
+ let s6_addr = decode.sin6_addr.u.Byte;
+ #[cfg(not(windows))]
+ let sin6_scope_id = decode.sin6_scope_id;
+ #[cfg(windows)]
+ let sin6_scope_id = decode.Anonymous.sin6_scope_id;
+ Ok(SocketAddrAny::V6(SocketAddrV6::new(
+ Ipv6Addr::from(s6_addr),
+ u16::from_be(decode.sin6_port),
+ u32::from_be(decode.sin6_flowinfo),
+ sin6_scope_id,
+ )))
+ }
+ #[cfg(unix)]
+ c::AF_UNIX => {
+ if len < offsetof_sun_path {
+ return Err(io::Errno::INVAL);
+ }
+ if len == offsetof_sun_path {
+ SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix)
+ } else {
+ let decode = &*storage.cast::<c::sockaddr_un>();
+
+ // On Linux check for Linux's [abstract namespace].
+ //
+ // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
+ #[cfg(linux_kernel)]
+ if decode.sun_path[0] == 0 {
+ return SocketAddrUnix::new_abstract_name(core::mem::transmute::<
+ &[c::c_char],
+ &[u8],
+ >(
+ &decode.sun_path[1..len - offsetof_sun_path],
+ ))
+ .map(SocketAddrAny::Unix);
+ }
+
+ // Otherwise we expect a NUL-terminated filesystem path.
+
+ // Trim off unused bytes from the end of `path_bytes`.
+ let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) {
+ // FreeBSD and illumos sometimes set the length to longer
+ // than the length of the NUL-terminated string. Find the
+ // NUL and truncate the string accordingly.
+ &decode.sun_path[..decode
+ .sun_path
+ .iter()
+ .position(|b| *b == 0)
+ .ok_or(io::Errno::INVAL)?]
+ } else {
+ // Otherwise, use the provided length.
+ let provided_len = len - 1 - offsetof_sun_path;
+ if decode.sun_path[provided_len] != 0 {
+ return Err(io::Errno::INVAL);
+ }
+ debug_assert_eq!(
+ CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(),
+ provided_len
+ );
+ &decode.sun_path[..provided_len]
+ };
+
+ SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
+ .map(SocketAddrAny::Unix)
+ }
+ }
+ _ => Err(io::Errno::INVAL),
+ }
+}
+
+/// Read an optional socket address returned from the OS.
+///
+/// # Safety
+///
+/// `storage` must point to a valid socket address returned from the OS.
+pub(crate) unsafe fn maybe_read_sockaddr_os(
+ storage: *const c::sockaddr_storage,
+ len: usize,
+) -> Option<SocketAddrAny> {
+ if len == 0 {
+ return None;
+ }
+
+ assert!(len >= size_of::<c::sa_family_t>());
+ let family = read_ss_family(storage).into();
+ if family == c::AF_UNSPEC {
+ None
+ } else {
+ Some(inner_read_sockaddr_os(family, storage, len))
+ }
+}
+
+/// Read a socket address returned from the OS.
+///
+/// # Safety
+///
+/// `storage` must point to a valid socket address returned from the OS.
+pub(crate) unsafe fn read_sockaddr_os(
+ storage: *const c::sockaddr_storage,
+ len: usize,
+) -> SocketAddrAny {
+ assert!(len >= size_of::<c::sa_family_t>());
+ let family = read_ss_family(storage).into();
+ inner_read_sockaddr_os(family, storage, len)
+}
+
+unsafe fn inner_read_sockaddr_os(
+ family: c::c_int,
+ storage: *const c::sockaddr_storage,
+ len: usize,
+) -> SocketAddrAny {
+ #[cfg(unix)]
+ let offsetof_sun_path = super::addr::offsetof_sun_path();
+
+ assert!(len >= size_of::<c::sa_family_t>());
+ match family {
+ c::AF_INET => {
+ assert!(len >= size_of::<c::sockaddr_in>());
+ let decode = &*storage.cast::<c::sockaddr_in>();
+ SocketAddrAny::V4(SocketAddrV4::new(
+ Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
+ u16::from_be(decode.sin_port),
+ ))
+ }
+ c::AF_INET6 => {
+ assert!(len >= size_of::<c::sockaddr_in6>());
+ let decode = &*storage.cast::<c::sockaddr_in6>();
+ SocketAddrAny::V6(SocketAddrV6::new(
+ Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
+ u16::from_be(decode.sin6_port),
+ u32::from_be(decode.sin6_flowinfo),
+ sockaddr_in6_sin6_scope_id(decode),
+ ))
+ }
+ #[cfg(unix)]
+ c::AF_UNIX => {
+ assert!(len >= offsetof_sun_path);
+ if len == offsetof_sun_path {
+ SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
+ } else {
+ let decode = &*storage.cast::<c::sockaddr_un>();
+
+ // On Linux check for Linux's [abstract namespace].
+ //
+ // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
+ #[cfg(linux_kernel)]
+ if decode.sun_path[0] == 0 {
+ return SocketAddrAny::Unix(
+ SocketAddrUnix::new_abstract_name(core::mem::transmute::<
+ &[c::c_char],
+ &[u8],
+ >(
+ &decode.sun_path[1..len - offsetof_sun_path],
+ ))
+ .unwrap(),
+ );
+ }
+
+ // Otherwise we expect a NUL-terminated filesystem path.
+ assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
+ let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
+
+ // FreeBSD and illumos sometimes set the length to longer than
+ // the length of the NUL-terminated string. Find the NUL and
+ // truncate the string accordingly.
+ #[cfg(any(solarish, target_os = "freebsd"))]
+ let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
+
+ SocketAddrAny::Unix(
+ SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
+ .unwrap(),
+ )
+ }
+ }
+ other => unimplemented!("{:?}", other),
+ }
+}