Skip to content

Commit

Permalink
Merge sockaddr_storage_to_addr and SockAddr::from_raw_sockaddr
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 committed Aug 29, 2021
1 parent 8e963a0 commit 1cab7bf
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 219 deletions.
40 changes: 32 additions & 8 deletions src/ifaddrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use std::iter::Iterator;
use std::mem;
use std::option::Option;

use crate::{Result, Errno};
use crate::sys::socket::SockAddr;
use crate::net::if_::*;
use crate::sys::socket::SockAddr;
use crate::{Errno, Result};

/// Describes a single address for an interface as returned by `getifaddrs`.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -46,8 +46,31 @@ impl InterfaceAddress {
/// Create an `InterfaceAddress` from the libc struct.
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockAddr::from_raw_sockaddr(info.ifa_addr) };
let netmask = unsafe { SockAddr::from_raw_sockaddr(info.ifa_netmask) };
let get_sockaddr = |sa: *const libc::sockaddr| {
if sa.is_null() {
return None;
}
// TODO: there's gotta be a better way to do this but this is basically
// what the man pages recommend
let len = match unsafe { (*sa).sa_family } as _ {
libc::AF_INET => mem::size_of::<libc::sockaddr_in>(),
libc::AF_INET6 => mem::size_of::<libc::sockaddr_in6>(),
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "illumos",
target_os = "fuchsia",
target_os = "solaris"
))]
libc::AF_PACKET => mem::size_of::<libc::sockaddr_in>(),
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_NETLINK => mem::size_of::<libc::sockaddr_nl>(),
_ => return None,
};
unsafe { SockAddr::from_raw_sockaddr(sa, len) }.ok()
};
let address = get_sockaddr(info.ifa_addr);
let netmask = get_sockaddr(info.ifa_netmask);
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
Expand All @@ -59,9 +82,9 @@ impl InterfaceAddress {

let ifu = get_ifu_from_sockaddr(info);
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
addr.destination = unsafe { SockAddr::from_raw_sockaddr(ifu) };
addr.destination = get_sockaddr(ifu);
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
addr.broadcast = unsafe { SockAddr::from_raw_sockaddr(ifu) };
addr.broadcast = get_sockaddr(ifu);
}

addr
Expand Down Expand Up @@ -127,9 +150,10 @@ pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
unsafe {
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
let addrs = addrs.assume_init();
InterfaceAddressIterator {
base: addrs.assume_init(),
next: addrs.assume_init(),
base: addrs,
next: addrs,
}
})
}
Expand Down
228 changes: 130 additions & 98 deletions src/sys/socket/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,41 @@ pub use self::datalink::LinkAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
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 {
macro_rules! address_family_enum {
($($(#[doc = $doc:tt])* $(#[cfg($cfg:meta)])* $Variant:ident = $constant:path),* $(,)?) => {
/// 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 {
$(
$(#[doc = $doc])*
$(#[cfg($cfg)])*
$Variant = $constant,
)*
}

impl AddressFamily {
/// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
/// the `sa_family` field of a `sockaddr`.
///
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
/// and System. Returns None for unsupported or unknown address families.
pub const fn from_i32(family: i32) -> Option<AddressFamily> {
match family {
$(
$(#[cfg($cfg)])*
$constant => Some(AddressFamily::$Variant),
)*
_ => None
}
}
}
};
}

address_family_enum! {
/// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
Unix = libc::AF_UNIX,
/// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
Expand Down Expand Up @@ -231,38 +260,6 @@ pub enum AddressFamily {
Unspec = libc::AF_UNSPEC,
}

impl AddressFamily {
/// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
/// the `sa_family` field of a `sockaddr`.
///
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
/// and System. Returns None for unsupported or unknown address families.
pub const fn from_i32(family: i32) -> Option<AddressFamily> {
match family {
libc::AF_UNIX => Some(AddressFamily::Unix),
libc::AF_INET => Some(AddressFamily::Inet),
libc::AF_INET6 => Some(AddressFamily::Inet6),
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_NETLINK => Some(AddressFamily::Netlink),
#[cfg(any(target_os = "macos", target_os = "macos"))]
libc::AF_SYSTEM => Some(AddressFamily::System),
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_PACKET => Some(AddressFamily::Packet),
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "openbsd"))]
libc::AF_LINK => Some(AddressFamily::Link),
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_VSOCK => Some(AddressFamily::Vsock),
_ => None
}
}
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum InetAddr {
V4(libc::sockaddr_in),
Expand Down Expand Up @@ -515,7 +512,7 @@ impl fmt::Display for Ipv6Addr {
}

/// A wrapper around `sockaddr_un`.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy)]
pub struct UnixAddr {
// INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
sun: libc::sockaddr_un,
Expand Down Expand Up @@ -678,6 +675,17 @@ fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}

impl fmt::Debug for UnixAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind() {
UnixAddrKind::Pathname(path) => path.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 fmt::Display for UnixAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind() {
Expand Down Expand Up @@ -790,58 +798,86 @@ impl SockAddr {
format!("{}", self)
}

/// Creates a `SockAddr` struct from libc's sockaddr.
/// Return the appropriate `SockAddr` type from a `sockaddr` of a certain size.
///
/// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System.
/// Returns None for unsupported families.
/// In C this would usually be done by casting. The `len` argument
/// should be the number of bytes in the `sockaddr` that are actually
/// allocated and valid. It must be at least as large as all the useful parts
/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not
/// include the terminating null.
///
/// # Safety
///
/// unsafe because it takes a raw pointer as argument. The caller must
/// ensure that the pointer is valid.
#[cfg(not(target_os = "fuchsia"))]
pub unsafe fn from_raw_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> {
if addr.is_null() {
None
} else {
match AddressFamily::from_i32(i32::from((*addr).sa_family)) {
Some(AddressFamily::Unix) => None,
Some(AddressFamily::Inet) => Some(SockAddr::Inet(
InetAddr::V4(*(addr as *const libc::sockaddr_in)))),
Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
InetAddr::V6(*(addr as *const libc::sockaddr_in6)))),
#[cfg(any(target_os = "android", target_os = "linux"))]
Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
NetlinkAddr(*(addr as *const libc::sockaddr_nl)))),
#[cfg(any(target_os = "ios", target_os = "macos"))]
Some(AddressFamily::System) => Some(SockAddr::SysControl(
SysControlAddr(*(addr as *const libc::sockaddr_ctl)))),
#[cfg(any(target_os = "android", target_os = "linux"))]
Some(AddressFamily::Packet) => Some(SockAddr::Link(
LinkAddr(*(addr as *const libc::sockaddr_ll)))),
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "openbsd"))]
Some(AddressFamily::Link) => {
let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
if ether_addr.is_empty() {
None
} else {
Some(SockAddr::Link(ether_addr))
}
},
#[cfg(any(target_os = "android", target_os = "linux"))]
Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(
VsockAddr(*(addr as *const libc::sockaddr_vm)))),
// Other address families are currently not supported and simply yield a None
// entry instead of a proper conversion to a `SockAddr`.
Some(_) | None => None,
/// `addr` must be a valid, non-null pointer, and `len` should describe the
/// number of bytes within `*addr` that are initialized and represent data.
pub unsafe fn from_raw_sockaddr(addr: *const libc::sockaddr, len: usize) -> Result<SockAddr> {
let af = (*addr).sa_family;
if len < mem::size_of_val(&af) {
return Err(Error::from(Errno::ENOTCONN));
}

let af = AddressFamily::from_i32(af.into()).ok_or(Errno::EAFNOSUPPORT)?;
match af {
AddressFamily::Inet => {
use libc::sockaddr_in;
assert!(len as usize >= mem::size_of::<sockaddr_in>());
let sin = *(addr as *const sockaddr_in);
Ok(SockAddr::Inet(InetAddr::V4(sin)))
}
AddressFamily::Inet6 => {
use libc::sockaddr_in6;
assert!(len as usize >= mem::size_of::<sockaddr_in6>());
let sin6 = *(addr as *const sockaddr_in6);
Ok(SockAddr::Inet(InetAddr::V6(sin6)))
}
AddressFamily::Unix => {
use libc::sockaddr_un;
let pathlen = len - offset_of!(sockaddr_un, sun_path);
let sun = *(addr as *const sockaddr_un);
Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
AddressFamily::Packet => {
// Don't assert anything about the size.
// Apparently the Linux kernel can return smaller sizes when
// the value in the last element of sockaddr_ll (`sll_addr`) is
// smaller than the declared size of that field
let sll = *(addr as *const libc::sockaddr_ll);
Ok(SockAddr::Link(LinkAddr(sll)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
AddressFamily::Netlink => {
let snl = *(addr as *const libc::sockaddr_nl);
Ok(SockAddr::Netlink(NetlinkAddr(snl)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
AddressFamily::Alg => {
let salg = *(addr as *const libc::sockaddr_alg);
Ok(SockAddr::Alg(AlgAddr(salg)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
AddressFamily::Vsock => {
let svm = *(addr as *const libc::sockaddr_vm);
Ok(SockAddr::Vsock(VsockAddr(svm)))
}
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "openbsd"))]
AddressFamily::Link => {
let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
Ok(SockAddr::Link(ether_addr))
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
AddressFamily::System => {
let sctl = SysControlAddr(*(addr as *const libc::sockaddr_ctl));
Ok(SockAddr::SysControl(sctl))
}
_ => Err(Errno::EAFNOSUPPORT),
}

}

/// Conversion from nix's SockAddr type to the underlying libc sockaddr type.
Expand Down Expand Up @@ -1379,8 +1415,12 @@ mod tests {
fn test_macos_loopback_datalink_addr() {
let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0];
let sa = bytes.as_ptr() as *const libc::sockaddr;
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
assert!(_sock_addr.is_none());
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
if let SockAddr::Link(link_addr) = sock_addr {
assert!(link_addr.is_empty())
} else {
panic!("bad family")
}
}

#[cfg(any(target_os = "dragonfly",
Expand All @@ -1394,11 +1434,7 @@ mod tests {
let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80];
let ptr = bytes.as_ptr();
let sa = ptr as *const libc::sockaddr;
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };

assert!(_sock_addr.is_some());

let sock_addr = _sock_addr.unwrap();
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };

assert_eq!(sock_addr.family(), AddressFamily::Link);

Expand All @@ -1416,11 +1452,7 @@ mod tests {
let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
let ptr = bytes.as_ptr();
let sa = ptr as *const libc::sockaddr;
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };

assert!(_sock_addr.is_some());

let sock_addr = _sock_addr.unwrap();
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };

assert_eq!(sock_addr.family(), AddressFamily::Link);

Expand Down
Loading

0 comments on commit 1cab7bf

Please sign in to comment.