Skip to main content

rustpython_stdlib/
socket.rs

1// spell-checker:disable
2
3pub(crate) use _socket::module_def;
4
5#[cfg(feature = "ssl")]
6pub(super) use _socket::{PySocket, SelectKind, sock_select, timeout_error_msg};
7
8#[pymodule]
9mod _socket {
10    use crate::common::lock::{PyMappedRwLockReadGuard, PyRwLock, PyRwLockReadGuard};
11    use crate::vm::{
12        AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
13        builtins::{
14            PyBaseExceptionRef, PyListRef, PyModule, PyOSError, PyStrRef, PyTupleRef, PyTypeRef,
15            PyUtf8StrRef,
16        },
17        common::os::ErrorExt,
18        convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject},
19        function::{
20            ArgBytesLike, ArgIntoFloat, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath,
21            OptionalArg, OptionalOption,
22        },
23        types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable},
24        utils::ToCString,
25    };
26
27    pub(crate) fn module_exec(vm: &VirtualMachine, module: &Py<PyModule>) -> PyResult<()> {
28        #[cfg(windows)]
29        crate::vm::windows::init_winsock();
30
31        __module_exec(vm, module);
32        Ok(())
33    }
34    use core::{
35        mem::MaybeUninit,
36        net::{Ipv4Addr, Ipv6Addr, SocketAddr},
37        time::Duration,
38    };
39    use crossbeam_utils::atomic::AtomicCell;
40    use num_traits::ToPrimitive;
41    use socket2::Socket;
42    use std::{
43        ffi,
44        io::{self, Read, Write},
45        net::{self, Shutdown, ToSocketAddrs},
46        time::Instant,
47    };
48
49    #[cfg(unix)]
50    use libc as c;
51    #[cfg(windows)]
52    mod c {
53        pub use windows_sys::Win32::NetworkManagement::IpHelper::{if_indextoname, if_nametoindex};
54        pub use windows_sys::Win32::Networking::WinSock::{
55            INADDR_ANY, INADDR_BROADCAST, INADDR_LOOPBACK, INADDR_NONE,
56        };
57
58        pub use windows_sys::Win32::Networking::WinSock::{
59            AF_APPLETALK, AF_DECnet, AF_IPX, AF_LINK, AI_ADDRCONFIG, AI_ALL, AI_CANONNAME,
60            AI_NUMERICSERV, AI_V4MAPPED, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_HDRINCL,
61            IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_OPTIONS, IP_RECVDSTADDR,
62            IP_TOS, IP_TTL, IPPORT_RESERVED, IPPROTO_AH, IPPROTO_DSTOPTS, IPPROTO_EGP, IPPROTO_ESP,
63            IPPROTO_FRAGMENT, IPPROTO_GGP, IPPROTO_HOPOPTS, IPPROTO_ICMP, IPPROTO_ICMPV6,
64            IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IP as IPPROTO_IPIP, IPPROTO_IPV4,
65            IPPROTO_IPV6, IPPROTO_ND, IPPROTO_NONE, IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW,
66            IPPROTO_ROUTING, IPPROTO_TCP, IPPROTO_UDP, IPV6_CHECKSUM, IPV6_DONTFRAG, IPV6_HOPLIMIT,
67            IPV6_HOPOPTS, IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, IPV6_MULTICAST_HOPS,
68            IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_PKTINFO, IPV6_RECVRTHDR, IPV6_RECVTCLASS,
69            IPV6_RTHDR, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, MSG_BCAST, MSG_CTRUNC,
70            MSG_DONTROUTE, MSG_MCAST, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, NI_DGRAM,
71            NI_MAXHOST, NI_MAXSERV, NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV,
72            RCVALL_IPLEVEL, RCVALL_OFF, RCVALL_ON, RCVALL_SOCKETLEVELONLY, SD_BOTH as SHUT_RDWR,
73            SD_RECEIVE as SHUT_RD, SD_SEND as SHUT_WR, SIO_KEEPALIVE_VALS, SIO_LOOPBACK_FAST_PATH,
74            SIO_RCVALL, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF,
75            SO_REUSEADDR, SO_SNDBUF, SO_TYPE, SO_USELOOPBACK, SOCK_DGRAM, SOCK_RAW, SOCK_RDM,
76            SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SOMAXCONN, TCP_NODELAY, WSAEBADF,
77            WSAECONNRESET, WSAENOTSOCK, WSAEWOULDBLOCK,
78        };
79        pub use windows_sys::Win32::Networking::WinSock::{
80            INVALID_SOCKET, SOCKET_ERROR, WSA_FLAG_OVERLAPPED, WSADuplicateSocketW,
81            WSAGetLastError, WSAIoctl, WSAPROTOCOL_INFOW, WSASocketW,
82        };
83        pub use windows_sys::Win32::Networking::WinSock::{
84            SO_REUSEADDR as SO_EXCLUSIVEADDRUSE, getprotobyname, getservbyname, getservbyport,
85            getsockopt, setsockopt,
86        };
87        pub use windows_sys::Win32::Networking::WinSock::{
88            WSA_NOT_ENOUGH_MEMORY as EAI_MEMORY, WSAEAFNOSUPPORT as EAI_FAMILY,
89            WSAEINVAL as EAI_BADFLAGS, WSAESOCKTNOSUPPORT as EAI_SOCKTYPE,
90            WSAHOST_NOT_FOUND as EAI_NODATA, WSAHOST_NOT_FOUND as EAI_NONAME,
91            WSANO_RECOVERY as EAI_FAIL, WSATRY_AGAIN as EAI_AGAIN,
92            WSATYPE_NOT_FOUND as EAI_SERVICE,
93        };
94        pub const IF_NAMESIZE: usize =
95            windows_sys::Win32::NetworkManagement::Ndis::IF_MAX_STRING_SIZE as _;
96        pub const AF_UNSPEC: i32 = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as _;
97        pub const AF_INET: i32 = windows_sys::Win32::Networking::WinSock::AF_INET as _;
98        pub const AF_INET6: i32 = windows_sys::Win32::Networking::WinSock::AF_INET6 as _;
99        pub const AI_PASSIVE: i32 = windows_sys::Win32::Networking::WinSock::AI_PASSIVE as _;
100        pub const AI_NUMERICHOST: i32 =
101            windows_sys::Win32::Networking::WinSock::AI_NUMERICHOST as _;
102        pub const FROM_PROTOCOL_INFO: i32 = -1;
103    }
104    // constants
105    #[pyattr(name = "has_ipv6")]
106    const HAS_IPV6: bool = true;
107    #[pyattr]
108    // put IPPROTO_MAX later
109    use c::{
110        AF_INET, AF_INET6, AF_UNSPEC, INADDR_ANY, INADDR_LOOPBACK, INADDR_NONE, IPPROTO_ICMP,
111        IPPROTO_ICMPV6, IPPROTO_IP, IPPROTO_IPV6, IPPROTO_TCP, IPPROTO_TCP as SOL_TCP, IPPROTO_UDP,
112        MSG_CTRUNC, MSG_DONTROUTE, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, NI_DGRAM, NI_MAXHOST,
113        NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV, SHUT_RD, SHUT_RDWR, SHUT_WR,
114        SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_REUSEADDR,
115        SO_SNDBUF, SO_TYPE, SOCK_DGRAM, SOCK_STREAM, SOL_SOCKET, TCP_NODELAY,
116    };
117
118    #[cfg(not(target_os = "redox"))]
119    #[pyattr]
120    use c::{
121        AF_APPLETALK, AF_DECnet, AF_IPX, IPPROTO_AH, IPPROTO_DSTOPTS, IPPROTO_EGP, IPPROTO_ESP,
122        IPPROTO_FRAGMENT, IPPROTO_HOPOPTS, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_NONE,
123        IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING,
124    };
125
126    #[cfg(unix)]
127    #[pyattr]
128    use c::{AF_UNIX, SO_REUSEPORT};
129
130    #[pyattr]
131    use c::{AI_ADDRCONFIG, AI_NUMERICHOST, AI_NUMERICSERV, AI_PASSIVE};
132
133    #[cfg(not(target_os = "redox"))]
134    #[pyattr]
135    use c::{SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET};
136
137    #[cfg(target_os = "android")]
138    #[pyattr]
139    use c::{SOL_ATALK, SOL_AX25, SOL_IPX, SOL_NETROM, SOL_ROSE};
140
141    #[cfg(target_os = "freebsd")]
142    #[pyattr]
143    use c::SO_SETFIB;
144
145    #[cfg(target_os = "linux")]
146    #[pyattr]
147    use c::{
148        CAN_BCM, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_FLAG, CAN_ERR_MASK, CAN_ISOTP, CAN_J1939,
149        CAN_RAW, CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES, CAN_RAW_FILTER, CAN_RAW_JOIN_FILTERS,
150        CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS, CAN_RTR_FLAG, CAN_SFF_MASK, IPPROTO_MPTCP,
151        J1939_IDLE_ADDR, J1939_MAX_UNICAST_ADDR, J1939_NLA_BYTES_ACKED, J1939_NLA_PAD,
152        J1939_NO_ADDR, J1939_NO_NAME, J1939_NO_PGN, J1939_PGN_ADDRESS_CLAIMED,
153        J1939_PGN_ADDRESS_COMMANDED, J1939_PGN_MAX, J1939_PGN_PDU1_MAX, J1939_PGN_REQUEST,
154        SCM_J1939_DEST_ADDR, SCM_J1939_DEST_NAME, SCM_J1939_ERRQUEUE, SCM_J1939_PRIO,
155        SO_J1939_ERRQUEUE, SO_J1939_FILTER, SO_J1939_PROMISC, SO_J1939_SEND_PRIO, SOL_CAN_BASE,
156        SOL_CAN_RAW,
157    };
158
159    // CAN BCM opcodes
160    #[cfg(target_os = "linux")]
161    #[pyattr]
162    const CAN_BCM_TX_SETUP: i32 = 1;
163    #[cfg(target_os = "linux")]
164    #[pyattr]
165    const CAN_BCM_TX_DELETE: i32 = 2;
166    #[cfg(target_os = "linux")]
167    #[pyattr]
168    const CAN_BCM_TX_READ: i32 = 3;
169    #[cfg(target_os = "linux")]
170    #[pyattr]
171    const CAN_BCM_TX_SEND: i32 = 4;
172    #[cfg(target_os = "linux")]
173    #[pyattr]
174    const CAN_BCM_RX_SETUP: i32 = 5;
175    #[cfg(target_os = "linux")]
176    #[pyattr]
177    const CAN_BCM_RX_DELETE: i32 = 6;
178    #[cfg(target_os = "linux")]
179    #[pyattr]
180    const CAN_BCM_RX_READ: i32 = 7;
181    #[cfg(target_os = "linux")]
182    #[pyattr]
183    const CAN_BCM_TX_STATUS: i32 = 8;
184    #[cfg(target_os = "linux")]
185    #[pyattr]
186    const CAN_BCM_TX_EXPIRED: i32 = 9;
187    #[cfg(target_os = "linux")]
188    #[pyattr]
189    const CAN_BCM_RX_STATUS: i32 = 10;
190    #[cfg(target_os = "linux")]
191    #[pyattr]
192    const CAN_BCM_RX_TIMEOUT: i32 = 11;
193    #[cfg(target_os = "linux")]
194    #[pyattr]
195    const CAN_BCM_RX_CHANGED: i32 = 12;
196
197    // CAN BCM flags (linux/can/bcm.h)
198    #[cfg(target_os = "linux")]
199    #[pyattr]
200    const CAN_BCM_SETTIMER: i32 = 0x0001;
201    #[cfg(target_os = "linux")]
202    #[pyattr]
203    const CAN_BCM_STARTTIMER: i32 = 0x0002;
204    #[cfg(target_os = "linux")]
205    #[pyattr]
206    const CAN_BCM_TX_COUNTEVT: i32 = 0x0004;
207    #[cfg(target_os = "linux")]
208    #[pyattr]
209    const CAN_BCM_TX_ANNOUNCE: i32 = 0x0008;
210    #[cfg(target_os = "linux")]
211    #[pyattr]
212    const CAN_BCM_TX_CP_CAN_ID: i32 = 0x0010;
213    #[cfg(target_os = "linux")]
214    #[pyattr]
215    const CAN_BCM_RX_FILTER_ID: i32 = 0x0020;
216    #[cfg(target_os = "linux")]
217    #[pyattr]
218    const CAN_BCM_RX_CHECK_DLC: i32 = 0x0040;
219    #[cfg(target_os = "linux")]
220    #[pyattr]
221    const CAN_BCM_RX_NO_AUTOTIMER: i32 = 0x0080;
222    #[cfg(target_os = "linux")]
223    #[pyattr]
224    const CAN_BCM_RX_ANNOUNCE_RESUME: i32 = 0x0100;
225    #[cfg(target_os = "linux")]
226    #[pyattr]
227    const CAN_BCM_TX_RESET_MULTI_IDX: i32 = 0x0200;
228    #[cfg(target_os = "linux")]
229    #[pyattr]
230    const CAN_BCM_RX_RTR_FRAME: i32 = 0x0400;
231    #[cfg(target_os = "linux")]
232    #[pyattr]
233    const CAN_BCM_CAN_FD_FRAME: i32 = 0x0800;
234
235    #[cfg(all(target_os = "linux", target_env = "gnu"))]
236    #[pyattr]
237    use c::SOL_RDS;
238
239    #[cfg(target_os = "netbsd")]
240    #[pyattr]
241    use c::IPPROTO_VRRP;
242
243    #[cfg(target_vendor = "apple")]
244    #[pyattr]
245    use c::{AF_SYSTEM, PF_SYSTEM, SYSPROTO_CONTROL, TCP_KEEPALIVE};
246
247    // RFC3542 IPv6 socket options for macOS (netinet6/in6.h)
248    // Not available in libc, define manually
249    #[cfg(target_vendor = "apple")]
250    #[pyattr]
251    const IPV6_RECVHOPLIMIT: i32 = 37;
252    #[cfg(target_vendor = "apple")]
253    #[pyattr]
254    const IPV6_RECVRTHDR: i32 = 38;
255    #[cfg(target_vendor = "apple")]
256    #[pyattr]
257    const IPV6_RECVHOPOPTS: i32 = 39;
258    #[cfg(target_vendor = "apple")]
259    #[pyattr]
260    const IPV6_RECVDSTOPTS: i32 = 40;
261    #[cfg(target_vendor = "apple")]
262    #[pyattr]
263    const IPV6_USE_MIN_MTU: i32 = 42;
264    #[cfg(target_vendor = "apple")]
265    #[pyattr]
266    const IPV6_RECVPATHMTU: i32 = 43;
267    #[cfg(target_vendor = "apple")]
268    #[pyattr]
269    const IPV6_PATHMTU: i32 = 44;
270    #[cfg(target_vendor = "apple")]
271    #[pyattr]
272    const IPV6_NEXTHOP: i32 = 48;
273    #[cfg(target_vendor = "apple")]
274    #[pyattr]
275    const IPV6_HOPOPTS: i32 = 49;
276    #[cfg(target_vendor = "apple")]
277    #[pyattr]
278    const IPV6_DSTOPTS: i32 = 50;
279    #[cfg(target_vendor = "apple")]
280    #[pyattr]
281    const IPV6_RTHDR: i32 = 51;
282    #[cfg(target_vendor = "apple")]
283    #[pyattr]
284    const IPV6_RTHDRDSTOPTS: i32 = 57;
285    #[cfg(target_vendor = "apple")]
286    #[pyattr]
287    const IPV6_RTHDR_TYPE_0: i32 = 0;
288
289    #[cfg(windows)]
290    #[pyattr]
291    use c::{
292        IPPORT_RESERVED, IPPROTO_IPV4, RCVALL_IPLEVEL, RCVALL_OFF, RCVALL_ON,
293        RCVALL_SOCKETLEVELONLY, SIO_KEEPALIVE_VALS, SIO_LOOPBACK_FAST_PATH, SIO_RCVALL,
294        SO_EXCLUSIVEADDRUSE,
295    };
296
297    #[cfg(not(windows))]
298    #[pyattr]
299    const IPPORT_RESERVED: i32 = 1024;
300
301    #[pyattr]
302    const IPPORT_USERRESERVED: i32 = 5000;
303
304    #[cfg(any(unix, target_os = "android"))]
305    #[pyattr]
306    use c::{
307        EAI_SYSTEM, MSG_EOR, SO_ACCEPTCONN, SO_DEBUG, SO_DONTROUTE, SO_RCVLOWAT, SO_RCVTIMEO,
308        SO_SNDLOWAT, SO_SNDTIMEO,
309    };
310
311    #[cfg(any(target_os = "android", target_os = "linux"))]
312    #[pyattr]
313    use c::{
314        ALG_OP_DECRYPT, ALG_OP_ENCRYPT, ALG_SET_AEAD_ASSOCLEN, ALG_SET_AEAD_AUTHSIZE, ALG_SET_IV,
315        ALG_SET_KEY, ALG_SET_OP, IP_DEFAULT_MULTICAST_LOOP, IP_RECVOPTS, IP_RETOPTS, IPV6_DSTOPTS,
316        IPV6_NEXTHOP, IPV6_PATHMTU, IPV6_RECVDSTOPTS, IPV6_RECVHOPLIMIT, IPV6_RECVHOPOPTS,
317        IPV6_RECVPATHMTU, IPV6_RTHDRDSTOPTS, NETLINK_CRYPTO, NETLINK_DNRTMSG, NETLINK_FIREWALL,
318        NETLINK_IP6_FW, NETLINK_NFLOG, NETLINK_ROUTE, NETLINK_USERSOCK, NETLINK_XFRM, SO_PASSSEC,
319        SO_PEERSEC, SOL_ALG,
320    };
321
322    #[cfg(any(target_os = "android", target_vendor = "apple"))]
323    #[pyattr]
324    use c::{AI_DEFAULT, AI_MASK, AI_V4MAPPED_CFG};
325
326    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
327    #[pyattr]
328    use c::MSG_NOTIFICATION;
329
330    #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
331    #[pyattr]
332    use c::TCP_USER_TIMEOUT;
333
334    #[cfg(any(unix, target_os = "android", windows))]
335    #[pyattr]
336    use c::{
337        INADDR_BROADCAST, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF,
338        IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF,
339        IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
340    };
341
342    #[cfg(any(unix, target_os = "android", windows))]
343    #[pyattr]
344    const INADDR_UNSPEC_GROUP: u32 = 0xe0000000;
345
346    #[cfg(any(unix, target_os = "android", windows))]
347    #[pyattr]
348    const INADDR_ALLHOSTS_GROUP: u32 = 0xe0000001;
349
350    #[cfg(any(unix, target_os = "android", windows))]
351    #[pyattr]
352    const INADDR_MAX_LOCAL_GROUP: u32 = 0xe00000ff;
353
354    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
355    #[pyattr]
356    use c::{
357        AF_ALG, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BRIDGE, AF_CAN, AF_ECONET, AF_IRDA,
358        AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PPPOX, AF_RDS, AF_SECURITY,
359        AF_TIPC, AF_VSOCK, AF_WANPIPE, AF_X25, IP_TRANSPARENT, MSG_CONFIRM, MSG_ERRQUEUE,
360        MSG_FASTOPEN, MSG_MORE, PF_CAN, PF_PACKET, PF_RDS, SCM_CREDENTIALS, SO_BINDTODEVICE,
361        SO_MARK, SOL_IP, SOL_TIPC, SOL_UDP, TCP_CORK, TCP_DEFER_ACCEPT, TCP_LINGER2, TCP_QUICKACK,
362        TCP_SYNCNT, TCP_WINDOW_CLAMP,
363    };
364
365    // gated on presence of AF_VSOCK:
366    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
367    #[pyattr]
368    const SO_VM_SOCKETS_BUFFER_SIZE: u32 = 0;
369
370    // gated on presence of AF_VSOCK:
371    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
372    #[pyattr]
373    const SO_VM_SOCKETS_BUFFER_MIN_SIZE: u32 = 1;
374
375    // gated on presence of AF_VSOCK:
376    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
377    #[pyattr]
378    const SO_VM_SOCKETS_BUFFER_MAX_SIZE: u32 = 2;
379
380    // gated on presence of AF_VSOCK:
381    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
382    #[pyattr]
383    const VMADDR_CID_ANY: u32 = 0xffffffff; // 0xffffffff
384
385    // gated on presence of AF_VSOCK:
386    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
387    #[pyattr]
388    const VMADDR_PORT_ANY: u32 = 0xffffffff; // 0xffffffff
389
390    // gated on presence of AF_VSOCK:
391    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
392    #[pyattr]
393    const VMADDR_CID_HOST: u32 = 2;
394
395    // gated on presence of AF_VSOCK:
396    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
397    #[pyattr]
398    const VM_SOCKETS_INVALID_VERSION: u32 = 0xffffffff; // 0xffffffff
399
400    // TODO: gated on https://github.com/rust-lang/libc/pull/1662
401    // // gated on presence of AF_VSOCK:
402    // #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
403    // #[pyattr(name = "IOCTL_VM_SOCKETS_GET_LOCAL_CID", once)]
404    // fn ioctl_vm_sockets_get_local_cid(_vm: &VirtualMachine) -> i32 {
405    //     c::_IO(7, 0xb9)
406    // }
407
408    #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
409    #[pyattr]
410    const SOL_IP: i32 = 0;
411
412    #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
413    #[pyattr]
414    const SOL_UDP: i32 = 17;
415
416    #[cfg(any(target_os = "android", target_os = "linux", windows))]
417    #[pyattr]
418    use c::{IP_OPTIONS, IPV6_HOPOPTS, IPV6_RECVRTHDR, IPV6_RTHDR};
419
420    #[cfg(any(
421        target_os = "dragonfly",
422        target_os = "freebsd",
423        target_vendor = "apple"
424    ))]
425    #[pyattr]
426    use c::{IPPROTO_HELLO, IPPROTO_XTP, LOCAL_PEERCRED, MSG_EOF};
427
428    #[cfg(any(target_os = "netbsd", target_os = "openbsd", windows))]
429    #[pyattr]
430    use c::{MSG_BCAST, MSG_MCAST};
431
432    #[cfg(any(
433        target_os = "android",
434        target_os = "fuchsia",
435        target_os = "freebsd",
436        target_os = "linux"
437    ))]
438    #[pyattr]
439    use c::{IPPROTO_UDPLITE, TCP_CONGESTION};
440
441    #[cfg(any(
442        target_os = "android",
443        target_os = "fuchsia",
444        target_os = "freebsd",
445        target_os = "linux"
446    ))]
447    #[pyattr]
448    const UDPLITE_SEND_CSCOV: i32 = 10;
449
450    #[cfg(any(
451        target_os = "android",
452        target_os = "fuchsia",
453        target_os = "freebsd",
454        target_os = "linux"
455    ))]
456    #[pyattr]
457    const UDPLITE_RECV_CSCOV: i32 = 11;
458
459    #[cfg(any(
460        target_os = "android",
461        target_os = "fuchsia",
462        target_os = "linux",
463        target_os = "openbsd"
464    ))]
465    #[pyattr]
466    use c::AF_KEY;
467
468    #[cfg(any(
469        target_os = "android",
470        target_os = "fuchsia",
471        target_os = "linux",
472        target_os = "redox"
473    ))]
474    #[pyattr]
475    use c::SO_DOMAIN;
476
477    #[cfg(any(
478        target_os = "android",
479        target_os = "fuchsia",
480        all(
481            target_os = "linux",
482            any(
483                target_arch = "aarch64",
484                target_arch = "x86",
485                target_arch = "loongarch64",
486                target_arch = "mips",
487                target_arch = "powerpc",
488                target_arch = "powerpc64",
489                target_arch = "riscv64",
490                target_arch = "s390x",
491                target_arch = "x86_64"
492            )
493        ),
494        target_os = "redox"
495    ))]
496    #[pyattr]
497    use c::SO_PRIORITY;
498
499    #[cfg(any(
500        target_os = "dragonfly",
501        target_os = "freebsd",
502        target_os = "netbsd",
503        target_os = "openbsd"
504    ))]
505    #[pyattr]
506    use c::IPPROTO_MOBILE;
507
508    #[cfg(any(
509        target_os = "dragonfly",
510        target_os = "freebsd",
511        target_os = "netbsd",
512        target_vendor = "apple"
513    ))]
514    #[pyattr]
515    use c::SCM_CREDS;
516
517    #[cfg(any(
518        target_os = "freebsd",
519        target_os = "fuchsia",
520        target_os = "linux",
521        target_vendor = "apple"
522    ))]
523    #[pyattr]
524    use c::TCP_FASTOPEN;
525
526    #[cfg(any(
527        target_os = "android",
528        target_os = "freebsd",
529        target_os = "fuchsia",
530        all(
531            target_os = "linux",
532            any(
533                target_arch = "aarch64",
534                target_arch = "x86",
535                target_arch = "loongarch64",
536                target_arch = "mips",
537                target_arch = "powerpc",
538                target_arch = "powerpc64",
539                target_arch = "riscv64",
540                target_arch = "s390x",
541                target_arch = "x86_64"
542            )
543        ),
544        target_os = "redox"
545    ))]
546    #[pyattr]
547    use c::SO_PROTOCOL;
548
549    #[cfg(any(
550        target_os = "android",
551        target_os = "dragonfly",
552        target_os = "freebsd",
553        target_os = "linux",
554        target_vendor = "apple",
555        windows
556    ))]
557    #[pyattr]
558    use c::IPV6_DONTFRAG;
559
560    #[cfg(any(
561        target_os = "android",
562        target_os = "dragonfly",
563        target_os = "fuchsia",
564        target_os = "linux",
565        target_os = "redox"
566    ))]
567    #[pyattr]
568    use c::{SO_PASSCRED, SO_PEERCRED};
569
570    #[cfg(any(
571        target_os = "android",
572        target_os = "freebsd",
573        target_os = "fuchsia",
574        target_os = "linux",
575        target_os = "netbsd"
576    ))]
577    #[pyattr]
578    use c::TCP_INFO;
579
580    #[cfg(any(
581        target_os = "android",
582        target_os = "freebsd",
583        target_os = "fuchsia",
584        target_os = "linux",
585        target_vendor = "apple"
586    ))]
587    #[pyattr]
588    use c::IP_RECVTOS;
589
590    #[cfg(any(
591        target_os = "android",
592        target_os = "netbsd",
593        target_os = "redox",
594        target_vendor = "apple",
595        windows
596    ))]
597    #[pyattr]
598    use c::NI_MAXSERV;
599
600    #[cfg(any(
601        target_os = "dragonfly",
602        target_os = "freebsd",
603        target_os = "netbsd",
604        target_os = "openbsd",
605        target_vendor = "apple"
606    ))]
607    #[pyattr]
608    use c::{IPPROTO_EON, IPPROTO_IPCOMP};
609
610    #[cfg(any(
611        target_os = "dragonfly",
612        target_os = "freebsd",
613        target_os = "netbsd",
614        target_vendor = "apple",
615        windows
616    ))]
617    #[pyattr]
618    use c::IPPROTO_ND;
619
620    #[cfg(any(
621        target_os = "android",
622        target_os = "dragonfly",
623        target_os = "freebsd",
624        target_os = "linux",
625        target_vendor = "apple",
626        windows
627    ))]
628    #[pyattr]
629    use c::{IPV6_CHECKSUM, IPV6_HOPLIMIT};
630
631    #[cfg(any(
632        target_os = "android",
633        target_os = "freebsd",
634        target_os = "fuchsia",
635        target_os = "linux",
636        target_os = "netbsd"
637    ))]
638    #[pyattr]
639    use c::IPPROTO_SCTP; // also in windows
640
641    #[cfg(any(
642        target_os = "android",
643        target_os = "freebsd",
644        target_os = "fuchsia",
645        target_os = "linux",
646        target_vendor = "apple",
647        windows
648    ))]
649    #[pyattr]
650    use c::{AI_ALL, AI_V4MAPPED};
651
652    #[cfg(any(
653        target_os = "android",
654        target_os = "linux",
655        target_os = "netbsd",
656        target_os = "openbsd",
657        target_vendor = "apple",
658        windows
659    ))]
660    #[pyattr]
661    use c::EAI_NODATA;
662
663    #[cfg(any(
664        target_os = "dragonfly",
665        target_os = "freebsd",
666        target_os = "netbsd",
667        target_os = "openbsd",
668        target_vendor = "apple",
669        windows
670    ))]
671    #[pyattr]
672    use c::{
673        AF_LINK, IP_RECVDSTADDR, IPPROTO_GGP, IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, SO_USELOOPBACK,
674    };
675
676    #[cfg(any(
677        target_os = "android",
678        target_os = "dragonfly",
679        target_os = "freebsd",
680        target_os = "fuchsia",
681        target_os = "linux",
682        target_os = "netbsd",
683        target_os = "openbsd"
684    ))]
685    #[pyattr]
686    use c::{MSG_CMSG_CLOEXEC, MSG_NOSIGNAL};
687
688    #[cfg(any(
689        target_os = "android",
690        target_os = "dragonfly",
691        target_os = "freebsd",
692        target_os = "fuchsia",
693        target_os = "linux",
694        target_os = "netbsd",
695        target_os = "redox"
696    ))]
697    #[pyattr]
698    use c::TCP_KEEPIDLE;
699
700    #[cfg(any(
701        target_os = "android",
702        target_os = "dragonfly",
703        target_os = "freebsd",
704        target_os = "fuchsia",
705        target_os = "linux",
706        target_os = "netbsd",
707        target_vendor = "apple"
708    ))]
709    #[pyattr]
710    use c::{TCP_KEEPCNT, TCP_KEEPINTVL};
711
712    #[cfg(any(
713        target_os = "android",
714        target_os = "dragonfly",
715        target_os = "freebsd",
716        target_os = "fuchsia",
717        target_os = "linux",
718        target_os = "netbsd",
719        target_os = "openbsd",
720        target_os = "redox"
721    ))]
722    #[pyattr]
723    use c::{SOCK_CLOEXEC, SOCK_NONBLOCK};
724
725    #[cfg(any(
726        target_os = "android",
727        target_os = "dragonfly",
728        target_os = "freebsd",
729        target_os = "fuchsia",
730        target_os = "linux",
731        target_os = "netbsd",
732        target_os = "openbsd",
733        target_vendor = "apple"
734    ))]
735    #[pyattr]
736    use c::{
737        AF_ROUTE, AF_SNA, EAI_OVERFLOW, IPPROTO_GRE, IPPROTO_RSVP, IPPROTO_TP, IPV6_RECVPKTINFO,
738        MSG_DONTWAIT, SCM_RIGHTS, TCP_MAXSEG,
739    };
740
741    #[cfg(any(
742        target_os = "android",
743        target_os = "dragonfly",
744        target_os = "freebsd",
745        target_os = "linux",
746        target_os = "netbsd",
747        target_os = "openbsd",
748        target_vendor = "apple",
749        windows
750    ))]
751    #[pyattr]
752    use c::IPV6_PKTINFO;
753
754    #[cfg(any(
755        target_os = "android",
756        target_os = "freebsd",
757        target_os = "fuchsia",
758        target_os = "linux",
759        target_os = "netbsd",
760        target_os = "openbsd",
761        target_vendor = "apple",
762        windows
763    ))]
764    #[pyattr]
765    use c::AI_CANONNAME;
766
767    #[cfg(any(
768        target_os = "android",
769        target_os = "dragonfly",
770        target_os = "freebsd",
771        target_os = "fuchsia",
772        target_os = "linux",
773        target_os = "netbsd",
774        target_os = "openbsd",
775        target_vendor = "apple",
776        windows
777    ))]
778    #[pyattr]
779    use c::{
780        EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NONAME, EAI_SERVICE,
781        EAI_SOCKTYPE, IP_HDRINCL, IP_TOS, IPV6_RECVTCLASS, IPV6_TCLASS, SOMAXCONN,
782    };
783
784    #[cfg(not(any(
785        target_os = "android",
786        target_os = "dragonfly",
787        target_os = "freebsd",
788        target_os = "fuchsia",
789        target_os = "linux",
790        target_os = "netbsd",
791        target_os = "openbsd",
792        target_vendor = "apple",
793        windows
794    )))]
795    #[pyattr]
796    const SOMAXCONN: i32 = 5; // Common value
797
798    // HERE IS WHERE THE BLUETOOTH CONSTANTS START
799    // TODO: there should be a more intelligent way of detecting bluetooth on a platform.
800    //       CPython uses header-detection, but blocks NetBSD and DragonFly BSD
801    #[cfg(any(
802        target_os = "android",
803        target_os = "freebsd",
804        target_os = "fuchsia",
805        target_os = "linux",
806        target_os = "openbsd"
807    ))]
808    #[pyattr]
809    use c::AF_BLUETOOTH;
810
811    #[cfg(any(
812        target_os = "android",
813        target_os = "freebsd",
814        target_os = "fuchsia",
815        target_os = "linux",
816        target_os = "openbsd"
817    ))]
818    #[pyattr]
819    const BDADDR_ANY: &str = "00:00:00:00:00:00";
820    #[cfg(any(
821        target_os = "android",
822        target_os = "freebsd",
823        target_os = "fuchsia",
824        target_os = "linux",
825        target_os = "openbsd"
826    ))]
827    #[pyattr]
828    const BDADDR_LOCAL: &str = "00:00:00:FF:FF:FF";
829    // HERE IS WHERE THE BLUETOOTH CONSTANTS END
830
831    #[cfg(windows)]
832    #[pyattr]
833    use windows_sys::Win32::Networking::WinSock::{
834        IPPROTO_CBT, IPPROTO_ICLFXBM, IPPROTO_IGP, IPPROTO_L2TP, IPPROTO_PGM, IPPROTO_RDP,
835        IPPROTO_SCTP, IPPROTO_ST,
836    };
837
838    #[pyattr]
839    fn error(vm: &VirtualMachine) -> PyTypeRef {
840        vm.ctx.exceptions.os_error.to_owned()
841    }
842
843    #[pyattr]
844    fn timeout(vm: &VirtualMachine) -> PyTypeRef {
845        vm.ctx.exceptions.timeout_error.to_owned()
846    }
847
848    #[pyattr(once)]
849    fn herror(vm: &VirtualMachine) -> PyTypeRef {
850        vm.ctx.new_exception_type(
851            "socket",
852            "herror",
853            Some(vec![vm.ctx.exceptions.os_error.to_owned()]),
854        )
855    }
856    #[pyattr(once)]
857    fn gaierror(vm: &VirtualMachine) -> PyTypeRef {
858        vm.ctx.new_exception_type(
859            "socket",
860            "gaierror",
861            Some(vec![vm.ctx.exceptions.os_error.to_owned()]),
862        )
863    }
864
865    #[pyfunction]
866    const fn htonl(x: u32) -> u32 {
867        u32::to_be(x)
868    }
869
870    #[pyfunction]
871    const fn htons(x: u16) -> u16 {
872        u16::to_be(x)
873    }
874
875    #[pyfunction]
876    const fn ntohl(x: u32) -> u32 {
877        u32::from_be(x)
878    }
879
880    #[pyfunction]
881    const fn ntohs(x: u16) -> u16 {
882        u16::from_be(x)
883    }
884
885    #[cfg(unix)]
886    type RawSocket = std::os::unix::io::RawFd;
887    #[cfg(windows)]
888    type RawSocket = std::os::windows::raw::SOCKET;
889
890    #[cfg(unix)]
891    macro_rules! errcode {
892        ($e:ident) => {
893            c::$e
894        };
895    }
896    #[cfg(windows)]
897    macro_rules! errcode {
898    ($e:ident) => {
899        paste::paste!(c::[<WSA $e>])
900    };
901}
902
903    #[cfg(windows)]
904    use windows_sys::Win32::NetworkManagement::IpHelper;
905
906    fn get_raw_sock(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<RawSocket> {
907        #[cfg(unix)]
908        type CastFrom = libc::c_long;
909        #[cfg(windows)]
910        type CastFrom = libc::c_longlong;
911
912        // should really just be to_index() but test_socket tests the error messages explicitly
913        if obj.fast_isinstance(vm.ctx.types.float_type) {
914            return Err(vm.new_type_error("integer argument expected, got float"));
915        }
916        let int = obj
917            .try_index_opt(vm)
918            .unwrap_or_else(|| Err(vm.new_type_error("an integer is required")))?;
919        int.try_to_primitive::<CastFrom>(vm)
920            .map(|sock| sock as RawSocket)
921    }
922
923    #[cfg(target_os = "linux")]
924    #[derive(FromArgs)]
925    struct SendmsgAfalgArgs {
926        #[pyarg(any, default)]
927        msg: Vec<ArgBytesLike>,
928        #[pyarg(named)]
929        op: u32,
930        #[pyarg(named, default)]
931        iv: Option<ArgBytesLike>,
932        #[pyarg(named, default)]
933        assoclen: OptionalArg<isize>,
934        #[pyarg(named, default)]
935        flags: i32,
936    }
937
938    #[pyattr(name = "socket")]
939    #[pyattr(name = "SocketType")]
940    #[pyclass(name = "socket")]
941    #[derive(Debug, PyPayload)]
942    pub struct PySocket {
943        kind: AtomicCell<i32>,
944        family: AtomicCell<i32>,
945        proto: AtomicCell<i32>,
946        pub(crate) timeout: AtomicCell<f64>,
947        sock: PyRwLock<Option<Socket>>,
948    }
949
950    const _: () = assert!(core::mem::size_of::<Option<Socket>>() == core::mem::size_of::<Socket>());
951
952    impl Default for PySocket {
953        fn default() -> Self {
954            Self {
955                kind: AtomicCell::default(),
956                family: AtomicCell::default(),
957                proto: AtomicCell::default(),
958                timeout: AtomicCell::new(-1.0),
959                sock: PyRwLock::new(None),
960            }
961        }
962    }
963
964    #[cfg(windows)]
965    const CLOSED_ERR: i32 = c::WSAENOTSOCK;
966    #[cfg(unix)]
967    const CLOSED_ERR: i32 = c::EBADF;
968
969    impl Read for &PySocket {
970        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
971            (&mut &*self.sock()?).read(buf)
972        }
973    }
974
975    impl Write for &PySocket {
976        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
977            (&mut &*self.sock()?).write(buf)
978        }
979
980        fn flush(&mut self) -> std::io::Result<()> {
981            (&mut &*self.sock()?).flush()
982        }
983    }
984
985    impl PySocket {
986        pub fn sock_opt(&self) -> Option<PyMappedRwLockReadGuard<'_, Socket>> {
987            let sock = PyRwLockReadGuard::try_map(self.sock.read(), |sock| sock.as_ref());
988            sock.ok()
989        }
990
991        pub fn sock(&self) -> io::Result<PyMappedRwLockReadGuard<'_, Socket>> {
992            self.sock_opt()
993                .ok_or_else(|| io::Error::from_raw_os_error(CLOSED_ERR))
994        }
995
996        fn init_inner(
997            &self,
998            family: i32,
999            socket_kind: i32,
1000            proto: i32,
1001            sock: Socket,
1002        ) -> io::Result<()> {
1003            self.family.store(family);
1004            // Mask out SOCK_NONBLOCK and SOCK_CLOEXEC flags from stored type
1005            // to ensure consistent cross-platform behavior
1006            #[cfg(any(
1007                target_os = "android",
1008                target_os = "dragonfly",
1009                target_os = "freebsd",
1010                target_os = "fuchsia",
1011                target_os = "illumos",
1012                target_os = "linux",
1013                target_os = "netbsd",
1014                target_os = "openbsd",
1015                target_os = "redox"
1016            ))]
1017            let masked_kind = socket_kind & !(c::SOCK_NONBLOCK | c::SOCK_CLOEXEC);
1018            #[cfg(not(any(
1019                target_os = "android",
1020                target_os = "dragonfly",
1021                target_os = "freebsd",
1022                target_os = "fuchsia",
1023                target_os = "illumos",
1024                target_os = "linux",
1025                target_os = "netbsd",
1026                target_os = "openbsd",
1027                target_os = "redox"
1028            )))]
1029            let masked_kind = socket_kind;
1030            self.kind.store(masked_kind);
1031            self.proto.store(proto);
1032            let mut s = self.sock.write();
1033            let sock = s.insert(sock);
1034            // If SOCK_NONBLOCK is set, use timeout 0 (non-blocking)
1035            #[cfg(any(
1036                target_os = "android",
1037                target_os = "dragonfly",
1038                target_os = "freebsd",
1039                target_os = "fuchsia",
1040                target_os = "illumos",
1041                target_os = "linux",
1042                target_os = "netbsd",
1043                target_os = "openbsd",
1044                target_os = "redox"
1045            ))]
1046            let timeout = if socket_kind & c::SOCK_NONBLOCK != 0 {
1047                0.0
1048            } else {
1049                DEFAULT_TIMEOUT.load()
1050            };
1051            #[cfg(not(any(
1052                target_os = "android",
1053                target_os = "dragonfly",
1054                target_os = "freebsd",
1055                target_os = "fuchsia",
1056                target_os = "illumos",
1057                target_os = "linux",
1058                target_os = "netbsd",
1059                target_os = "openbsd",
1060                target_os = "redox"
1061            )))]
1062            let timeout = DEFAULT_TIMEOUT.load();
1063            self.timeout.store(timeout);
1064            if timeout >= 0.0 {
1065                sock.set_nonblocking(true)?;
1066            }
1067            Ok(())
1068        }
1069
1070        /// returns Err(blocking)
1071        pub fn get_timeout(&self) -> Result<Duration, bool> {
1072            let timeout = self.timeout.load();
1073            if timeout > 0.0 {
1074                Ok(Duration::from_secs_f64(timeout))
1075            } else {
1076                Err(timeout != 0.0)
1077            }
1078        }
1079
1080        fn sock_op<F, R>(
1081            &self,
1082            vm: &VirtualMachine,
1083            select: SelectKind,
1084            f: F,
1085        ) -> Result<R, IoOrPyException>
1086        where
1087            F: FnMut() -> io::Result<R>,
1088        {
1089            let timeout = self.get_timeout().ok();
1090            self.sock_op_timeout_err(vm, select, timeout, f)
1091        }
1092
1093        fn sock_op_timeout_err<F, R>(
1094            &self,
1095            vm: &VirtualMachine,
1096            select: SelectKind,
1097            timeout: Option<Duration>,
1098            mut f: F,
1099        ) -> Result<R, IoOrPyException>
1100        where
1101            F: FnMut() -> io::Result<R>,
1102        {
1103            let deadline = timeout.map(Deadline::new);
1104
1105            loop {
1106                if deadline.is_some() || matches!(select, SelectKind::Connect) {
1107                    let interval = deadline.as_ref().map(|d| d.time_until()).transpose()?;
1108                    let sock = self.sock()?;
1109                    let res = vm.allow_threads(|| sock_select(&sock, select, interval));
1110                    match res {
1111                        Ok(true) => return Err(IoOrPyException::Timeout),
1112                        Err(e) if e.kind() == io::ErrorKind::Interrupted => {
1113                            vm.check_signals()?;
1114                            continue;
1115                        }
1116                        Err(e) => return Err(e.into()),
1117                        Ok(false) => {} // no timeout, continue as normal
1118                    }
1119                }
1120
1121                let err = loop {
1122                    // Detach thread state around the blocking syscall so
1123                    // stop-the-world can park this thread (e.g. before fork).
1124                    match vm.allow_threads(&mut f) {
1125                        Ok(x) => return Ok(x),
1126                        Err(e) if e.kind() == io::ErrorKind::Interrupted => vm.check_signals()?,
1127                        Err(e) => break e,
1128                    }
1129                };
1130                if timeout.is_some() && err.kind() == io::ErrorKind::WouldBlock {
1131                    continue;
1132                }
1133                return Err(err.into());
1134            }
1135        }
1136
1137        fn extract_address(
1138            &self,
1139            addr: PyObjectRef,
1140            caller: &str,
1141            vm: &VirtualMachine,
1142        ) -> Result<socket2::SockAddr, IoOrPyException> {
1143            let family = self.family.load();
1144            match family {
1145                #[cfg(unix)]
1146                c::AF_UNIX => {
1147                    use crate::vm::function::ArgStrOrBytesLike;
1148                    use std::os::unix::ffi::OsStrExt;
1149                    let buf = ArgStrOrBytesLike::try_from_object(vm, addr)?;
1150                    let bytes = &*buf.borrow_bytes();
1151                    let path = match &buf {
1152                        ArgStrOrBytesLike::Buf(_) => ffi::OsStr::from_bytes(bytes).into(),
1153                        ArgStrOrBytesLike::Str(s) => vm.fsencode(s)?,
1154                    };
1155                    socket2::SockAddr::unix(path)
1156                        .map_err(|_| vm.new_os_error("AF_UNIX path too long".to_owned()).into())
1157                }
1158                c::AF_INET => {
1159                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
1160                        vm.new_type_error(format!(
1161                            "{}(): AF_INET address must be tuple, not {}",
1162                            caller,
1163                            obj.class().name()
1164                        ))
1165                    })?;
1166                    if tuple.len() != 2 {
1167                        return Err(vm
1168                            .new_type_error("AF_INET address must be a pair (host, post)")
1169                            .into());
1170                    }
1171                    let addr = Address::from_tuple(&tuple, vm)?;
1172                    let mut addr4 = get_addr(vm, addr.host, c::AF_INET)?;
1173                    match &mut addr4 {
1174                        SocketAddr::V4(addr4) => {
1175                            addr4.set_port(addr.port);
1176                        }
1177                        SocketAddr::V6(_) => unreachable!(),
1178                    }
1179                    Ok(addr4.into())
1180                }
1181                c::AF_INET6 => {
1182                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
1183                        vm.new_type_error(format!(
1184                            "{}(): AF_INET6 address must be tuple, not {}",
1185                            caller,
1186                            obj.class().name()
1187                        ))
1188                    })?;
1189                    match tuple.len() {
1190                        2..=4 => {}
1191                        _ => return Err(vm.new_type_error(
1192                            "AF_INET6 address must be a tuple (host, port[, flowinfo[, scopeid]])",
1193                        ).into()),
1194                    }
1195                    let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&tuple, vm)?;
1196                    let mut addr6 = get_addr(vm, addr.host, c::AF_INET6)?;
1197                    match &mut addr6 {
1198                        SocketAddr::V6(addr6) => {
1199                            addr6.set_port(addr.port);
1200                            addr6.set_flowinfo(flowinfo);
1201                            addr6.set_scope_id(scopeid);
1202                        }
1203                        SocketAddr::V4(_) => unreachable!(),
1204                    }
1205                    Ok(addr6.into())
1206                }
1207                #[cfg(target_os = "linux")]
1208                c::AF_CAN => {
1209                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
1210                        vm.new_type_error(format!(
1211                            "{}(): AF_CAN address must be tuple, not {}",
1212                            caller,
1213                            obj.class().name()
1214                        ))
1215                    })?;
1216                    if tuple.is_empty() || tuple.len() > 2 {
1217                        return Err(vm
1218                            .new_type_error(
1219                                "AF_CAN address must be a tuple (interface,) or (interface, addr)",
1220                            )
1221                            .into());
1222                    }
1223                    let interface: PyStrRef = tuple[0].clone().downcast().map_err(|obj| {
1224                        vm.new_type_error(format!(
1225                            "{}(): AF_CAN interface must be str, not {}",
1226                            caller,
1227                            obj.class().name()
1228                        ))
1229                    })?;
1230                    let interface = interface.try_into_utf8(vm).map_err(IoOrPyException::from)?;
1231                    let ifname = interface.as_str();
1232
1233                    // Get interface index
1234                    let ifindex = if ifname.is_empty() {
1235                        0 // Bind to all CAN interfaces
1236                    } else {
1237                        // Check interface name length (IFNAMSIZ is typically 16)
1238                        if ifname.len() >= 16 {
1239                            return Err(vm
1240                                .new_os_error("interface name too long".to_owned())
1241                                .into());
1242                        }
1243                        let cstr = alloc::ffi::CString::new(ifname)
1244                            .map_err(|_| vm.new_os_error("invalid interface name".to_owned()))?;
1245                        let idx = unsafe { libc::if_nametoindex(cstr.as_ptr()) };
1246                        if idx == 0 {
1247                            return Err(io::Error::last_os_error().into());
1248                        }
1249                        idx as i32
1250                    };
1251
1252                    // Create sockaddr_can
1253                    let mut storage: libc::sockaddr_storage = unsafe { core::mem::zeroed() };
1254                    let can_addr =
1255                        &mut storage as *mut libc::sockaddr_storage as *mut libc::sockaddr_can;
1256                    unsafe {
1257                        (*can_addr).can_family = libc::AF_CAN as libc::sa_family_t;
1258                        (*can_addr).can_ifindex = ifindex;
1259                    }
1260                    let storage: socket2::SockAddrStorage =
1261                        unsafe { core::mem::transmute(storage) };
1262                    Ok(unsafe {
1263                        socket2::SockAddr::new(
1264                            storage,
1265                            core::mem::size_of::<libc::sockaddr_can>() as libc::socklen_t,
1266                        )
1267                    })
1268                }
1269                #[cfg(target_os = "linux")]
1270                c::AF_ALG => {
1271                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
1272                        vm.new_type_error(format!(
1273                            "{}(): AF_ALG address must be tuple, not {}",
1274                            caller,
1275                            obj.class().name()
1276                        ))
1277                    })?;
1278                    if tuple.len() != 2 {
1279                        return Err(vm
1280                            .new_type_error("AF_ALG address must be a tuple (type, name)")
1281                            .into());
1282                    }
1283                    let alg_type: PyStrRef = tuple[0].clone().downcast().map_err(|obj| {
1284                        vm.new_type_error(format!(
1285                            "{}(): AF_ALG type must be str, not {}",
1286                            caller,
1287                            obj.class().name()
1288                        ))
1289                    })?;
1290                    let alg_name: PyStrRef = tuple[1].clone().downcast().map_err(|obj| {
1291                        vm.new_type_error(format!(
1292                            "{}(): AF_ALG name must be str, not {}",
1293                            caller,
1294                            obj.class().name()
1295                        ))
1296                    })?;
1297
1298                    let alg_type = alg_type.try_into_utf8(vm).map_err(IoOrPyException::from)?;
1299                    let alg_name = alg_name.try_into_utf8(vm).map_err(IoOrPyException::from)?;
1300                    let type_str = alg_type.as_str();
1301                    let name_str = alg_name.as_str();
1302
1303                    // salg_type is 14 bytes, salg_name is 64 bytes
1304                    if type_str.len() >= 14 {
1305                        return Err(vm.new_value_error("type too long").into());
1306                    }
1307                    if name_str.len() >= 64 {
1308                        return Err(vm.new_value_error("name too long").into());
1309                    }
1310
1311                    // Create sockaddr_alg
1312                    let mut storage: libc::sockaddr_storage = unsafe { core::mem::zeroed() };
1313                    let alg_addr =
1314                        &mut storage as *mut libc::sockaddr_storage as *mut libc::sockaddr_alg;
1315                    unsafe {
1316                        (*alg_addr).salg_family = libc::AF_ALG as libc::sa_family_t;
1317                        // Copy type string
1318                        for (i, b) in type_str.bytes().enumerate() {
1319                            (*alg_addr).salg_type[i] = b;
1320                        }
1321                        // Copy name string
1322                        for (i, b) in name_str.bytes().enumerate() {
1323                            (*alg_addr).salg_name[i] = b;
1324                        }
1325                    }
1326                    let storage: socket2::SockAddrStorage =
1327                        unsafe { core::mem::transmute(storage) };
1328                    Ok(unsafe {
1329                        socket2::SockAddr::new(
1330                            storage,
1331                            core::mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t,
1332                        )
1333                    })
1334                }
1335                _ => Err(vm.new_os_error(format!("{caller}(): bad family")).into()),
1336            }
1337        }
1338
1339        fn connect_inner(
1340            &self,
1341            address: PyObjectRef,
1342            caller: &str,
1343            vm: &VirtualMachine,
1344        ) -> Result<(), IoOrPyException> {
1345            let sock_addr = self.extract_address(address, caller, vm)?;
1346
1347            let sock = self.sock()?;
1348            let err = match vm.allow_threads(|| sock.connect(&sock_addr)) {
1349                Ok(()) => return Ok(()),
1350                Err(e) => e,
1351            };
1352
1353            let wait_connect = if err.kind() == io::ErrorKind::Interrupted {
1354                vm.check_signals()?;
1355                self.timeout.load() != 0.0
1356            } else {
1357                #[cfg(unix)]
1358                use c::EINPROGRESS;
1359                #[cfg(windows)]
1360                use c::WSAEWOULDBLOCK as EINPROGRESS;
1361
1362                self.timeout.load() > 0.0 && err.raw_os_error() == Some(EINPROGRESS)
1363            };
1364
1365            if wait_connect {
1366                // basically, connect() is async, and it registers an "error" on the socket when it's
1367                // done connecting. SelectKind::Connect fills the errorfds fd_set, so if we wake up
1368                // from poll and the error is EISCONN then we know that the connect is done
1369                self.sock_op(vm, SelectKind::Connect, || {
1370                    let sock = self.sock()?;
1371                    let err = sock.take_error()?;
1372                    match err {
1373                        Some(e) if e.posix_errno() == libc::EISCONN => Ok(()),
1374                        Some(e) => Err(e),
1375                        // TODO: is this accurate?
1376                        None => Ok(()),
1377                    }
1378                })
1379            } else {
1380                Err(err.into())
1381            }
1382        }
1383    }
1384
1385    impl DefaultConstructor for PySocket {}
1386
1387    #[derive(FromArgs)]
1388    pub struct SocketInitArgs {
1389        #[pyarg(any, optional)]
1390        family: OptionalArg<i32>,
1391        #[pyarg(any, optional)]
1392        r#type: OptionalArg<i32>,
1393        #[pyarg(any, optional)]
1394        proto: OptionalArg<i32>,
1395        #[pyarg(any, optional)]
1396        fileno: OptionalOption<PyObjectRef>,
1397    }
1398
1399    impl Initializer for PySocket {
1400        type Args = SocketInitArgs;
1401
1402        fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
1403            Self::_init(zelf, args, vm).map_err(|e| e.into_pyexception(vm))
1404        }
1405    }
1406
1407    impl Representable for PySocket {
1408        #[inline]
1409        fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1410            Ok(format!(
1411                "<socket object, fd={}, family={}, type={}, proto={}>",
1412                zelf.fileno(),
1413                zelf.family.load(),
1414                zelf.kind.load(),
1415                zelf.proto.load(),
1416            ))
1417        }
1418    }
1419
1420    impl Destructor for PySocket {
1421        fn del(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<()> {
1422            // Emit ResourceWarning if socket is still open
1423            if zelf.sock.read().is_some() {
1424                let laddr = if let Ok(sock) = zelf.sock()
1425                    && let Ok(addr) = sock.local_addr()
1426                    && let Ok(repr) = get_addr_tuple(&addr, vm).repr(vm)
1427                {
1428                    format!(", laddr={}", repr.as_wtf8())
1429                } else {
1430                    String::new()
1431                };
1432
1433                let msg = format!(
1434                    "unclosed <socket.socket fd={}, family={}, type={}, proto={}{}>",
1435                    zelf.fileno(),
1436                    zelf.family.load(),
1437                    zelf.kind.load(),
1438                    zelf.proto.load(),
1439                    laddr
1440                );
1441                let _ = crate::vm::warn::warn(
1442                    vm.ctx.new_str(msg).into(),
1443                    Some(vm.ctx.exceptions.resource_warning.to_owned()),
1444                    1,
1445                    None,
1446                    vm,
1447                );
1448            }
1449            let _ = zelf.close();
1450            Ok(())
1451        }
1452    }
1453
1454    #[pyclass(
1455        with(Constructor, Initializer, Representable, Destructor),
1456        flags(BASETYPE)
1457    )]
1458    impl PySocket {
1459        fn _init(
1460            zelf: PyRef<Self>,
1461            args: <Self as Initializer>::Args,
1462            vm: &VirtualMachine,
1463        ) -> Result<(), IoOrPyException> {
1464            let mut family = args.family.unwrap_or(-1);
1465            let mut socket_kind = args.r#type.unwrap_or(-1);
1466            let mut proto = args.proto.unwrap_or(-1);
1467
1468            let fileno = args.fileno;
1469            let sock;
1470
1471            // On Windows, fileno can be bytes from socket.share() for fromshare()
1472            #[cfg(windows)]
1473            if let Some(fileno_obj) = fileno.flatten() {
1474                use crate::vm::builtins::PyBytes;
1475                if let Ok(bytes) = fileno_obj.clone().downcast::<PyBytes>() {
1476                    let bytes_data = bytes.as_bytes();
1477                    let expected_size = core::mem::size_of::<c::WSAPROTOCOL_INFOW>();
1478
1479                    if bytes_data.len() != expected_size {
1480                        return Err(vm
1481                            .new_value_error(format!(
1482                                "socket descriptor string has wrong size, should be {} bytes",
1483                                expected_size
1484                            ))
1485                            .into());
1486                    }
1487
1488                    let mut info: c::WSAPROTOCOL_INFOW = unsafe { core::mem::zeroed() };
1489                    unsafe {
1490                        core::ptr::copy_nonoverlapping(
1491                            bytes_data.as_ptr(),
1492                            &mut info as *mut c::WSAPROTOCOL_INFOW as *mut u8,
1493                            expected_size,
1494                        );
1495                    }
1496
1497                    let fd = unsafe {
1498                        c::WSASocketW(
1499                            c::FROM_PROTOCOL_INFO,
1500                            c::FROM_PROTOCOL_INFO,
1501                            c::FROM_PROTOCOL_INFO,
1502                            &info,
1503                            0,
1504                            c::WSA_FLAG_OVERLAPPED,
1505                        )
1506                    };
1507
1508                    if fd == c::INVALID_SOCKET {
1509                        return Err(Self::wsa_error().into());
1510                    }
1511
1512                    crate::vm::stdlib::nt::raw_set_handle_inheritable(fd as _, false)?;
1513
1514                    family = info.iAddressFamily;
1515                    socket_kind = info.iSocketType;
1516                    proto = info.iProtocol;
1517
1518                    sock = unsafe { sock_from_raw_unchecked(fd as RawSocket) };
1519                    return Ok(zelf.init_inner(family, socket_kind, proto, sock)?);
1520                }
1521
1522                // Not bytes, treat as regular fileno
1523                let fileno = get_raw_sock(fileno_obj, vm)?;
1524                sock = sock_from_raw(fileno, vm)?;
1525                match sock.local_addr() {
1526                    Ok(addr) if family == -1 => family = addr.family() as i32,
1527                    Err(e)
1528                        if family == -1
1529                            || matches!(
1530                                e.raw_os_error(),
1531                                Some(errcode!(ENOTSOCK)) | Some(errcode!(EBADF))
1532                            ) =>
1533                    {
1534                        core::mem::forget(sock);
1535                        return Err(e.into());
1536                    }
1537                    _ => {}
1538                }
1539                if socket_kind == -1 {
1540                    socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into();
1541                }
1542                proto = 0;
1543                return Ok(zelf.init_inner(family, socket_kind, proto, sock)?);
1544            }
1545
1546            #[cfg(not(windows))]
1547            let fileno = fileno
1548                .flatten()
1549                .map(|obj| get_raw_sock(obj, vm))
1550                .transpose()?;
1551            #[cfg(not(windows))]
1552            if let Some(fileno) = fileno {
1553                sock = sock_from_raw(fileno, vm)?;
1554                match sock.local_addr() {
1555                    Ok(addr) if family == -1 => family = addr.family() as i32,
1556                    Err(e)
1557                        if family == -1
1558                            || matches!(
1559                                e.raw_os_error(),
1560                                Some(errcode!(ENOTSOCK)) | Some(errcode!(EBADF))
1561                            ) =>
1562                    {
1563                        core::mem::forget(sock);
1564                        return Err(e.into());
1565                    }
1566                    _ => {}
1567                }
1568                if socket_kind == -1 {
1569                    socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into();
1570                }
1571                cfg_if::cfg_if! {
1572                    if #[cfg(any(
1573                        target_os = "android",
1574                        target_os = "freebsd",
1575                        target_os = "fuchsia",
1576                        target_os = "linux",
1577                    ))] {
1578                        if proto == -1 {
1579                            proto = sock.protocol()?.map_or(0, Into::into);
1580                        }
1581                    } else {
1582                        proto = 0;
1583                    }
1584                }
1585                return Ok(zelf.init_inner(family, socket_kind, proto, sock)?);
1586            }
1587
1588            // No fileno provided, create new socket
1589            {
1590                if family == -1 {
1591                    family = c::AF_INET as _
1592                }
1593                if socket_kind == -1 {
1594                    socket_kind = c::SOCK_STREAM
1595                }
1596                if proto == -1 {
1597                    proto = 0
1598                }
1599                sock = Socket::new(family.into(), socket_kind.into(), Some(proto.into()))?;
1600            };
1601            Ok(zelf.init_inner(family, socket_kind, proto, sock)?)
1602        }
1603
1604        #[pymethod]
1605        fn connect(
1606            &self,
1607            address: PyObjectRef,
1608            vm: &VirtualMachine,
1609        ) -> Result<(), IoOrPyException> {
1610            self.connect_inner(address, "connect", vm)
1611        }
1612
1613        #[pymethod]
1614        fn connect_ex(&self, address: PyObjectRef, vm: &VirtualMachine) -> PyResult<i32> {
1615            match self.connect_inner(address, "connect_ex", vm) {
1616                Ok(()) => Ok(0),
1617                Err(err) => err.errno(),
1618            }
1619        }
1620
1621        #[pymethod]
1622        fn bind(&self, address: PyObjectRef, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
1623            let sock_addr = self.extract_address(address, "bind", vm)?;
1624            Ok(self.sock()?.bind(&sock_addr)?)
1625        }
1626
1627        #[pymethod]
1628        fn listen(&self, backlog: OptionalArg<i32>) -> io::Result<()> {
1629            let backlog = backlog.unwrap_or(128);
1630            let backlog = if backlog < 0 { 0 } else { backlog };
1631            self.sock()?.listen(backlog)
1632        }
1633
1634        #[pymethod]
1635        fn _accept(
1636            &self,
1637            vm: &VirtualMachine,
1638        ) -> Result<(RawSocket, PyObjectRef), IoOrPyException> {
1639            // Use accept_raw() instead of accept() to avoid socket2's set_common_flags()
1640            // which tries to set SO_NOSIGPIPE and fails with EINVAL on Unix domain sockets on macOS
1641            let (sock, addr) = self.sock_op(vm, SelectKind::Read, || self.sock()?.accept_raw())?;
1642            let fd = into_sock_fileno(sock);
1643            Ok((fd, get_addr_tuple(&addr, vm)))
1644        }
1645
1646        #[pymethod]
1647        fn recv(
1648            &self,
1649            bufsize: usize,
1650            flags: OptionalArg<i32>,
1651            vm: &VirtualMachine,
1652        ) -> Result<Vec<u8>, IoOrPyException> {
1653            let flags = flags.unwrap_or(0);
1654            let mut buffer = Vec::with_capacity(bufsize);
1655            let sock = self.sock()?;
1656            let n = self.sock_op(vm, SelectKind::Read, || {
1657                sock.recv_with_flags(buffer.spare_capacity_mut(), flags)
1658            })?;
1659            unsafe { buffer.set_len(n) };
1660            Ok(buffer)
1661        }
1662
1663        #[pymethod]
1664        fn recv_into(
1665            &self,
1666            buf: ArgMemoryBuffer,
1667            nbytes: OptionalArg<isize>,
1668            flags: OptionalArg<i32>,
1669            vm: &VirtualMachine,
1670        ) -> Result<usize, IoOrPyException> {
1671            let flags = flags.unwrap_or(0);
1672            let sock = self.sock()?;
1673            let mut buf = buf.borrow_buf_mut();
1674            let buf = &mut *buf;
1675
1676            // Handle nbytes parameter
1677            let read_len = if let OptionalArg::Present(nbytes) = nbytes {
1678                let nbytes = nbytes
1679                    .to_usize()
1680                    .ok_or_else(|| vm.new_value_error("negative buffersize in recv_into"))?;
1681                nbytes.min(buf.len())
1682            } else {
1683                buf.len()
1684            };
1685
1686            let buf = &mut buf[..read_len];
1687            self.sock_op(vm, SelectKind::Read, || {
1688                sock.recv_with_flags(unsafe { slice_as_uninit(buf) }, flags)
1689            })
1690        }
1691
1692        #[pymethod]
1693        fn recvfrom(
1694            &self,
1695            bufsize: isize,
1696            flags: OptionalArg<i32>,
1697            vm: &VirtualMachine,
1698        ) -> Result<(Vec<u8>, PyObjectRef), IoOrPyException> {
1699            let flags = flags.unwrap_or(0);
1700            let bufsize = bufsize
1701                .to_usize()
1702                .ok_or_else(|| vm.new_value_error("negative buffersize in recvfrom"))?;
1703            let mut buffer = Vec::with_capacity(bufsize);
1704            let (n, addr) = self.sock_op(vm, SelectKind::Read, || {
1705                self.sock()?
1706                    .recv_from_with_flags(buffer.spare_capacity_mut(), flags)
1707            })?;
1708            unsafe { buffer.set_len(n) };
1709            Ok((buffer, get_addr_tuple(&addr, vm)))
1710        }
1711
1712        #[pymethod]
1713        fn recvfrom_into(
1714            &self,
1715            buf: ArgMemoryBuffer,
1716            nbytes: OptionalArg<isize>,
1717            flags: OptionalArg<i32>,
1718            vm: &VirtualMachine,
1719        ) -> Result<(usize, PyObjectRef), IoOrPyException> {
1720            let mut buf = buf.borrow_buf_mut();
1721            let buf = &mut *buf;
1722            let buf = match nbytes {
1723                OptionalArg::Present(i) => {
1724                    let i = i.to_usize().ok_or_else(|| {
1725                        vm.new_value_error("negative buffersize in recvfrom_into")
1726                    })?;
1727                    buf.get_mut(..i).ok_or_else(|| {
1728                        vm.new_value_error("nbytes is greater than the length of the buffer")
1729                    })?
1730                }
1731                OptionalArg::Missing => buf,
1732            };
1733            let flags = flags.unwrap_or(0);
1734            let sock = self.sock()?;
1735            let (n, addr) = self.sock_op(vm, SelectKind::Read, || {
1736                sock.recv_from_with_flags(unsafe { slice_as_uninit(buf) }, flags)
1737            })?;
1738            Ok((n, get_addr_tuple(&addr, vm)))
1739        }
1740
1741        #[pymethod]
1742        fn send(
1743            &self,
1744            bytes: ArgBytesLike,
1745            flags: OptionalArg<i32>,
1746            vm: &VirtualMachine,
1747        ) -> Result<usize, IoOrPyException> {
1748            let flags = flags.unwrap_or(0);
1749            let buf = bytes.borrow_buf();
1750            let buf = &*buf;
1751            self.sock_op(vm, SelectKind::Write, || {
1752                self.sock()?.send_with_flags(buf, flags)
1753            })
1754        }
1755
1756        #[pymethod]
1757        fn sendall(
1758            &self,
1759            bytes: ArgBytesLike,
1760            flags: OptionalArg<i32>,
1761            vm: &VirtualMachine,
1762        ) -> Result<(), IoOrPyException> {
1763            let flags = flags.unwrap_or(0);
1764
1765            let timeout = self.get_timeout().ok();
1766
1767            let deadline = timeout.map(Deadline::new);
1768
1769            let buf = bytes.borrow_buf();
1770            let buf = &*buf;
1771            let mut buf_offset = 0;
1772            // now we have like 3 layers of interrupt loop :)
1773            while buf_offset < buf.len() {
1774                let interval = deadline.as_ref().map(|d| d.time_until()).transpose()?;
1775                self.sock_op_timeout_err(vm, SelectKind::Write, interval, || {
1776                    let subbuf = &buf[buf_offset..];
1777                    buf_offset += self.sock()?.send_with_flags(subbuf, flags)?;
1778                    Ok(())
1779                })?;
1780                vm.check_signals()?;
1781            }
1782            Ok(())
1783        }
1784
1785        #[pymethod]
1786        fn sendto(
1787            &self,
1788            bytes: ArgBytesLike,
1789            arg2: PyObjectRef,
1790            arg3: OptionalArg<PyObjectRef>,
1791            vm: &VirtualMachine,
1792        ) -> Result<usize, IoOrPyException> {
1793            // signature is bytes[, flags], address
1794            let (flags, address) = match arg3 {
1795                OptionalArg::Present(arg3) => {
1796                    // should just be i32::try_from_obj but tests check for error message
1797                    let int = arg2
1798                        .try_index_opt(vm)
1799                        .unwrap_or_else(|| Err(vm.new_type_error("an integer is required")))?;
1800                    let flags = int.try_to_primitive::<i32>(vm)?;
1801                    (flags, arg3)
1802                }
1803                OptionalArg::Missing => (0, arg2),
1804            };
1805            let addr = self.extract_address(address, "sendto", vm)?;
1806            let buf = bytes.borrow_buf();
1807            let buf = &*buf;
1808            self.sock_op(vm, SelectKind::Write, || {
1809                self.sock()?.send_to_with_flags(buf, &addr, flags)
1810            })
1811        }
1812
1813        #[cfg(all(unix, not(target_os = "redox")))]
1814        #[pymethod]
1815        fn sendmsg(
1816            &self,
1817            buffers: Vec<ArgBytesLike>,
1818            ancdata: OptionalArg,
1819            flags: OptionalArg<i32>,
1820            addr: OptionalOption,
1821            vm: &VirtualMachine,
1822        ) -> PyResult<usize> {
1823            let flags = flags.unwrap_or(0);
1824            let mut msg = socket2::MsgHdr::new();
1825
1826            let sockaddr;
1827            if let Some(addr) = addr.flatten() {
1828                sockaddr = self
1829                    .extract_address(addr, "sendmsg", vm)
1830                    .map_err(|e| e.into_pyexception(vm))?;
1831                msg = msg.with_addr(&sockaddr);
1832            }
1833
1834            let buffers = buffers
1835                .iter()
1836                .map(|buf| buf.borrow_buf())
1837                .collect::<Vec<_>>();
1838            let buffers = buffers
1839                .iter()
1840                .map(|buf| io::IoSlice::new(buf))
1841                .collect::<Vec<_>>();
1842            msg = msg.with_buffers(&buffers);
1843
1844            let control_buf;
1845            if let OptionalArg::Present(ancdata) = ancdata {
1846                let cmsgs = vm.extract_elements_with(
1847                    &ancdata,
1848                    |obj| -> PyResult<(i32, i32, ArgBytesLike)> {
1849                        let seq: Vec<PyObjectRef> = obj.try_into_value(vm)?;
1850                        let [lvl, typ, data]: [PyObjectRef; 3] = seq
1851                            .try_into()
1852                            .map_err(|_| vm.new_type_error("expected a sequence of length 3"))?;
1853                        Ok((
1854                            lvl.try_into_value(vm)?,
1855                            typ.try_into_value(vm)?,
1856                            data.try_into_value(vm)?,
1857                        ))
1858                    },
1859                )?;
1860                control_buf = Self::pack_cmsgs_to_send(&cmsgs, vm)?;
1861                if !control_buf.is_empty() {
1862                    msg = msg.with_control(&control_buf);
1863                }
1864            }
1865
1866            self.sock_op(vm, SelectKind::Write, || {
1867                let sock = self.sock()?;
1868                sock.sendmsg(&msg, flags)
1869            })
1870            .map_err(|e| e.into_pyexception(vm))
1871        }
1872
1873        /// sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]]) -> int
1874        ///
1875        /// Set operation mode and target IV for an AF_ALG socket.
1876        #[cfg(target_os = "linux")]
1877        #[pymethod]
1878        fn sendmsg_afalg(&self, args: SendmsgAfalgArgs, vm: &VirtualMachine) -> PyResult<usize> {
1879            let msg = args.msg;
1880            let op = args.op;
1881            let iv = args.iv;
1882            let flags = args.flags;
1883
1884            // Validate assoclen - must be non-negative if provided
1885            let assoclen: Option<u32> = match args.assoclen {
1886                OptionalArg::Present(val) if val < 0 => {
1887                    return Err(vm.new_type_error("assoclen must be non-negative"));
1888                }
1889                OptionalArg::Present(val) => Some(val as u32),
1890                OptionalArg::Missing => None,
1891            };
1892
1893            // Build control messages for AF_ALG
1894            let mut control_buf = Vec::new();
1895
1896            // Add ALG_SET_OP control message
1897            {
1898                let op_bytes = op.to_ne_bytes();
1899                let space =
1900                    unsafe { libc::CMSG_SPACE(core::mem::size_of::<u32>() as u32) } as usize;
1901                let old_len = control_buf.len();
1902                control_buf.resize(old_len + space, 0u8);
1903
1904                let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1905                unsafe {
1906                    (*cmsg).cmsg_len = libc::CMSG_LEN(core::mem::size_of::<u32>() as u32) as _;
1907                    (*cmsg).cmsg_level = libc::SOL_ALG;
1908                    (*cmsg).cmsg_type = libc::ALG_SET_OP;
1909                    let data = libc::CMSG_DATA(cmsg);
1910                    core::ptr::copy_nonoverlapping(op_bytes.as_ptr(), data, op_bytes.len());
1911                }
1912            }
1913
1914            // Add ALG_SET_IV control message if iv is provided
1915            if let Some(iv_data) = iv {
1916                let iv_bytes = iv_data.borrow_buf();
1917                // struct af_alg_iv { __u32 ivlen; __u8 iv[]; }
1918                let iv_struct_size = 4 + iv_bytes.len();
1919                let space = unsafe { libc::CMSG_SPACE(iv_struct_size as u32) } as usize;
1920                let old_len = control_buf.len();
1921                control_buf.resize(old_len + space, 0u8);
1922
1923                let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1924                unsafe {
1925                    (*cmsg).cmsg_len = libc::CMSG_LEN(iv_struct_size as u32) as _;
1926                    (*cmsg).cmsg_level = libc::SOL_ALG;
1927                    (*cmsg).cmsg_type = libc::ALG_SET_IV;
1928                    let data = libc::CMSG_DATA(cmsg);
1929                    // Write ivlen
1930                    let ivlen = (iv_bytes.len() as u32).to_ne_bytes();
1931                    core::ptr::copy_nonoverlapping(ivlen.as_ptr(), data, 4);
1932                    // Write iv
1933                    core::ptr::copy_nonoverlapping(iv_bytes.as_ptr(), data.add(4), iv_bytes.len());
1934                }
1935            }
1936
1937            // Add ALG_SET_AEAD_ASSOCLEN control message if assoclen is provided
1938            if let Some(assoclen_val) = assoclen {
1939                let assoclen_bytes = assoclen_val.to_ne_bytes();
1940                let space =
1941                    unsafe { libc::CMSG_SPACE(core::mem::size_of::<u32>() as u32) } as usize;
1942                let old_len = control_buf.len();
1943                control_buf.resize(old_len + space, 0u8);
1944
1945                let cmsg = control_buf[old_len..].as_mut_ptr() as *mut libc::cmsghdr;
1946                unsafe {
1947                    (*cmsg).cmsg_len = libc::CMSG_LEN(core::mem::size_of::<u32>() as u32) as _;
1948                    (*cmsg).cmsg_level = libc::SOL_ALG;
1949                    (*cmsg).cmsg_type = libc::ALG_SET_AEAD_ASSOCLEN;
1950                    let data = libc::CMSG_DATA(cmsg);
1951                    core::ptr::copy_nonoverlapping(
1952                        assoclen_bytes.as_ptr(),
1953                        data,
1954                        assoclen_bytes.len(),
1955                    );
1956                }
1957            }
1958
1959            // Build buffers
1960            let buffers = msg.iter().map(|buf| buf.borrow_buf()).collect::<Vec<_>>();
1961            let iovecs: Vec<libc::iovec> = buffers
1962                .iter()
1963                .map(|buf| libc::iovec {
1964                    iov_base: buf.as_ptr() as *mut _,
1965                    iov_len: buf.len(),
1966                })
1967                .collect();
1968
1969            // Set up msghdr
1970            let mut msghdr: libc::msghdr = unsafe { core::mem::zeroed() };
1971            msghdr.msg_iov = iovecs.as_ptr() as *mut _;
1972            msghdr.msg_iovlen = iovecs.len() as _;
1973            if !control_buf.is_empty() {
1974                msghdr.msg_control = control_buf.as_mut_ptr() as *mut _;
1975                msghdr.msg_controllen = control_buf.len() as _;
1976            }
1977
1978            self.sock_op(vm, SelectKind::Write, || {
1979                let sock = self.sock()?;
1980                let fd = sock_fileno(&sock);
1981                let ret = unsafe { libc::sendmsg(fd as libc::c_int, &msghdr, flags) };
1982                if ret < 0 {
1983                    Err(io::Error::last_os_error())
1984                } else {
1985                    Ok(ret as usize)
1986                }
1987            })
1988            .map_err(|e| e.into_pyexception(vm))
1989        }
1990
1991        /// recvmsg(bufsize[, ancbufsize[, flags]]) -> (data, ancdata, msg_flags, address)
1992        ///
1993        /// Receive normal data and ancillary data from the socket.
1994        #[cfg(all(unix, not(target_os = "redox")))]
1995        #[pymethod]
1996        fn recvmsg(
1997            &self,
1998            bufsize: isize,
1999            ancbufsize: OptionalArg<isize>,
2000            flags: OptionalArg<i32>,
2001            vm: &VirtualMachine,
2002        ) -> PyResult<PyTupleRef> {
2003            use core::mem::MaybeUninit;
2004
2005            if bufsize < 0 {
2006                return Err(vm.new_value_error("negative buffer size in recvmsg"));
2007            }
2008            let bufsize = bufsize as usize;
2009
2010            let ancbufsize = ancbufsize.unwrap_or(0);
2011            if ancbufsize < 0 {
2012                return Err(vm.new_value_error("negative ancillary buffer size in recvmsg"));
2013            }
2014            let ancbufsize = ancbufsize as usize;
2015            let flags = flags.unwrap_or(0);
2016
2017            // Allocate buffers
2018            let mut data_buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); bufsize];
2019            let mut anc_buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); ancbufsize];
2020            let mut addr_storage: libc::sockaddr_storage = unsafe { core::mem::zeroed() };
2021
2022            // Set up iovec
2023            let mut iov = [libc::iovec {
2024                iov_base: data_buf.as_mut_ptr().cast(),
2025                iov_len: bufsize,
2026            }];
2027
2028            // Set up msghdr
2029            let mut msg: libc::msghdr = unsafe { core::mem::zeroed() };
2030            msg.msg_name = (&mut addr_storage as *mut libc::sockaddr_storage).cast();
2031            msg.msg_namelen = core::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
2032            msg.msg_iov = iov.as_mut_ptr();
2033            msg.msg_iovlen = 1;
2034            if ancbufsize > 0 {
2035                msg.msg_control = anc_buf.as_mut_ptr().cast();
2036                msg.msg_controllen = ancbufsize as _;
2037            }
2038
2039            let n = self
2040                .sock_op(vm, SelectKind::Read, || {
2041                    let sock = self.sock()?;
2042                    let fd = sock_fileno(&sock);
2043                    let ret = unsafe { libc::recvmsg(fd as libc::c_int, &mut msg, flags) };
2044                    if ret < 0 {
2045                        Err(io::Error::last_os_error())
2046                    } else {
2047                        Ok(ret as usize)
2048                    }
2049                })
2050                .map_err(|e| e.into_pyexception(vm))?;
2051
2052            // Build data bytes
2053            let data = unsafe {
2054                data_buf.set_len(n);
2055                core::mem::transmute::<Vec<MaybeUninit<u8>>, Vec<u8>>(data_buf)
2056            };
2057
2058            // Build ancdata list
2059            let ancdata = Self::parse_ancillary_data(&msg, vm)?;
2060
2061            // Build address tuple
2062            let address = if msg.msg_namelen > 0 {
2063                let storage: socket2::SockAddrStorage =
2064                    unsafe { core::mem::transmute(addr_storage) };
2065                let addr = unsafe { socket2::SockAddr::new(storage, msg.msg_namelen) };
2066                get_addr_tuple(&addr, vm)
2067            } else {
2068                vm.ctx.none()
2069            };
2070
2071            Ok(vm.ctx.new_tuple(vec![
2072                vm.ctx.new_bytes(data).into(),
2073                ancdata,
2074                vm.ctx.new_int(msg.msg_flags).into(),
2075                address,
2076            ]))
2077        }
2078
2079        /// Parse ancillary data from a received message header
2080        #[cfg(all(unix, not(target_os = "redox")))]
2081        fn parse_ancillary_data(msg: &libc::msghdr, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
2082            let mut result = Vec::new();
2083
2084            // Calculate buffer end for truncation handling
2085            let ctrl_buf = msg.msg_control as *const u8;
2086            let ctrl_end = unsafe { ctrl_buf.add(msg.msg_controllen as _) };
2087
2088            let mut cmsg: *mut libc::cmsghdr = unsafe { libc::CMSG_FIRSTHDR(msg) };
2089            while !cmsg.is_null() {
2090                let cmsg_ref = unsafe { &*cmsg };
2091                let data_ptr = unsafe { libc::CMSG_DATA(cmsg) };
2092
2093                // Calculate data length, respecting buffer truncation
2094                let data_len_from_cmsg =
2095                    cmsg_ref.cmsg_len as usize - (data_ptr as usize - cmsg as usize);
2096                let available = ctrl_end as usize - data_ptr as usize;
2097                let data_len = data_len_from_cmsg.min(available);
2098
2099                let data = unsafe { core::slice::from_raw_parts(data_ptr, data_len) };
2100
2101                let tuple = vm.ctx.new_tuple(vec![
2102                    vm.ctx.new_int(cmsg_ref.cmsg_level).into(),
2103                    vm.ctx.new_int(cmsg_ref.cmsg_type).into(),
2104                    vm.ctx.new_bytes(data.to_vec()).into(),
2105                ]);
2106
2107                result.push(tuple.into());
2108
2109                cmsg = unsafe { libc::CMSG_NXTHDR(msg, cmsg) };
2110            }
2111
2112            Ok(vm.ctx.new_list(result).into())
2113        }
2114
2115        // based on nix's implementation
2116        #[cfg(all(unix, not(target_os = "redox")))]
2117        fn pack_cmsgs_to_send(
2118            cmsgs: &[(i32, i32, ArgBytesLike)],
2119            vm: &VirtualMachine,
2120        ) -> PyResult<Vec<u8>> {
2121            use core::{mem, ptr};
2122
2123            if cmsgs.is_empty() {
2124                return Ok(vec![]);
2125            }
2126
2127            let capacity = cmsgs
2128                .iter()
2129                .map(|(_, _, buf)| buf.len())
2130                .try_fold(0, |sum, len| {
2131                    let space = checked_cmsg_space(len).ok_or_else(|| {
2132                        vm.new_os_error("ancillary data item too large".to_owned())
2133                    })?;
2134                    usize::checked_add(sum, space)
2135                        .ok_or_else(|| vm.new_os_error("too much ancillary data".to_owned()))
2136                })?;
2137
2138            let mut cmsg_buffer = vec![0u8; capacity];
2139
2140            // make a dummy msghdr so we can use the CMSG_* apis
2141            let mut mhdr = unsafe { mem::zeroed::<libc::msghdr>() };
2142            mhdr.msg_control = cmsg_buffer.as_mut_ptr().cast();
2143            mhdr.msg_controllen = capacity as _;
2144
2145            let mut pmhdr: *mut libc::cmsghdr = unsafe { libc::CMSG_FIRSTHDR(&mhdr) };
2146            for (lvl, typ, buf) in cmsgs {
2147                if pmhdr.is_null() {
2148                    return Err(vm.new_runtime_error(
2149                        "unexpected NULL result from CMSG_FIRSTHDR/CMSG_NXTHDR",
2150                    ));
2151                }
2152                let data = &*buf.borrow_buf();
2153                assert_eq!(data.len(), buf.len());
2154                // Safe because we know that pmhdr is valid, and we initialized it with
2155                // sufficient space
2156                unsafe {
2157                    (*pmhdr).cmsg_level = *lvl;
2158                    (*pmhdr).cmsg_type = *typ;
2159                    (*pmhdr).cmsg_len = libc::CMSG_LEN(data.len() as _) as _;
2160                    ptr::copy_nonoverlapping(data.as_ptr(), libc::CMSG_DATA(pmhdr), data.len());
2161                }
2162
2163                // Safe because mhdr is valid
2164                pmhdr = unsafe { libc::CMSG_NXTHDR(&mhdr, pmhdr) };
2165            }
2166
2167            Ok(cmsg_buffer)
2168        }
2169
2170        #[pymethod]
2171        fn close(&self) -> io::Result<()> {
2172            let sock = self.sock.write().take();
2173            if let Some(sock) = sock {
2174                close_inner(into_sock_fileno(sock))?;
2175            }
2176            Ok(())
2177        }
2178
2179        #[pymethod]
2180        #[inline]
2181        fn detach(&self) -> i64 {
2182            let sock = self.sock.write().take();
2183            sock.map_or(INVALID_SOCKET as i64, |s| into_sock_fileno(s) as i64)
2184        }
2185
2186        #[pymethod]
2187        fn fileno(&self) -> i64 {
2188            self.sock
2189                .read()
2190                .as_ref()
2191                .map_or(INVALID_SOCKET as i64, |s| sock_fileno(s) as i64)
2192        }
2193
2194        #[pymethod]
2195        fn getsockname(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
2196            let addr = self.sock()?.local_addr()?;
2197
2198            Ok(get_addr_tuple(&addr, vm))
2199        }
2200
2201        #[pymethod]
2202        fn getpeername(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
2203            let addr = self.sock()?.peer_addr()?;
2204
2205            Ok(get_addr_tuple(&addr, vm))
2206        }
2207
2208        #[pymethod]
2209        fn gettimeout(&self) -> Option<f64> {
2210            let timeout = self.timeout.load();
2211            if timeout >= 0.0 { Some(timeout) } else { None }
2212        }
2213
2214        #[pymethod]
2215        fn setblocking(&self, block: bool) -> io::Result<()> {
2216            self.timeout.store(if block { -1.0 } else { 0.0 });
2217            self.sock()?.set_nonblocking(!block)
2218        }
2219
2220        #[pymethod]
2221        fn getblocking(&self) -> bool {
2222            self.timeout.load() != 0.0
2223        }
2224
2225        #[pymethod]
2226        fn settimeout(&self, timeout: Option<ArgIntoFloat>, vm: &VirtualMachine) -> PyResult<()> {
2227            let timeout = match timeout {
2228                Some(t) => {
2229                    let f = t.into_float();
2230                    if f.is_nan() {
2231                        return Err(vm.new_value_error("Invalid value NaN (not a number)"));
2232                    }
2233                    if f < 0.0 || !f.is_finite() {
2234                        return Err(vm.new_value_error("Timeout value out of range"));
2235                    }
2236                    Some(f)
2237                }
2238                None => None,
2239            };
2240            self.timeout.store(timeout.unwrap_or(-1.0));
2241            // even if timeout is > 0 the socket needs to be nonblocking in order for us to select() on
2242            // it
2243            self.sock()
2244                .map_err(|e| e.into_pyexception(vm))?
2245                .set_nonblocking(timeout.is_some())
2246                .map_err(|e| e.into_pyexception(vm))
2247        }
2248
2249        #[pymethod]
2250        fn getsockopt(
2251            &self,
2252            level: i32,
2253            name: i32,
2254            buflen: OptionalArg<i32>,
2255            vm: &VirtualMachine,
2256        ) -> Result<PyObjectRef, IoOrPyException> {
2257            let sock = self.sock()?;
2258            let fd = sock_fileno(&sock);
2259            let buflen = buflen.unwrap_or(0);
2260            if buflen == 0 {
2261                let mut flag: libc::c_int = 0;
2262                let mut flagsize = core::mem::size_of::<libc::c_int>() as _;
2263                let ret = unsafe {
2264                    c::getsockopt(
2265                        fd as _,
2266                        level,
2267                        name,
2268                        &mut flag as *mut libc::c_int as *mut _,
2269                        &mut flagsize,
2270                    )
2271                };
2272                if ret < 0 {
2273                    return Err(crate::common::os::errno_io_error().into());
2274                }
2275                Ok(vm.ctx.new_int(flag).into())
2276            } else {
2277                if buflen <= 0 || buflen > 1024 {
2278                    return Err(vm
2279                        .new_os_error("getsockopt buflen out of range".to_owned())
2280                        .into());
2281                }
2282                let mut buf = vec![0u8; buflen as usize];
2283                let mut buflen = buflen as _;
2284                let ret = unsafe {
2285                    c::getsockopt(
2286                        fd as _,
2287                        level,
2288                        name,
2289                        buf.as_mut_ptr() as *mut _,
2290                        &mut buflen,
2291                    )
2292                };
2293                if ret < 0 {
2294                    return Err(crate::common::os::errno_io_error().into());
2295                }
2296                buf.truncate(buflen as usize);
2297                Ok(vm.ctx.new_bytes(buf).into())
2298            }
2299        }
2300
2301        #[pymethod]
2302        fn setsockopt(
2303            &self,
2304            level: i32,
2305            name: i32,
2306            value: Option<Either<ArgBytesLike, i32>>,
2307            optlen: OptionalArg<u32>,
2308            vm: &VirtualMachine,
2309        ) -> Result<(), IoOrPyException> {
2310            let sock = self.sock()?;
2311            let fd = sock_fileno(&sock);
2312            let ret = match (value, optlen) {
2313                (Some(Either::A(b)), OptionalArg::Missing) => b.with_ref(|b| unsafe {
2314                    c::setsockopt(fd as _, level, name, b.as_ptr() as *const _, b.len() as _)
2315                }),
2316                (Some(Either::B(ref val)), OptionalArg::Missing) => unsafe {
2317                    c::setsockopt(
2318                        fd as _,
2319                        level,
2320                        name,
2321                        val as *const i32 as *const _,
2322                        core::mem::size_of::<i32>() as _,
2323                    )
2324                },
2325                (None, OptionalArg::Present(optlen)) => unsafe {
2326                    c::setsockopt(fd as _, level, name, core::ptr::null(), optlen as _)
2327                },
2328                _ => {
2329                    return Err(vm
2330                        .new_type_error("expected the value arg xor the optlen arg")
2331                        .into());
2332                }
2333            };
2334            if ret < 0 {
2335                Err(crate::common::os::errno_io_error().into())
2336            } else {
2337                Ok(())
2338            }
2339        }
2340
2341        #[pymethod]
2342        fn shutdown(&self, how: i32, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
2343            let how = match how {
2344                c::SHUT_RD => Shutdown::Read,
2345                c::SHUT_WR => Shutdown::Write,
2346                c::SHUT_RDWR => Shutdown::Both,
2347                _ => {
2348                    return Err(vm
2349                        .new_value_error("`how` must be SHUT_RD, SHUT_WR, or SHUT_RDWR")
2350                        .into());
2351                }
2352            };
2353            Ok(self.sock()?.shutdown(how)?)
2354        }
2355
2356        #[cfg(windows)]
2357        fn wsa_error() -> io::Error {
2358            io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
2359        }
2360
2361        #[cfg(windows)]
2362        #[pymethod]
2363        fn ioctl(
2364            &self,
2365            cmd: PyObjectRef,
2366            option: PyObjectRef,
2367            vm: &VirtualMachine,
2368        ) -> Result<u32, IoOrPyException> {
2369            use crate::vm::builtins::PyInt;
2370            use crate::vm::convert::TryFromObject;
2371
2372            let sock = self.sock()?;
2373            let fd = sock_fileno(&sock);
2374            let mut recv: u32 = 0;
2375
2376            // Convert cmd to u32, returning ValueError for invalid/negative values
2377            let cmd_int = cmd
2378                .downcast::<PyInt>()
2379                .map_err(|_| vm.new_type_error("an integer is required"))?;
2380            let cmd_val = cmd_int.as_bigint();
2381            let cmd: u32 = cmd_val
2382                .to_u32()
2383                .ok_or_else(|| vm.new_value_error(format!("invalid ioctl command {}", cmd_val)))?;
2384
2385            match cmd {
2386                c::SIO_RCVALL | c::SIO_LOOPBACK_FAST_PATH => {
2387                    // Option must be an integer, not None
2388                    if vm.is_none(&option) {
2389                        return Err(vm
2390                            .new_type_error("an integer is required (got type NoneType)")
2391                            .into());
2392                    }
2393                    let option_val: u32 = TryFromObject::try_from_object(vm, option)?;
2394                    let ret = unsafe {
2395                        c::WSAIoctl(
2396                            fd as _,
2397                            cmd,
2398                            &option_val as *const u32 as *const _,
2399                            core::mem::size_of::<u32>() as u32,
2400                            core::ptr::null_mut(),
2401                            0,
2402                            &mut recv,
2403                            core::ptr::null_mut(),
2404                            None,
2405                        )
2406                    };
2407                    if ret == c::SOCKET_ERROR {
2408                        return Err(Self::wsa_error().into());
2409                    }
2410                    Ok(recv)
2411                }
2412                c::SIO_KEEPALIVE_VALS => {
2413                    let tuple: PyTupleRef = option
2414                        .downcast()
2415                        .map_err(|_| vm.new_type_error("SIO_KEEPALIVE_VALS requires a tuple"))?;
2416                    if tuple.len() != 3 {
2417                        return Err(vm
2418                            .new_type_error(
2419                                "SIO_KEEPALIVE_VALS requires (onoff, keepalivetime, keepaliveinterval)",
2420                            )
2421                            .into());
2422                    }
2423
2424                    #[repr(C)]
2425                    struct TcpKeepalive {
2426                        onoff: u32,
2427                        keepalivetime: u32,
2428                        keepaliveinterval: u32,
2429                    }
2430
2431                    let ka = TcpKeepalive {
2432                        onoff: TryFromObject::try_from_object(vm, tuple[0].clone())?,
2433                        keepalivetime: TryFromObject::try_from_object(vm, tuple[1].clone())?,
2434                        keepaliveinterval: TryFromObject::try_from_object(vm, tuple[2].clone())?,
2435                    };
2436
2437                    let ret = unsafe {
2438                        c::WSAIoctl(
2439                            fd as _,
2440                            cmd,
2441                            &ka as *const TcpKeepalive as *const _,
2442                            core::mem::size_of::<TcpKeepalive>() as u32,
2443                            core::ptr::null_mut(),
2444                            0,
2445                            &mut recv,
2446                            core::ptr::null_mut(),
2447                            None,
2448                        )
2449                    };
2450                    if ret == c::SOCKET_ERROR {
2451                        return Err(Self::wsa_error().into());
2452                    }
2453                    Ok(recv)
2454                }
2455                _ => Err(vm
2456                    .new_value_error(format!("invalid ioctl command {}", cmd))
2457                    .into()),
2458            }
2459        }
2460
2461        #[cfg(windows)]
2462        #[pymethod]
2463        fn share(&self, process_id: u32, _vm: &VirtualMachine) -> Result<Vec<u8>, IoOrPyException> {
2464            let sock = self.sock()?;
2465            let fd = sock_fileno(&sock);
2466
2467            let mut info: MaybeUninit<c::WSAPROTOCOL_INFOW> = MaybeUninit::uninit();
2468
2469            let ret = unsafe { c::WSADuplicateSocketW(fd as _, process_id, info.as_mut_ptr()) };
2470
2471            if ret == c::SOCKET_ERROR {
2472                return Err(Self::wsa_error().into());
2473            }
2474
2475            let info = unsafe { info.assume_init() };
2476            let bytes = unsafe {
2477                core::slice::from_raw_parts(
2478                    &info as *const c::WSAPROTOCOL_INFOW as *const u8,
2479                    core::mem::size_of::<c::WSAPROTOCOL_INFOW>(),
2480                )
2481            };
2482
2483            Ok(bytes.to_vec())
2484        }
2485
2486        #[pygetset(name = "type")]
2487        fn kind(&self) -> i32 {
2488            self.kind.load()
2489        }
2490
2491        #[pygetset]
2492        fn family(&self) -> i32 {
2493            self.family.load()
2494        }
2495
2496        #[pygetset]
2497        fn proto(&self) -> i32 {
2498            self.proto.load()
2499        }
2500    }
2501
2502    struct Address {
2503        host: PyUtf8StrRef,
2504        port: u16,
2505    }
2506
2507    impl ToSocketAddrs for Address {
2508        type Iter = alloc::vec::IntoIter<SocketAddr>;
2509        fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
2510            (self.host.as_str(), self.port).to_socket_addrs()
2511        }
2512    }
2513
2514    impl TryFromObject for Address {
2515        fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
2516            let tuple = PyTupleRef::try_from_object(vm, obj)?;
2517            if tuple.len() != 2 {
2518                Err(vm.new_type_error("Address tuple should have only 2 values"))
2519            } else {
2520                Self::from_tuple(&tuple, vm)
2521            }
2522        }
2523    }
2524
2525    impl Address {
2526        fn from_tuple(tuple: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<Self> {
2527            let host = PyStrRef::try_from_object(vm, tuple[0].clone())?;
2528            let host = host.try_into_utf8(vm)?;
2529            let port = i32::try_from_borrowed_object(vm, &tuple[1])?;
2530            let port = port
2531                .to_u16()
2532                .ok_or_else(|| vm.new_overflow_error("port must be 0-65535."))?;
2533            Ok(Self { host, port })
2534        }
2535
2536        fn from_tuple_ipv6(
2537            tuple: &[PyObjectRef],
2538            vm: &VirtualMachine,
2539        ) -> PyResult<(Self, u32, u32)> {
2540            let addr = Self::from_tuple(tuple, vm)?;
2541            let flowinfo = tuple
2542                .get(2)
2543                .map(|obj| u32::try_from_borrowed_object(vm, obj))
2544                .transpose()?
2545                .unwrap_or(0);
2546            let scopeid = tuple
2547                .get(3)
2548                .map(|obj| u32::try_from_borrowed_object(vm, obj))
2549                .transpose()?
2550                .unwrap_or(0);
2551            if flowinfo > 0xfffff {
2552                return Err(vm.new_overflow_error("flowinfo must be 0-1048575."));
2553            }
2554            Ok((addr, flowinfo, scopeid))
2555        }
2556    }
2557
2558    fn get_ip_addr_tuple(addr: &SocketAddr, vm: &VirtualMachine) -> PyObjectRef {
2559        match addr {
2560            SocketAddr::V4(addr) => (addr.ip().to_string(), addr.port()).to_pyobject(vm),
2561            SocketAddr::V6(addr) => (
2562                addr.ip().to_string(),
2563                addr.port(),
2564                addr.flowinfo(),
2565                addr.scope_id(),
2566            )
2567                .to_pyobject(vm),
2568        }
2569    }
2570
2571    fn get_addr_tuple(addr: &socket2::SockAddr, vm: &VirtualMachine) -> PyObjectRef {
2572        if let Some(addr) = addr.as_socket() {
2573            return get_ip_addr_tuple(&addr, vm);
2574        }
2575        #[cfg(unix)]
2576        if addr.is_unix() {
2577            use std::os::unix::ffi::OsStrExt;
2578            if let Some(abstractpath) = addr.as_abstract_namespace() {
2579                return vm.ctx.new_bytes([b"\0", abstractpath].concat()).into();
2580            }
2581            // necessary on macos
2582            let path = ffi::OsStr::as_bytes(addr.as_pathname().unwrap_or("".as_ref()).as_ref());
2583            let nul_pos = memchr::memchr(b'\0', path).unwrap_or(path.len());
2584            let path = ffi::OsStr::from_bytes(&path[..nul_pos]);
2585            return vm.fsdecode(path).into();
2586        }
2587        #[cfg(target_os = "linux")]
2588        {
2589            let family = addr.family();
2590            if family == libc::AF_CAN as libc::sa_family_t {
2591                // AF_CAN address: (interface_name,) or (interface_name, can_id)
2592                let can_addr = unsafe { &*(addr.as_ptr() as *const libc::sockaddr_can) };
2593                let ifindex = can_addr.can_ifindex;
2594                let ifname = if ifindex == 0 {
2595                    String::new()
2596                } else {
2597                    let mut buf = [0u8; libc::IF_NAMESIZE];
2598                    let ret = unsafe {
2599                        libc::if_indextoname(
2600                            ifindex as libc::c_uint,
2601                            buf.as_mut_ptr() as *mut libc::c_char,
2602                        )
2603                    };
2604                    if ret.is_null() {
2605                        String::new()
2606                    } else {
2607                        let nul_pos = memchr::memchr(b'\0', &buf).unwrap_or(buf.len());
2608                        String::from_utf8_lossy(&buf[..nul_pos]).into_owned()
2609                    }
2610                };
2611                return vm.ctx.new_tuple(vec![vm.ctx.new_str(ifname).into()]).into();
2612            }
2613            if family == libc::AF_ALG as libc::sa_family_t {
2614                // AF_ALG address: (type, name)
2615                let alg_addr = unsafe { &*(addr.as_ptr() as *const libc::sockaddr_alg) };
2616                let type_bytes = &alg_addr.salg_type;
2617                let name_bytes = &alg_addr.salg_name;
2618                let type_nul = memchr::memchr(b'\0', type_bytes).unwrap_or(type_bytes.len());
2619                let name_nul = memchr::memchr(b'\0', name_bytes).unwrap_or(name_bytes.len());
2620                let type_str = String::from_utf8_lossy(&type_bytes[..type_nul]).into_owned();
2621                let name_str = String::from_utf8_lossy(&name_bytes[..name_nul]).into_owned();
2622                return vm
2623                    .ctx
2624                    .new_tuple(vec![
2625                        vm.ctx.new_str(type_str).into(),
2626                        vm.ctx.new_str(name_str).into(),
2627                    ])
2628                    .into();
2629            }
2630        }
2631        // TODO: support more address families
2632        (String::new(), 0).to_pyobject(vm)
2633    }
2634
2635    #[pyfunction]
2636    fn gethostname(vm: &VirtualMachine) -> PyResult<PyStrRef> {
2637        gethostname::gethostname()
2638            .into_string()
2639            .map(|hostname| vm.ctx.new_str(hostname))
2640            .map_err(|err| vm.new_os_error(err.into_string().unwrap()))
2641    }
2642
2643    #[cfg(all(unix, not(target_os = "redox")))]
2644    #[pyfunction]
2645    fn sethostname(hostname: PyUtf8StrRef) -> nix::Result<()> {
2646        nix::unistd::sethostname(hostname.as_str())
2647    }
2648
2649    #[pyfunction]
2650    fn inet_aton(ip_string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
2651        ip_string
2652            .as_str()
2653            .parse::<Ipv4Addr>()
2654            .map(|ip_addr| Vec::<u8>::from(ip_addr.octets()))
2655            .map_err(|_| {
2656                vm.new_os_error("illegal IP address string passed to inet_aton".to_owned())
2657            })
2658    }
2659
2660    #[pyfunction]
2661    fn inet_ntoa(packed_ip: ArgBytesLike, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2662        let packed_ip = packed_ip.borrow_buf();
2663        let packed_ip = <&[u8; 4]>::try_from(&*packed_ip)
2664            .map_err(|_| vm.new_os_error("packed IP wrong length for inet_ntoa".to_owned()))?;
2665        Ok(vm.ctx.new_str(Ipv4Addr::from(*packed_ip).to_string()))
2666    }
2667
2668    fn cstr_opt_as_ptr(x: &OptionalArg<ffi::CString>) -> *const libc::c_char {
2669        x.as_ref().map_or_else(core::ptr::null, |s| s.as_ptr())
2670    }
2671
2672    #[pyfunction]
2673    fn getservbyname(
2674        servicename: PyStrRef,
2675        protocolname: OptionalArg<PyStrRef>,
2676        vm: &VirtualMachine,
2677    ) -> PyResult<u16> {
2678        let cstr_name = servicename.to_cstring(vm)?;
2679        let cstr_proto = protocolname
2680            .as_ref()
2681            .map(|s| s.to_cstring(vm))
2682            .transpose()?;
2683        let cstr_proto = cstr_opt_as_ptr(&cstr_proto);
2684        let serv = unsafe { c::getservbyname(cstr_name.as_ptr() as _, cstr_proto as _) };
2685        if serv.is_null() {
2686            return Err(vm.new_os_error("service/proto not found".to_owned()));
2687        }
2688        let port = unsafe { (*serv).s_port };
2689        Ok(u16::from_be(port as u16))
2690    }
2691
2692    #[pyfunction]
2693    fn getservbyport(
2694        port: i32,
2695        protocolname: OptionalArg<PyStrRef>,
2696        vm: &VirtualMachine,
2697    ) -> PyResult<String> {
2698        let port = port
2699            .to_u16()
2700            .ok_or_else(|| vm.new_overflow_error("getservbyport: port must be 0-65535."))?;
2701        let cstr_proto = protocolname
2702            .as_ref()
2703            .map(|s| s.to_cstring(vm))
2704            .transpose()?;
2705        let cstr_proto = cstr_opt_as_ptr(&cstr_proto);
2706        let serv = unsafe { c::getservbyport(port.to_be() as _, cstr_proto as _) };
2707        if serv.is_null() {
2708            return Err(vm.new_os_error("port/proto not found".to_owned()));
2709        }
2710        let s = unsafe { ffi::CStr::from_ptr((*serv).s_name as _) };
2711        Ok(s.to_string_lossy().into_owned())
2712    }
2713
2714    unsafe fn slice_as_uninit<T>(v: &mut [T]) -> &mut [MaybeUninit<T>] {
2715        unsafe { &mut *(v as *mut [T] as *mut [MaybeUninit<T>]) }
2716    }
2717
2718    enum IoOrPyException {
2719        Timeout,
2720        Py(PyBaseExceptionRef),
2721        Io(io::Error),
2722    }
2723    impl From<PyBaseExceptionRef> for IoOrPyException {
2724        fn from(exc: PyBaseExceptionRef) -> Self {
2725            Self::Py(exc)
2726        }
2727    }
2728    impl From<PyRef<PyOSError>> for IoOrPyException {
2729        fn from(exc: PyRef<PyOSError>) -> Self {
2730            Self::Py(exc.upcast())
2731        }
2732    }
2733    impl From<io::Error> for IoOrPyException {
2734        fn from(err: io::Error) -> Self {
2735            Self::Io(err)
2736        }
2737    }
2738    impl IoOrPyException {
2739        fn errno(self) -> PyResult<i32> {
2740            match self {
2741                Self::Timeout => Ok(errcode!(EWOULDBLOCK)),
2742                Self::Io(err) => Ok(err.posix_errno()),
2743                Self::Py(exc) => Err(exc),
2744            }
2745        }
2746    }
2747    impl IntoPyException for IoOrPyException {
2748        #[inline]
2749        fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
2750            match self {
2751                Self::Timeout => timeout_error(vm).upcast(),
2752                Self::Py(exc) => exc,
2753                Self::Io(err) => err.into_pyexception(vm),
2754            }
2755        }
2756    }
2757
2758    #[derive(Copy, Clone)]
2759    pub(crate) enum SelectKind {
2760        Read,
2761        Write,
2762        Connect,
2763    }
2764
2765    /// returns true if timed out
2766    pub(crate) fn sock_select(
2767        sock: &Socket,
2768        kind: SelectKind,
2769        interval: Option<Duration>,
2770    ) -> io::Result<bool> {
2771        #[cfg(unix)]
2772        {
2773            use nix::poll::*;
2774            use std::os::fd::AsFd;
2775            let events = match kind {
2776                SelectKind::Read => PollFlags::POLLIN,
2777                SelectKind::Write => PollFlags::POLLOUT,
2778                SelectKind::Connect => PollFlags::POLLOUT | PollFlags::POLLERR,
2779            };
2780            let mut pollfd = [PollFd::new(sock.as_fd(), events)];
2781            let timeout = match interval {
2782                Some(d) => d.try_into().unwrap_or(PollTimeout::MAX),
2783                None => PollTimeout::NONE,
2784            };
2785            let ret = poll(&mut pollfd, timeout)?;
2786            Ok(ret == 0)
2787        }
2788        #[cfg(windows)]
2789        {
2790            use crate::select;
2791
2792            let fd = sock_fileno(sock);
2793
2794            let mut reads = select::FdSet::new();
2795            let mut writes = select::FdSet::new();
2796            let mut errs = select::FdSet::new();
2797
2798            let fd = fd as usize;
2799            match kind {
2800                SelectKind::Read => reads.insert(fd),
2801                SelectKind::Write => writes.insert(fd),
2802                SelectKind::Connect => {
2803                    writes.insert(fd);
2804                    errs.insert(fd);
2805                }
2806            }
2807
2808            let mut interval = interval.map(|dur| select::timeval {
2809                tv_sec: dur.as_secs() as _,
2810                tv_usec: dur.subsec_micros() as _,
2811            });
2812
2813            select::select(
2814                fd as i32 + 1,
2815                &mut reads,
2816                &mut writes,
2817                &mut errs,
2818                interval.as_mut(),
2819            )
2820            .map(|ret| ret == 0)
2821        }
2822    }
2823
2824    #[derive(FromArgs)]
2825    struct GAIOptions {
2826        #[pyarg(positional)]
2827        host: Option<ArgStrOrBytesLike>,
2828        #[pyarg(positional)]
2829        port: Option<Either<ArgStrOrBytesLike, i32>>,
2830
2831        #[pyarg(positional, default = c::AF_UNSPEC)]
2832        family: i32,
2833        #[pyarg(positional, default = 0)]
2834        ty: i32,
2835        #[pyarg(positional, default = 0)]
2836        proto: i32,
2837        #[pyarg(positional, default = 0)]
2838        flags: i32,
2839    }
2840
2841    #[pyfunction]
2842    fn getaddrinfo(
2843        opts: GAIOptions,
2844        vm: &VirtualMachine,
2845    ) -> Result<Vec<PyObjectRef>, IoOrPyException> {
2846        let hints = dns_lookup::AddrInfoHints {
2847            socktype: opts.ty,
2848            protocol: opts.proto,
2849            address: opts.family,
2850            flags: opts.flags,
2851        };
2852
2853        // Encode host: str uses IDNA encoding, bytes must be valid UTF-8
2854        let host_encoded: Option<String> = match opts.host.as_ref() {
2855            Some(ArgStrOrBytesLike::Str(s)) => {
2856                let encoded =
2857                    vm.state
2858                        .codec_registry
2859                        .encode_text(s.to_owned(), "idna", None, vm)?;
2860                let host_str = core::str::from_utf8(encoded.as_bytes())
2861                    .map_err(|_| vm.new_runtime_error("idna output is not utf8"))?;
2862                Some(host_str.to_owned())
2863            }
2864            Some(ArgStrOrBytesLike::Buf(b)) => {
2865                let bytes = b.borrow_buf();
2866                let host_str = core::str::from_utf8(&bytes)
2867                    .map_err(|_| vm.new_unicode_decode_error("host bytes is not utf8"))?;
2868                Some(host_str.to_owned())
2869            }
2870            None => None,
2871        };
2872        let host = host_encoded.as_deref();
2873
2874        // Encode port: str/bytes as service name, int as port number
2875        let port_encoded: Option<String> = match opts.port.as_ref() {
2876            Some(Either::A(sb)) => {
2877                let port_str = match sb {
2878                    ArgStrOrBytesLike::Str(s) => {
2879                        // For str, check for surrogates and raise UnicodeEncodeError if found
2880                        s.to_str()
2881                            .ok_or_else(|| vm.new_unicode_encode_error("surrogates not allowed"))?
2882                            .to_owned()
2883                    }
2884                    ArgStrOrBytesLike::Buf(b) => {
2885                        // For bytes, check if it's valid UTF-8
2886                        let bytes = b.borrow_buf();
2887                        core::str::from_utf8(&bytes)
2888                            .map_err(|_| vm.new_unicode_decode_error("port is not utf8"))?
2889                            .to_owned()
2890                    }
2891                };
2892                Some(port_str)
2893            }
2894            Some(Either::B(i)) => Some(i.to_string()),
2895            None => None,
2896        };
2897        let port = port_encoded.as_deref();
2898
2899        let addrs = dns_lookup::getaddrinfo(host, port, Some(hints))
2900            .map_err(|err| convert_socket_error(vm, err, SocketError::GaiError))?;
2901
2902        let list = addrs
2903            .map(|ai| {
2904                ai.map(|ai| {
2905                    vm.new_tuple((
2906                        ai.address,
2907                        ai.socktype,
2908                        ai.protocol,
2909                        ai.canonname,
2910                        get_ip_addr_tuple(&ai.sockaddr, vm),
2911                    ))
2912                    .into()
2913                })
2914            })
2915            .collect::<io::Result<Vec<_>>>()?;
2916        Ok(list)
2917    }
2918
2919    #[pyfunction]
2920    fn gethostbyaddr(
2921        addr: PyUtf8StrRef,
2922        vm: &VirtualMachine,
2923    ) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
2924        let addr = get_addr(vm, addr, c::AF_UNSPEC)?;
2925        let (hostname, _) = dns_lookup::getnameinfo(&addr, 0)
2926            .map_err(|e| convert_socket_error(vm, e, SocketError::HError))?;
2927        Ok((
2928            hostname,
2929            vm.ctx.new_list(vec![]),
2930            vm.ctx
2931                .new_list(vec![vm.ctx.new_str(addr.ip().to_string()).into()]),
2932        ))
2933    }
2934
2935    #[pyfunction]
2936    fn gethostbyname(name: PyUtf8StrRef, vm: &VirtualMachine) -> Result<String, IoOrPyException> {
2937        let addr = get_addr(vm, name, c::AF_INET)?;
2938        match addr {
2939            SocketAddr::V4(ip) => Ok(ip.ip().to_string()),
2940            _ => unreachable!(),
2941        }
2942    }
2943
2944    #[pyfunction]
2945    fn gethostbyname_ex(
2946        name: PyUtf8StrRef,
2947        vm: &VirtualMachine,
2948    ) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
2949        let addr = get_addr(vm, name, c::AF_INET)?;
2950        let (hostname, _) = dns_lookup::getnameinfo(&addr, 0)
2951            .map_err(|e| convert_socket_error(vm, e, SocketError::HError))?;
2952        Ok((
2953            hostname,
2954            vm.ctx.new_list(vec![]),
2955            vm.ctx
2956                .new_list(vec![vm.ctx.new_str(addr.ip().to_string()).into()]),
2957        ))
2958    }
2959
2960    #[pyfunction]
2961    fn inet_pton(af_inet: i32, ip_string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
2962        static ERROR_MSG: &str = "illegal IP address string passed to inet_pton";
2963        let ip_addr = match af_inet {
2964            c::AF_INET => ip_string
2965                .as_str()
2966                .parse::<Ipv4Addr>()
2967                .map_err(|_| vm.new_os_error(ERROR_MSG.to_owned()))?
2968                .octets()
2969                .to_vec(),
2970            c::AF_INET6 => ip_string
2971                .as_str()
2972                .parse::<Ipv6Addr>()
2973                .map_err(|_| vm.new_os_error(ERROR_MSG.to_owned()))?
2974                .octets()
2975                .to_vec(),
2976            _ => return Err(vm.new_os_error("Address family not supported by protocol".to_owned())),
2977        };
2978        Ok(ip_addr)
2979    }
2980
2981    #[pyfunction]
2982    fn inet_ntop(af_inet: i32, packed_ip: ArgBytesLike, vm: &VirtualMachine) -> PyResult<String> {
2983        let packed_ip = packed_ip.borrow_buf();
2984        match af_inet {
2985            c::AF_INET => {
2986                let packed_ip = <&[u8; 4]>::try_from(&*packed_ip).map_err(|_| {
2987                    vm.new_value_error("invalid length of packed IP address string")
2988                })?;
2989                Ok(Ipv4Addr::from(*packed_ip).to_string())
2990            }
2991            c::AF_INET6 => {
2992                let packed_ip = <&[u8; 16]>::try_from(&*packed_ip).map_err(|_| {
2993                    vm.new_value_error("invalid length of packed IP address string")
2994                })?;
2995                Ok(get_ipv6_addr_str(Ipv6Addr::from(*packed_ip)))
2996            }
2997            _ => Err(vm.new_value_error(format!("unknown address family {af_inet}"))),
2998        }
2999    }
3000
3001    #[pyfunction]
3002    fn getprotobyname(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
3003        let cstr = name.to_cstring(vm)?;
3004        let proto = unsafe { c::getprotobyname(cstr.as_ptr() as _) };
3005        if proto.is_null() {
3006            return Err(vm.new_os_error("protocol not found".to_owned()));
3007        }
3008        let num = unsafe { (*proto).p_proto };
3009        Ok(vm.ctx.new_int(num).into())
3010    }
3011
3012    #[pyfunction]
3013    fn getnameinfo(
3014        address: PyTupleRef,
3015        flags: i32,
3016        vm: &VirtualMachine,
3017    ) -> Result<(String, String), IoOrPyException> {
3018        match address.len() {
3019            2..=4 => {}
3020            _ => {
3021                return Err(vm.new_type_error("illegal sockaddr argument").into());
3022            }
3023        }
3024        let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&address, vm)?;
3025        let hints = dns_lookup::AddrInfoHints {
3026            address: c::AF_UNSPEC,
3027            socktype: c::SOCK_DGRAM,
3028            flags: c::AI_NUMERICHOST,
3029            protocol: 0,
3030        };
3031        let service = addr.port.to_string();
3032        let host_str = addr.host.as_str();
3033        let mut res = dns_lookup::getaddrinfo(Some(host_str), Some(&service), Some(hints))
3034            .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?
3035            .filter_map(Result::ok);
3036        let mut ainfo = res.next().unwrap();
3037        if res.next().is_some() {
3038            return Err(vm
3039                .new_os_error("sockaddr resolved to multiple addresses".to_owned())
3040                .into());
3041        }
3042        match &mut ainfo.sockaddr {
3043            SocketAddr::V4(_) => {
3044                if address.len() != 2 {
3045                    return Err(vm
3046                        .new_os_error("IPv4 sockaddr must be 2 tuple".to_owned())
3047                        .into());
3048                }
3049            }
3050            SocketAddr::V6(addr) => {
3051                addr.set_flowinfo(flowinfo);
3052                addr.set_scope_id(scopeid);
3053            }
3054        }
3055        dns_lookup::getnameinfo(&ainfo.sockaddr, flags)
3056            .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))
3057    }
3058
3059    #[cfg(unix)]
3060    #[pyfunction]
3061    fn socketpair(
3062        family: OptionalArg<i32>,
3063        socket_kind: OptionalArg<i32>,
3064        proto: OptionalArg<i32>,
3065    ) -> Result<(PySocket, PySocket), IoOrPyException> {
3066        let family = family.unwrap_or(libc::AF_UNIX);
3067        let socket_kind = socket_kind.unwrap_or(libc::SOCK_STREAM);
3068        let proto = proto.unwrap_or(0);
3069        let (a, b) = Socket::pair(family.into(), socket_kind.into(), Some(proto.into()))?;
3070        let py_a = PySocket::default();
3071        py_a.init_inner(family, socket_kind, proto, a)?;
3072        let py_b = PySocket::default();
3073        py_b.init_inner(family, socket_kind, proto, b)?;
3074        Ok((py_a, py_b))
3075    }
3076
3077    #[cfg(all(unix, not(target_os = "redox")))]
3078    type IfIndex = c::c_uint;
3079    #[cfg(windows)]
3080    type IfIndex = u32; // NET_IFINDEX but windows-sys 0.59 doesn't have it
3081
3082    #[cfg(not(target_os = "redox"))]
3083    #[pyfunction]
3084    fn if_nametoindex(name: FsPath, vm: &VirtualMachine) -> PyResult<IfIndex> {
3085        let name = name.to_cstring(vm)?;
3086        // in case 'if_nametoindex' does not set errno
3087        crate::common::os::set_errno(libc::ENODEV);
3088        let ret = unsafe { c::if_nametoindex(name.as_ptr() as _) };
3089        if ret == 0 {
3090            Err(vm.new_last_errno_error())
3091        } else {
3092            Ok(ret)
3093        }
3094    }
3095
3096    #[cfg(not(target_os = "redox"))]
3097    #[pyfunction]
3098    fn if_indextoname(index: IfIndex, vm: &VirtualMachine) -> PyResult<String> {
3099        let mut buf = [0; c::IF_NAMESIZE + 1];
3100        // in case 'if_indextoname' does not set errno
3101        crate::common::os::set_errno(libc::ENXIO);
3102        let ret = unsafe { c::if_indextoname(index, buf.as_mut_ptr()) };
3103        if ret.is_null() {
3104            Err(vm.new_last_errno_error())
3105        } else {
3106            let buf = unsafe { ffi::CStr::from_ptr(buf.as_ptr() as _) };
3107            Ok(buf.to_string_lossy().into_owned())
3108        }
3109    }
3110
3111    #[cfg(any(
3112        windows,
3113        target_os = "dragonfly",
3114        target_os = "freebsd",
3115        target_os = "fuchsia",
3116        target_os = "ios",
3117        target_os = "linux",
3118        target_os = "macos",
3119        target_os = "netbsd",
3120        target_os = "openbsd",
3121    ))]
3122    #[pyfunction]
3123    fn if_nameindex(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
3124        #[cfg(not(windows))]
3125        {
3126            let list = nix::net::if_::if_nameindex()
3127                .map_err(|err| err.into_pyexception(vm))?
3128                .to_slice()
3129                .iter()
3130                .map(|iface| {
3131                    let tup: (u32, String) =
3132                        (iface.index(), iface.name().to_string_lossy().into_owned());
3133                    tup.to_pyobject(vm)
3134                })
3135                .collect();
3136
3137            Ok(list)
3138        }
3139        #[cfg(windows)]
3140        {
3141            use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH;
3142
3143            let table = MibTable::get_raw().map_err(|err| err.into_pyexception(vm))?;
3144            let list = table.as_slice().iter().map(|entry| {
3145                let name =
3146                    get_name(&entry.InterfaceLuid).map_err(|err| err.into_pyexception(vm))?;
3147                let tup = (entry.InterfaceIndex, name.to_string_lossy());
3148                Ok(tup.to_pyobject(vm))
3149            });
3150            let list = list.collect::<PyResult<_>>()?;
3151            return Ok(list);
3152
3153            fn get_name(luid: &NET_LUID_LH) -> io::Result<widestring::WideCString> {
3154                let mut buf = [0; c::IF_NAMESIZE + 1];
3155                let ret = unsafe {
3156                    IpHelper::ConvertInterfaceLuidToNameW(luid, buf.as_mut_ptr(), buf.len())
3157                };
3158                if ret == 0 {
3159                    Ok(widestring::WideCString::from_ustr_truncate(
3160                        widestring::WideStr::from_slice(&buf[..]),
3161                    ))
3162                } else {
3163                    Err(io::Error::from_raw_os_error(ret as i32))
3164                }
3165            }
3166            struct MibTable {
3167                ptr: core::ptr::NonNull<IpHelper::MIB_IF_TABLE2>,
3168            }
3169            impl MibTable {
3170                fn get_raw() -> io::Result<Self> {
3171                    let mut ptr = core::ptr::null_mut();
3172                    let ret = unsafe { IpHelper::GetIfTable2Ex(IpHelper::MibIfTableRaw, &mut ptr) };
3173                    if ret == 0 {
3174                        let ptr = unsafe { core::ptr::NonNull::new_unchecked(ptr) };
3175                        Ok(Self { ptr })
3176                    } else {
3177                        Err(io::Error::from_raw_os_error(ret as i32))
3178                    }
3179                }
3180            }
3181            impl MibTable {
3182                fn as_slice(&self) -> &[IpHelper::MIB_IF_ROW2] {
3183                    unsafe {
3184                        let p = self.ptr.as_ptr();
3185                        let ptr = &raw const (*p).Table as *const IpHelper::MIB_IF_ROW2;
3186                        core::slice::from_raw_parts(ptr, (*p).NumEntries as usize)
3187                    }
3188                }
3189            }
3190            impl Drop for MibTable {
3191                fn drop(&mut self) {
3192                    unsafe { IpHelper::FreeMibTable(self.ptr.as_ptr() as *mut _) };
3193                }
3194            }
3195        }
3196    }
3197
3198    fn get_addr(
3199        vm: &VirtualMachine,
3200        pyname: PyUtf8StrRef,
3201        af: i32,
3202    ) -> Result<SocketAddr, IoOrPyException> {
3203        let name = pyname.as_str();
3204        if name.is_empty() {
3205            let hints = dns_lookup::AddrInfoHints {
3206                address: af,
3207                socktype: c::SOCK_DGRAM,
3208                flags: c::AI_PASSIVE,
3209                protocol: 0,
3210            };
3211            let mut res = dns_lookup::getaddrinfo(None, Some("0"), Some(hints))
3212                .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?;
3213            let ainfo = res.next().unwrap()?;
3214            if res.next().is_some() {
3215                return Err(vm
3216                    .new_os_error("wildcard resolved to multiple address".to_owned())
3217                    .into());
3218            }
3219            return Ok(ainfo.sockaddr);
3220        }
3221        if name == "255.255.255.255" || name == "<broadcast>" {
3222            match af {
3223                c::AF_INET | c::AF_UNSPEC => {}
3224                _ => {
3225                    return Err(vm
3226                        .new_os_error("address family mismatched".to_owned())
3227                        .into());
3228                }
3229            }
3230            return Ok(SocketAddr::V4(net::SocketAddrV4::new(
3231                c::INADDR_BROADCAST.into(),
3232                0,
3233            )));
3234        }
3235        if let c::AF_INET | c::AF_UNSPEC = af
3236            && let Ok(addr) = name.parse::<Ipv4Addr>()
3237        {
3238            return Ok(SocketAddr::V4(net::SocketAddrV4::new(addr, 0)));
3239        }
3240        if matches!(af, c::AF_INET | c::AF_UNSPEC)
3241            && !name.contains('%')
3242            && let Ok(addr) = name.parse::<Ipv6Addr>()
3243        {
3244            return Ok(SocketAddr::V6(net::SocketAddrV6::new(addr, 0, 0, 0)));
3245        }
3246        let hints = dns_lookup::AddrInfoHints {
3247            address: af,
3248            ..Default::default()
3249        };
3250        let name = vm
3251            .state
3252            .codec_registry
3253            .encode_text(pyname.into_wtf8(), "idna", None, vm)?;
3254        let name = core::str::from_utf8(name.as_bytes())
3255            .map_err(|_| vm.new_runtime_error("idna output is not utf8"))?;
3256        let mut res = dns_lookup::getaddrinfo(Some(name), None, Some(hints))
3257            .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?;
3258        Ok(res.next().unwrap().map(|ainfo| ainfo.sockaddr)?)
3259    }
3260
3261    fn sock_from_raw(fileno: RawSocket, vm: &VirtualMachine) -> PyResult<Socket> {
3262        let invalid = {
3263            cfg_if::cfg_if! {
3264                if #[cfg(windows)] {
3265                    fileno == INVALID_SOCKET
3266                } else {
3267                    fileno < 0
3268                }
3269            }
3270        };
3271        if invalid {
3272            return Err(vm.new_value_error("negative file descriptor"));
3273        }
3274        Ok(unsafe { sock_from_raw_unchecked(fileno) })
3275    }
3276    /// SAFETY: fileno must not be equal to INVALID_SOCKET
3277    unsafe fn sock_from_raw_unchecked(fileno: RawSocket) -> Socket {
3278        #[cfg(unix)]
3279        {
3280            use std::os::unix::io::FromRawFd;
3281            unsafe { Socket::from_raw_fd(fileno) }
3282        }
3283        #[cfg(windows)]
3284        {
3285            use std::os::windows::io::FromRawSocket;
3286            unsafe { Socket::from_raw_socket(fileno) }
3287        }
3288    }
3289    pub(super) fn sock_fileno(sock: &Socket) -> RawSocket {
3290        #[cfg(unix)]
3291        {
3292            use std::os::unix::io::AsRawFd;
3293            sock.as_raw_fd()
3294        }
3295        #[cfg(windows)]
3296        {
3297            use std::os::windows::io::AsRawSocket;
3298            sock.as_raw_socket()
3299        }
3300    }
3301    fn into_sock_fileno(sock: Socket) -> RawSocket {
3302        #[cfg(unix)]
3303        {
3304            use std::os::unix::io::IntoRawFd;
3305            sock.into_raw_fd()
3306        }
3307        #[cfg(windows)]
3308        {
3309            use std::os::windows::io::IntoRawSocket;
3310            sock.into_raw_socket()
3311        }
3312    }
3313
3314    pub(super) const INVALID_SOCKET: RawSocket = {
3315        #[cfg(unix)]
3316        {
3317            -1
3318        }
3319        #[cfg(windows)]
3320        {
3321            windows_sys::Win32::Networking::WinSock::INVALID_SOCKET as RawSocket
3322        }
3323    };
3324
3325    fn convert_socket_error(
3326        vm: &VirtualMachine,
3327        err: dns_lookup::LookupError,
3328        err_kind: SocketError,
3329    ) -> IoOrPyException {
3330        if let dns_lookup::LookupErrorKind::System = err.kind() {
3331            return io::Error::from(err).into();
3332        }
3333        let strerr = {
3334            #[cfg(unix)]
3335            {
3336                let s = match err_kind {
3337                    SocketError::GaiError => unsafe {
3338                        ffi::CStr::from_ptr(libc::gai_strerror(err.error_num()))
3339                    },
3340                    SocketError::HError => unsafe {
3341                        ffi::CStr::from_ptr(libc::hstrerror(err.error_num()))
3342                    },
3343                };
3344                s.to_str().unwrap()
3345            }
3346            #[cfg(windows)]
3347            {
3348                "getaddrinfo failed"
3349            }
3350        };
3351        let exception_cls = match err_kind {
3352            SocketError::GaiError => gaierror(vm),
3353            SocketError::HError => herror(vm),
3354        };
3355        vm.new_os_subtype_error(exception_cls, Some(err.error_num()), strerr)
3356            .into()
3357    }
3358
3359    fn timeout_error(vm: &VirtualMachine) -> PyRef<PyOSError> {
3360        timeout_error_msg(vm, "timed out".to_owned())
3361    }
3362    pub(crate) fn timeout_error_msg(vm: &VirtualMachine, msg: String) -> PyRef<PyOSError> {
3363        vm.new_os_subtype_error(timeout(vm), None, msg)
3364    }
3365
3366    fn get_ipv6_addr_str(ipv6: Ipv6Addr) -> String {
3367        match ipv6.to_ipv4() {
3368            // instead of "::0.0.ddd.ddd" it's "::xxxx"
3369            Some(v4) if !ipv6.is_unspecified() && matches!(v4.octets(), [0, 0, _, _]) => {
3370                format!("::{:x}", u32::from(v4))
3371            }
3372            _ => ipv6.to_string(),
3373        }
3374    }
3375
3376    pub(crate) struct Deadline {
3377        deadline: Instant,
3378    }
3379
3380    impl Deadline {
3381        fn new(timeout: Duration) -> Self {
3382            Self {
3383                deadline: Instant::now() + timeout,
3384            }
3385        }
3386        fn time_until(&self) -> Result<Duration, IoOrPyException> {
3387            self.deadline
3388                .checked_duration_since(Instant::now())
3389                // past the deadline already
3390                .ok_or(IoOrPyException::Timeout)
3391        }
3392    }
3393
3394    static DEFAULT_TIMEOUT: AtomicCell<f64> = AtomicCell::new(-1.0);
3395
3396    #[pyfunction]
3397    fn getdefaulttimeout() -> Option<f64> {
3398        let timeout = DEFAULT_TIMEOUT.load();
3399        if timeout >= 0.0 { Some(timeout) } else { None }
3400    }
3401
3402    #[pyfunction]
3403    fn setdefaulttimeout(timeout: Option<ArgIntoFloat>, vm: &VirtualMachine) -> PyResult<()> {
3404        let val = match timeout {
3405            Some(t) => {
3406                let f = t.into_float();
3407                if f.is_nan() {
3408                    return Err(vm.new_value_error("Invalid value NaN (not a number)"));
3409                }
3410                if f < 0.0 || !f.is_finite() {
3411                    return Err(vm.new_value_error("Timeout value out of range"));
3412                }
3413                f
3414            }
3415            None => -1.0,
3416        };
3417        DEFAULT_TIMEOUT.store(val);
3418        Ok(())
3419    }
3420
3421    #[pyfunction]
3422    fn dup(x: PyObjectRef, vm: &VirtualMachine) -> Result<RawSocket, IoOrPyException> {
3423        let sock = get_raw_sock(x, vm)?;
3424        let sock = core::mem::ManuallyDrop::new(sock_from_raw(sock, vm)?);
3425        let newsock = sock.try_clone()?;
3426        let fd = into_sock_fileno(newsock);
3427        #[cfg(windows)]
3428        crate::vm::stdlib::nt::raw_set_handle_inheritable(fd as _, false)?;
3429        Ok(fd)
3430    }
3431
3432    #[pyfunction]
3433    fn close(x: PyObjectRef, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
3434        Ok(close_inner(get_raw_sock(x, vm)?)?)
3435    }
3436
3437    fn close_inner(x: RawSocket) -> io::Result<()> {
3438        #[cfg(unix)]
3439        use libc::close;
3440        #[cfg(windows)]
3441        use windows_sys::Win32::Networking::WinSock::closesocket as close;
3442        let ret = unsafe { close(x as _) };
3443        if ret < 0 {
3444            let err = std::io::Error::last_os_error();
3445            if err.raw_os_error() != Some(errcode!(ECONNRESET)) {
3446                return Err(err);
3447            }
3448        }
3449        Ok(())
3450    }
3451
3452    enum SocketError {
3453        HError,
3454        GaiError,
3455    }
3456
3457    #[cfg(all(unix, not(target_os = "redox")))]
3458    fn checked_cmsg_len(len: usize) -> Option<usize> {
3459        // SAFETY: CMSG_LEN is always safe
3460        let cmsg_len = |length| unsafe { libc::CMSG_LEN(length) };
3461        if len as u64 > (i32::MAX as u64 - cmsg_len(0) as u64) {
3462            return None;
3463        }
3464        let res = cmsg_len(len as _) as usize;
3465        if res > i32::MAX as usize || res < len {
3466            return None;
3467        }
3468        Some(res)
3469    }
3470
3471    #[cfg(all(unix, not(target_os = "redox")))]
3472    fn checked_cmsg_space(len: usize) -> Option<usize> {
3473        // SAFETY: CMSG_SPACE is always safe
3474        let cmsg_space = |length| unsafe { libc::CMSG_SPACE(length) };
3475        if len as u64 > (i32::MAX as u64 - cmsg_space(1) as u64) {
3476            return None;
3477        }
3478        let res = cmsg_space(len as _) as usize;
3479        if res > i32::MAX as usize || res < len {
3480            return None;
3481        }
3482        Some(res)
3483    }
3484
3485    #[cfg(all(unix, not(target_os = "redox")))]
3486    #[pyfunction(name = "CMSG_LEN")]
3487    fn cmsg_len(length: usize, vm: &VirtualMachine) -> PyResult<usize> {
3488        checked_cmsg_len(length)
3489            .ok_or_else(|| vm.new_overflow_error("CMSG_LEN() argument out of range"))
3490    }
3491
3492    #[cfg(all(unix, not(target_os = "redox")))]
3493    #[pyfunction(name = "CMSG_SPACE")]
3494    fn cmsg_space(length: usize, vm: &VirtualMachine) -> PyResult<usize> {
3495        checked_cmsg_space(length)
3496            .ok_or_else(|| vm.new_overflow_error("CMSG_SPACE() argument out of range"))
3497    }
3498}