Skip to content

Commit

Permalink
do not assume SocketAddr can be casted from/to sockaddr
Browse files Browse the repository at this point in the history
upcoming stdlib 1.64 will switch to pure rust types

rust-lang/rust#78802

fix #3
  • Loading branch information
a-ba committed Aug 21, 2022
1 parent c069d35 commit 5dc8a80
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 28 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ This crate provides a type that can act as a platform-native socket address

## Motivation

The std crate provides `SocketAddr` for managing socket addresses. Its `V4` variant
encapsulates `libc::sockaddr_in` and its `V6` variant encapsulates `libc::sockaddr_in6`.
However there is no easy way to convert `SocketAddr` from/into a `libc::sockaddr` because
`SocketAddr` is a rust enum.
The std crate provides `SocketAddr` for managing socket addresses. However there is no easy way to
convert `SocketAddr` from/into a `libc::sockaddr` because `SocketAddr` has a different internal
layout.

This crate provides `OsSocketAddr` which holds a `libc::sockaddr` (containing an IPv4 or IPv6
address) and the conversion functions from/into `SocketAddr`.
Expand Down
68 changes: 44 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
//!
//! # Motivation
//!
//! The std crate provides [SocketAddr] for managing socket addresses. Its `V4` variant
//! encapsulates [libc::sockaddr_in] and its `V6` variant encapsulates [libc::sockaddr_in6].
//! However there is no easy way to convert `SocketAddr` from/into a `libc::sockaddr` because
//! `SocketAddr` is a rust enum.
//! The std crate provides [SocketAddr] for managing socket addresses. However there is no easy way
//! to convert `SocketAddr` from/into a `libc::sockaddr` because `SocketAddr` has a different
//! internal layout.
//!
//! This crate provides [OsSocketAddr] which holds a `libc::sockaddr` (containing an IPv4 or IPv6
//! address) and the conversion functions from/into `SocketAddr`.
Expand Down Expand Up @@ -95,15 +95,16 @@
extern crate libc;

use std::convert::TryInto;
use std::net::SocketAddr;
use std::net::{Ipv4Addr,Ipv6Addr,SocketAddr,SocketAddrV4,SocketAddrV6};

#[cfg(target_family = "unix")]
use libc::{sockaddr, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6};
use libc::{sockaddr, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6, in_addr, in6_addr};

#[cfg(target_family = "windows")]
use winapi::{
shared::{
ws2def::{AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in},
ws2def::{AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
IN_ADDR as in_addr, IN6_ADDR as in6_addr },
ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6,
},
um::ws2tcpip::socklen_t,
Expand All @@ -120,7 +121,9 @@ use winapi::{
/// See [crate] level documentation.
///
#[derive(Copy, Clone)]
pub struct OsSocketAddr {
#[repr(C)]
pub union OsSocketAddr {
sa4: sockaddr_in,
sa6: sockaddr_in6,
}

Expand Down Expand Up @@ -169,7 +172,7 @@ impl OsSocketAddr {
/// * `AF_INET6` -> the size of [sockaddr_in6]
/// * *other* -> 0
pub fn len(&self) -> socklen_t {
(match self.sa6.sin6_family as i32 {
(match unsafe { self.sa6.sin6_family } as i32 {
AF_INET => std::mem::size_of::<sockaddr_in>(),
AF_INET6 => std::mem::size_of::<sockaddr_in6>(),
_ => 0,
Expand All @@ -183,12 +186,12 @@ impl OsSocketAddr {

/// Get a pointer to the internal buffer
pub fn as_ptr(&self) -> *const sockaddr {
&self.sa6 as *const _ as *const _
unsafe { &self.sa6 as *const _ as *const _ }
}

/// Get a mutable pointer to the internal buffer
pub fn as_mut_ptr(&mut self) -> *mut sockaddr {
&mut self.sa6 as *mut _ as *mut _
unsafe { &mut self.sa6 as *mut _ as *mut _ }
}
}

Expand Down Expand Up @@ -243,8 +246,16 @@ impl TryInto<SocketAddr> for OsSocketAddr {
fn try_into(self) -> Result<SocketAddr, BadFamilyError> {
unsafe {
match self.sa6.sin6_family as i32 {
AF_INET => Ok(SocketAddr::V4(*(self.as_ptr() as *const _))),
AF_INET6 => Ok(SocketAddr::V6(*(self.as_ptr() as *const _))),
AF_INET => Ok(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(self.sa4.sin_addr.s_addr)),
u16::from_be(self.sa4.sin_port),
))),
AF_INET6 => Ok(SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::from(u128::from_be_bytes(self.sa6.sin6_addr.s6_addr)),
u16::from_be(self.sa6.sin6_port),
self.sa6.sin6_flowinfo,
self.sa6.sin6_scope_id,
))),
f => Err(BadFamilyError(f)),
}
}
Expand All @@ -266,17 +277,26 @@ impl std::fmt::Display for BadFamilyError {

impl From<SocketAddr> for OsSocketAddr {
fn from(addr: SocketAddr) -> Self {
OsSocketAddr {
sa6: unsafe {
match addr {
SocketAddr::V4(addr) => {
let mut sa6 = std::mem::zeroed();
*(&mut sa6 as *mut _ as *mut _) = addr;
sa6
}
SocketAddr::V6(addr) => *(&addr as *const _ as *const _),
}
},
match addr {
SocketAddr::V4(addr) => Self{ sa4: sockaddr_in{
#[cfg(target_os = "macos")]
sin_len: std::mem::size_of::<sockaddr_in>() as u8,

sin_family: AF_INET as u16,
sin_port: u16::to_be(addr.port()),
sin_addr: in_addr{ s_addr: u32::to_be((*addr.ip()).into()) },
sin_zero: [0; 8],
}},
SocketAddr::V6(addr) => Self{ sa6: sockaddr_in6{
#[cfg(target_os = "macos")]
sin_len: std::mem::size_of::<sockaddr_in6>() as u8,

sin6_family: AF_INET6 as u16,
sin6_port: u16::to_be(addr.port()),
sin6_flowinfo: addr.flowinfo(),
sin6_addr: in6_addr{ s6_addr: u128::to_be_bytes((*addr.ip()).into()) },
sin6_scope_id: addr.scope_id(),
}}
}
}
}
Expand Down

0 comments on commit 5dc8a80

Please sign in to comment.