Skip to content

Commit

Permalink
Refactor SockAddr
Browse files Browse the repository at this point in the history
* Moves raw type definitions, such as sockaddr_storage, into the sys
  module importing them in the sockaddr modules.
* Moves SockAddr::unix to src/sys/unix.rs.
* Moves the tests into the tests module.
  • Loading branch information
Thomasdezeeuw committed May 8, 2020
1 parent 43b75d3 commit fd1565a
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 164 deletions.
221 changes: 65 additions & 156 deletions src/sockaddr.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
use std::fmt;
use std::mem::{self, MaybeUninit};
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::ptr;
use std::{fmt, ptr};

#[cfg(any(unix, target_os = "redox"))]
use libc::{
sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
use crate::sys::{
c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
AF_INET6,
};
#[cfg(windows)]
use winapi::shared::ws2def::{
ADDRESS_FAMILY as sa_family_t, AF_INET, AF_INET6, SOCKADDR as sockaddr,
SOCKADDR_IN as sockaddr_in, SOCKADDR_STORAGE as sockaddr_storage,
};
#[cfg(windows)]
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6;
#[cfg(windows)]
use winapi::um::ws2tcpip::socklen_t;

/// The address of a socket.
///
Expand All @@ -27,153 +16,86 @@ pub struct SockAddr {
len: socklen_t,
}

impl fmt::Debug for SockAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = fmt.debug_struct("SockAddr");
builder.field("family", &self.family());
if let Some(addr) = self.as_inet() {
builder.field("inet", &addr);
} else if let Some(addr) = self.as_inet6() {
builder.field("inet6", &addr);
}
builder.finish()
}
}

impl SockAddr {
/// Constructs a `SockAddr` from its raw components.
///
/// # Safety
///
/// It is up to the user to ensure the `addr` pointer and `len` length are
/// correct.
pub unsafe fn from_raw_parts(addr: *const sockaddr, len: socklen_t) -> SockAddr {
let mut storage = MaybeUninit::<sockaddr_storage>::uninit();
ptr::copy_nonoverlapping(
addr as *const _ as *const u8,
&mut storage as *mut _ as *mut u8,
storage.as_mut_ptr() as *mut u8,
len as usize,
);

SockAddr {
// This is safe as we written the address to `storage` above.
storage: storage.assume_init(),
len: len,
}
}

/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
///
/// This function is only available on Unix.
///
/// # Failure
///
/// Returns an error if the path is longer than `SUN_LEN`.
#[cfg(all(unix, feature = "all"))]
pub fn unix<P>(path: P) -> ::std::io::Result<SockAddr>
where
P: AsRef<::std::path::Path>,
{
use libc::{c_char, sockaddr_un, AF_UNIX};
use std::cmp::Ordering;
use std::io;
use std::os::unix::ffi::OsStrExt;

unsafe {
let mut addr = mem::zeroed::<sockaddr_un>();
addr.sun_family = AF_UNIX as sa_family_t;

let bytes = path.as_ref().as_os_str().as_bytes();

match (bytes.get(0), bytes.len().cmp(&addr.sun_path.len())) {
// Abstract paths don't need a null terminator
(Some(&0), Ordering::Greater) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be no longer than SUN_LEN",
));
}
(Some(&0), _) => {}
(_, Ordering::Greater) | (_, Ordering::Equal) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be shorter than SUN_LEN",
));
}
_ => {}
}

for (dst, src) in addr.sun_path.iter_mut().zip(bytes) {
*dst = *src as c_char;
}
// null byte for pathname is already there since we zeroed up front
/// Returns this address's family.
pub fn family(&self) -> sa_family_t {
self.storage.ss_family
}

let base = &addr as *const _ as usize;
let path = &addr.sun_path as *const _ as usize;
let sun_path_offset = path - base;
/// Returns the size of this address in bytes.
pub fn len(&self) -> socklen_t {
self.len
}

let mut len = sun_path_offset + bytes.len();
match bytes.get(0) {
Some(&0) | None => {}
Some(_) => len += 1,
}
Ok(SockAddr::from_raw_parts(
&addr as *const _ as *const _,
len as socklen_t,
))
}
/// Returns a raw pointer to the address.
pub fn as_ptr(&self) -> *const sockaddr {
&self.storage as *const _ as *const _
}

unsafe fn as_<T>(&self, family: sa_family_t) -> Option<T> {
if self.storage.ss_family != family {
return None;
/// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IP v4)
/// or `AF_INET6` (IP v6) family, otherwise returns `None`.
pub fn as_std(&self) -> Option<SocketAddr> {
let storage: *const sockaddr_storage = (&self.storage) as *const _;
match self.storage.ss_family as c_int {
AF_INET => Some(SocketAddr::V4(unsafe {
*(storage as *const sockaddr_in as *const _)
})),
AF_INET6 => Some(SocketAddr::V6(unsafe {
*(storage as *const sockaddr_in6 as *const _)
})),
_ => None,
}

Some(mem::transmute_copy(&self.storage))
}

/// Returns this address as a `SocketAddrV4` if it is in the `AF_INET`
/// family.
pub fn as_inet(&self) -> Option<SocketAddrV4> {
unsafe { self.as_(AF_INET as sa_family_t) }
if self.storage.ss_family as c_int == AF_INET {
let storage: *const sockaddr_storage = (&self.storage) as *const _;
Some(unsafe { *(storage as *const sockaddr_in as *const _) })
} else {
None
}
}

/// Returns this address as a `SocketAddrV6` if it is in the `AF_INET6`
/// family.
pub fn as_inet6(&self) -> Option<SocketAddrV6> {
unsafe { self.as_(AF_INET6 as sa_family_t) }
}

/// Returns this address as a `SocketAddr` if it is in the `AF_INET`
/// or `AF_INET6` family, otherwise returns `None`.
pub fn as_std(&self) -> Option<SocketAddr> {
if let Some(addr) = self.as_inet() {
Some(SocketAddr::V4(addr))
} else if let Some(addr) = self.as_inet6() {
Some(SocketAddr::V6(addr))
if self.storage.ss_family as c_int == AF_INET6 {
let storage: *const sockaddr_storage = (&self.storage) as *const _;
Some(unsafe { *(storage as *const sockaddr_in6 as *const _) })
} else {
None
}
}

/// Returns this address's family.
pub fn family(&self) -> sa_family_t {
self.storage.ss_family
}

/// Returns the size of this address in bytes.
pub fn len(&self) -> socklen_t {
self.len
}

/// Returns a raw pointer to the address.
pub fn as_ptr(&self) -> *const sockaddr {
&self.storage as *const _ as *const _
}
}

// SocketAddrV4 and SocketAddrV6 are just wrappers around sockaddr_in and sockaddr_in6

// check to make sure that the sizes at least match up
fn _size_checks(v4: SocketAddrV4, v6: SocketAddrV6) {
unsafe {
mem::transmute::<SocketAddrV4, sockaddr_in>(v4);
mem::transmute::<SocketAddrV6, sockaddr_in6>(v6);
impl From<SocketAddr> for SockAddr {
fn from(addr: SocketAddr) -> SockAddr {
match addr {
SocketAddr::V4(addr) => addr.into(),
SocketAddr::V6(addr) => addr.into(),
}
}
}

Expand All @@ -199,36 +121,23 @@ impl From<SocketAddrV6> for SockAddr {
}
}

impl From<SocketAddr> for SockAddr {
fn from(addr: SocketAddr) -> SockAddr {
match addr {
SocketAddr::V4(addr) => addr.into(),
SocketAddr::V6(addr) => addr.into(),
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn inet() {
let raw = "127.0.0.1:80".parse::<SocketAddrV4>().unwrap();
let addr = SockAddr::from(raw);
assert!(addr.as_inet6().is_none());
let addr = addr.as_inet().unwrap();
assert_eq!(raw, addr);
}

#[test]
fn inet6() {
let raw = "[2001:db8::ff00:42:8329]:80"
.parse::<SocketAddrV6>()
.unwrap();
let addr = SockAddr::from(raw);
assert!(addr.as_inet().is_none());
let addr = addr.as_inet6().unwrap();
assert_eq!(raw, addr);
impl fmt::Debug for SockAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = fmt.debug_struct("SockAddr");
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "hermit",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "vxworks",
))]
f.field("ss_len", &self.storage.ss_len);
f.field("ss_family", &self.storage.ss_family)
.field("len", &self.len)
.finish()
}
}
68 changes: 62 additions & 6 deletions src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;
use std::fmt;
use std::io;
use std::io::{Read, Write};
use std::mem;
use std::net::Shutdown;
use std::net::{self, Ipv4Addr, Ipv6Addr};
#[cfg(feature = "all")]
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
use std::os::unix::prelude::*;
#[cfg(feature = "all")]
use std::path::Path;
#[cfg(feature = "all")]
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use std::{cmp, fmt, io, mem};

use libc::{self, c_void, socklen_t, ssize_t};
use libc::{self, c_void, ssize_t};

use crate::{Domain, Type};

Expand All @@ -34,6 +37,10 @@ pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM};
pub(crate) use libc::{SOCK_RAW, SOCK_SEQPACKET};
// Used in `Protocol`.
pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP};
// Used in `SockAddr`.
pub(crate) use libc::{
sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t,
};

cfg_if::cfg_if! {
if #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
Expand Down Expand Up @@ -184,6 +191,55 @@ impl_debug!(
libc::IPPROTO_UDP,
);

/// Unix only API.
impl SockAddr {
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
///
/// This function is only available on Unix.
///
/// # Failure
///
/// Returns an error if the path is longer than `SUN_LEN`.
#[cfg(feature = "all")]
pub fn unix<P>(path: P) -> io::Result<SockAddr>
where
P: AsRef<Path>,
{
// Safety: zeroed `sockaddr_un` is valid.
let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };

let bytes = path.as_ref().as_os_str().as_bytes();
if bytes.len() >= addr.sun_path.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be shorter than SUN_LEN",
));
}

addr.sun_family = libc::AF_UNIX as sa_family_t;
// Safety: `bytes` and `addr.sun_path` are not overlapping and `bytes`
// points to valid memory.
unsafe {
ptr::copy_nonoverlapping(
bytes.as_ptr(),
addr.sun_path.as_mut_ptr() as *mut u8,
bytes.len(),
)
};
// Zeroed memory above, so the path is already null terminated.

let base = &addr as *const _ as usize;
let path = &addr.sun_path as *const _ as usize;
let sun_path_offset = path - base;
let mut len = sun_path_offset + bytes.len();
match bytes.first() {
Some(&0) | None => {}
Some(_) => len += 1,
};
Ok(unsafe { SockAddr::from_raw_parts(&addr as *const _ as *const _, len as socklen_t) })
}
}

pub struct Socket {
fd: c_int,
}
Expand Down
7 changes: 7 additions & 0 deletions src/sys/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ pub(crate) const IPPROTO_ICMP: c_int = winapi::shared::ws2def::IPPROTO_ICMP as c
pub(crate) const IPPROTO_ICMPV6: c_int = winapi::shared::ws2def::IPPROTO_ICMPV6 as c_int;
pub(crate) const IPPROTO_TCP: c_int = winapi::shared::ws2def::IPPROTO_TCP as c_int;
pub(crate) const IPPROTO_UDP: c_int = winapi::shared::ws2def::IPPROTO_UDP as c_int;
// Used in `SockAddr`.
pub(crate) use winapi::shared::ws2def::{
ADDRESS_FAMILY as sa_family_t, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
SOCKADDR_STORAGE as sockaddr_storage,
};
pub(crate) use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6;
pub(crate) use winapi::um::ws2tcpip::socklen_t;

impl_debug!(
crate::Domain,
Expand Down

0 comments on commit fd1565a

Please sign in to comment.