Evan's Blog
EvanZhu0721 / 公开仓库

SBMS

镜像已同步

SBMS complete the calculation rules for the default logical desktop topology relationship and the physical dimensions of the displays in the Windows multi-screen collaboration system.

上游 GitHub
READ-ONLY MIRROR

README、源码与 Release 已保存到本站;源码视图固定到 commit a37d6c4b。

main/ root / src
a37d6c4b· 固定源码快照
src/network.rs9.2 KB
1use std::mem::{MaybeUninit, size_of};
2use std::net::Ipv4Addr;
3
4use windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_NO_DATA, NO_ERROR};
5use windows::Win32::NetworkManagement::IpHelper::{
6 GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER,
7 GAA_FLAG_SKIP_MULTICAST, GET_ADAPTERS_ADDRESSES_FLAGS, GetAdaptersAddresses,
8 IF_TYPE_ETHERNET_CSMACD, IF_TYPE_FASTETHER, IF_TYPE_FASTETHER_FX, IF_TYPE_GIGABITETHERNET,
9 IF_TYPE_IEEE80211, IF_TYPE_PPP, IF_TYPE_PROP_VIRTUAL, IF_TYPE_SOFTWARE_LOOPBACK,
10 IF_TYPE_TUNNEL, IF_TYPE_WWANPP, IF_TYPE_WWANPP2, IP_ADAPTER_ADDRESSES_LH,
11 IP_ADAPTER_GATEWAY_ADDRESS_LH,
12};
13use windows::Win32::NetworkManagement::Ndis::IfOperStatusUp;
14use windows::Win32::Networking::WinSock::{
15 AF_INET, IpDadStateDeprecated, IpDadStatePreferred, SOCKADDR_IN, SOCKET_ADDRESS,
16};
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19struct LanIpv4Candidate {
20 address: Ipv4Addr,
21 interface_type: u32,
22 has_gateway: bool,
23 has_physical_address: bool,
24 dad_rank: u8,
25 metric: u32,
26}
27
28pub(crate) fn preferred_lan_ipv4() -> Result<Option<Ipv4Addr>, String> {
29 let flags = GET_ADAPTERS_ADDRESSES_FLAGS(
30 GAA_FLAG_INCLUDE_GATEWAYS.0
31 | GAA_FLAG_SKIP_ANYCAST.0
32 | GAA_FLAG_SKIP_MULTICAST.0
33 | GAA_FLAG_SKIP_DNS_SERVER.0,
34 );
35 let mut required_bytes = 0_u32;
36 let sizing_status =
37 unsafe { GetAdaptersAddresses(AF_INET.0.into(), flags, None, None, &mut required_bytes) };
38 if sizing_status == ERROR_NO_DATA.0 || (sizing_status == NO_ERROR.0 && required_bytes == 0) {
39 return Ok(None);
40 }
41 if sizing_status != ERROR_BUFFER_OVERFLOW.0 {
42 return Err(format_windows_error(
43 "Couldn’t size the network adapter list",
44 sizing_status,
45 ));
46 }
47
48 for _ in 0..2 {
49 let record_count = (required_bytes as usize).div_ceil(size_of::<IP_ADAPTER_ADDRESSES_LH>());
50 let mut storage = Vec::<MaybeUninit<IP_ADAPTER_ADDRESSES_LH>>::with_capacity(record_count);
51 storage.resize_with(record_count, MaybeUninit::uninit);
52 let adapters = storage.as_mut_ptr().cast::<IP_ADAPTER_ADDRESSES_LH>();
53 let status = unsafe {
54 GetAdaptersAddresses(
55 AF_INET.0.into(),
56 flags,
57 None,
58 Some(adapters),
59 &mut required_bytes,
60 )
61 };
62 if status == ERROR_BUFFER_OVERFLOW.0 {
63 continue;
64 }
65 if status == ERROR_NO_DATA.0 {
66 return Ok(None);
67 }
68 if status != NO_ERROR.0 {
69 return Err(format_windows_error(
70 "Couldn’t enumerate network adapters",
71 status,
72 ));
73 }
74
75 let mut candidates = Vec::new();
76 let mut adapter = adapters;
77 while let Some(current) = unsafe { adapter.as_ref() } {
78 if current.OperStatus == IfOperStatusUp && !excluded_interface_type(current.IfType) {
79 let has_gateway = has_usable_ipv4_gateway(current.FirstGatewayAddress);
80 let has_physical_address = current.PhysicalAddressLength > 0;
81 let mut unicast = current.FirstUnicastAddress;
82 while let Some(address) = unsafe { unicast.as_ref() } {
83 let dad_rank = if address.DadState == IpDadStatePreferred {
84 Some(0)
85 } else if address.DadState == IpDadStateDeprecated {
86 Some(1)
87 } else {
88 None
89 };
90 if let Some(dad_rank) = dad_rank
91 && let Some(ipv4) = socket_address_ipv4(&address.Address)
92 && usable_lan_ipv4(ipv4)
93 {
94 candidates.push(LanIpv4Candidate {
95 address: ipv4,
96 interface_type: current.IfType,
97 has_gateway,
98 has_physical_address,
99 dad_rank,
100 metric: current.Ipv4Metric,
101 });
102 }
103 unicast = address.Next;
104 }
105 }
106 adapter = current.Next;
107 }
108 return Ok(select_lan_ipv4(&candidates));
109 }
110
111 Err("The network adapter list changed repeatedly while it was being read".into())
112}
113
114fn socket_address_ipv4(address: &SOCKET_ADDRESS) -> Option<Ipv4Addr> {
115 if address.lpSockaddr.is_null()
116 || address.iSockaddrLength < i32::try_from(size_of::<SOCKADDR_IN>()).unwrap_or(i32::MAX)
117 {
118 return None;
119 }
120 let socket = unsafe { &*address.lpSockaddr.cast::<SOCKADDR_IN>() };
121 if socket.sin_family != AF_INET {
122 return None;
123 }
124 let octets = unsafe { socket.sin_addr.S_un.S_un_b };
125 Some(Ipv4Addr::new(
126 octets.s_b1,
127 octets.s_b2,
128 octets.s_b3,
129 octets.s_b4,
130 ))
131}
132
133fn has_usable_ipv4_gateway(mut gateway: *mut IP_ADAPTER_GATEWAY_ADDRESS_LH) -> bool {
134 while let Some(current) = unsafe { gateway.as_ref() } {
135 if socket_address_ipv4(&current.Address).is_some_and(usable_lan_ipv4) {
136 return true;
137 }
138 gateway = current.Next;
139 }
140 false
141}
142
143fn format_windows_error(context: &str, code: u32) -> String {
144 format!(
145 "{context}: {}",
146 std::io::Error::from_raw_os_error(code as i32)
147 )
148}
149
150fn excluded_interface_type(interface_type: u32) -> bool {
151 matches!(
152 interface_type,
153 IF_TYPE_PPP | IF_TYPE_SOFTWARE_LOOPBACK | IF_TYPE_PROP_VIRTUAL | IF_TYPE_TUNNEL
154 )
155}
156
157fn physical_interface_rank(interface_type: u32) -> u8 {
158 match interface_type {
159 IF_TYPE_ETHERNET_CSMACD
160 | IF_TYPE_FASTETHER
161 | IF_TYPE_FASTETHER_FX
162 | IF_TYPE_GIGABITETHERNET
163 | IF_TYPE_IEEE80211
164 | IF_TYPE_WWANPP
165 | IF_TYPE_WWANPP2 => 0,
166 _ => 1,
167 }
168}
169
170fn usable_lan_ipv4(address: Ipv4Addr) -> bool {
171 !address.is_unspecified()
172 && !address.is_loopback()
173 && !address.is_link_local()
174 && !address.is_multicast()
175 && !address.is_broadcast()
176}
177
178fn select_lan_ipv4(candidates: &[LanIpv4Candidate]) -> Option<Ipv4Addr> {
179 candidates
180 .iter()
181 .min_by_key(|candidate| {
182 (
183 !candidate.has_gateway,
184 !candidate.has_physical_address,
185 physical_interface_rank(candidate.interface_type),
186 candidate.dad_rank,
187 candidate.metric,
188 candidate.address.octets(),
189 )
190 })
191 .map(|candidate| candidate.address)
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 const ETHERNET: u32 = IF_TYPE_ETHERNET_CSMACD;
199
200 fn candidate(
201 address: [u8; 4],
202 interface_type: u32,
203 has_gateway: bool,
204 has_physical_address: bool,
205 preferred: bool,
206 metric: u32,
207 ) -> LanIpv4Candidate {
208 LanIpv4Candidate {
209 address: Ipv4Addr::from(address),
210 interface_type,
211 has_gateway,
212 has_physical_address,
213 dad_rank: u8::from(!preferred),
214 metric,
215 }
216 }
217
218 #[test]
219 fn prefers_a_physical_gateway_over_virtual_or_gatewayless_interfaces() {
220 let candidates = [
221 candidate(
222 [100, 109, 89, 106],
223 IF_TYPE_PROP_VIRTUAL,
224 true,
225 true,
226 true,
227 1,
228 ),
229 candidate([192, 168, 50, 9], ETHERNET, false, true, true, 5),
230 candidate([192, 168, 5, 71], ETHERNET, true, true, true, 25),
231 ];
232
233 assert_eq!(
234 select_lan_ipv4(&candidates),
235 Some(Ipv4Addr::new(192, 168, 5, 71))
236 );
237 }
238
239 #[test]
240 fn metric_breaks_ties_between_physical_gateway_interfaces() {
241 let candidates = [
242 candidate([192, 168, 1, 20], ETHERNET, true, true, true, 35),
243 candidate([192, 168, 5, 71], IF_TYPE_IEEE80211, true, true, true, 10),
244 ];
245
246 assert_eq!(
247 select_lan_ipv4(&candidates),
248 Some(Ipv4Addr::new(192, 168, 5, 71))
249 );
250 }
251
252 #[test]
253 fn preferred_address_wins_over_deprecated_address_on_the_same_adapter() {
254 let candidates = [
255 candidate([192, 168, 5, 60], ETHERNET, true, true, false, 5),
256 candidate([192, 168, 5, 71], ETHERNET, true, true, true, 25),
257 ];
258
259 assert_eq!(
260 select_lan_ipv4(&candidates),
261 Some(Ipv4Addr::new(192, 168, 5, 71))
262 );
263 }
264
265 #[test]
266 fn rejects_non_lan_ipv4_special_ranges() {
267 for address in [
268 Ipv4Addr::UNSPECIFIED,
269 Ipv4Addr::LOCALHOST,
270 Ipv4Addr::new(169, 254, 1, 1),
271 Ipv4Addr::new(224, 0, 0, 1),
272 Ipv4Addr::BROADCAST,
273 ] {
274 assert!(!usable_lan_ipv4(address), "{address}");
275 }
276 assert!(usable_lan_ipv4(Ipv4Addr::new(192, 168, 5, 71)));
277 }
278
279 #[test]
280 fn live_scan_never_returns_a_filtered_address() {
281 if let Some(address) = preferred_lan_ipv4().expect("Windows adapter scan should succeed") {
282 println!("preferred LAN IPv4: {address}");
283 assert!(usable_lan_ipv4(address));
284 }
285 }
286}
287