Skip to content

Commit 1bc7f71

Browse files
committed
do not assume SocketAddr is castable from/to sockaddr
upcoming stdlib 1.64 will switch to pure rust types rust-lang/rust#78802 fix #3
1 parent c069d35 commit 1bc7f71

File tree

2 files changed

+97
-29
lines changed

2 files changed

+97
-29
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ This crate provides a type that can act as a platform-native socket address
1414

1515
## Motivation
1616

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

2221
This crate provides `OsSocketAddr` which holds a `libc::sockaddr` (containing an IPv4 or IPv6
2322
address) and the conversion functions from/into `SocketAddr`.

src/lib.rs

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
//!
44
//! # Motivation
55
//!
6-
//! The std crate provides [SocketAddr] for managing socket addresses. Its `V4` variant
7-
//! encapsulates [libc::sockaddr_in] and its `V6` variant encapsulates [libc::sockaddr_in6].
8-
//! However there is no easy way to convert `SocketAddr` from/into a `libc::sockaddr` because
9-
//! `SocketAddr` is a rust enum.
6+
//! The std crate provides [SocketAddr] for managing socket addresses. However there is no easy way
7+
//! to convert `SocketAddr` from/into a `libc::sockaddr` because `SocketAddr` has a different
8+
//! internal layout.
9+
1010
//!
1111
//! This crate provides [OsSocketAddr] which holds a `libc::sockaddr` (containing an IPv4 or IPv6
1212
//! address) and the conversion functions from/into `SocketAddr`.
@@ -95,16 +95,18 @@
9595
extern crate libc;
9696

9797
use std::convert::TryInto;
98-
use std::net::SocketAddr;
98+
use std::net::{Ipv4Addr,Ipv6Addr,SocketAddr,SocketAddrV4,SocketAddrV6};
9999

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

103103
#[cfg(target_family = "windows")]
104104
use winapi::{
105105
shared::{
106+
inaddr::in_addr, in6addr::in6_addr,
106107
ws2def::{AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in},
107108
ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6,
109+
ws2ipdef::SOCKADDR_IN6_LH_u,
108110
},
109111
um::ws2tcpip::socklen_t,
110112
};
@@ -120,7 +122,9 @@ use winapi::{
120122
/// See [crate] level documentation.
121123
///
122124
#[derive(Copy, Clone)]
123-
pub struct OsSocketAddr {
125+
#[repr(C)]
126+
pub union OsSocketAddr {
127+
sa4: sockaddr_in,
124128
sa6: sockaddr_in6,
125129
}
126130

@@ -169,7 +173,7 @@ impl OsSocketAddr {
169173
/// * `AF_INET6` -> the size of [sockaddr_in6]
170174
/// * *other* -> 0
171175
pub fn len(&self) -> socklen_t {
172-
(match self.sa6.sin6_family as i32 {
176+
(match unsafe { self.sa6.sin6_family } as i32 {
173177
AF_INET => std::mem::size_of::<sockaddr_in>(),
174178
AF_INET6 => std::mem::size_of::<sockaddr_in6>(),
175179
_ => 0,
@@ -183,12 +187,12 @@ impl OsSocketAddr {
183187

184188
/// Get a pointer to the internal buffer
185189
pub fn as_ptr(&self) -> *const sockaddr {
186-
&self.sa6 as *const _ as *const _
190+
unsafe { &self.sa6 as *const _ as *const _ }
187191
}
188192

189193
/// Get a mutable pointer to the internal buffer
190194
pub fn as_mut_ptr(&mut self) -> *mut sockaddr {
191-
&mut self.sa6 as *mut _ as *mut _
195+
unsafe { &mut self.sa6 as *mut _ as *mut _ }
192196
}
193197
}
194198

@@ -243,8 +247,29 @@ impl TryInto<SocketAddr> for OsSocketAddr {
243247
fn try_into(self) -> Result<SocketAddr, BadFamilyError> {
244248
unsafe {
245249
match self.sa6.sin6_family as i32 {
246-
AF_INET => Ok(SocketAddr::V4(*(self.as_ptr() as *const _))),
247-
AF_INET6 => Ok(SocketAddr::V6(*(self.as_ptr() as *const _))),
250+
AF_INET => {
251+
#[cfg(not(target_family = "windows"))]
252+
let ip = self.sa4.sin_addr.s_addr;
253+
#[cfg(target_family = "windows")]
254+
let ip = *self.sa4.sin_addr.S_un.S_addr();
255+
256+
Ok(SocketAddr::V4(SocketAddrV4::new(
257+
Ipv4Addr::from(u32::from_be(ip)),
258+
u16::from_be(self.sa4.sin_port),
259+
)))
260+
},
261+
AF_INET6 => {
262+
#[cfg(not(target_family = "windows"))]
263+
let (ip, scope_id) = (self.sa6.sin6_addr.s6_addr, self.sa6.sin6_scope_id);
264+
#[cfg(target_family = "windows")]
265+
let (ip, scope_id) = (*self.sa6.sin6_addr.u.Byte(), *self.sa6.u.sin6_scope_id());
266+
267+
Ok(SocketAddr::V6(SocketAddrV6::new(
268+
Ipv6Addr::from(u128::from_be_bytes(ip)),
269+
u16::from_be(self.sa6.sin6_port),
270+
self.sa6.sin6_flowinfo,
271+
scope_id)))
272+
},
248273
f => Err(BadFamilyError(f)),
249274
}
250275
}
@@ -266,17 +291,64 @@ impl std::fmt::Display for BadFamilyError {
266291

267292
impl From<SocketAddr> for OsSocketAddr {
268293
fn from(addr: SocketAddr) -> Self {
269-
OsSocketAddr {
270-
sa6: unsafe {
271-
match addr {
272-
SocketAddr::V4(addr) => {
273-
let mut sa6 = std::mem::zeroed();
274-
*(&mut sa6 as *mut _ as *mut _) = addr;
275-
sa6
276-
}
277-
SocketAddr::V6(addr) => *(&addr as *const _ as *const _),
294+
match addr {
295+
SocketAddr::V4(addr) => {
296+
let raw_ip = u32::to_be((*addr.ip()).into());
297+
Self{ sa4: sockaddr_in{
298+
#[cfg(not(target_os = "macos"))]
299+
sin_family: AF_INET as u16,
300+
301+
#[cfg(target_os = "macos")]
302+
sin_len: std::mem::size_of::<sockaddr_in>() as u8,
303+
#[cfg(target_os = "macos")]
304+
sin_family: AF_INET as u8,
305+
306+
#[cfg(not(target_family = "windows"))]
307+
sin_addr: in_addr{ s_addr: raw_ip },
308+
#[cfg(target_family = "windows")]
309+
sin_addr: unsafe {
310+
let mut ip : in_addr = std::mem::zeroed();
311+
*ip.S_un.S_addr_mut() = raw_ip;
312+
ip
313+
},
314+
315+
sin_port: u16::to_be(addr.port()),
316+
sin_zero: [0; 8],
278317
}
279-
},
318+
}},
319+
SocketAddr::V6(addr) => {
320+
let raw_ip = u128::to_be_bytes((*addr.ip()).into());
321+
Self{ sa6: sockaddr_in6{
322+
#[cfg(not(target_os = "macos"))]
323+
sin6_family: AF_INET6 as u16,
324+
325+
#[cfg(target_os = "macos")]
326+
sin6_len: std::mem::size_of::<sockaddr_in6>() as u8,
327+
#[cfg(target_os = "macos")]
328+
sin6_family: AF_INET6 as u8,
329+
330+
#[cfg(not(target_family = "windows"))]
331+
sin6_addr: in6_addr{ s6_addr: raw_ip },
332+
#[cfg(target_family = "windows")]
333+
sin6_addr: unsafe {
334+
let mut ip : in6_addr = std::mem::zeroed();
335+
*ip.u.Byte_mut() = raw_ip;
336+
ip
337+
},
338+
339+
#[cfg(not(target_family = "windows"))]
340+
sin6_scope_id: addr.scope_id(),
341+
#[cfg(target_family = "windows")]
342+
u: unsafe {
343+
let mut u : SOCKADDR_IN6_LH_u = std::mem::zeroed();
344+
*u.sin6_scope_id_mut() = addr.scope_id();
345+
u
346+
},
347+
348+
sin6_port: u16::to_be(addr.port()),
349+
sin6_flowinfo: addr.flowinfo(),
350+
}}
351+
}
280352
}
281353
}
282354
}
@@ -304,9 +376,6 @@ mod tests {
304376
#[cfg(target_family = "unix")]
305377
use libc::{in6_addr, in_addr};
306378

307-
#[cfg(target_family = "windows")]
308-
use winapi::shared::{in6addr::in6_addr, inaddr::in_addr};
309-
310379
fn check_as_mut(osa: &mut OsSocketAddr) {
311380
let ptr = osa as *mut _ as usize;
312381
let buf = osa.as_mut();

0 commit comments

Comments
 (0)