Skip to content

Commit 1cab7bf

Browse files
committed
Merge sockaddr_storage_to_addr and SockAddr::from_raw_sockaddr
1 parent 8e963a0 commit 1cab7bf

File tree

4 files changed

+187
-219
lines changed

4 files changed

+187
-219
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),
@@ -515,7 +512,7 @@ impl fmt::Display for Ipv6Addr {
515512
}
516513

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

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

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

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

13861426
#[cfg(any(target_os = "dragonfly",
@@ -1394,11 +1434,7 @@ mod tests {
13941434
let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80];
13951435
let ptr = bytes.as_ptr();
13961436
let sa = ptr as *const libc::sockaddr;
1397-
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
1398-
1399-
assert!(_sock_addr.is_some());
1400-
1401-
let sock_addr = _sock_addr.unwrap();
1437+
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
14021438

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

@@ -1416,11 +1452,7 @@ mod tests {
14161452
let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
14171453
let ptr = bytes.as_ptr();
14181454
let sa = ptr as *const libc::sockaddr;
1419-
let _sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa) };
1420-
1421-
assert!(_sock_addr.is_some());
1422-
1423-
let sock_addr = _sock_addr.unwrap();
1455+
let sock_addr = unsafe { SockAddr::from_raw_sockaddr(sa, bytes.len()).unwrap() };
14241456

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

0 commit comments

Comments
 (0)