use crate::container::types::ContainerPortMappingData;
use core::mem::{size_of, zeroed};
use core::ptr::copy_nonoverlapping;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use windows_sys::Win32::Networking::WinSock::{
AF_INET, AF_INET6, IN_ADDR, IN_ADDR_0, IN6_ADDR, IN6_ADDR_0, SOCKADDR_IN, SOCKADDR_IN6,
SOCKADDR_STORAGE,
};
fn sockaddr_in_v4(addr: Ipv4Addr, port: u16) -> SOCKADDR_STORAGE {
let mut sin: SOCKADDR_IN = unsafe { zeroed() };
sin.sin_family = AF_INET;
sin.sin_port = port.to_be();
sin.sin_addr = IN_ADDR {
S_un: IN_ADDR_0 {
S_addr: u32::from_ne_bytes(addr.octets()),
},
};
let mut storage: SOCKADDR_STORAGE = unsafe { zeroed() };
unsafe {
copy_nonoverlapping(
&sin as *const SOCKADDR_IN as *const u8,
&mut storage as *mut SOCKADDR_STORAGE as *mut u8,
size_of::<SOCKADDR_IN>(),
);
}
storage
}
fn sockaddr_in_v6(addr: Ipv6Addr, port: u16) -> SOCKADDR_STORAGE {
let mut sin6: SOCKADDR_IN6 = unsafe { zeroed() };
sin6.sin6_family = AF_INET6;
sin6.sin6_port = port.to_be();
sin6.sin6_addr = IN6_ADDR {
u: IN6_ADDR_0 {
Byte: addr.octets(),
},
};
let mut storage: SOCKADDR_STORAGE = unsafe { zeroed() };
unsafe {
copy_nonoverlapping(
&sin6 as *const SOCKADDR_IN6 as *const u8,
&mut storage as *mut SOCKADDR_STORAGE as *mut u8,
size_of::<SOCKADDR_IN6>(),
);
}
storage
}
pub(crate) fn build_sockaddrs(mappings: &[ContainerPortMappingData]) -> Vec<SOCKADDR_STORAGE> {
let bound_count = mappings.iter().filter(|m| m.bind_ip.is_some()).count();
let mut sockaddrs = Vec::with_capacity(bound_count);
for mapping in mappings {
match mapping.bind_ip {
Some(IpAddr::V4(ipv4)) => sockaddrs.push(sockaddr_in_v4(ipv4, mapping.windows_port)),
Some(IpAddr::V6(ipv6)) => sockaddrs.push(sockaddr_in_v6(ipv6, mapping.windows_port)),
None => {}
}
}
sockaddrs
}
#[cfg(test)]
mod tests {
use super::*;
use wslcsdk_sys::types::WslcPortProtocol;
fn mapping(bind_ip: Option<IpAddr>, windows_port: u16) -> ContainerPortMappingData {
ContainerPortMappingData {
windows_port,
container_port: 80,
protocol: WslcPortProtocol::Tcp,
bind_ip,
}
}
fn as_bytes(storage: &SOCKADDR_STORAGE) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
storage as *const SOCKADDR_STORAGE as *const u8,
size_of::<SOCKADDR_STORAGE>(),
)
}
}
#[test]
fn test_ipv4_socketaddr_layout() {
let ip: Ipv4Addr = "127.0.0.1".parse().expect("解析失败");
let storage = sockaddr_in_v4(ip, 8080);
let bytes = as_bytes(&storage);
assert_eq!(u16::from_ne_bytes([bytes[0], bytes[1]]), AF_INET);
assert_eq!(&bytes[2..4], &8080u16.to_be_bytes());
assert_eq!(&bytes[4..8], &[127, 0, 0, 1]);
assert!(bytes[8..].iter().all(|b| *b == 0), "尾部余量应为零");
}
#[test]
fn test_ipv6_socketaddr_layout() {
let ip: Ipv6Addr = "::1".parse().expect("解析失败");
let storage = sockaddr_in_v6(ip, 9090);
let bytes = as_bytes(&storage);
assert_eq!(u16::from_ne_bytes([bytes[0], bytes[1]]), AF_INET6);
assert_eq!(&bytes[2..4], &9090u16.to_be_bytes());
let mut expected = [0u8; 16];
expected[15] = 1;
assert_eq!(&bytes[8..24], &expected[..]);
}
#[test]
fn test_port_is_stored_in_network_byte_order() {
let storage = sockaddr_in_v4("0.0.0.0".parse().expect("解析失败"), 0x1234);
assert_eq!(&as_bytes(&storage)[2..4], &[0x12, 0x34]);
}
#[test]
fn test_build_sockaddrs_skips_unbound_entries() {
let mappings = vec![
mapping(None, 1000),
mapping(Some("127.0.0.1".parse().expect("解析失败")), 1001),
mapping(None, 1002),
mapping(Some("::1".parse().expect("解析失败")), 1003),
];
let sockaddrs = build_sockaddrs(&mappings);
assert_eq!(sockaddrs.len(), 2);
assert_eq!(family_of(&sockaddrs[0]), AF_INET);
assert_eq!(family_of(&sockaddrs[1]), AF_INET6);
}
fn family_of(storage: &SOCKADDR_STORAGE) -> u16 {
let bytes = as_bytes(storage);
u16::from_ne_bytes([bytes[0], bytes[1]])
}
#[test]
fn test_build_sockaddrs_empty_input_yields_empty_output() {
assert!(build_sockaddrs(&[]).is_empty());
assert!(build_sockaddrs(&[mapping(None, 8080)]).is_empty());
}
#[test]
fn test_storage_size_is_sufficient_for_both_families() {
assert!(size_of::<SOCKADDR_STORAGE>() >= size_of::<SOCKADDR_IN>());
assert!(size_of::<SOCKADDR_STORAGE>() >= size_of::<SOCKADDR_IN6>());
}
}