aboutsummaryrefslogtreecommitdiff
path: root/src/sys/unix/net.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys/unix/net.rs')
-rw-r--r--src/sys/unix/net.rs140
1 files changed, 103 insertions, 37 deletions
diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs
index 2671b42..2f8d618 100644
--- a/src/sys/unix/net.rs
+++ b/src/sys/unix/net.rs
@@ -1,11 +1,8 @@
-#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))]
-use std::net::SocketAddr;
+use std::io;
+use std::mem::size_of;
+use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
-#[cfg(all(feature = "os-poll", any(feature = "udp")))]
-pub(crate) fn new_ip_socket(
- addr: SocketAddr,
- socket_type: libc::c_int,
-) -> std::io::Result<libc::c_int> {
+pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
let domain = match addr {
SocketAddr::V4(..) => libc::AF_INET,
SocketAddr::V6(..) => libc::AF_INET6,
@@ -15,14 +12,7 @@ pub(crate) fn new_ip_socket(
}
/// Create a new non-blocking socket.
-#[cfg(all(
- feature = "os-poll",
- any(feature = "tcp", feature = "udp", feature = "uds")
-))]
-pub(crate) fn new_socket(
- domain: libc::c_int,
- socket_type: libc::c_int,
-) -> std::io::Result<libc::c_int> {
+pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> {
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
@@ -46,7 +36,7 @@ pub(crate) fn new_socket(
libc::SOL_SOCKET,
libc::SO_NOSIGPIPE,
&1 as *const libc::c_int as *const libc::c_void,
- std::mem::size_of::<libc::c_int>() as libc::socklen_t
+ size_of::<libc::c_int>() as libc::socklen_t
))
.map(|_| socket)
});
@@ -70,34 +60,110 @@ pub(crate) fn new_socket(
socket
}
-#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))]
-pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) {
- use std::mem::size_of_val;
+/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
+/// SocketAddr* types into their system representation. The benefit of this specific
+/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
+/// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
+#[repr(C)]
+pub(crate) union SocketAddrCRepr {
+ v4: libc::sockaddr_in,
+ v6: libc::sockaddr_in6,
+}
+
+impl SocketAddrCRepr {
+ pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
+ self as *const _ as *const libc::sockaddr
+ }
+}
+/// Converts a Rust `SocketAddr` into the system representation.
+pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
match addr {
- SocketAddr::V4(ref addr) => (
- addr as *const _ as *const libc::sockaddr,
- size_of_val(addr) as libc::socklen_t,
- ),
- SocketAddr::V6(ref addr) => (
- addr as *const _ as *const libc::sockaddr,
- size_of_val(addr) as libc::socklen_t,
- ),
+ SocketAddr::V4(ref addr) => {
+ // `s_addr` is stored as BE on all machine and the array is in BE order.
+ // So the native endian conversion method is used so that it's never swapped.
+ let sin_addr = libc::in_addr {
+ s_addr: u32::from_ne_bytes(addr.ip().octets()),
+ };
+
+ let sockaddr_in = libc::sockaddr_in {
+ sin_family: libc::AF_INET as libc::sa_family_t,
+ sin_port: addr.port().to_be(),
+ sin_addr,
+ sin_zero: [0; 8],
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin_len: 0,
+ };
+
+ let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
+ let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t;
+ (sockaddr, socklen)
+ }
+ SocketAddr::V6(ref addr) => {
+ let sockaddr_in6 = libc::sockaddr_in6 {
+ sin6_family: libc::AF_INET6 as libc::sa_family_t,
+ sin6_port: addr.port().to_be(),
+ sin6_addr: libc::in6_addr {
+ s6_addr: addr.ip().octets(),
+ },
+ sin6_flowinfo: addr.flowinfo(),
+ sin6_scope_id: addr.scope_id(),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin6_len: 0,
+ #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+ __sin6_src_id: 0,
+ };
+
+ let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
+ let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
+ (sockaddr, socklen)
+ }
}
}
-/// `storage` must be initialised to `sockaddr_in` or `sockaddr_in6`.
-#[cfg(all(feature = "os-poll", feature = "tcp"))]
+/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
+///
+/// # Safety
+///
+/// `storage` must have the `ss_family` field correctly initialized.
+/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
pub(crate) unsafe fn to_socket_addr(
storage: *const libc::sockaddr_storage,
-) -> std::io::Result<SocketAddr> {
+) -> io::Result<SocketAddr> {
match (*storage).ss_family as libc::c_int {
- libc::AF_INET => Ok(SocketAddr::V4(
- *(storage as *const libc::sockaddr_in as *const _),
- )),
- libc::AF_INET6 => Ok(SocketAddr::V6(
- *(storage as *const libc::sockaddr_in6 as *const _),
- )),
- _ => Err(std::io::ErrorKind::InvalidInput.into()),
+ libc::AF_INET => {
+ // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+ let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
+ let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
+ let port = u16::from_be(addr.sin_port);
+ Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
+ }
+ libc::AF_INET6 => {
+ // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+ let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
+ let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
+ let port = u16::from_be(addr.sin6_port);
+ Ok(SocketAddr::V6(SocketAddrV6::new(
+ ip,
+ port,
+ addr.sin6_flowinfo,
+ addr.sin6_scope_id,
+ )))
+ }
+ _ => Err(io::ErrorKind::InvalidInput.into()),
}
}