aboutsummaryrefslogtreecommitdiff
path: root/src/sys/socket/addr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys/socket/addr.rs')
-rw-r--r--src/sys/socket/addr.rs221
1 files changed, 143 insertions, 78 deletions
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index d486056..b119642 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -1,5 +1,5 @@
use super::sa_family_t;
-use crate::{Error, Result, NixPath};
+use crate::{Result, NixPath};
use crate::errno::Errno;
use memoffset::offset_of;
use std::{fmt, mem, net, ptr, slice};
@@ -32,6 +32,7 @@ pub use self::vsock::VsockAddr;
/// These constants specify the protocol family to be used
/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
#[repr(i32)]
+#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum AddressFamily {
/// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
@@ -236,7 +237,7 @@ impl AddressFamily {
///
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
/// and System. Returns None for unsupported or unknown address families.
- pub fn from_i32(family: i32) -> Option<AddressFamily> {
+ pub const fn from_i32(family: i32) -> Option<AddressFamily> {
match family {
libc::AF_UNIX => Some(AddressFamily::Unix),
libc::AF_INET => Some(AddressFamily::Inet),
@@ -269,6 +270,7 @@ pub enum InetAddr {
}
impl InetAddr {
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
pub fn from_std(std: &net::SocketAddr) -> InetAddr {
match *std {
net::SocketAddr::V4(ref addr) => {
@@ -292,6 +294,7 @@ impl InetAddr {
}
}
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
pub fn new(ip: IpAddr, port: u16) -> InetAddr {
match ip {
IpAddr::V4(ref ip) => {
@@ -313,7 +316,7 @@ impl InetAddr {
}
}
/// Gets the IP address associated with this socket address.
- pub fn ip(&self) -> IpAddr {
+ pub const fn ip(&self) -> IpAddr {
match *self {
InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)),
InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)),
@@ -321,7 +324,7 @@ impl InetAddr {
}
/// Gets the port number associated with this socket address
- pub fn port(&self) -> u16 {
+ pub const fn port(&self) -> u16 {
match *self {
InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port),
InetAddr::V4(ref sa) => u16::from_be(sa.sin_port),
@@ -343,6 +346,7 @@ impl InetAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -372,7 +376,7 @@ impl IpAddr {
/// Create a new IpAddr that contains an IPv4 address.
///
/// The result will represent the IP address a.b.c.d
- pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
+ pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(a, b, c, d))
}
@@ -381,7 +385,7 @@ impl IpAddr {
/// The result will represent the IP address a:b:c:d:e:f
#[allow(clippy::many_single_char_names)]
#[allow(clippy::too_many_arguments)]
- pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
+ pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
}
@@ -392,7 +396,7 @@ impl IpAddr {
}
}
- pub fn to_std(&self) -> net::IpAddr {
+ pub const fn to_std(&self) -> net::IpAddr {
match *self {
IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()),
IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()),
@@ -420,11 +424,11 @@ pub struct Ipv4Addr(pub libc::in_addr);
impl Ipv4Addr {
#[allow(clippy::identity_op)] // More readable this way
- pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
- let ip = ((u32::from(a) << 24) |
- (u32::from(b) << 16) |
- (u32::from(c) << 8) |
- (u32::from(d) << 0)).to_be();
+ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
+ let ip = (((a as u32) << 24) |
+ ((b as u32) << 16) |
+ ((c as u32) << 8) |
+ ((d as u32) << 0)).to_be();
Ipv4Addr(libc::in_addr { s_addr: ip })
}
@@ -436,16 +440,16 @@ impl Ipv4Addr {
Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
}
- pub fn any() -> Ipv4Addr {
+ pub const fn any() -> Ipv4Addr {
Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY })
}
- pub fn octets(self) -> [u8; 4] {
+ pub const fn octets(self) -> [u8; 4] {
let bits = u32::from_be(self.0.s_addr);
[(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8]
}
- pub fn to_std(self) -> net::Ipv4Addr {
+ pub const fn to_std(self) -> net::Ipv4Addr {
let bits = self.octets();
net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
}
@@ -486,7 +490,7 @@ macro_rules! to_u16_array {
impl Ipv6Addr {
#[allow(clippy::many_single_char_names)]
#[allow(clippy::too_many_arguments)]
- pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
+ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)})
}
@@ -496,11 +500,11 @@ impl Ipv6Addr {
}
/// Return the eight 16-bit segments that make up this address
- pub fn segments(&self) -> [u16; 8] {
+ pub const fn segments(&self) -> [u16; 8] {
to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
}
- pub fn to_std(&self) -> net::Ipv6Addr {
+ pub const fn to_std(&self) -> net::Ipv6Addr {
let s = self.segments();
net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
}
@@ -513,15 +517,42 @@ impl fmt::Display for Ipv6Addr {
}
/// A wrapper around `sockaddr_un`.
-///
-/// This also tracks the length of `sun_path` address (excluding
-/// a terminating null), because it may not be null-terminated. For example,
-/// unconnected and Linux abstract sockets are never null-terminated, and POSIX
-/// does not require that `sun_len` include the terminating null even for normal
-/// sockets. Note that the actual sockaddr length is greater by
-/// `offset_of!(libc::sockaddr_un, sun_path)`
#[derive(Clone, Copy, Debug)]
-pub struct UnixAddr(pub libc::sockaddr_un, pub usize);
+pub struct UnixAddr {
+ // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
+ sun: libc::sockaddr_un,
+ path_len: usize,
+}
+
+// linux man page unix(7) says there are 3 kinds of unix socket:
+// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
+// unnamed: addrlen = sizeof(sa_family_t)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+ Pathname(&'a Path),
+ Unnamed,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Abstract(&'a [u8]),
+}
+impl<'a> UnixAddrKind<'a> {
+ /// Safety: sun & path_len must be valid
+ unsafe fn get(sun: &'a libc::sockaddr_un, path_len: usize) -> Self {
+ if path_len == 0 {
+ return Self::Unnamed;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ if sun.sun_path[0] == 0 {
+ let name =
+ slice::from_raw_parts(sun.sun_path.as_ptr().add(1) as *const u8, path_len - 1);
+ return Self::Abstract(name);
+ }
+ let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len - 1);
+ Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+ }
+}
impl UnixAddr {
/// Create a new sockaddr_un representing a filesystem path.
@@ -535,15 +566,15 @@ impl UnixAddr {
let bytes = cstr.to_bytes();
- if bytes.len() > ret.sun_path.len() {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ if bytes.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
}
ptr::copy_nonoverlapping(bytes.as_ptr(),
ret.sun_path.as_mut_ptr() as *mut u8,
bytes.len());
- Ok(UnixAddr(ret, bytes.len()))
+ Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1))
}
})?
}
@@ -562,8 +593,8 @@ impl UnixAddr {
.. mem::zeroed()
};
- if path.len() + 1 > ret.sun_path.len() {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ if path.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
}
// Abstract addresses are represented by sun_path[0] ==
@@ -572,28 +603,39 @@ impl UnixAddr {
ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
path.len());
- Ok(UnixAddr(ret, path.len() + 1))
+ Ok(UnixAddr::from_raw_parts(ret, path.len() + 1))
+ }
+ }
+
+ /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen"
+ /// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length
+ /// of the data in `sun_path`.
+ ///
+ /// # Safety
+ /// This pair of sockaddr_un & path_len must be a valid unix addr, which means:
+ /// - path_len <= sockaddr_un.sun_path.len()
+ /// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and
+ /// sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0
+ pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, mut path_len: usize) -> UnixAddr {
+ if let UnixAddrKind::Pathname(_) = UnixAddrKind::get(&sun, path_len) {
+ if sun.sun_path[path_len - 1] != 0 {
+ assert_eq!(sun.sun_path[path_len], 0);
+ path_len += 1
+ }
}
+ UnixAddr { sun, path_len }
}
- fn sun_path(&self) -> &[u8] {
- unsafe { slice::from_raw_parts(self.0.sun_path.as_ptr() as *const u8, self.1) }
+ fn kind(&self) -> UnixAddrKind<'_> {
+ // SAFETY: our sockaddr is always valid because of the invariant on the struct
+ unsafe { UnixAddrKind::get(&self.sun, self.path_len) }
}
/// If this address represents a filesystem path, return that path.
pub fn path(&self) -> Option<&Path> {
- if self.1 == 0 || self.0.sun_path[0] == 0 {
- // unnamed or abstract
- None
- } else {
- let p = self.sun_path();
- // POSIX only requires that `sun_len` be at least long enough to
- // contain the pathname, and it need not be null-terminated. So we
- // need to create a string that is the shorter of the
- // null-terminated length or the full length.
- let ptr = &self.0.sun_path as *const libc::c_char;
- let reallen = unsafe { libc::strnlen(ptr, p.len()) };
- Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..reallen])))
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => Some(path),
+ _ => None,
}
}
@@ -603,31 +645,55 @@ impl UnixAddr {
/// leading null byte. `None` is returned for unnamed or path-backed sockets.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn as_abstract(&self) -> Option<&[u8]> {
- if self.1 >= 1 && self.0.sun_path[0] == 0 {
- Some(&self.sun_path()[1..])
- } else {
- // unnamed or filesystem path
- None
+ match self.kind() {
+ UnixAddrKind::Abstract(name) => Some(name),
+ _ => None,
}
}
+
+ /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
+ #[inline]
+ pub fn path_len(&self) -> usize {
+ self.path_len
+ }
+ /// Returns a pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_ptr(&self) -> *const libc::sockaddr_un {
+ &self.sun
+ }
+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+ &mut self.sun
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+ use fmt::Write;
+ f.write_str("@\"")?;
+ for &b in abs {
+ use fmt::Display;
+ char::from(b).escape_default().fmt(f)?;
+ }
+ f.write_char('"')?;
+ Ok(())
}
impl fmt::Display for UnixAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.1 == 0 {
- f.write_str("<unbound UNIX socket>")
- } else if let Some(path) = self.path() {
- path.display().fmt(f)
- } else {
- let display = String::from_utf8_lossy(&self.sun_path()[1..]);
- write!(f, "@{}", display)
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => path.display().fmt(f),
+ UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
}
}
}
impl PartialEq for UnixAddr {
fn eq(&self, other: &UnixAddr) -> bool {
- self.sun_path() == other.sun_path()
+ self.kind() == other.kind()
}
}
@@ -635,12 +701,13 @@ impl Eq for UnixAddr {}
impl Hash for UnixAddr {
fn hash<H: Hasher>(&self, s: &mut H) {
- ( self.0.sun_family, self.sun_path() ).hash(s)
+ self.kind().hash(s)
}
}
/// Represents a socket address
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum SockAddr {
Inet(InetAddr),
Unix(UnixAddr),
@@ -686,7 +753,7 @@ impl SockAddr {
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> {
- SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a))
+ SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -720,6 +787,7 @@ impl SockAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -801,12 +869,12 @@ impl SockAddr {
},
mem::size_of_val(addr) as libc::socklen_t
),
- SockAddr::Unix(UnixAddr(ref addr, len)) => (
+ SockAddr::Unix(UnixAddr { ref sun, path_len }) => (
// This cast is always allowed in C
unsafe {
- &*(addr as *const libc::sockaddr_un as *const libc::sockaddr)
+ &*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
},
- (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
+ (path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
),
#[cfg(any(target_os = "android", target_os = "linux"))]
SockAddr::Netlink(NetlinkAddr(ref sa)) => (
@@ -913,11 +981,11 @@ pub mod netlink {
NetlinkAddr(addr)
}
- pub fn pid(&self) -> u32 {
+ pub const fn pid(&self) -> u32 {
self.0.nl_pid
}
- pub fn groups(&self) -> u32 {
+ pub const fn groups(&self) -> u32 {
self.0.nl_groups
}
}
@@ -998,7 +1066,7 @@ pub mod sys_control {
use libc::{self, c_uchar};
use std::{fmt, mem};
use std::os::unix::io::RawFd;
- use crate::{Errno, Error, Result};
+ use crate::{Errno, Result};
// FIXME: Move type into `libc`
#[repr(C)]
@@ -1009,7 +1077,7 @@ pub mod sys_control {
pub ctl_name: [c_uchar; MAX_KCTL_NAME],
}
- const CTL_IOC_MAGIC: u8 = 'N' as u8;
+ const CTL_IOC_MAGIC: u8 = b'N';
const CTL_IOC_INFO: u8 = 3;
const MAX_KCTL_NAME: usize = 96;
@@ -1020,7 +1088,7 @@ pub mod sys_control {
pub struct SysControlAddr(pub libc::sockaddr_ctl);
impl SysControlAddr {
- pub fn new(id: u32, unit: u32) -> SysControlAddr {
+ pub const fn new(id: u32, unit: u32) -> SysControlAddr {
let addr = libc::sockaddr_ctl {
sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
sc_family: AddressFamily::System as c_uchar,
@@ -1035,7 +1103,7 @@ pub mod sys_control {
pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
if name.len() > MAX_KCTL_NAME {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ return Err(Errno::ENAMETOOLONG);
}
let mut ctl_name = [0; MAX_KCTL_NAME];
@@ -1047,11 +1115,11 @@ pub mod sys_control {
Ok(SysControlAddr::new(info.ctl_id, unit))
}
- pub fn id(&self) -> u32 {
+ pub const fn id(&self) -> u32 {
self.0.sc_id
}
- pub fn unit(&self) -> u32 {
+ pub const fn unit(&self) -> u32 {
self.0.sc_unit
}
}
@@ -1372,11 +1440,8 @@ mod tests {
let name = String::from("nix\0abstract\0test");
let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
- let sun_path1 = addr.sun_path();
- let sun_path2 = [0u8, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
- assert_eq!(sun_path1.len(), sun_path2.len());
- for i in 0..sun_path1.len() {
- assert_eq!(sun_path1[i], sun_path2[i]);
- }
+ let sun_path1 = unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+ let sun_path2 = [0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
+ assert_eq!(sun_path1, sun_path2);
}
}