Skip to content

Commit e53cee3

Browse files
committed
Merge sockaddr_storage_to_addr and SockAddr::from_raw_sockaddr
1 parent 8cb9dc5 commit e53cee3

File tree

4 files changed

+186
-218
lines changed

4 files changed

+186
-218
lines changed

src/ifaddrs.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use std::iter::Iterator;
99
use std::mem;
1010
use std::option::Option;
1111

12-
use crate::{Result, Errno};
13-
use crate::sys::socket::SockAddr;
1412
use crate::net::if_::*;
13+
use crate::sys::socket::SockAddr;
14+
use crate::{Errno, Result};
1515

1616
/// Describes a single address for an interface as returned by `getifaddrs`.
1717
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -46,8 +46,31 @@ impl InterfaceAddress {
4646
/// Create an `InterfaceAddress` from the libc struct.
4747
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
4848
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
49-
let address = unsafe { SockAddr::from_raw_sockaddr(info.ifa_addr) };
50-
let netmask = unsafe { SockAddr::from_raw_sockaddr(info.ifa_netmask) };
49+
let get_sockaddr = |sa: *const libc::sockaddr| {
50+
if sa.is_null() {
51+
return None;
52+
}
53+
// TODO: there's gotta be a better way to do this but this is basically
54+
// what the man pages recommend
55+
let len = match unsafe { (*sa).sa_family } as _ {
56+
libc::AF_INET => mem::size_of::<libc::sockaddr_in>(),
57+
libc::AF_INET6 => mem::size_of::<libc::sockaddr_in6>(),
58+
#[cfg(any(
59+
target_os = "android",
60+
target_os = "linux",
61+
target_os = "illumos",
62+
target_os = "fuchsia",
63+
target_os = "solaris"
64+
))]
65+
libc::AF_PACKET => mem::size_of::<libc::sockaddr_in>(),
66+
#[cfg(any(target_os = "android", target_os = "linux"))]
67+
libc::AF_NETLINK => mem::size_of::<libc::sockaddr_nl>(),
68+
_ => return None,
69+
};
70+
unsafe { SockAddr::from_raw_sockaddr(sa, len) }.ok()
71+
};
72+
let address = get_sockaddr(info.ifa_addr);
73+
let netmask = get_sockaddr(info.ifa_netmask);
5174
let mut addr = InterfaceAddress {
5275
interface_name: ifname.to_string_lossy().to_string(),
5376
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
@@ -59,9 +82,9 @@ impl InterfaceAddress {
5982

6083
let ifu = get_ifu_from_sockaddr(info);
6184
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
62-
addr.destination = unsafe { SockAddr::from_raw_sockaddr(ifu) };
85+
addr.destination = get_sockaddr(ifu);
6386
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
64-
addr.broadcast = unsafe { SockAddr::from_raw_sockaddr(ifu) };
87+
addr.broadcast = get_sockaddr(ifu);
6588
}
6689

6790
addr
@@ -127,9 +150,10 @@ pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
127150
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
128151
unsafe {
129152
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
153+
let addrs = addrs.assume_init();
130154
InterfaceAddressIterator {
131-
base: addrs.assume_init(),
132-
next: addrs.assume_init(),
155+
base: addrs,
156+
next: addrs,
133157
}
134158
})
135159
}

src/sys/socket/addr.rs

Lines changed: 130 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,41 @@ pub use self::datalink::LinkAddr;
2929
#[cfg(any(target_os = "android", target_os = "linux"))]
3030
pub use self::vsock::VsockAddr;
3131

32-
/// These constants specify the protocol family to be used
33-
/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
34-
#[repr(i32)]
35-
#[non_exhaustive]
36-
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
37-
pub enum AddressFamily {
32+
macro_rules! address_family_enum {
33+
($($(#[doc = $doc:tt])* $(#[cfg($cfg:meta)])* $Variant:ident = $constant:path),* $(,)?) => {
34+
/// These constants specify the protocol family to be used
35+
/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
36+
#[repr(i32)]
37+
#[non_exhaustive]
38+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
39+
pub enum AddressFamily {
40+
$(
41+
$(#[doc = $doc])*
42+
$(#[cfg($cfg)])*
43+
$Variant = $constant,
44+
)*
45+
}
46+
47+
impl AddressFamily {
48+
/// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
49+
/// the `sa_family` field of a `sockaddr`.
50+
///
51+
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
52+
/// and System. Returns None for unsupported or unknown address families.
53+
pub const fn from_i32(family: i32) -> Option<AddressFamily> {
54+
match family {
55+
$(
56+
$(#[cfg($cfg)])*
57+
$constant => Some(AddressFamily::$Variant),
58+
)*
59+
_ => None
60+
}
61+
}
62+
}
63+
};
64+
}
65+
66+
address_family_enum! {
3867
/// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
3968
Unix = libc::AF_UNIX,
4069
/// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
@@ -231,38 +260,6 @@ pub enum AddressFamily {
231260
Unspec = libc::AF_UNSPEC,
232261
}
233262

234-
impl AddressFamily {
235-
/// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
236-
/// the `sa_family` field of a `sockaddr`.
237-
///
238-
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
239-
/// and System. Returns None for unsupported or unknown address families.
240-
pub const fn from_i32(family: i32) -> Option<AddressFamily> {
241-
match family {
242-
libc::AF_UNIX => Some(AddressFamily::Unix),
243-
libc::AF_INET => Some(AddressFamily::Inet),
244-
libc::AF_INET6 => Some(AddressFamily::Inet6),
245-
#[cfg(any(target_os = "android", target_os = "linux"))]
246-
libc::AF_NETLINK => Some(AddressFamily::Netlink),
247-
#[cfg(any(target_os = "macos", target_os = "macos"))]
248-
libc::AF_SYSTEM => Some(AddressFamily::System),
249-
#[cfg(any(target_os = "android", target_os = "linux"))]
250-
libc::AF_PACKET => Some(AddressFamily::Packet),
251-
#[cfg(any(target_os = "dragonfly",
252-
target_os = "freebsd",
253-
target_os = "ios",
254-
target_os = "macos",
255-
target_os = "netbsd",
256-
target_os = "illumos",
257-
target_os = "openbsd"))]
258-
libc::AF_LINK => Some(AddressFamily::Link),
259-
#[cfg(any(target_os = "android", target_os = "linux"))]
260-
libc::AF_VSOCK => Some(AddressFamily::Vsock),
261-
_ => None
262-
}
263-
}
264-
}
265-
266263
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
267264
pub enum InetAddr {
268265
V4(libc::sockaddr_in),
@@ -517,7 +514,7 @@ impl fmt::Display for Ipv6Addr {
517514
}
518515

519516
/// A wrapper around `sockaddr_un`.
520-
#[derive(Clone, Copy, Debug)]
517+
#[derive(Clone, Copy)]
521518
pub struct UnixAddr {
522519
// INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
523520
sun: libc::sockaddr_un,
@@ -680,6 +677,17 @@ fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
680677
Ok(())
681678
}
682679

680+
impl fmt::Debug for UnixAddr {
681+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
682+
match self.kind() {
683+
UnixAddrKind::Pathname(path) => path.fmt(f),
684+
UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
685+
#[cfg(any(target_os = "android", target_os = "linux"))]
686+
UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
687+
}
688+
}
689+
}
690+
683691
impl fmt::Display for UnixAddr {
684692
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
685693
match self.kind() {
@@ -792,58 +800,86 @@ impl SockAddr {
792800
format!("{}", self)
793801
}
794802

795-
/// Creates a `SockAddr` struct from libc's sockaddr.
803+
/// Return the appropriate `SockAddr` type from a `sockaddr` of a certain size.
796804
///
797-
/// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System.
798-
/// Returns None for unsupported families.
805+
/// In C this would usually be done by casting. The `len` argument
806+
/// should be the number of bytes in the `sockaddr` that are actually
807+
/// allocated and valid. It must be at least as large as all the useful parts
808+
/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not
809+
/// include the terminating null.
799810
///
800811
/// # Safety
801-
///
802-
/// unsafe because it takes a raw pointer as argument. The caller must
803-
/// ensure that the pointer is valid.
804-
#[cfg(not(target_os = "fuchsia"))]
805-
pub unsafe fn from_raw_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> {
806-
if addr.is_null() {
807-
None
808-
} else {
809-
match AddressFamily::from_i32(i32::from((*addr).sa_family)) {
810-
Some(AddressFamily::Unix) => None,
811-
Some(AddressFamily::Inet) => Some(SockAddr::Inet(
812-
InetAddr::V4(*(addr as *const libc::sockaddr_in)))),
813-
Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
814-
InetAddr::V6(*(addr as *const libc::sockaddr_in6)))),
815-
#[cfg(any(target_os = "android", target_os = "linux"))]
816-
Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
817-
NetlinkAddr(*(addr as *const libc::sockaddr_nl)))),
818-
#[cfg(any(target_os = "ios", target_os = "macos"))]
819-
Some(AddressFamily::System) => Some(SockAddr::SysControl(
820-
SysControlAddr(*(addr as *const libc::sockaddr_ctl)))),
821-
#[cfg(any(target_os = "android", target_os = "linux"))]
822-
Some(AddressFamily::Packet) => Some(SockAddr::Link(
823-
LinkAddr(*(addr as *const libc::sockaddr_ll)))),
824-
#[cfg(any(target_os = "dragonfly",
825-
target_os = "freebsd",
826-
target_os = "ios",
827-
target_os = "macos",
828-
target_os = "netbsd",
829-
target_os = "illumos",
830-
target_os = "openbsd"))]
831-
Some(AddressFamily::Link) => {
832-
let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
833-
if ether_addr.is_empty() {
834-
None
835-
} else {
836-
Some(SockAddr::Link(ether_addr))
837-
}
838-
},
839-
#[cfg(any(target_os = "android", target_os = "linux"))]
840-
Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(
841-
VsockAddr(*(addr as *const libc::sockaddr_vm)))),
842-
// Other address families are currently not supported and simply yield a None
843-
// entry instead of a proper conversion to a `SockAddr`.
844-
Some(_) | None => None,
812+
/// `addr` must be a valid, non-null pointer, and `len` should describe the
813+
/// number of bytes within `*addr` that are initialized and represent data.
814+
pub unsafe fn from_raw_sockaddr(addr: *const libc::sockaddr, len: usize) -> Result<SockAddr> {
815+
let af = (*addr).sa_family;
816+
if len < mem::size_of_val(&af) {
817+
return Err(Errno::ENOTCONN);
818+
}
819+
820+
let af = AddressFamily::from_i32(af.into()).ok_or(Errno::EAFNOSUPPORT)?;
821+
match af {
822+
AddressFamily::Inet => {
823+
use libc::sockaddr_in;
824+
assert!(len as usize >= mem::size_of::<sockaddr_in>());
825+
let sin = *(addr as *const sockaddr_in);
826+
Ok(SockAddr::Inet(InetAddr::V4(sin)))
827+
}
828+
AddressFamily::Inet6 => {
829+
use libc::sockaddr_in6;
830+
assert!(len as usize >= mem::size_of::<sockaddr_in6>());
831+
let sin6 = *(addr as *const sockaddr_in6);
832+
Ok(SockAddr::Inet(InetAddr::V6(sin6)))
833+
}
834+
AddressFamily::Unix => {
835+
use libc::sockaddr_un;
836+
let pathlen = len - offset_of!(sockaddr_un, sun_path);
837+
let sun = *(addr as *const sockaddr_un);
838+
Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
839+
}
840+
#[cfg(any(target_os = "android", target_os = "linux"))]
841+
AddressFamily::Packet => {
842+
// Don't assert anything about the size.
843+
// Apparently the Linux kernel can return smaller sizes when
844+
// the value in the last element of sockaddr_ll (`sll_addr`) is
845+
// smaller than the declared size of that field
846+
let sll = *(addr as *const libc::sockaddr_ll);
847+
Ok(SockAddr::Link(LinkAddr(sll)))
848+
}
849+
#[cfg(any(target_os = "android", target_os = "linux"))]
850+
AddressFamily::Netlink => {
851+
let snl = *(addr as *const libc::sockaddr_nl);
852+
Ok(SockAddr::Netlink(NetlinkAddr(snl)))
853+
}
854+
#[cfg(any(target_os = "android", target_os = "linux"))]
855+
AddressFamily::Alg => {
856+
let salg = *(addr as *const libc::sockaddr_alg);
857+
Ok(SockAddr::Alg(AlgAddr(salg)))
858+
}
859+
#[cfg(any(target_os = "android", target_os = "linux"))]
860+
AddressFamily::Vsock => {
861+
let svm = *(addr as *const libc::sockaddr_vm);
862+
Ok(SockAddr::Vsock(VsockAddr(svm)))
863+
}
864+
#[cfg(any(target_os = "dragonfly",
865+
target_os = "freebsd",
866+
target_os = "ios",
867+
target_os = "macos",
868+
target_os = "netbsd",
869+
target_os = "illumos",
870+
target_os = "openbsd"))]
871+
AddressFamily::Link => {
872+
let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
873+
Ok(SockAddr::Link(ether_addr))
874+
}
875+
#[cfg(any(target_os = "ios", target_os = "macos"))]
876+
AddressFamily::System => {
877+
let sctl = SysControlAddr(*(addr as *const libc::sockaddr_ctl));
878+
Ok(SockAddr::SysControl(sctl))
845879
}
880+
_ => Err(Errno::EAFNOSUPPORT),
846881
}
882+
847883
}
848884

849885
/// Conversion from nix's SockAddr type to the underlying libc sockaddr type.
@@ -1381,8 +1417,12 @@ mod tests {
13811417
fn test_macos_loopback_datalink_addr() {
13821418
let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0];
13831419
let sa = bytes.as_ptr() as *const libc::sockaddr;
1384-
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
1385-
assert!(_sock_addr.is_none());
1420+
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
1421+
if let SockAddr::Link(link_addr) = sock_addr {
1422+
assert!(link_addr.is_empty())
1423+
} else {
1424+
panic!("bad family")
1425+
}
13861426
}
13871427

13881428
#[cfg(any(target_os = "dragonfly",
@@ -1396,11 +1436,7 @@ mod tests {
13961436
let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80];
13971437
let ptr = bytes.as_ptr();
13981438
let sa = ptr as *const libc::sockaddr;
1399-
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
1400-
1401-
assert!(_sock_addr.is_some());
1402-
1403-
let sock_addr = _sock_addr.unwrap();
1439+
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
14041440

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

@@ -1418,11 +1454,7 @@ mod tests {
14181454
let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
14191455
let ptr = bytes.as_ptr();
14201456
let sa = ptr as *const libc::sockaddr;
1421-
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
1422-
1423-
assert!(_sock_addr.is_some());
1424-
1425-
let sock_addr = _sock_addr.unwrap();
1457+
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
14261458

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

0 commit comments

Comments
 (0)