Skip to content

Commit

Permalink
Merge pull request #54 from mstyura/fix-sockaddr-to-c-conversion
Browse files Browse the repository at this point in the history
Fix std::net::SocketAddr to libc::sockaddr conversion.
  • Loading branch information
faern committed Aug 21, 2024
2 parents fd43df6 + 5f28321 commit d180e68
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Line wrap the file at 100 chars. Th


## [Unreleased]
- Fix `std::net::SocketAddr` conversion to `libc::sockaddr`

## [0.6.0] - 2024-01-31
### Changed
Expand Down
66 changes: 56 additions & 10 deletions system-configuration/src/network_reachability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,24 +344,35 @@ impl<T: Fn(ReachabilityFlags) + Sync + Send> NetworkReachabilityCallbackContext<
/// libc::sockaddr_in6, depending on the passed in standard library SocketAddr.
fn to_c_sockaddr(addr: SocketAddr) -> Box<libc::sockaddr> {
let ptr = match addr {
// See reference conversion from socket2:
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sockaddr.rs#L277-L287
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sys/unix.rs#L1356-L1363
SocketAddr::V4(addr) => Box::into_raw(Box::new(libc::sockaddr_in {
sin_len: std::mem::size_of::<libc::sockaddr_in>() as u8,
sin_family: libc::AF_INET as u8,
sin_port: addr.port(),
sin_addr: libc::in_addr {
s_addr: u32::from(*addr.ip()),
sin_family: libc::AF_INET as libc::sa_family_t,
sin_port: addr.port().to_be(),
sin_addr: {
// `s_addr` is stored as BE on all machines, and the array is in BE order.
// So the native endian conversion method is used so that it's never
// swapped.
libc::in_addr {
s_addr: u32::from_ne_bytes(addr.ip().octets()),
}
},
sin_zero: [0i8; 8],
sin_zero: Default::default(),
})) as *mut c_void,
// See reference conversion from socket2:
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sockaddr.rs#L314-L331
// https://github.com/rust-lang/socket2/blob/3a938932829ea6ee3025d2d7a86c7b095c76e6c3/src/sys/unix.rs#L1369-L1373
SocketAddr::V6(addr) => Box::into_raw(Box::new(libc::sockaddr_in6 {
sin6_len: std::mem::size_of::<libc::sockaddr_in6>() as u8,
sin6_family: libc::AF_INET6 as u8,
sin6_port: addr.port(),
sin6_flowinfo: 0,
sin6_family: libc::AF_INET6 as libc::sa_family_t,
sin6_port: addr.port().to_be(),
sin6_flowinfo: addr.flowinfo(),
sin6_addr: libc::in6_addr {
s6_addr: addr.ip().octets(),
},
sin6_scope_id: 0,
sin6_scope_id: addr.scope_id(),
})) as *mut c_void,
};

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

use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop};
use std::ffi::CString;
use std::{
ffi::CString,
net::{Ipv4Addr, Ipv6Addr},
};

#[test]
fn test_network_reachability_from_addr() {
Expand Down Expand Up @@ -434,6 +448,38 @@ mod test {
}
}

#[test]
fn test_sockaddr_local_to_dns_google_pair_reachability() {
let sockaddrs = [
"[2001:4860:4860::8844]:443".parse::<SocketAddr>().unwrap(),
"8.8.4.4:443".parse().unwrap(),
];
for remote_addr in sockaddrs {
match std::net::TcpStream::connect(remote_addr) {
Err(_) => {
let local_addr = if remote_addr.is_ipv4() {
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0)
} else {
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0)
};
let reachability =
SCNetworkReachability::from_addr_pair(local_addr, remote_addr);
let reachability_flags = reachability.reachability().unwrap();
// Verify that not established tcp connection path is reported as not reachable.
assert!(!reachability_flags.contains(ReachabilityFlags::REACHABLE));
}
Ok(tcp) => {
let local = tcp.local_addr().unwrap();
let remote = tcp.peer_addr().unwrap();
let reachability = SCNetworkReachability::from_addr_pair(local, remote);
let reachability_flags = reachability.reachability().unwrap();
// Verify established tcp connection path is reported as reachable.
assert!(reachability_flags.contains(ReachabilityFlags::REACHABLE));
}
}
}
}

#[test]
fn test_reachability_ref_from_host() {
let valid_inputs = vec!["example.com", "host-in-local-network", "en0"];
Expand Down

0 comments on commit d180e68

Please sign in to comment.