Skip to content

Commit d180e68

Browse files
committed
Merge pull request #54 from mstyura/fix-sockaddr-to-c-conversion
Fix std::net::SocketAddr to libc::sockaddr conversion.
2 parents fd43df6 + 5f28321 commit d180e68

File tree

2 files changed

+57
-10
lines changed

2 files changed

+57
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Line wrap the file at 100 chars. Th
2222

2323

2424
## [Unreleased]
25+
- Fix `std::net::SocketAddr` conversion to `libc::sockaddr`
2526

2627
## [0.6.0] - 2024-01-31
2728
### Changed

system-configuration/src/network_reachability.rs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,24 +344,35 @@ impl<T: Fn(ReachabilityFlags) + Sync + Send> NetworkReachabilityCallbackContext<
344344
/// libc::sockaddr_in6, depending on the passed in standard library SocketAddr.
345345
fn to_c_sockaddr(addr: SocketAddr) -> Box<libc::sockaddr> {
346346
let ptr = match addr {
347+
// See reference conversion from socket2:
348+
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sockaddr.rs#L277-L287
349+
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sys/unix.rs#L1356-L1363
347350
SocketAddr::V4(addr) => Box::into_raw(Box::new(libc::sockaddr_in {
348351
sin_len: std::mem::size_of::<libc::sockaddr_in>() as u8,
349-
sin_family: libc::AF_INET as u8,
350-
sin_port: addr.port(),
351-
sin_addr: libc::in_addr {
352-
s_addr: u32::from(*addr.ip()),
352+
sin_family: libc::AF_INET as libc::sa_family_t,
353+
sin_port: addr.port().to_be(),
354+
sin_addr: {
355+
// `s_addr` is stored as BE on all machines, and the array is in BE order.
356+
// So the native endian conversion method is used so that it's never
357+
// swapped.
358+
libc::in_addr {
359+
s_addr: u32::from_ne_bytes(addr.ip().octets()),
360+
}
353361
},
354-
sin_zero: [0i8; 8],
362+
sin_zero: Default::default(),
355363
})) as *mut c_void,
364+
// See reference conversion from socket2:
365+
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sockaddr.rs#L314-L331
366+
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sys/unix.rs#L1369-L1373
356367
SocketAddr::V6(addr) => Box::into_raw(Box::new(libc::sockaddr_in6 {
357368
sin6_len: std::mem::size_of::<libc::sockaddr_in6>() as u8,
358-
sin6_family: libc::AF_INET6 as u8,
359-
sin6_port: addr.port(),
360-
sin6_flowinfo: 0,
369+
sin6_family: libc::AF_INET6 as libc::sa_family_t,
370+
sin6_port: addr.port().to_be(),
371+
sin6_flowinfo: addr.flowinfo(),
361372
sin6_addr: libc::in6_addr {
362373
s6_addr: addr.ip().octets(),
363374
},
364-
sin6_scope_id: 0,
375+
sin6_scope_id: addr.scope_id(),
365376
})) as *mut c_void,
366377
};
367378

@@ -373,7 +384,10 @@ mod test {
373384
use super::*;
374385

375386
use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop};
376-
use std::ffi::CString;
387+
use std::{
388+
ffi::CString,
389+
net::{Ipv4Addr, Ipv6Addr},
390+
};
377391

378392
#[test]
379393
fn test_network_reachability_from_addr() {
@@ -434,6 +448,38 @@ mod test {
434448
}
435449
}
436450

451+
#[test]
452+
fn test_sockaddr_local_to_dns_google_pair_reachability() {
453+
let sockaddrs = [
454+
"[2001:4860:4860::8844]:443".parse::<SocketAddr>().unwrap(),
455+
"8.8.4.4:443".parse().unwrap(),
456+
];
457+
for remote_addr in sockaddrs {
458+
match std::net::TcpStream::connect(remote_addr) {
459+
Err(_) => {
460+
let local_addr = if remote_addr.is_ipv4() {
461+
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0)
462+
} else {
463+
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0)
464+
};
465+
let reachability =
466+
SCNetworkReachability::from_addr_pair(local_addr, remote_addr);
467+
let reachability_flags = reachability.reachability().unwrap();
468+
// Verify that not established tcp connection path is reported as not reachable.
469+
assert!(!reachability_flags.contains(ReachabilityFlags::REACHABLE));
470+
}
471+
Ok(tcp) => {
472+
let local = tcp.local_addr().unwrap();
473+
let remote = tcp.peer_addr().unwrap();
474+
let reachability = SCNetworkReachability::from_addr_pair(local, remote);
475+
let reachability_flags = reachability.reachability().unwrap();
476+
// Verify established tcp connection path is reported as reachable.
477+
assert!(reachability_flags.contains(ReachabilityFlags::REACHABLE));
478+
}
479+
}
480+
}
481+
}
482+
437483
#[test]
438484
fn test_reachability_ref_from_host() {
439485
let valid_inputs = vec!["example.com", "host-in-local-network", "en0"];

0 commit comments

Comments
 (0)