Skip to main content

rustpython_host_env/
socket.rs

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/// Returns the system's hostname.
11#[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/// Returns a universally administered MAC address when available, otherwise
45/// the first locally administered address, or `None` when lookup fails.
46#[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/// Integer names the VM published from `libc as c` on unix. Windows already
98/// takes the overlapping set from this module.
99#[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// bionic (Android) does not define the CAN/ALG sockaddr structs.
490#[cfg(target_os = "linux")]
491pub use libc::{sockaddr_alg, sockaddr_can};
492
493/// Port numbers some Unix libcs omit. Windows takes `IPPORT_RESERVED` from
494/// WinSock and defines `IPPORT_USERRESERVED` beside the other extras.
495#[cfg(unix)]
496pub const IPPORT_RESERVED: i32 = 1024;
497#[cfg(unix)]
498pub const IPPORT_USERRESERVED: i32 = 5000;
499
500/// Multicast groups as unsigned host-order words. Windows publishes the
501/// same bits as signed C longs (`INADDR_*_GROUP` below).
502#[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/// POSIX `<netdb.h>` `NI_MAXSERV`. libc omits it on linux-gnu.
510#[cfg(any(target_os = "linux", target_os = "android"))]
511pub const NI_MAXSERV: i32 = 32;
512
513/// `<netinet/in.h>` join/leave and routing-header type. libc omits them
514/// on linux-gnu.
515#[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// spell-checker:ignore SETTIMER STARTTIMER COUNTEVT AUTOTIMER
523/// `linux/can/bcm.h` opcodes and flags. Not in libc.
524#[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/// `linux/vm_sockets.h`. libc does not bind the vsock sockopt / cid names.
574#[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/// `netinet/udplite.h`. libc does not bind the coverage sockopts.
590#[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/// `SOL_IP` / `SOL_UDP` when libc does not bind them. Linux takes the
606/// header names; Windows takes WinSock `SOL_IP`. Elsewhere they are the
607/// IANA protocol numbers.
608#[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/// `SOMAXCONN` when neither libc nor WinSock publishes it.
624#[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/// Bluetooth wildcard / local addresses. Header strings, not libc names.
638#[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/// RFC 3542 IPv6 socket options (`netinet6/in6.h`). Not in libc.
656#[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/// Darwin `<netdb.h>` `EAI_*` names the libc crate does not re-export.
684#[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/// Set the system's hostname from its filesystem-encoded bytes.
694///
695/// `socketmodule.c socket_sethostname` reads the argument as a buffer and
696/// passes `buf.buf`/`buf.len` straight to the syscall, so a name is not
697/// required to be UTF-8; taking `&[u8]` keeps that true here as well.
698#[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/// `winsock2.h` `SOMAXCONN`. windows-sys still exports the Winsock 1.1 value 5.
1208#[cfg(windows)]
1209pub const SOMAXCONN: i32 = 0x7fff_ffff;
1210
1211/// Signed C-long readings of option words, plus names the Winsock headers
1212/// do not define that the module still carries.
1213#[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/// `PyUnicode_FromWideChar` / `Py_BuildValue("Iu")`: a LUID name is kept as
1532/// WTF-8, so an unpaired surrogate is not replaced with U+FFFD.
1533#[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/// `UuidFromStringW`. The status is the RPC code, not `GetLastError`.
1608#[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/// `UuidToStringW` / `RpcStringFreeW`.
1626#[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/// Address families the Windows SDK exposes beyond the older MSVC census.
1646#[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/// `hvsocket.h`: the only protocol an `AF_HYPERV` socket is opened with.
1660#[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/// `SOCKADDR_HV` (`hvsocket.h`).
1688#[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/// `SOCKADDR_BTH`.
1712#[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/// `setbdaddr`: six hex octets separated by `:`.
1732#[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/// `makebdaddr`: `XX:XX:XX:XX:XX:XX`, most significant octet first.
1751#[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}