1#[cfg(unix)]
2use crate::os::CheckLibcResult;
3#[cfg(unix)]
4use core::ffi::CStr;
5#[cfg(unix)]
6use std::os::fd::AsRawFd;
7#[cfg(unix)]
8use std::{io, os::fd::BorrowedFd};
9
10#[cfg(not(target_arch = "wasm32"))]
12pub fn hostname() -> std::ffi::OsString {
13 gethostname::gethostname()
14}
15
16#[cfg(not(target_arch = "wasm32"))]
17pub use ::dns_lookup as dns;
18#[cfg(not(target_arch = "wasm32"))]
19pub use ::socket2 as raw;
20
21#[cfg(not(any(
22 target_os = "ios",
23 target_os = "android",
24 target_os = "windows",
25 target_arch = "wasm32",
26 target_os = "redox"
27)))]
28fn select_mac_address(addresses: impl IntoIterator<Item = [u8; 6]>) -> Option<[u8; 6]> {
29 let mut first_local = None;
30
31 for address in addresses {
32 if address == [0; 6] {
33 continue;
34 }
35 if address[0] & 0x02 == 0 {
36 return Some(address);
37 }
38 first_local.get_or_insert(address);
39 }
40
41 first_local
42}
43
44#[cfg(not(any(
47 target_os = "ios",
48 target_os = "android",
49 target_os = "windows",
50 target_arch = "wasm32",
51 target_os = "redox"
52)))]
53pub fn mac_address() -> Option<[u8; 6]> {
54 let addresses = mac_address::MacAddressIterator::new().ok()?;
55 select_mac_address(addresses.map(|address| address.bytes()))
56}
57
58#[cfg(test)]
59#[cfg(not(any(
60 target_os = "ios",
61 target_os = "android",
62 target_os = "windows",
63 target_arch = "wasm32",
64 target_os = "redox"
65)))]
66mod mac_address_tests {
67 use super::select_mac_address;
68
69 #[test]
70 fn prefers_universally_administered_address() {
71 let local = [0x02, 0, 0, 0, 0, 1];
72 let universal = [0x00, 0, 0, 0, 0, 2];
73
74 assert_eq!(select_mac_address([local, universal]), Some(universal));
75 }
76
77 #[test]
78 fn falls_back_to_first_locally_administered_address() {
79 let first = [0x02, 0, 0, 0, 0, 1];
80 let second = [0x06, 0, 0, 0, 0, 2];
81
82 assert_eq!(select_mac_address([first, second]), Some(first));
83 }
84
85 #[test]
86 fn ignores_zero_address() {
87 let universal = [0x00, 0, 0, 0, 0, 1];
88
89 assert_eq!(select_mac_address([[0; 6], universal]), Some(universal));
90 assert_eq!(select_mac_address([[0; 6]]), None);
91 }
92}
93
94#[cfg(unix)]
95pub use libc::{AF_UNIX, SOCK_STREAM, sa_family_t, sockaddr_storage, socklen_t};
96
97#[cfg(unix)]
100pub use libc::{
101 AF_INET, AF_INET6, AF_UNSPEC, AI_ADDRCONFIG, AI_CANONNAME, AI_NUMERICHOST, AI_NUMERICSERV,
102 AI_PASSIVE, INADDR_ANY, INADDR_BROADCAST, INADDR_LOOPBACK, INADDR_NONE, IPPROTO_ICMP,
103 IPPROTO_ICMPV6, IPPROTO_IP, IPPROTO_IPV6, IPPROTO_TCP, IPPROTO_UDP, MSG_CTRUNC, MSG_DONTROUTE,
104 MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, NI_DGRAM, NI_MAXHOST, NI_NAMEREQD, NI_NOFQDN,
105 NI_NUMERICHOST, NI_NUMERICSERV, SHUT_RD, SHUT_RDWR, SHUT_WR, SO_BROADCAST, SO_ERROR,
106 SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_REUSEADDR, SO_SNDBUF, SO_TYPE, SOCK_DGRAM,
107 SOL_SOCKET, SOMAXCONN, TCP_NODELAY,
108};
109
110#[cfg(all(unix, not(target_os = "redox")))]
111pub use libc::{
112 AF_APPLETALK, AF_DECnet, AF_IPX, IPPROTO_AH, IPPROTO_DSTOPTS, IPPROTO_EGP, IPPROTO_ESP,
113 IPPROTO_FRAGMENT, IPPROTO_HOPOPTS, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_NONE,
114 IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET,
115};
116
117#[cfg(unix)]
118pub use libc::SO_REUSEPORT;
119
120#[cfg(any(unix, target_os = "android"))]
121pub use libc::{
122 EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NONAME, EAI_SERVICE,
123 EAI_SOCKTYPE, EAI_SYSTEM, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_HDRINCL, IP_MULTICAST_IF,
124 IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TOS, IP_TTL, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF,
125 IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, MSG_EOR, SO_ACCEPTCONN, SO_DEBUG,
126 SO_DONTROUTE, SO_RCVLOWAT, SO_RCVTIMEO, SO_SNDLOWAT, SO_SNDTIMEO,
127};
128
129#[cfg(any(target_os = "android", target_os = "linux"))]
130pub use libc::{IP_OPTIONS, IPV6_HOPOPTS, IPV6_RECVRTHDR, IPV6_RTHDR};
131
132#[cfg(any(
133 target_os = "android",
134 target_os = "dragonfly",
135 target_os = "freebsd",
136 target_os = "linux",
137 target_vendor = "apple",
138))]
139pub use libc::IPV6_DONTFRAG;
140
141#[cfg(any(
142 target_os = "android",
143 target_os = "dragonfly",
144 target_os = "freebsd",
145 target_os = "linux",
146 target_vendor = "apple",
147))]
148pub use libc::{IPV6_CHECKSUM, IPV6_HOPLIMIT};
149
150#[cfg(any(
151 target_os = "android",
152 target_os = "freebsd",
153 target_os = "fuchsia",
154 target_os = "linux",
155 target_vendor = "apple",
156))]
157pub use libc::{AI_ALL, AI_V4MAPPED};
158
159#[cfg(any(
160 target_os = "android",
161 target_os = "linux",
162 target_os = "netbsd",
163 target_os = "openbsd",
164 target_vendor = "apple",
165))]
166pub use libc::EAI_NODATA;
167
168#[cfg(any(
169 target_os = "android",
170 target_os = "dragonfly",
171 target_os = "freebsd",
172 target_os = "linux",
173 target_os = "netbsd",
174 target_os = "openbsd",
175 target_vendor = "apple",
176))]
177pub use libc::IPV6_PKTINFO;
178
179#[cfg(any(
180 target_os = "android",
181 target_os = "dragonfly",
182 target_os = "freebsd",
183 target_os = "fuchsia",
184 target_os = "linux",
185 target_os = "netbsd",
186 target_os = "openbsd",
187 target_vendor = "apple",
188))]
189pub use libc::{IPV6_RECVTCLASS, IPV6_TCLASS};
190
191#[cfg(any(
192 target_os = "dragonfly",
193 target_os = "freebsd",
194 target_os = "netbsd",
195 target_os = "openbsd",
196 target_vendor = "apple"
197))]
198pub use libc::{
199 AF_LINK, IP_RECVDSTADDR, IPPROTO_GGP, IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, SO_USELOOPBACK,
200};
201
202#[cfg(any(
203 target_os = "dragonfly",
204 target_os = "freebsd",
205 target_os = "netbsd",
206 target_vendor = "apple",
207))]
208pub use libc::IPPROTO_ND;
209
210#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
211pub use libc::{MSG_BCAST, MSG_MCAST};
212
213#[cfg(any(target_os = "netbsd", target_os = "redox", target_vendor = "apple"))]
214pub use libc::NI_MAXSERV;
215
216#[cfg(any(
217 target_os = "android",
218 target_os = "dragonfly",
219 target_os = "freebsd",
220 target_os = "linux",
221 target_os = "netbsd",
222 target_os = "openbsd",
223 target_vendor = "apple"
224))]
225pub use libc::{
226 AF_ROUTE, AF_SNA, EAI_OVERFLOW, IPPROTO_GRE, IPPROTO_RSVP, IPPROTO_TP, IPV6_RECVPKTINFO,
227 MSG_DONTWAIT, SCM_RIGHTS, TCP_MAXSEG,
228};
229
230#[cfg(any(
231 target_os = "android",
232 target_os = "dragonfly",
233 target_os = "freebsd",
234 target_os = "fuchsia",
235 target_os = "linux",
236 target_os = "netbsd",
237 target_os = "openbsd",
238 target_os = "redox"
239))]
240pub use libc::{SOCK_CLOEXEC, SOCK_NONBLOCK};
241
242#[cfg(any(
243 target_os = "android",
244 target_os = "dragonfly",
245 target_os = "freebsd",
246 target_os = "fuchsia",
247 target_os = "linux",
248 target_os = "netbsd",
249 target_vendor = "apple"
250))]
251pub use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
252
253#[cfg(any(
254 target_os = "android",
255 target_os = "dragonfly",
256 target_os = "freebsd",
257 target_os = "fuchsia",
258 target_os = "linux",
259 target_os = "netbsd",
260 target_os = "redox"
261))]
262pub use libc::TCP_KEEPIDLE;
263
264#[cfg(any(
265 target_os = "android",
266 target_os = "dragonfly",
267 target_os = "freebsd",
268 target_os = "fuchsia",
269 target_os = "linux",
270 target_os = "netbsd",
271 target_os = "openbsd"
272))]
273pub use libc::{MSG_CMSG_CLOEXEC, MSG_NOSIGNAL};
274
275#[cfg(target_vendor = "apple")]
276pub use libc::{
277 AF_SYSTEM, IP_ADD_SOURCE_MEMBERSHIP, IP_BLOCK_SOURCE, IP_DROP_SOURCE_MEMBERSHIP, IP_PKTINFO,
278 IP_RECVTTL, IP_UNBLOCK_SOURCE, IPPROTO_MAX, IPPROTO_SCTP, MSG_NOSIGNAL, PF_SYSTEM,
279 SYSPROTO_CONTROL, TCP_CONNECTION_INFO, TCP_KEEPALIVE,
280};
281
282#[cfg(target_os = "linux")]
283pub use libc::{
284 CAN_BCM, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_FLAG, CAN_ERR_MASK, CAN_ISOTP, CAN_J1939, CAN_RAW,
285 CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES, CAN_RAW_FILTER, CAN_RAW_JOIN_FILTERS, CAN_RAW_LOOPBACK,
286 CAN_RAW_RECV_OWN_MSGS, CAN_RTR_FLAG, CAN_SFF_MASK, IPPROTO_MPTCP, J1939_IDLE_ADDR,
287 J1939_MAX_UNICAST_ADDR, J1939_NLA_BYTES_ACKED, J1939_NLA_PAD, J1939_NO_ADDR, J1939_NO_NAME,
288 J1939_NO_PGN, J1939_PGN_ADDRESS_CLAIMED, J1939_PGN_ADDRESS_COMMANDED, J1939_PGN_MAX,
289 J1939_PGN_PDU1_MAX, J1939_PGN_REQUEST, SCM_J1939_DEST_ADDR, SCM_J1939_DEST_NAME,
290 SCM_J1939_ERRQUEUE, SCM_J1939_PRIO, SO_J1939_ERRQUEUE, SO_J1939_FILTER, SO_J1939_PROMISC,
291 SO_J1939_SEND_PRIO, SOL_CAN_BASE, SOL_CAN_RAW,
292};
293
294#[cfg(all(target_os = "linux", target_env = "gnu"))]
295pub use libc::SOL_RDS;
296
297#[cfg(target_os = "android")]
298pub use libc::{SOL_ATALK, SOL_AX25, SOL_IPX, SOL_NETROM, SOL_ROSE};
299
300#[cfg(target_os = "freebsd")]
301pub use libc::SO_SETFIB;
302
303#[cfg(target_os = "netbsd")]
304pub use libc::IPPROTO_VRRP;
305
306#[cfg(any(target_os = "android", target_os = "linux"))]
307pub use libc::{
308 ALG_OP_DECRYPT, ALG_OP_ENCRYPT, ALG_SET_AEAD_ASSOCLEN, ALG_SET_AEAD_AUTHSIZE, ALG_SET_IV,
309 ALG_SET_KEY, ALG_SET_OP, IP_DEFAULT_MULTICAST_LOOP, IP_RECVOPTS, IP_RETOPTS, IPV6_DSTOPTS,
310 IPV6_NEXTHOP, IPV6_PATHMTU, IPV6_RECVDSTOPTS, IPV6_RECVHOPLIMIT, IPV6_RECVHOPOPTS,
311 IPV6_RECVPATHMTU, IPV6_RTHDRDSTOPTS, NETLINK_CRYPTO, NETLINK_DNRTMSG, NETLINK_FIREWALL,
312 NETLINK_IP6_FW, NETLINK_NFLOG, NETLINK_ROUTE, NETLINK_USERSOCK, NETLINK_XFRM, SO_PASSSEC,
313 SO_PEERSEC, SOL_ALG,
314};
315
316#[cfg(any(target_os = "android", target_vendor = "apple"))]
317pub use libc::{AI_DEFAULT, AI_MASK, AI_V4MAPPED_CFG};
318
319#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
320pub use libc::MSG_NOTIFICATION;
321
322#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
323pub use libc::TCP_USER_TIMEOUT;
324
325#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
326pub use libc::{
327 AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BRIDGE, AF_ECONET, AF_IRDA, AF_LLC, AF_NETBEUI,
328 AF_NETLINK, AF_NETROM, AF_PACKET, AF_PPPOX, AF_RDS, AF_SECURITY, AF_TIPC, AF_VSOCK, AF_WANPIPE,
329 AF_X25, IP_TRANSPARENT, MSG_CONFIRM, MSG_ERRQUEUE, MSG_FASTOPEN, MSG_MORE, PF_CAN, PF_PACKET,
330 PF_RDS, SCM_CREDENTIALS, SO_BINDTODEVICE, SO_MARK, SOL_IP, SOL_TIPC, SOL_UDP, TCP_CORK,
331 TCP_DEFER_ACCEPT, TCP_LINGER2, TCP_QUICKACK, TCP_SYNCNT, TCP_WINDOW_CLAMP,
332};
333
334#[cfg(any(
335 target_os = "dragonfly",
336 target_os = "freebsd",
337 target_vendor = "apple"
338))]
339pub use libc::{IPPROTO_HELLO, IPPROTO_XTP, LOCAL_PEERCRED, MSG_EOF};
340
341#[cfg(any(
342 target_os = "android",
343 target_os = "fuchsia",
344 target_os = "freebsd",
345 target_os = "linux"
346))]
347pub use libc::{IPPROTO_UDPLITE, TCP_CONGESTION};
348
349#[cfg(any(
350 target_os = "android",
351 target_os = "fuchsia",
352 target_os = "linux",
353 target_os = "openbsd"
354))]
355pub use libc::AF_KEY;
356
357#[cfg(any(
358 target_os = "android",
359 target_os = "fuchsia",
360 target_os = "linux",
361 target_os = "redox"
362))]
363pub use libc::SO_DOMAIN;
364
365#[cfg(any(
366 target_os = "android",
367 target_os = "fuchsia",
368 all(
369 target_os = "linux",
370 any(
371 target_arch = "aarch64",
372 target_arch = "x86",
373 target_arch = "loongarch64",
374 target_arch = "mips",
375 target_arch = "powerpc",
376 target_arch = "powerpc64",
377 target_arch = "riscv64",
378 target_arch = "s390x",
379 target_arch = "x86_64"
380 )
381 ),
382 target_os = "redox"
383))]
384pub use libc::SO_PRIORITY;
385
386#[cfg(any(
387 target_os = "dragonfly",
388 target_os = "freebsd",
389 target_os = "netbsd",
390 target_os = "openbsd"
391))]
392pub use libc::IPPROTO_MOBILE;
393
394#[cfg(any(
395 target_os = "dragonfly",
396 target_os = "freebsd",
397 target_os = "netbsd",
398 target_vendor = "apple"
399))]
400pub use libc::SCM_CREDS;
401
402#[cfg(any(
403 target_os = "freebsd",
404 target_os = "fuchsia",
405 target_os = "linux",
406 target_vendor = "apple"
407))]
408pub use libc::TCP_FASTOPEN;
409
410#[cfg(any(
411 target_os = "android",
412 target_os = "freebsd",
413 target_os = "fuchsia",
414 all(
415 target_os = "linux",
416 any(
417 target_arch = "aarch64",
418 target_arch = "x86",
419 target_arch = "loongarch64",
420 target_arch = "mips",
421 target_arch = "powerpc",
422 target_arch = "powerpc64",
423 target_arch = "riscv64",
424 target_arch = "s390x",
425 target_arch = "x86_64"
426 )
427 ),
428 target_os = "redox"
429))]
430pub use libc::SO_PROTOCOL;
431
432#[cfg(any(
433 target_os = "android",
434 target_os = "dragonfly",
435 target_os = "fuchsia",
436 target_os = "linux",
437 target_os = "redox"
438))]
439pub use libc::{SO_PASSCRED, SO_PEERCRED};
440
441#[cfg(any(
442 target_os = "android",
443 target_os = "freebsd",
444 target_os = "fuchsia",
445 target_os = "linux",
446 target_os = "netbsd"
447))]
448pub use libc::TCP_INFO;
449
450#[cfg(any(
451 target_os = "android",
452 target_os = "freebsd",
453 target_os = "fuchsia",
454 target_os = "linux",
455 target_vendor = "apple"
456))]
457pub use libc::IP_RECVTOS;
458
459#[cfg(any(
460 target_os = "dragonfly",
461 target_os = "freebsd",
462 target_os = "netbsd",
463 target_os = "openbsd",
464 target_vendor = "apple"
465))]
466pub use libc::{IPPROTO_EON, IPPROTO_IPCOMP};
467
468#[cfg(any(
469 target_os = "android",
470 target_os = "freebsd",
471 target_os = "fuchsia",
472 target_os = "linux",
473 target_os = "netbsd"
474))]
475pub use libc::IPPROTO_SCTP;
476
477#[cfg(any(
478 target_os = "android",
479 target_os = "freebsd",
480 target_os = "fuchsia",
481 target_os = "linux",
482 target_os = "openbsd"
483))]
484pub use libc::AF_BLUETOOTH;
485
486#[cfg(any(target_os = "linux", target_os = "android"))]
487pub use libc::{AF_ALG, AF_CAN};
488
489#[cfg(target_os = "linux")]
491pub use libc::{sockaddr_alg, sockaddr_can};
492
493#[cfg(unix)]
496pub const IPPORT_RESERVED: i32 = 1024;
497#[cfg(unix)]
498pub const IPPORT_USERRESERVED: i32 = 5000;
499
500#[cfg(unix)]
503pub const INADDR_UNSPEC_GROUP: u32 = 0xe000_0000;
504#[cfg(unix)]
505pub const INADDR_ALLHOSTS_GROUP: u32 = 0xe000_0001;
506#[cfg(unix)]
507pub const INADDR_MAX_LOCAL_GROUP: u32 = 0xe000_00ff;
508
509#[cfg(any(target_os = "linux", target_os = "android"))]
511pub const NI_MAXSERV: i32 = 32;
512
513#[cfg(any(target_os = "linux", target_os = "android"))]
516pub const IPV6_JOIN_GROUP: i32 = 20;
517#[cfg(any(target_os = "linux", target_os = "android"))]
518pub const IPV6_LEAVE_GROUP: i32 = 21;
519#[cfg(any(target_os = "linux", target_os = "android"))]
520pub const IPV6_RTHDR_TYPE_0: i32 = 0;
521
522#[cfg(target_os = "linux")]
525pub const CAN_BCM_TX_SETUP: i32 = 1;
526#[cfg(target_os = "linux")]
527pub const CAN_BCM_TX_DELETE: i32 = 2;
528#[cfg(target_os = "linux")]
529pub const CAN_BCM_TX_READ: i32 = 3;
530#[cfg(target_os = "linux")]
531pub const CAN_BCM_TX_SEND: i32 = 4;
532#[cfg(target_os = "linux")]
533pub const CAN_BCM_RX_SETUP: i32 = 5;
534#[cfg(target_os = "linux")]
535pub const CAN_BCM_RX_DELETE: i32 = 6;
536#[cfg(target_os = "linux")]
537pub const CAN_BCM_RX_READ: i32 = 7;
538#[cfg(target_os = "linux")]
539pub const CAN_BCM_TX_STATUS: i32 = 8;
540#[cfg(target_os = "linux")]
541pub const CAN_BCM_TX_EXPIRED: i32 = 9;
542#[cfg(target_os = "linux")]
543pub const CAN_BCM_RX_STATUS: i32 = 10;
544#[cfg(target_os = "linux")]
545pub const CAN_BCM_RX_TIMEOUT: i32 = 11;
546#[cfg(target_os = "linux")]
547pub const CAN_BCM_RX_CHANGED: i32 = 12;
548#[cfg(target_os = "linux")]
549pub const CAN_BCM_SETTIMER: i32 = 0x0001;
550#[cfg(target_os = "linux")]
551pub const CAN_BCM_STARTTIMER: i32 = 0x0002;
552#[cfg(target_os = "linux")]
553pub const CAN_BCM_TX_COUNTEVT: i32 = 0x0004;
554#[cfg(target_os = "linux")]
555pub const CAN_BCM_TX_ANNOUNCE: i32 = 0x0008;
556#[cfg(target_os = "linux")]
557pub const CAN_BCM_TX_CP_CAN_ID: i32 = 0x0010;
558#[cfg(target_os = "linux")]
559pub const CAN_BCM_RX_FILTER_ID: i32 = 0x0020;
560#[cfg(target_os = "linux")]
561pub const CAN_BCM_RX_CHECK_DLC: i32 = 0x0040;
562#[cfg(target_os = "linux")]
563pub const CAN_BCM_RX_NO_AUTOTIMER: i32 = 0x0080;
564#[cfg(target_os = "linux")]
565pub const CAN_BCM_RX_ANNOUNCE_RESUME: i32 = 0x0100;
566#[cfg(target_os = "linux")]
567pub const CAN_BCM_TX_RESET_MULTI_IDX: i32 = 0x0200;
568#[cfg(target_os = "linux")]
569pub const CAN_BCM_RX_RTR_FRAME: i32 = 0x0400;
570#[cfg(target_os = "linux")]
571pub const CAN_BCM_CAN_FD_FRAME: i32 = 0x0800;
572
573#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
575pub const SO_VM_SOCKETS_BUFFER_SIZE: u32 = 0;
576#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
577pub const SO_VM_SOCKETS_BUFFER_MIN_SIZE: u32 = 1;
578#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
579pub const SO_VM_SOCKETS_BUFFER_MAX_SIZE: u32 = 2;
580#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
581pub const VMADDR_CID_ANY: u32 = 0xffff_ffff;
582#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
583pub const VMADDR_PORT_ANY: u32 = 0xffff_ffff;
584#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
585pub const VMADDR_CID_HOST: u32 = 2;
586#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
587pub const VM_SOCKETS_INVALID_VERSION: u32 = 0xffff_ffff;
588
589#[cfg(any(
591 target_os = "android",
592 target_os = "fuchsia",
593 target_os = "freebsd",
594 target_os = "linux"
595))]
596pub const UDPLITE_SEND_CSCOV: i32 = 10;
597#[cfg(any(
598 target_os = "android",
599 target_os = "fuchsia",
600 target_os = "freebsd",
601 target_os = "linux"
602))]
603pub const UDPLITE_RECV_CSCOV: i32 = 11;
604
605#[cfg(not(any(
609 target_os = "android",
610 target_os = "fuchsia",
611 target_os = "linux",
612 windows
613)))]
614pub const SOL_IP: i32 = 0;
615#[cfg(not(any(
616 target_os = "android",
617 target_os = "fuchsia",
618 target_os = "linux",
619 windows
620)))]
621pub const SOL_UDP: i32 = 17;
622
623#[cfg(not(any(
625 target_os = "android",
626 target_os = "dragonfly",
627 target_os = "freebsd",
628 target_os = "fuchsia",
629 target_os = "linux",
630 target_os = "netbsd",
631 target_os = "openbsd",
632 target_vendor = "apple",
633 windows
634)))]
635pub const SOMAXCONN: i32 = 5;
636
637#[cfg(any(
639 target_os = "android",
640 target_os = "freebsd",
641 target_os = "fuchsia",
642 target_os = "linux",
643 target_os = "openbsd"
644))]
645pub const BDADDR_ANY: &str = "00:00:00:00:00:00";
646#[cfg(any(
647 target_os = "android",
648 target_os = "freebsd",
649 target_os = "fuchsia",
650 target_os = "linux",
651 target_os = "openbsd"
652))]
653pub const BDADDR_LOCAL: &str = "00:00:00:FF:FF:FF";
654
655#[cfg(target_vendor = "apple")]
657pub const IPV6_RECVHOPLIMIT: i32 = 37;
658#[cfg(target_vendor = "apple")]
659pub const IPV6_RECVRTHDR: i32 = 38;
660#[cfg(target_vendor = "apple")]
661pub const IPV6_RECVHOPOPTS: i32 = 39;
662#[cfg(target_vendor = "apple")]
663pub const IPV6_RECVDSTOPTS: i32 = 40;
664#[cfg(target_vendor = "apple")]
665pub const IPV6_USE_MIN_MTU: i32 = 42;
666#[cfg(target_vendor = "apple")]
667pub const IPV6_RECVPATHMTU: i32 = 43;
668#[cfg(target_vendor = "apple")]
669pub const IPV6_PATHMTU: i32 = 44;
670#[cfg(target_vendor = "apple")]
671pub const IPV6_NEXTHOP: i32 = 48;
672#[cfg(target_vendor = "apple")]
673pub const IPV6_HOPOPTS: i32 = 49;
674#[cfg(target_vendor = "apple")]
675pub const IPV6_DSTOPTS: i32 = 50;
676#[cfg(target_vendor = "apple")]
677pub const IPV6_RTHDR: i32 = 51;
678#[cfg(target_vendor = "apple")]
679pub const IPV6_RTHDRDSTOPTS: i32 = 57;
680#[cfg(target_vendor = "apple")]
681pub const IPV6_RTHDR_TYPE_0: i32 = 0;
682
683#[cfg(target_vendor = "apple")]
685pub const EAI_ADDRFAMILY: i32 = 1;
686#[cfg(target_vendor = "apple")]
687pub const EAI_BADHINTS: i32 = 12;
688#[cfg(target_vendor = "apple")]
689pub const EAI_PROTOCOL: i32 = 13;
690#[cfg(target_vendor = "apple")]
691pub const EAI_MAX: i32 = 15;
692
693#[cfg(all(unix, not(target_os = "redox")))]
699pub fn sethostname(hostname: &[u8]) -> io::Result<()> {
700 use std::os::unix::ffi::OsStrExt;
701 nix::unistd::sethostname(std::ffi::OsStr::from_bytes(hostname)).map_err(io::Error::from)
702}
703
704#[cfg(unix)]
705pub fn close_socket_ignore_connreset(socket: libc::c_int) -> io::Result<()> {
706 let ret = unsafe { libc::close(socket) };
707 if ret < 0 {
708 let err = io::Error::last_os_error();
709 if err.raw_os_error() != Some(libc::ECONNRESET) {
710 return Err(err);
711 }
712 }
713 Ok(())
714}
715
716#[cfg(unix)]
717pub fn getsockopt_int(fd: libc::c_int, level: i32, name: i32) -> io::Result<i32> {
718 let mut flag: libc::c_int = 0;
719 let mut flagsize = core::mem::size_of::<libc::c_int>() as libc::socklen_t;
720 unsafe {
721 libc::getsockopt(
722 fd,
723 level,
724 name,
725 &mut flag as *mut libc::c_int as *mut _,
726 &mut flagsize,
727 )
728 }
729 .check_libc_neg()?;
730 Ok(flag)
731}
732
733#[cfg(unix)]
734pub fn getsockopt_bytes(
735 fd: libc::c_int,
736 level: i32,
737 name: i32,
738 buflen: usize,
739) -> io::Result<Vec<u8>> {
740 let mut buf = vec![0u8; buflen];
741 let mut optlen = buflen as libc::socklen_t;
742 unsafe { libc::getsockopt(fd, level, name, buf.as_mut_ptr() as *mut _, &mut optlen) }
743 .check_libc_neg()?;
744 buf.truncate(optlen as usize);
745 Ok(buf)
746}
747
748#[cfg(unix)]
749pub fn setsockopt_bytes(fd: libc::c_int, level: i32, name: i32, value: &[u8]) -> io::Result<()> {
750 unsafe {
751 libc::setsockopt(
752 fd,
753 level,
754 name,
755 value.as_ptr() as *const _,
756 value.len() as libc::socklen_t,
757 )
758 }
759 .check_libc_neg()?;
760 Ok(())
761}
762
763#[cfg(unix)]
764pub fn setsockopt_int(fd: libc::c_int, level: i32, name: i32, value: i32) -> io::Result<()> {
765 unsafe {
766 libc::setsockopt(
767 fd,
768 level,
769 name,
770 &value as *const i32 as *const _,
771 core::mem::size_of::<i32>() as libc::socklen_t,
772 )
773 }
774 .check_libc_neg()?;
775 Ok(())
776}
777
778#[cfg(unix)]
779pub fn setsockopt_none(fd: libc::c_int, level: i32, name: i32, optlen: u32) -> io::Result<()> {
780 unsafe {
781 libc::setsockopt(
782 fd,
783 level,
784 name,
785 core::ptr::null(),
786 optlen as libc::socklen_t,
787 )
788 }
789 .check_libc_neg()?;
790 Ok(())
791}
792
793#[cfg(any(
794 target_os = "dragonfly",
795 target_os = "freebsd",
796 target_os = "fuchsia",
797 target_os = "ios",
798 target_os = "linux",
799 target_os = "macos",
800 target_os = "netbsd",
801 target_os = "openbsd",
802))]
803pub fn if_nameindex() -> io::Result<Vec<(u32, String)>> {
804 let list = nix::net::if_::if_nameindex().map_err(io::Error::from)?;
805 Ok(list
806 .to_slice()
807 .iter()
808 .map(|iface| (iface.index(), iface.name().to_string_lossy().into_owned()))
809 .collect())
810}
811
812#[cfg(unix)]
813pub fn if_nametoindex_checked(name: &CStr) -> io::Result<u32> {
814 let ret = unsafe { libc::if_nametoindex(name.as_ptr()) };
815 if ret == 0 {
816 Err(io::Error::last_os_error())
817 } else {
818 Ok(ret)
819 }
820}
821
822#[cfg(unix)]
823pub fn if_indextoname_checked(index: u32) -> io::Result<String> {
824 let mut buf = [0u8; libc::IF_NAMESIZE];
825 let ret = unsafe {
826 libc::if_indextoname(index as libc::c_uint, buf.as_mut_ptr() as *mut libc::c_char)
827 };
828 if ret.is_null() {
829 Err(io::Error::last_os_error())
830 } else {
831 let buf = unsafe { CStr::from_ptr(buf.as_ptr() as *const libc::c_char) };
832 Ok(buf.to_string_lossy().into_owned())
833 }
834}
835
836#[cfg(unix)]
837pub fn gai_error_string(err: i32) -> String {
838 unsafe { CStr::from_ptr(libc::gai_strerror(err)) }
839 .to_string_lossy()
840 .into_owned()
841}
842
843#[cfg(unix)]
844pub fn h_error_string(err: i32) -> String {
845 unsafe { CStr::from_ptr(libc::hstrerror(err)) }
846 .to_string_lossy()
847 .into_owned()
848}
849
850#[cfg(all(unix, not(target_os = "redox")))]
851#[derive(Debug, Clone)]
852pub struct AncillaryMessage {
853 pub level: i32,
854 pub kind: i32,
855 pub data: Vec<u8>,
856}
857
858#[cfg(all(unix, not(target_os = "redox")))]
859pub type SocketAddressBytes = [u8; core::mem::size_of::<libc::sockaddr_storage>()];
860
861#[cfg(all(unix, not(target_os = "redox")))]
862#[derive(Debug, Clone)]
863pub struct RawSocketAddress {
864 pub storage: SocketAddressBytes,
865 pub len: usize,
866}
867
868#[cfg(all(unix, not(target_os = "redox")))]
869#[derive(Debug, Clone)]
870pub struct RecvMsgResult {
871 pub data: Vec<u8>,
872 pub ancdata: Vec<AncillaryMessage>,
873 pub msg_flags: i32,
874 pub address: Option<RawSocketAddress>,
875}
876
877#[cfg(all(unix, not(target_os = "redox")))]
878#[derive(Debug, Clone, Copy, PartialEq, Eq)]
879pub enum AncillaryPackError {
880 ItemTooLarge,
881 TooMuchData,
882 UnexpectedNullHeader,
883}
884
885#[cfg(all(unix, not(target_os = "redox")))]
886pub fn checked_cmsg_len(len: usize) -> Option<usize> {
887 let cmsg_len = |length| unsafe { libc::CMSG_LEN(length) };
888 if len as u64 > (i32::MAX as u64 - cmsg_len(0) as u64) {
889 return None;
890 }
891 let res = cmsg_len(len as _) as usize;
892 if res > i32::MAX as usize || res < len {
893 return None;
894 }
895 Some(res)
896}
897
898#[cfg(all(unix, not(target_os = "redox")))]
899pub fn checked_cmsg_space(len: usize) -> Option<usize> {
900 let cmsg_space = |length| unsafe { libc::CMSG_SPACE(length) };
901 if len as u64 > (i32::MAX as u64 - cmsg_space(1) as u64) {
902 return None;
903 }
904 let res = cmsg_space(len as _) as usize;
905 if res > i32::MAX as usize || res < len {
906 return None;
907 }
908 Some(res)
909}
910
911#[cfg(all(unix, not(target_os = "redox")))]
912pub fn pack_ancillary_messages(cmsgs: &[(i32, i32, &[u8])]) -> Result<Vec<u8>, AncillaryPackError> {
913 use core::{mem, ptr};
914
915 if cmsgs.is_empty() {
916 return Ok(vec![]);
917 }
918
919 let capacity = cmsgs
920 .iter()
921 .map(|(_, _, buf)| buf.len())
922 .try_fold(0usize, |sum, len| {
923 let space = checked_cmsg_space(len).ok_or(AncillaryPackError::ItemTooLarge)?;
924 usize::checked_add(sum, space).ok_or(AncillaryPackError::TooMuchData)
925 })?;
926
927 let mut cmsg_buffer = vec![0u8; capacity];
928 let mut mhdr = unsafe { mem::zeroed::<libc::msghdr>() };
929 mhdr.msg_control = cmsg_buffer.as_mut_ptr().cast();
930 mhdr.msg_controllen = capacity as _;
931
932 let mut pmhdr: *mut libc::cmsghdr = unsafe { libc::CMSG_FIRSTHDR(&mhdr) };
933 for (lvl, typ, data) in cmsgs {
934 if pmhdr.is_null() {
935 return Err(AncillaryPackError::UnexpectedNullHeader);
936 }
937 let cmsg_len = checked_cmsg_len(data.len()).ok_or(AncillaryPackError::ItemTooLarge)?;
938 unsafe {
939 (*pmhdr).cmsg_level = *lvl;
940 (*pmhdr).cmsg_type = *typ;
941 (*pmhdr).cmsg_len = cmsg_len as _;
942 ptr::copy_nonoverlapping(data.as_ptr(), libc::CMSG_DATA(pmhdr), data.len());
943 pmhdr = libc::CMSG_NXTHDR(&mhdr, pmhdr);
944 }
945 }
946
947 Ok(cmsg_buffer)
948}
949
950#[cfg(all(unix, not(target_os = "redox")))]
951pub fn parse_ancillary_messages(control: &[u8]) -> Vec<AncillaryMessage> {
952 use core::mem;
953
954 if control.is_empty() {
955 return Vec::new();
956 }
957
958 let mut msg = unsafe { mem::zeroed::<libc::msghdr>() };
959 msg.msg_control = control.as_ptr() as *mut _;
960 msg.msg_controllen = control.len() as _;
961
962 let ctrl_buf = msg.msg_control as *const u8;
963 let ctrl_end = unsafe { ctrl_buf.add(msg.msg_controllen as _) };
964
965 let mut result = Vec::new();
966 let mut cmsg: *mut libc::cmsghdr = unsafe { libc::CMSG_FIRSTHDR(&msg) };
967 while !cmsg.is_null() {
968 let cmsg_ref = unsafe { &*cmsg };
969 let data_ptr = unsafe { libc::CMSG_DATA(cmsg) };
970 let data_len_from_cmsg = cmsg_ref.cmsg_len as usize - (data_ptr as usize - cmsg as usize);
971 let available = ctrl_end as usize - data_ptr as usize;
972 let data_len = data_len_from_cmsg.min(available);
973 let data = unsafe { core::slice::from_raw_parts(data_ptr, data_len) };
974 result.push(AncillaryMessage {
975 level: cmsg_ref.cmsg_level,
976 kind: cmsg_ref.cmsg_type,
977 data: data.to_vec(),
978 });
979 cmsg = unsafe { libc::CMSG_NXTHDR(&msg, cmsg) };
980 }
981
982 result
983}
984
985#[cfg(all(unix, not(target_os = "redox")))]
986pub fn recvmsg(
987 fd: BorrowedFd<'_>,
988 bufsize: usize,
989 ancbufsize: usize,
990 flags: i32,
991) -> io::Result<RecvMsgResult> {
992 use core::mem::MaybeUninit;
993
994 let mut data_buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); bufsize];
995 let mut anc_buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); ancbufsize];
996 let mut addr_storage: libc::sockaddr_storage = unsafe { core::mem::zeroed() };
997
998 let mut iov = [libc::iovec {
999 iov_base: data_buf.as_mut_ptr().cast(),
1000 iov_len: bufsize,
1001 }];
1002
1003 let mut msg: libc::msghdr = unsafe { core::mem::zeroed() };
1004 msg.msg_name = (&mut addr_storage as *mut libc::sockaddr_storage).cast();
1005 msg.msg_namelen = core::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
1006 msg.msg_iov = iov.as_mut_ptr();
1007 msg.msg_iovlen = 1;
1008 if ancbufsize > 0 {
1009 msg.msg_control = anc_buf.as_mut_ptr().cast();
1010 msg.msg_controllen = ancbufsize as _;
1011 }
1012
1013 let ret = unsafe { libc::recvmsg(fd.as_raw_fd(), &mut msg, flags) }.check_libc_neg()?;
1014
1015 let data = unsafe {
1016 data_buf.set_len(ret as usize);
1017 core::mem::transmute::<Vec<MaybeUninit<u8>>, Vec<u8>>(data_buf)
1018 };
1019 let control = unsafe {
1020 core::slice::from_raw_parts(anc_buf.as_ptr().cast::<u8>(), msg.msg_controllen as usize)
1021 };
1022 let ancdata = parse_ancillary_messages(control);
1023 let address = if msg.msg_namelen > 0 {
1024 let storage = unsafe {
1025 core::mem::transmute::<libc::sockaddr_storage, SocketAddressBytes>(addr_storage)
1026 };
1027 Some(RawSocketAddress {
1028 storage,
1029 len: msg.msg_namelen as usize,
1030 })
1031 } else {
1032 None
1033 };
1034
1035 Ok(RecvMsgResult {
1036 data,
1037 ancdata,
1038 msg_flags: msg.msg_flags,
1039 address,
1040 })
1041}
1042
1043#[cfg(target_os = "linux")]
1044pub fn sendmsg_afalg(
1045 fd: BorrowedFd<'_>,
1046 buffers: &[io::IoSlice<'_>],
1047 op: u32,
1048 iv: Option<&[u8]>,
1049 assoclen: Option<u32>,
1050 flags: i32,
1051) -> io::Result<usize> {
1052 let mut control_buf = Vec::new();
1053
1054 {
1055 let op_bytes = op.to_ne_bytes();
1056 let space = unsafe { libc::CMSG_SPACE(core::mem::size_of::<u32>() as u32) } as usize;
1057 let old_len = control_buf.len();
1058 control_buf.resize(old_len + space, 0u8);
1059
1060 let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1061 unsafe {
1062 (*cmsg).cmsg_len = libc::CMSG_LEN(core::mem::size_of::<u32>() as u32) as _;
1063 (*cmsg).cmsg_level = libc::SOL_ALG;
1064 (*cmsg).cmsg_type = libc::ALG_SET_OP;
1065 let data = libc::CMSG_DATA(cmsg);
1066 core::ptr::copy_nonoverlapping(op_bytes.as_ptr(), data, op_bytes.len());
1067 }
1068 }
1069
1070 if let Some(iv_bytes) = iv {
1071 let iv_struct_size = 4 + iv_bytes.len();
1072 let space = unsafe { libc::CMSG_SPACE(iv_struct_size as u32) } as usize;
1073 let old_len = control_buf.len();
1074 control_buf.resize(old_len + space, 0u8);
1075
1076 let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1077 unsafe {
1078 (*cmsg).cmsg_len = libc::CMSG_LEN(iv_struct_size as u32) as _;
1079 (*cmsg).cmsg_level = libc::SOL_ALG;
1080 (*cmsg).cmsg_type = libc::ALG_SET_IV;
1081 let data = libc::CMSG_DATA(cmsg);
1082 let ivlen = (iv_bytes.len() as u32).to_ne_bytes();
1083 core::ptr::copy_nonoverlapping(ivlen.as_ptr(), data, 4);
1084 core::ptr::copy_nonoverlapping(iv_bytes.as_ptr(), data.add(4), iv_bytes.len());
1085 }
1086 }
1087
1088 if let Some(assoclen_val) = assoclen {
1089 let assoclen_bytes = assoclen_val.to_ne_bytes();
1090 let space = unsafe { libc::CMSG_SPACE(core::mem::size_of::<u32>() as u32) } as usize;
1091 let old_len = control_buf.len();
1092 control_buf.resize(old_len + space, 0u8);
1093
1094 let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1095 unsafe {
1096 (*cmsg).cmsg_len = libc::CMSG_LEN(core::mem::size_of::<u32>() as u32) as _;
1097 (*cmsg).cmsg_level = libc::SOL_ALG;
1098 (*cmsg).cmsg_type = libc::ALG_SET_AEAD_ASSOCLEN;
1099 let data = libc::CMSG_DATA(cmsg);
1100 core::ptr::copy_nonoverlapping(assoclen_bytes.as_ptr(), data, assoclen_bytes.len());
1101 }
1102 }
1103
1104 let iovecs: Vec<libc::iovec> = buffers
1105 .iter()
1106 .map(|buf| libc::iovec {
1107 iov_base: buf.as_ptr() as *mut _,
1108 iov_len: buf.len(),
1109 })
1110 .collect();
1111
1112 let mut msghdr: libc::msghdr = unsafe { core::mem::zeroed() };
1113 msghdr.msg_iov = iovecs.as_ptr() as *mut _;
1114 msghdr.msg_iovlen = iovecs.len() as _;
1115 if !control_buf.is_empty() {
1116 msghdr.msg_control = control_buf.as_mut_ptr() as *mut _;
1117 msghdr.msg_controllen = control_buf.len() as _;
1118 }
1119
1120 let ret = unsafe { libc::sendmsg(fd.as_raw_fd(), &msghdr, flags) }.check_libc_neg()?;
1121 Ok(ret as usize)
1122}
1123
1124#[cfg(windows)]
1125use core::{ffi::CStr, ptr::NonNull};
1126#[cfg(windows)]
1127use rustpython_wtf8::Wtf8Buf;
1128#[cfg(windows)]
1129use std::io;
1130#[cfg(windows)]
1131use windows_sys::Win32::{
1132 NetworkManagement::{
1133 IpHelper::{
1134 ConvertInterfaceLuidToNameW, FreeMibTable, GetIfTable2Ex, MIB_IF_ROW2, MIB_IF_TABLE2,
1135 MibIfTableRaw, if_indextoname, if_nametoindex,
1136 },
1137 Ndis::{IF_MAX_STRING_SIZE, NET_LUID_LH},
1138 },
1139 Networking::WinSock::{
1140 FROM_PROTOCOL_INFO, SOCKET, WSA_FLAG_OVERLAPPED, WSADuplicateSocketW, WSAGetLastError,
1141 WSAIoctl, WSAPROTOCOL_INFOW, WSASocketW,
1142 },
1143};
1144
1145#[cfg(windows)]
1146pub use windows_sys::Win32::Networking::WinSock::{
1147 AF_APPLETALK, AF_DECnet, AF_IPX, AF_LINK, AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICSERV,
1148 AI_V4MAPPED, FIONBIO, INADDR_ANY, INADDR_BROADCAST, INADDR_LOOPBACK, INADDR_NONE,
1149 INVALID_SOCKET, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_HDRINCL, IP_MULTICAST_IF,
1150 IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_OPTIONS, IP_RECVDSTADDR, IP_TOS, IP_TTL,
1151 IPPORT_RESERVED, IPPROTO_AH, IPPROTO_CBT, IPPROTO_DSTOPTS, IPPROTO_EGP, IPPROTO_ESP,
1152 IPPROTO_FRAGMENT, IPPROTO_GGP, IPPROTO_HOPOPTS, IPPROTO_ICLFXBM, IPPROTO_ICMP, IPPROTO_ICMPV6,
1153 IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IGP, IPPROTO_IP, IPPROTO_IP as IPPROTO_IPIP, IPPROTO_IPV4,
1154 IPPROTO_IPV6, IPPROTO_L2TP, IPPROTO_MAX, IPPROTO_ND, IPPROTO_NONE, IPPROTO_PGM, IPPROTO_PIM,
1155 IPPROTO_PUP, IPPROTO_RAW, IPPROTO_RDP, IPPROTO_ROUTING, IPPROTO_SCTP, IPPROTO_ST, IPPROTO_TCP,
1156 IPPROTO_UDP, IPV6_CHECKSUM, IPV6_DONTFRAG, IPV6_HOPLIMIT, IPV6_HOPOPTS, IPV6_JOIN_GROUP,
1157 IPV6_LEAVE_GROUP, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_PKTINFO,
1158 IPV6_RECVRTHDR, IPV6_RECVTCLASS, IPV6_RTHDR, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
1159 MSG_BCAST, MSG_CTRUNC, MSG_DONTROUTE, MSG_MCAST, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL,
1160 NI_DGRAM, NI_MAXHOST, NI_MAXSERV, NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV,
1161 POLLIN, RCVALL_IPLEVEL, RCVALL_OFF, RCVALL_ON, RCVALL_SOCKETLEVELONLY, SD_BOTH,
1162 SD_BOTH as SHUT_RDWR, SD_RECEIVE, SD_RECEIVE as SHUT_RD, SD_SEND, SD_SEND as SHUT_WR,
1163 SIO_KEEPALIVE_VALS, SIO_LOOPBACK_FAST_PATH, SIO_RCVALL, SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG,
1164 SO_DONTROUTE, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO,
1165 SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, SO_USELOOPBACK, SOCK_DGRAM, SOCK_RAW, SOCK_RDM,
1166 SOCK_SEQPACKET, SOCK_STREAM, SOCKET_ERROR, SOCKET_ERROR as SOCKET_ERROR_CODE, SOL_IP,
1167 SOL_SOCKET, TCP_MAXSEG, TCP_NODELAY, WSAEBADF, WSAECONNABORTED, WSAECONNRESET, WSAEINTR,
1168 WSAENOTSOCK, WSAEWOULDBLOCK, getprotobyname, getservbyname, getservbyport, getsockopt,
1169 setsockopt,
1170};
1171
1172#[cfg(windows)]
1173pub const SO_EXCLUSIVEADDRUSE: i32 = -5;
1174#[cfg(windows)]
1175pub const EAI_MEMORY: i32 = windows_sys::Win32::Networking::WinSock::WSA_NOT_ENOUGH_MEMORY;
1176#[cfg(windows)]
1177pub const EAI_FAMILY: i32 = windows_sys::Win32::Networking::WinSock::WSAEAFNOSUPPORT;
1178#[cfg(windows)]
1179pub const EAI_BADFLAGS: i32 = windows_sys::Win32::Networking::WinSock::WSAEINVAL;
1180#[cfg(windows)]
1181pub const EAI_SOCKTYPE: i32 = windows_sys::Win32::Networking::WinSock::WSAESOCKTNOSUPPORT;
1182#[cfg(windows)]
1183pub const EAI_NODATA: i32 = windows_sys::Win32::Networking::WinSock::WSAHOST_NOT_FOUND;
1184#[cfg(windows)]
1185pub const EAI_NONAME: i32 = windows_sys::Win32::Networking::WinSock::WSAHOST_NOT_FOUND;
1186#[cfg(windows)]
1187pub const EAI_FAIL: i32 = windows_sys::Win32::Networking::WinSock::WSANO_RECOVERY;
1188#[cfg(windows)]
1189pub const EAI_AGAIN: i32 = windows_sys::Win32::Networking::WinSock::WSATRY_AGAIN;
1190#[cfg(windows)]
1191pub const EAI_SERVICE: i32 = windows_sys::Win32::Networking::WinSock::WSATYPE_NOT_FOUND;
1192#[cfg(windows)]
1193pub const IF_NAMESIZE: usize = IF_MAX_STRING_SIZE as usize;
1194#[cfg(windows)]
1195pub const AF_UNSPEC: i32 = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as i32;
1196#[cfg(windows)]
1197pub const AF_INET: i32 = windows_sys::Win32::Networking::WinSock::AF_INET as i32;
1198#[cfg(windows)]
1199pub const AF_INET6: i32 = windows_sys::Win32::Networking::WinSock::AF_INET6 as i32;
1200#[cfg(windows)]
1201pub const AI_PASSIVE: i32 = windows_sys::Win32::Networking::WinSock::AI_PASSIVE as i32;
1202#[cfg(windows)]
1203pub const AI_NUMERICHOST: i32 = windows_sys::Win32::Networking::WinSock::AI_NUMERICHOST as i32;
1204#[cfg(windows)]
1205pub const FROM_PROTOCOL_INFO_VALUE: i32 = FROM_PROTOCOL_INFO;
1206
1207#[cfg(windows)]
1209pub const SOMAXCONN: i32 = 0x7fff_ffff;
1210
1211#[cfg(windows)]
1214pub const SOL_TCP: i32 = 6;
1215#[cfg(windows)]
1216pub const SOL_UDP: i32 = 17;
1217#[cfg(windows)]
1218pub const SOL_RFCOMM: i32 = 3;
1219#[cfg(windows)]
1220pub const SO_SNDLOWAT: i32 = 0x1003;
1221#[cfg(windows)]
1222pub const SO_RCVLOWAT: i32 = 0x1004;
1223#[cfg(windows)]
1224pub const SO_ORIGINAL_DST: i32 = 12303;
1225#[cfg(windows)]
1226pub const SO_BTH_ENCRYPT: i32 = 2;
1227#[cfg(windows)]
1228pub const SO_BTH_MTU: i32 = 0x8000_0007u32 as i32;
1229#[cfg(windows)]
1230pub const SO_BTH_MTU_MAX: i32 = 0x8000_0008u32 as i32;
1231#[cfg(windows)]
1232pub const SO_BTH_MTU_MIN: i32 = 0x8000_000au32 as i32;
1233#[cfg(windows)]
1234pub const TCP_KEEPIDLE: i32 = 3;
1235#[cfg(windows)]
1236pub const TCP_FASTOPEN: i32 = 15;
1237#[cfg(windows)]
1238pub const TCP_KEEPCNT: i32 = 16;
1239#[cfg(windows)]
1240pub const TCP_KEEPINTVL: i32 = 17;
1241#[cfg(windows)]
1242pub const IPPORT_USERRESERVED: i32 = 5000;
1243#[cfg(windows)]
1244pub const INADDR_UNSPEC_GROUP: i32 = 0xe000_0000u32 as i32;
1245#[cfg(windows)]
1246pub const INADDR_ALLHOSTS_GROUP: i32 = 0xe000_0001u32 as i32;
1247#[cfg(windows)]
1248pub const INADDR_MAX_LOCAL_GROUP: i32 = 0xe000_00ffu32 as i32;
1249#[cfg(windows)]
1250pub const IP_ADD_SOURCE_MEMBERSHIP: i32 = 15;
1251#[cfg(windows)]
1252pub const IP_DROP_SOURCE_MEMBERSHIP: i32 = 16;
1253#[cfg(windows)]
1254pub const IP_BLOCK_SOURCE: i32 = 17;
1255#[cfg(windows)]
1256pub const IP_UNBLOCK_SOURCE: i32 = 18;
1257#[cfg(windows)]
1258pub const IP_PKTINFO: i32 = 19;
1259#[cfg(windows)]
1260pub const IP_RECVTTL: i32 = 21;
1261#[cfg(windows)]
1262pub const IP_RECVTOS: i32 = 40;
1263#[cfg(windows)]
1264pub const IP_RECVERR: i32 = 75;
1265#[cfg(windows)]
1266pub const IPV6_RECVERR: i32 = 75;
1267#[cfg(windows)]
1268pub const MSG_ERRQUEUE: i32 = 0x1000;
1269#[cfg(windows)]
1270pub const RCVALL_MAX: i32 = 3;
1271#[cfg(windows)]
1272pub const BDADDR_ANY: &str = "00:00:00:00:00:00";
1273#[cfg(windows)]
1274pub const BDADDR_LOCAL: &str = "00:00:00:FF:FF:FF";
1275
1276#[cfg(windows)]
1277pub type RawSocket = SOCKET;
1278
1279#[cfg(windows)]
1280pub const INVALID_RAW_SOCKET: RawSocket = INVALID_SOCKET as RawSocket;
1281
1282#[cfg(windows)]
1283#[repr(C)]
1284pub struct TcpKeepalive {
1285 pub onoff: u32,
1286 pub keepalivetime: u32,
1287 pub keepaliveinterval: u32,
1288}
1289
1290#[cfg(windows)]
1291pub struct SharedSocket {
1292 pub raw: RawSocket,
1293 pub family: i32,
1294 pub socket_type: i32,
1295 pub protocol: i32,
1296}
1297
1298#[cfg(windows)]
1299pub fn last_socket_error() -> io::Error {
1300 io::Error::from_raw_os_error(unsafe { WSAGetLastError() })
1301}
1302
1303#[cfg(windows)]
1304pub fn set_socket_inheritable(socket: RawSocket, inheritable: bool) -> io::Result<()> {
1305 crate::nt::set_handle_inheritable(socket as _, inheritable)
1306}
1307
1308#[cfg(windows)]
1309pub fn close_socket_ignore_connreset(socket: RawSocket) -> io::Result<()> {
1310 let ret = unsafe { windows_sys::Win32::Networking::WinSock::closesocket(socket) };
1311 if ret != 0 {
1312 let err = last_socket_error();
1313 if err.raw_os_error() != Some(WSAECONNRESET) {
1314 return Err(err);
1315 }
1316 }
1317 Ok(())
1318}
1319
1320#[cfg(windows)]
1321pub fn getsockopt_int(socket: RawSocket, level: i32, name: i32) -> io::Result<i32> {
1322 let mut flag = 0i32;
1323 let mut optlen = core::mem::size_of::<i32>() as i32;
1324 let ret = unsafe {
1325 getsockopt(
1326 socket,
1327 level,
1328 name,
1329 &mut flag as *mut i32 as *mut _,
1330 &mut optlen,
1331 )
1332 };
1333 if ret == SOCKET_ERROR {
1334 Err(crate::os::errno_io_error())
1335 } else {
1336 Ok(flag)
1337 }
1338}
1339
1340#[cfg(windows)]
1341pub fn getsockopt_bytes(
1342 socket: RawSocket,
1343 level: i32,
1344 name: i32,
1345 buflen: usize,
1346) -> io::Result<Vec<u8>> {
1347 let mut buf = vec![0u8; buflen];
1348 let mut optlen = buflen as i32;
1349 let ret = unsafe { getsockopt(socket, level, name, buf.as_mut_ptr() as *mut _, &mut optlen) };
1350 if ret == SOCKET_ERROR {
1351 Err(crate::os::errno_io_error())
1352 } else {
1353 buf.truncate(optlen as usize);
1354 Ok(buf)
1355 }
1356}
1357
1358#[cfg(windows)]
1359pub fn setsockopt_bytes(socket: RawSocket, level: i32, name: i32, value: &[u8]) -> io::Result<()> {
1360 let ret = unsafe {
1361 setsockopt(
1362 socket,
1363 level,
1364 name,
1365 value.as_ptr() as *const _,
1366 value.len() as i32,
1367 )
1368 };
1369 if ret == SOCKET_ERROR {
1370 Err(crate::os::errno_io_error())
1371 } else {
1372 Ok(())
1373 }
1374}
1375
1376#[cfg(windows)]
1377pub fn setsockopt_int(socket: RawSocket, level: i32, name: i32, value: i32) -> io::Result<()> {
1378 let ret = unsafe {
1379 setsockopt(
1380 socket,
1381 level,
1382 name,
1383 &value as *const i32 as *const _,
1384 core::mem::size_of::<i32>() as i32,
1385 )
1386 };
1387 if ret == SOCKET_ERROR {
1388 Err(crate::os::errno_io_error())
1389 } else {
1390 Ok(())
1391 }
1392}
1393
1394#[cfg(windows)]
1395pub fn setsockopt_none(socket: RawSocket, level: i32, name: i32, optlen: u32) -> io::Result<()> {
1396 let ret = unsafe { setsockopt(socket, level, name, core::ptr::null(), optlen as i32) };
1397 if ret == SOCKET_ERROR {
1398 Err(crate::os::errno_io_error())
1399 } else {
1400 Ok(())
1401 }
1402}
1403
1404#[cfg(windows)]
1405pub fn protocol_info_size() -> usize {
1406 core::mem::size_of::<WSAPROTOCOL_INFOW>()
1407}
1408
1409#[cfg(windows)]
1410pub fn socket_from_share_data(bytes: &[u8]) -> io::Result<SharedSocket> {
1411 let mut info: WSAPROTOCOL_INFOW = unsafe { core::mem::zeroed() };
1412 unsafe {
1413 core::ptr::copy_nonoverlapping(
1414 bytes.as_ptr(),
1415 &mut info as *mut WSAPROTOCOL_INFOW as *mut u8,
1416 protocol_info_size(),
1417 );
1418 }
1419
1420 let raw = unsafe {
1421 WSASocketW(
1422 FROM_PROTOCOL_INFO,
1423 FROM_PROTOCOL_INFO,
1424 FROM_PROTOCOL_INFO,
1425 &info,
1426 0,
1427 WSA_FLAG_OVERLAPPED,
1428 )
1429 };
1430 if raw == INVALID_SOCKET {
1431 return Err(last_socket_error());
1432 }
1433
1434 crate::nt::set_handle_inheritable(raw as _, false)?;
1435
1436 Ok(SharedSocket {
1437 raw,
1438 family: info.iAddressFamily,
1439 socket_type: info.iSocketType,
1440 protocol: info.iProtocol,
1441 })
1442}
1443
1444#[cfg(windows)]
1445pub fn share_socket(socket: RawSocket, process_id: u32) -> io::Result<Vec<u8>> {
1446 let mut info = core::mem::MaybeUninit::<WSAPROTOCOL_INFOW>::uninit();
1447 let ret = unsafe { WSADuplicateSocketW(socket, process_id, info.as_mut_ptr()) };
1448 if ret == SOCKET_ERROR {
1449 return Err(last_socket_error());
1450 }
1451 let info = unsafe { info.assume_init() };
1452 let bytes = unsafe {
1453 core::slice::from_raw_parts(
1454 &info as *const WSAPROTOCOL_INFOW as *const u8,
1455 core::mem::size_of::<WSAPROTOCOL_INFOW>(),
1456 )
1457 };
1458 Ok(bytes.to_vec())
1459}
1460
1461#[cfg(windows)]
1462pub fn ioctl_u32(socket: RawSocket, cmd: u32, option: u32) -> io::Result<u32> {
1463 let mut recv = 0u32;
1464 let ret = unsafe {
1465 WSAIoctl(
1466 socket,
1467 cmd,
1468 &option as *const u32 as *const _,
1469 core::mem::size_of::<u32>() as u32,
1470 core::ptr::null_mut(),
1471 0,
1472 &mut recv,
1473 core::ptr::null_mut(),
1474 None,
1475 )
1476 };
1477 if ret == SOCKET_ERROR {
1478 Err(last_socket_error())
1479 } else {
1480 Ok(recv)
1481 }
1482}
1483
1484#[cfg(windows)]
1485pub fn ioctl_keepalive(socket: RawSocket, keepalive: TcpKeepalive) -> io::Result<u32> {
1486 let mut recv = 0u32;
1487 let ret = unsafe {
1488 WSAIoctl(
1489 socket,
1490 windows_sys::Win32::Networking::WinSock::SIO_KEEPALIVE_VALS,
1491 &keepalive as *const TcpKeepalive as *const _,
1492 core::mem::size_of::<TcpKeepalive>() as u32,
1493 core::ptr::null_mut(),
1494 0,
1495 &mut recv,
1496 core::ptr::null_mut(),
1497 None,
1498 )
1499 };
1500 if ret == SOCKET_ERROR {
1501 Err(last_socket_error())
1502 } else {
1503 Ok(recv)
1504 }
1505}
1506
1507#[cfg(windows)]
1508pub fn if_nametoindex_checked(name: &CStr) -> io::Result<u32> {
1509 crate::os::set_errno(libc::ENODEV);
1510 let ret = unsafe { if_nametoindex(name.as_ptr() as _) };
1511 if ret == 0 {
1512 Err(crate::os::errno_io_error())
1513 } else {
1514 Ok(ret)
1515 }
1516}
1517
1518#[cfg(windows)]
1519pub fn if_indextoname_checked(index: u32) -> io::Result<String> {
1520 let mut buf = [0; IF_MAX_STRING_SIZE as usize + 1];
1521 crate::os::set_errno(libc::ENXIO);
1522 let ret = unsafe { if_indextoname(index, buf.as_mut_ptr()) };
1523 if ret.is_null() {
1524 Err(crate::os::errno_io_error())
1525 } else {
1526 let buf = unsafe { CStr::from_ptr(buf.as_ptr() as _) };
1527 Ok(buf.to_string_lossy().into_owned())
1528 }
1529}
1530
1531#[cfg(windows)]
1534fn if_name_from_wide(buf: &[u16]) -> Wtf8Buf {
1535 let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
1536 Wtf8Buf::from_wide(&buf[..len])
1537}
1538
1539#[cfg(windows)]
1540pub fn if_nameindex() -> io::Result<Vec<(u32, Wtf8Buf)>> {
1541 fn get_name(luid: &NET_LUID_LH) -> io::Result<Wtf8Buf> {
1542 let mut buf = [0u16; IF_MAX_STRING_SIZE as usize + 1];
1543 let ret = unsafe { ConvertInterfaceLuidToNameW(luid, buf.as_mut_ptr(), buf.len()) };
1544 if ret != 0 {
1545 return Err(io::Error::from_raw_os_error(ret as i32));
1546 }
1547 Ok(if_name_from_wide(&buf))
1548 }
1549
1550 struct MibTable {
1551 ptr: NonNull<MIB_IF_TABLE2>,
1552 }
1553
1554 impl MibTable {
1555 fn get_raw() -> io::Result<Self> {
1556 let mut ptr = core::ptr::null_mut();
1557 let ret = unsafe { GetIfTable2Ex(MibIfTableRaw, &mut ptr) };
1558 if ret == 0 {
1559 let ptr = unsafe { NonNull::new_unchecked(ptr) };
1560 Ok(Self { ptr })
1561 } else {
1562 Err(io::Error::from_raw_os_error(ret as i32))
1563 }
1564 }
1565
1566 fn as_slice(&self) -> &[MIB_IF_ROW2] {
1567 unsafe {
1568 let p = self.ptr.as_ptr();
1569 let ptr = &raw const (*p).Table as *const MIB_IF_ROW2;
1570 core::slice::from_raw_parts(ptr, (*p).NumEntries as usize)
1571 }
1572 }
1573 }
1574
1575 impl Drop for MibTable {
1576 fn drop(&mut self) {
1577 unsafe { FreeMibTable(self.ptr.as_ptr() as *mut _) };
1578 }
1579 }
1580
1581 let table = MibTable::get_raw()?;
1582 table
1583 .as_slice()
1584 .iter()
1585 .map(|entry| Ok((entry.InterfaceIndex, get_name(&entry.InterfaceLuid)?)))
1586 .collect()
1587}
1588
1589#[cfg(test)]
1590#[cfg(windows)]
1591mod if_name_from_wide_tests {
1592 use super::if_name_from_wide;
1593 use rustpython_wtf8::Wtf8Buf;
1594
1595 #[test]
1596 fn keeps_unpaired_surrogate() {
1597 let units = [b'e' as u16, 0xD800, 0];
1598 let name = if_name_from_wide(&units);
1599 assert_eq!(name, Wtf8Buf::from_wide(&[b'e' as u16, 0xD800]));
1600 assert_ne!(
1601 name.as_bytes(),
1602 String::from_utf16_lossy(&[b'e' as u16, 0xD800]).as_bytes()
1603 );
1604 }
1605}
1606
1607#[cfg(windows)]
1609pub fn uuid_from_string_w(wide: &widestring::WideCStr) -> Result<windows_sys::core::GUID, u32> {
1610 let mut guid = windows_sys::core::GUID {
1611 data1: 0,
1612 data2: 0,
1613 data3: 0,
1614 data4: [0; 8],
1615 };
1616 let status =
1617 unsafe { windows_sys::Win32::System::Rpc::UuidFromStringW(wide.as_ptr(), &mut guid) };
1618 if status == windows_sys::Win32::System::Rpc::RPC_S_OK {
1619 Ok(guid)
1620 } else {
1621 Err(status as u32)
1622 }
1623}
1624
1625#[cfg(windows)]
1627pub fn uuid_to_string_w(guid: &windows_sys::core::GUID) -> Result<String, u32> {
1628 use windows_sys::Win32::System::Rpc::{RPC_S_OK, RpcStringFreeW, UuidToStringW};
1629 let mut raw = core::ptr::null_mut();
1630 let status = unsafe { UuidToStringW(guid, &mut raw) };
1631 if status != RPC_S_OK {
1632 return Err(status as u32);
1633 }
1634 let mut len = 0usize;
1635 unsafe {
1636 while *raw.add(len) != 0 {
1637 len += 1;
1638 }
1639 }
1640 let text = String::from_utf16_lossy(unsafe { core::slice::from_raw_parts(raw, len) });
1641 unsafe { RpcStringFreeW(&mut raw) };
1642 Ok(text)
1643}
1644
1645#[cfg(windows)]
1647pub const AF_SNA: i32 = 11;
1648#[cfg(windows)]
1649pub const AF_IRDA: i32 = 26;
1650#[cfg(windows)]
1651pub const AF_HYPERV: i32 = windows_sys::Win32::Networking::WinSock::AF_HYPERV as i32;
1652#[cfg(windows)]
1653pub const AF_BLUETOOTH: i32 = windows_sys::Win32::Devices::Bluetooth::AF_BTH as i32;
1654#[cfg(windows)]
1655pub const AF_BTH: i32 = AF_BLUETOOTH;
1656#[cfg(windows)]
1657pub const BTHPROTO_RFCOMM: i32 = windows_sys::Win32::Devices::Bluetooth::BTHPROTO_RFCOMM as i32;
1658
1659#[cfg(windows)]
1661pub const HV_PROTOCOL_RAW: i32 = 1;
1662#[cfg(windows)]
1663pub const HVSOCKET_CONNECT_TIMEOUT: i32 = 0x01;
1664#[cfg(windows)]
1665pub const HVSOCKET_CONNECT_TIMEOUT_MAX: i32 = 300_000;
1666#[cfg(windows)]
1667pub const HVSOCKET_CONNECTED_SUSPEND: i32 = 0x04;
1668#[cfg(windows)]
1669pub const HVSOCKET_ADDRESS_FLAG_PASSTHRU: i32 = 0x01;
1670#[cfg(windows)]
1671pub const HV_GUID_ZERO: &str = "00000000-0000-0000-0000-000000000000";
1672#[cfg(windows)]
1673pub const HV_GUID_WILDCARD: &str = "00000000-0000-0000-0000-000000000000";
1674#[cfg(windows)]
1675pub const HV_GUID_BROADCAST: &str = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
1676#[cfg(windows)]
1677pub const HV_GUID_CHILDREN: &str = "90DB8B89-0D35-4F79-8CE9-49EA0AC8B7CD";
1678#[cfg(windows)]
1679pub const HV_GUID_LOOPBACK: &str = "E0E16197-DD56-4A10-9195-5EE7A155A838";
1680#[cfg(windows)]
1681pub const HV_GUID_PARENT: &str = "A42E7CDA-D03F-480C-9CC2-A4DE20ABB878";
1682
1683#[cfg(windows)]
1684pub const SIO_TCP_SET_ACK_FREQUENCY: i32 =
1685 windows_sys::Win32::Networking::WinSock::SIO_TCP_SET_ACK_FREQUENCY as i32;
1686
1687#[cfg(windows)]
1689#[repr(C)]
1690#[derive(Clone, Copy)]
1691pub struct SockaddrHv {
1692 pub family: u16,
1693 pub reserved: u16,
1694 pub vm_id: windows_sys::core::GUID,
1695 pub service_id: windows_sys::core::GUID,
1696}
1697
1698#[cfg(windows)]
1699pub fn sockaddr_hv(
1700 vm_id: windows_sys::core::GUID,
1701 service_id: windows_sys::core::GUID,
1702) -> SockaddrHv {
1703 SockaddrHv {
1704 family: AF_HYPERV as u16,
1705 reserved: 0,
1706 vm_id,
1707 service_id,
1708 }
1709}
1710
1711#[cfg(windows)]
1713pub fn sockaddr_bth_rfcomm(
1714 bd_addr: u64,
1715 port: u32,
1716) -> windows_sys::Win32::Devices::Bluetooth::SOCKADDR_BTH {
1717 windows_sys::Win32::Devices::Bluetooth::SOCKADDR_BTH {
1718 addressFamily: AF_BTH as u16,
1719 btAddr: bd_addr,
1720 serviceClassId: Default::default(),
1721 port,
1722 }
1723}
1724
1725#[cfg(windows)]
1726pub fn unpack_sockaddr_bth(ptr: *const u8) -> (u64, u32) {
1727 let bth = unsafe { &*(ptr.cast::<windows_sys::Win32::Devices::Bluetooth::SOCKADDR_BTH>()) };
1728 (bth.btAddr, bth.port)
1729}
1730
1731#[cfg(windows)]
1733pub fn parse_bdaddr(name: &str) -> Option<u64> {
1734 let mut parts = name.split(':');
1735 let mut value = 0u64;
1736 for _ in 0..6 {
1737 let part = parts.next()?;
1738 if part.is_empty() || part.len() > 2 {
1739 return None;
1740 }
1741 let octet = u8::from_str_radix(part, 16).ok()?;
1742 value = (value << 8) | u64::from(octet);
1743 }
1744 if parts.next().is_some() {
1745 return None;
1746 }
1747 Some(value)
1748}
1749
1750#[cfg(windows)]
1752pub fn format_bdaddr(bdaddr: u64) -> String {
1753 let octet = |i: u32| (bdaddr >> (8 * i)) & 0xFF;
1754 alloc::format!(
1755 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
1756 octet(5),
1757 octet(4),
1758 octet(3),
1759 octet(2),
1760 octet(1),
1761 octet(0)
1762 )
1763}