rustpython_stdlib/
socket.rs

1use crate::vm::{builtins::PyModule, PyRef, VirtualMachine};
2#[cfg(feature = "ssl")]
3pub(super) use _socket::{sock_select, timeout_error_msg, PySocket, SelectKind};
4
5pub fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
6    #[cfg(windows)]
7    crate::vm::windows::init_winsock();
8    _socket::make_module(vm)
9}
10
11#[pymodule]
12mod _socket {
13    use crate::common::lock::{PyMappedRwLockReadGuard, PyRwLock, PyRwLockReadGuard};
14    use crate::vm::{
15        builtins::{PyBaseExceptionRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
16        common::os::ErrorExt,
17        convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject},
18        function::{ArgBytesLike, ArgMemoryBuffer, Either, FsPath, OptionalArg, OptionalOption},
19        types::{Constructor, DefaultConstructor, Initializer, Representable},
20        utils::ToCString,
21        AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
22    };
23    use crossbeam_utils::atomic::AtomicCell;
24    use num_traits::ToPrimitive;
25    use socket2::Socket;
26    use std::{
27        ffi,
28        io::{self, Read, Write},
29        mem::MaybeUninit,
30        net::{self, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs},
31        time::{Duration, Instant},
32    };
33
34    #[cfg(unix)]
35    use libc as c;
36    #[cfg(windows)]
37    mod c {
38        pub use winapi::shared::netioapi::{if_indextoname, if_nametoindex};
39        pub use winapi::shared::ws2def::{
40            INADDR_ANY, INADDR_BROADCAST, INADDR_LOOPBACK, INADDR_NONE,
41        };
42        pub use winapi::um::winsock2::{
43            getprotobyname, getservbyname, getservbyport, getsockopt, setsockopt,
44            SO_EXCLUSIVEADDRUSE,
45        };
46        pub use winapi::um::ws2tcpip::{
47            EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NODATA, EAI_NONAME,
48            EAI_SERVICE, EAI_SOCKTYPE,
49        };
50        pub use windows_sys::Win32::Networking::WinSock::{
51            AF_DECnet, AF_APPLETALK, AF_IPX, AF_LINK, AI_ADDRCONFIG, AI_ALL, AI_CANONNAME,
52            AI_NUMERICSERV, AI_V4MAPPED, IPPORT_RESERVED, IPPROTO_AH, IPPROTO_DSTOPTS, IPPROTO_EGP,
53            IPPROTO_ESP, IPPROTO_FRAGMENT, IPPROTO_GGP, IPPROTO_HOPOPTS, IPPROTO_ICMP,
54            IPPROTO_ICMPV6, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IP as IPPROTO_IPIP,
55            IPPROTO_IPV4, IPPROTO_IPV6, IPPROTO_ND, IPPROTO_NONE, IPPROTO_PIM, IPPROTO_PUP,
56            IPPROTO_RAW, IPPROTO_ROUTING, IPPROTO_TCP, IPPROTO_UDP, IPV6_CHECKSUM, IPV6_DONTFRAG,
57            IPV6_HOPLIMIT, IPV6_HOPOPTS, IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, IPV6_MULTICAST_HOPS,
58            IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_PKTINFO, IPV6_RECVRTHDR, IPV6_RECVTCLASS,
59            IPV6_RTHDR, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
60            IP_DROP_MEMBERSHIP, IP_HDRINCL, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
61            IP_OPTIONS, IP_RECVDSTADDR, IP_TOS, IP_TTL, MSG_BCAST, MSG_CTRUNC, MSG_DONTROUTE,
62            MSG_MCAST, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, NI_DGRAM, NI_MAXHOST, NI_MAXSERV,
63            NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV, RCVALL_IPLEVEL, RCVALL_OFF,
64            RCVALL_ON, RCVALL_SOCKETLEVELONLY, SD_BOTH as SHUT_RDWR, SD_RECEIVE as SHUT_RD,
65            SD_SEND as SHUT_WR, SIO_KEEPALIVE_VALS, SIO_LOOPBACK_FAST_PATH, SIO_RCVALL, SOCK_DGRAM,
66            SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SOMAXCONN, SO_BROADCAST,
67            SO_ERROR, SO_LINGER, SO_OOBINLINE, SO_REUSEADDR, SO_TYPE, SO_USELOOPBACK, TCP_NODELAY,
68            WSAEBADF, WSAECONNRESET, WSAENOTSOCK, WSAEWOULDBLOCK,
69        };
70        pub const IF_NAMESIZE: usize =
71            windows_sys::Win32::NetworkManagement::Ndis::IF_MAX_STRING_SIZE as _;
72        pub const AF_UNSPEC: i32 = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as _;
73        pub const AF_INET: i32 = windows_sys::Win32::Networking::WinSock::AF_INET as _;
74        pub const AF_INET6: i32 = windows_sys::Win32::Networking::WinSock::AF_INET6 as _;
75        pub const AI_PASSIVE: i32 = windows_sys::Win32::Networking::WinSock::AI_PASSIVE as _;
76        pub const AI_NUMERICHOST: i32 =
77            windows_sys::Win32::Networking::WinSock::AI_NUMERICHOST as _;
78    }
79    // constants
80    #[pyattr(name = "has_ipv6")]
81    const HAS_IPV6: bool = true;
82    #[pyattr]
83    // put IPPROTO_MAX later
84    use c::{
85        AF_INET, AF_INET6, AF_UNSPEC, INADDR_ANY, INADDR_LOOPBACK, INADDR_NONE, IPPROTO_ICMP,
86        IPPROTO_ICMPV6, IPPROTO_IP, IPPROTO_IPV6, IPPROTO_TCP, IPPROTO_TCP as SOL_TCP, IPPROTO_UDP,
87        MSG_CTRUNC, MSG_DONTROUTE, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, NI_DGRAM, NI_MAXHOST,
88        NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV, SHUT_RD, SHUT_RDWR, SHUT_WR,
89        SOCK_DGRAM, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_LINGER, SO_OOBINLINE,
90        SO_REUSEADDR, SO_TYPE, TCP_NODELAY,
91    };
92
93    #[cfg(not(target_os = "redox"))]
94    #[pyattr]
95    use c::{
96        AF_DECnet, AF_APPLETALK, AF_IPX, IPPROTO_AH, IPPROTO_DSTOPTS, IPPROTO_EGP, IPPROTO_ESP,
97        IPPROTO_FRAGMENT, IPPROTO_HOPOPTS, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_NONE,
98        IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING,
99    };
100
101    #[cfg(unix)]
102    #[pyattr]
103    use c::{AF_UNIX, SO_REUSEPORT};
104
105    #[pyattr]
106    use c::{AI_ADDRCONFIG, AI_NUMERICHOST, AI_NUMERICSERV, AI_PASSIVE};
107
108    #[cfg(not(target_os = "redox"))]
109    #[pyattr]
110    use c::{SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET};
111
112    #[cfg(target_os = "android")]
113    #[pyattr]
114    use c::{SOL_ATALK, SOL_AX25, SOL_IPX, SOL_NETROM, SOL_ROSE};
115
116    #[cfg(target_os = "freebsd")]
117    #[pyattr]
118    use c::SO_SETFIB;
119
120    #[cfg(target_os = "linux")]
121    #[pyattr]
122    use c::{
123        CAN_BCM, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_FLAG, CAN_ERR_MASK, CAN_ISOTP, CAN_J1939,
124        CAN_RAW, CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES, CAN_RAW_FILTER, CAN_RAW_JOIN_FILTERS,
125        CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS, CAN_RTR_FLAG, CAN_SFF_MASK, IPPROTO_MPTCP,
126        J1939_IDLE_ADDR, J1939_MAX_UNICAST_ADDR, J1939_NLA_BYTES_ACKED, J1939_NLA_PAD,
127        J1939_NO_ADDR, J1939_NO_NAME, J1939_NO_PGN, J1939_PGN_ADDRESS_CLAIMED,
128        J1939_PGN_ADDRESS_COMMANDED, J1939_PGN_MAX, J1939_PGN_PDU1_MAX, J1939_PGN_REQUEST,
129        SCM_J1939_DEST_ADDR, SCM_J1939_DEST_NAME, SCM_J1939_ERRQUEUE, SCM_J1939_PRIO, SOL_CAN_BASE,
130        SOL_CAN_RAW, SO_J1939_ERRQUEUE, SO_J1939_FILTER, SO_J1939_PROMISC, SO_J1939_SEND_PRIO,
131    };
132
133    #[cfg(all(target_os = "linux", target_env = "gnu"))]
134    #[pyattr]
135    use c::SOL_RDS;
136
137    #[cfg(target_os = "netbsd")]
138    #[pyattr]
139    use c::IPPROTO_VRRP;
140
141    #[cfg(target_vendor = "apple")]
142    #[pyattr]
143    use c::{AF_SYSTEM, PF_SYSTEM, SYSPROTO_CONTROL, TCP_KEEPALIVE};
144
145    #[cfg(windows)]
146    #[pyattr]
147    use c::{
148        IPPORT_RESERVED, IPPROTO_IPV4, RCVALL_IPLEVEL, RCVALL_OFF, RCVALL_ON,
149        RCVALL_SOCKETLEVELONLY, SIO_KEEPALIVE_VALS, SIO_LOOPBACK_FAST_PATH, SIO_RCVALL,
150        SO_EXCLUSIVEADDRUSE,
151    };
152
153    #[cfg(not(windows))]
154    #[pyattr]
155    const IPPORT_RESERVED: i32 = 1024;
156
157    #[pyattr]
158    const IPPORT_USERRESERVED: i32 = 5000;
159
160    #[cfg(any(unix, target_os = "android"))]
161    #[pyattr]
162    use c::{
163        EAI_SYSTEM, MSG_EOR, SO_ACCEPTCONN, SO_DEBUG, SO_DONTROUTE, SO_KEEPALIVE, SO_RCVBUF,
164        SO_RCVLOWAT, SO_RCVTIMEO, SO_SNDBUF, SO_SNDLOWAT, SO_SNDTIMEO,
165    };
166
167    #[cfg(any(target_os = "android", target_os = "linux"))]
168    #[pyattr]
169    use c::{
170        ALG_OP_DECRYPT, ALG_OP_ENCRYPT, ALG_SET_AEAD_ASSOCLEN, ALG_SET_AEAD_AUTHSIZE, ALG_SET_IV,
171        ALG_SET_KEY, ALG_SET_OP, IPV6_DSTOPTS, IPV6_NEXTHOP, IPV6_PATHMTU, IPV6_RECVDSTOPTS,
172        IPV6_RECVHOPLIMIT, IPV6_RECVHOPOPTS, IPV6_RECVPATHMTU, IPV6_RTHDRDSTOPTS,
173        IP_DEFAULT_MULTICAST_LOOP, IP_RECVOPTS, IP_RETOPTS, NETLINK_CRYPTO, NETLINK_DNRTMSG,
174        NETLINK_FIREWALL, NETLINK_IP6_FW, NETLINK_NFLOG, NETLINK_ROUTE, NETLINK_USERSOCK,
175        NETLINK_XFRM, SOL_ALG, SO_PASSSEC, SO_PEERSEC,
176    };
177
178    #[cfg(any(target_os = "android", target_vendor = "apple"))]
179    #[pyattr]
180    use c::{AI_DEFAULT, AI_MASK, AI_V4MAPPED_CFG};
181
182    #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
183    #[pyattr]
184    use c::MSG_NOTIFICATION;
185
186    #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
187    #[pyattr]
188    use c::TCP_USER_TIMEOUT;
189
190    #[cfg(any(unix, target_os = "android", windows))]
191    #[pyattr]
192    use c::{
193        INADDR_BROADCAST, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP,
194        IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF,
195        IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL,
196    };
197
198    #[cfg(any(unix, target_os = "android", windows))]
199    #[pyattr]
200    const INADDR_UNSPEC_GROUP: u32 = 0xe0000000;
201
202    #[cfg(any(unix, target_os = "android", windows))]
203    #[pyattr]
204    const INADDR_ALLHOSTS_GROUP: u32 = 0xe0000001;
205
206    #[cfg(any(unix, target_os = "android", windows))]
207    #[pyattr]
208    const INADDR_MAX_LOCAL_GROUP: u32 = 0xe00000ff;
209
210    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
211    #[pyattr]
212    use c::{
213        AF_ALG, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BRIDGE, AF_CAN, AF_ECONET, AF_IRDA,
214        AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PPPOX, AF_RDS, AF_SECURITY,
215        AF_TIPC, AF_VSOCK, AF_WANPIPE, AF_X25, IP_TRANSPARENT, MSG_CONFIRM, MSG_ERRQUEUE,
216        MSG_FASTOPEN, MSG_MORE, PF_CAN, PF_PACKET, PF_RDS, SCM_CREDENTIALS, SOL_IP, SOL_TIPC,
217        SOL_UDP, SO_BINDTODEVICE, SO_MARK, TCP_CORK, TCP_DEFER_ACCEPT, TCP_LINGER2, TCP_QUICKACK,
218        TCP_SYNCNT, TCP_WINDOW_CLAMP,
219    };
220
221    // gated on presence of AF_VSOCK:
222    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
223    #[pyattr]
224    const SO_VM_SOCKETS_BUFFER_SIZE: u32 = 0;
225
226    // gated on presence of AF_VSOCK:
227    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
228    #[pyattr]
229    const SO_VM_SOCKETS_BUFFER_MIN_SIZE: u32 = 1;
230
231    // gated on presence of AF_VSOCK:
232    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
233    #[pyattr]
234    const SO_VM_SOCKETS_BUFFER_MAX_SIZE: u32 = 2;
235
236    // gated on presence of AF_VSOCK:
237    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
238    #[pyattr]
239    const VMADDR_CID_ANY: u32 = 0xffffffff; // 0xffffffff
240
241    // gated on presence of AF_VSOCK:
242    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
243    #[pyattr]
244    const VMADDR_PORT_ANY: u32 = 0xffffffff; // 0xffffffff
245
246    // gated on presence of AF_VSOCK:
247    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
248    #[pyattr]
249    const VMADDR_CID_HOST: u32 = 2;
250
251    // gated on presence of AF_VSOCK:
252    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
253    #[pyattr]
254    const VM_SOCKETS_INVALID_VERSION: u32 = 0xffffffff; // 0xffffffff
255
256    // TODO: gated on https://github.com/rust-lang/libc/pull/1662
257    // // gated on presence of AF_VSOCK:
258    // #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
259    // #[pyattr(name = "IOCTL_VM_SOCKETS_GET_LOCAL_CID", once)]
260    // fn ioctl_vm_sockets_get_local_cid(_vm: &VirtualMachine) -> i32 {
261    //     c::_IO(7, 0xb9)
262    // }
263
264    #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
265    #[pyattr]
266    const SOL_IP: i32 = 0;
267
268    #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
269    #[pyattr]
270    const SOL_UDP: i32 = 17;
271
272    #[cfg(any(target_os = "android", target_os = "linux", windows))]
273    #[pyattr]
274    use c::{IPV6_HOPOPTS, IPV6_RECVRTHDR, IPV6_RTHDR, IP_OPTIONS};
275
276    #[cfg(any(
277        target_os = "dragonfly",
278        target_os = "freebsd",
279        target_vendor = "apple"
280    ))]
281    #[pyattr]
282    use c::{IPPROTO_HELLO, IPPROTO_XTP, LOCAL_PEERCRED, MSG_EOF};
283
284    #[cfg(any(target_os = "netbsd", target_os = "openbsd", windows))]
285    #[pyattr]
286    use c::{MSG_BCAST, MSG_MCAST};
287
288    #[cfg(any(
289        target_os = "android",
290        target_os = "fuchsia",
291        target_os = "freebsd",
292        target_os = "linux"
293    ))]
294    #[pyattr]
295    use c::{IPPROTO_UDPLITE, TCP_CONGESTION};
296
297    #[cfg(any(
298        target_os = "android",
299        target_os = "fuchsia",
300        target_os = "freebsd",
301        target_os = "linux"
302    ))]
303    #[pyattr]
304    const UDPLITE_SEND_CSCOV: i32 = 10;
305
306    #[cfg(any(
307        target_os = "android",
308        target_os = "fuchsia",
309        target_os = "freebsd",
310        target_os = "linux"
311    ))]
312    #[pyattr]
313    const UDPLITE_RECV_CSCOV: i32 = 11;
314
315    #[cfg(any(
316        target_os = "android",
317        target_os = "fuchsia",
318        target_os = "linux",
319        target_os = "openbsd"
320    ))]
321    #[pyattr]
322    use c::AF_KEY;
323
324    #[cfg(any(
325        target_os = "android",
326        target_os = "fuchsia",
327        target_os = "linux",
328        target_os = "redox"
329    ))]
330    #[pyattr]
331    use c::SO_DOMAIN;
332
333    #[cfg(any(
334        target_os = "android",
335        target_os = "fuchsia",
336        all(
337            target_os = "linux",
338            any(
339                target_arch = "aarch64",
340                target_arch = "x86",
341                target_arch = "loongarch64",
342                target_arch = "mips",
343                target_arch = "powerpc",
344                target_arch = "powerpc64",
345                target_arch = "riscv64",
346                target_arch = "s390x",
347                target_arch = "x86_64"
348            )
349        ),
350        target_os = "redox"
351    ))]
352    #[pyattr]
353    use c::SO_PRIORITY;
354
355    #[cfg(any(
356        target_os = "dragonfly",
357        target_os = "freebsd",
358        target_os = "netbsd",
359        target_os = "openbsd"
360    ))]
361    #[pyattr]
362    use c::IPPROTO_MOBILE;
363
364    #[cfg(any(
365        target_os = "dragonfly",
366        target_os = "freebsd",
367        target_os = "netbsd",
368        target_vendor = "apple"
369    ))]
370    #[pyattr]
371    use c::SCM_CREDS;
372
373    #[cfg(any(
374        target_os = "freebsd",
375        target_os = "fuchsia",
376        target_os = "linux",
377        target_vendor = "apple"
378    ))]
379    #[pyattr]
380    use c::TCP_FASTOPEN;
381
382    #[cfg(any(
383        target_os = "android",
384        target_os = "freebsd",
385        target_os = "fuchsia",
386        all(
387            target_os = "linux",
388            any(
389                target_arch = "aarch64",
390                target_arch = "x86",
391                target_arch = "loongarch64",
392                target_arch = "mips",
393                target_arch = "powerpc",
394                target_arch = "powerpc64",
395                target_arch = "riscv64",
396                target_arch = "s390x",
397                target_arch = "x86_64"
398            )
399        ),
400        target_os = "redox"
401    ))]
402    #[pyattr]
403    use c::SO_PROTOCOL;
404
405    #[cfg(any(
406        target_os = "android",
407        target_os = "dragonfly",
408        target_os = "freebsd",
409        target_os = "linux",
410        windows
411    ))]
412    #[pyattr]
413    use c::IPV6_DONTFRAG;
414
415    #[cfg(any(
416        target_os = "android",
417        target_os = "dragonfly",
418        target_os = "fuchsia",
419        target_os = "linux",
420        target_os = "redox"
421    ))]
422    #[pyattr]
423    use c::{SO_PASSCRED, SO_PEERCRED};
424
425    #[cfg(any(
426        target_os = "android",
427        target_os = "freebsd",
428        target_os = "fuchsia",
429        target_os = "linux",
430        target_os = "netbsd"
431    ))]
432    #[pyattr]
433    use c::TCP_INFO;
434
435    #[cfg(any(
436        target_os = "android",
437        target_os = "freebsd",
438        target_os = "fuchsia",
439        target_os = "linux",
440        target_vendor = "apple"
441    ))]
442    #[pyattr]
443    use c::IP_RECVTOS;
444
445    #[cfg(any(
446        target_os = "android",
447        target_os = "netbsd",
448        target_os = "redox",
449        target_vendor = "apple",
450        windows
451    ))]
452    #[pyattr]
453    use c::NI_MAXSERV;
454
455    #[cfg(any(
456        target_os = "dragonfly",
457        target_os = "freebsd",
458        target_os = "netbsd",
459        target_os = "openbsd",
460        target_vendor = "apple"
461    ))]
462    #[pyattr]
463    use c::{IPPROTO_EON, IPPROTO_IPCOMP};
464
465    #[cfg(any(
466        target_os = "dragonfly",
467        target_os = "freebsd",
468        target_os = "netbsd",
469        target_vendor = "apple",
470        windows
471    ))]
472    #[pyattr]
473    use c::IPPROTO_ND;
474
475    #[cfg(any(
476        target_os = "android",
477        target_os = "dragonfly",
478        target_os = "freebsd",
479        target_os = "linux",
480        target_vendor = "apple",
481        windows
482    ))]
483    #[pyattr]
484    use c::{IPV6_CHECKSUM, IPV6_HOPLIMIT};
485
486    #[cfg(any(
487        target_os = "android",
488        target_os = "freebsd",
489        target_os = "fuchsia",
490        target_os = "linux",
491        target_os = "netbsd"
492    ))]
493    #[pyattr]
494    use c::IPPROTO_SCTP; // also in windows
495
496    #[cfg(any(
497        target_os = "android",
498        target_os = "freebsd",
499        target_os = "fuchsia",
500        target_os = "linux",
501        target_vendor = "apple",
502        windows
503    ))]
504    #[pyattr]
505    use c::{AI_ALL, AI_V4MAPPED};
506
507    #[cfg(any(
508        target_os = "android",
509        target_os = "linux",
510        target_os = "netbsd",
511        target_os = "openbsd",
512        target_vendor = "apple",
513        windows
514    ))]
515    #[pyattr]
516    use c::EAI_NODATA;
517
518    #[cfg(any(
519        target_os = "dragonfly",
520        target_os = "freebsd",
521        target_os = "netbsd",
522        target_os = "openbsd",
523        target_vendor = "apple",
524        windows
525    ))]
526    #[pyattr]
527    use c::{
528        AF_LINK, IPPROTO_GGP, IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, IP_RECVDSTADDR, SO_USELOOPBACK,
529    };
530
531    #[cfg(any(
532        target_os = "android",
533        target_os = "dragonfly",
534        target_os = "freebsd",
535        target_os = "fuchsia",
536        target_os = "linux",
537        target_os = "netbsd",
538        target_os = "openbsd"
539    ))]
540    #[pyattr]
541    use c::{MSG_CMSG_CLOEXEC, MSG_NOSIGNAL};
542
543    #[cfg(any(
544        target_os = "android",
545        target_os = "dragonfly",
546        target_os = "freebsd",
547        target_os = "fuchsia",
548        target_os = "linux",
549        target_os = "netbsd",
550        target_os = "redox"
551    ))]
552    #[pyattr]
553    use c::TCP_KEEPIDLE;
554
555    #[cfg(any(
556        target_os = "android",
557        target_os = "dragonfly",
558        target_os = "freebsd",
559        target_os = "fuchsia",
560        target_os = "linux",
561        target_os = "netbsd",
562        target_vendor = "apple"
563    ))]
564    #[pyattr]
565    use c::{TCP_KEEPCNT, TCP_KEEPINTVL};
566
567    #[cfg(any(
568        target_os = "android",
569        target_os = "dragonfly",
570        target_os = "freebsd",
571        target_os = "fuchsia",
572        target_os = "linux",
573        target_os = "netbsd",
574        target_os = "openbsd",
575        target_os = "redox"
576    ))]
577    #[pyattr]
578    use c::{SOCK_CLOEXEC, SOCK_NONBLOCK};
579
580    #[cfg(any(
581        target_os = "android",
582        target_os = "dragonfly",
583        target_os = "freebsd",
584        target_os = "fuchsia",
585        target_os = "linux",
586        target_os = "netbsd",
587        target_os = "openbsd",
588        target_vendor = "apple"
589    ))]
590    #[pyattr]
591    use c::{
592        AF_ROUTE, AF_SNA, EAI_OVERFLOW, IPPROTO_GRE, IPPROTO_RSVP, IPPROTO_TP, IPV6_RECVPKTINFO,
593        MSG_DONTWAIT, SCM_RIGHTS, TCP_MAXSEG,
594    };
595
596    #[cfg(any(
597        target_os = "android",
598        target_os = "dragonfly",
599        target_os = "freebsd",
600        target_os = "linux",
601        target_os = "netbsd",
602        target_os = "openbsd",
603        target_vendor = "apple",
604        windows
605    ))]
606    #[pyattr]
607    use c::IPV6_PKTINFO;
608
609    #[cfg(any(
610        target_os = "android",
611        target_os = "freebsd",
612        target_os = "fuchsia",
613        target_os = "linux",
614        target_os = "netbsd",
615        target_os = "openbsd",
616        target_vendor = "apple",
617        windows
618    ))]
619    #[pyattr]
620    use c::AI_CANONNAME;
621
622    #[cfg(any(
623        target_os = "android",
624        target_os = "dragonfly",
625        target_os = "freebsd",
626        target_os = "fuchsia",
627        target_os = "linux",
628        target_os = "netbsd",
629        target_os = "openbsd",
630        target_vendor = "apple",
631        windows
632    ))]
633    #[pyattr]
634    use c::{
635        EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NONAME, EAI_SERVICE,
636        EAI_SOCKTYPE, IPV6_RECVTCLASS, IPV6_TCLASS, IP_HDRINCL, IP_TOS, SOMAXCONN,
637    };
638
639    #[cfg(not(any(
640        target_os = "android",
641        target_os = "dragonfly",
642        target_os = "freebsd",
643        target_os = "fuchsia",
644        target_os = "linux",
645        target_os = "netbsd",
646        target_os = "openbsd",
647        target_vendor = "apple",
648        windows
649    )))]
650    #[pyattr]
651    const SOMAXCONN: i32 = 5; // Common value
652
653    // HERE IS WHERE THE BLUETOOTH CONSTANTS START
654    // TODO: there should be a more intelligent way of detecting bluetooth on a platform.
655    //       CPython uses header-detection, but blocks NetBSD and DragonFly BSD
656    #[cfg(any(
657        target_os = "android",
658        target_os = "freebsd",
659        target_os = "fuchsia",
660        target_os = "linux",
661        target_os = "openbsd"
662    ))]
663    #[pyattr]
664    use c::AF_BLUETOOTH;
665
666    #[cfg(any(
667        target_os = "android",
668        target_os = "freebsd",
669        target_os = "fuchsia",
670        target_os = "linux",
671        target_os = "openbsd"
672    ))]
673    #[pyattr]
674    const BDADDR_ANY: &str = "00:00:00:00:00:00";
675    #[cfg(any(
676        target_os = "android",
677        target_os = "freebsd",
678        target_os = "fuchsia",
679        target_os = "linux",
680        target_os = "openbsd"
681    ))]
682    #[pyattr]
683    const BDADDR_LOCAL: &str = "00:00:00:FF:FF:FF";
684    // HERE IS WHERE THE BLUETOOTH CONSTANTS END
685
686    #[cfg(windows)]
687    #[pyattr]
688    use windows_sys::Win32::Networking::WinSock::{
689        IPPROTO_CBT, IPPROTO_ICLFXBM, IPPROTO_IGP, IPPROTO_L2TP, IPPROTO_PGM, IPPROTO_RDP,
690        IPPROTO_SCTP, IPPROTO_ST,
691    };
692
693    #[pyattr]
694    fn error(vm: &VirtualMachine) -> PyTypeRef {
695        vm.ctx.exceptions.os_error.to_owned()
696    }
697
698    #[pyattr]
699    fn timeout(vm: &VirtualMachine) -> PyTypeRef {
700        vm.ctx.exceptions.timeout_error.to_owned()
701    }
702
703    #[pyattr(once)]
704    fn herror(vm: &VirtualMachine) -> PyTypeRef {
705        vm.ctx.new_exception_type(
706            "socket",
707            "herror",
708            Some(vec![vm.ctx.exceptions.os_error.to_owned()]),
709        )
710    }
711    #[pyattr(once)]
712    fn gaierror(vm: &VirtualMachine) -> PyTypeRef {
713        vm.ctx.new_exception_type(
714            "socket",
715            "gaierror",
716            Some(vec![vm.ctx.exceptions.os_error.to_owned()]),
717        )
718    }
719
720    #[pyfunction]
721    fn htonl(x: u32) -> u32 {
722        u32::to_be(x)
723    }
724    #[pyfunction]
725    fn htons(x: u16) -> u16 {
726        u16::to_be(x)
727    }
728    #[pyfunction]
729    fn ntohl(x: u32) -> u32 {
730        u32::from_be(x)
731    }
732    #[pyfunction]
733    fn ntohs(x: u16) -> u16 {
734        u16::from_be(x)
735    }
736
737    #[cfg(unix)]
738    type RawSocket = std::os::unix::io::RawFd;
739    #[cfg(windows)]
740    type RawSocket = std::os::windows::raw::SOCKET;
741
742    #[cfg(unix)]
743    macro_rules! errcode {
744        ($e:ident) => {
745            c::$e
746        };
747    }
748    #[cfg(windows)]
749    macro_rules! errcode {
750    ($e:ident) => {
751        paste::paste!(c::[<WSA $e>])
752    };
753}
754
755    #[cfg(windows)]
756    use winapi::shared::netioapi;
757
758    fn get_raw_sock(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<RawSocket> {
759        #[cfg(unix)]
760        type CastFrom = libc::c_long;
761        #[cfg(windows)]
762        type CastFrom = libc::c_longlong;
763
764        // should really just be to_index() but test_socket tests the error messages explicitly
765        if obj.fast_isinstance(vm.ctx.types.float_type) {
766            return Err(vm.new_type_error("integer argument expected, got float".to_owned()));
767        }
768        let int = obj
769            .try_index_opt(vm)
770            .unwrap_or_else(|| Err(vm.new_type_error("an integer is required".to_owned())))?;
771        int.try_to_primitive::<CastFrom>(vm)
772            .map(|sock| sock as RawSocket)
773    }
774
775    #[pyattr(name = "socket")]
776    #[pyattr(name = "SocketType")]
777    #[pyclass(name = "socket")]
778    #[derive(Debug, PyPayload)]
779    pub struct PySocket {
780        kind: AtomicCell<i32>,
781        family: AtomicCell<i32>,
782        proto: AtomicCell<i32>,
783        pub(crate) timeout: AtomicCell<f64>,
784        sock: PyRwLock<Option<Socket>>,
785    }
786
787    const _: () = assert!(std::mem::size_of::<Option<Socket>>() == std::mem::size_of::<Socket>());
788
789    impl Default for PySocket {
790        fn default() -> Self {
791            PySocket {
792                kind: AtomicCell::default(),
793                family: AtomicCell::default(),
794                proto: AtomicCell::default(),
795                timeout: AtomicCell::new(-1.0),
796                sock: PyRwLock::new(None),
797            }
798        }
799    }
800
801    #[cfg(windows)]
802    const CLOSED_ERR: i32 = c::WSAENOTSOCK;
803    #[cfg(unix)]
804    const CLOSED_ERR: i32 = c::EBADF;
805
806    impl Read for &PySocket {
807        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
808            (&mut &*self.sock()?).read(buf)
809        }
810    }
811    impl Write for &PySocket {
812        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
813            (&mut &*self.sock()?).write(buf)
814        }
815
816        fn flush(&mut self) -> std::io::Result<()> {
817            (&mut &*self.sock()?).flush()
818        }
819    }
820
821    impl PySocket {
822        pub fn sock_opt(&self) -> Option<PyMappedRwLockReadGuard<'_, Socket>> {
823            let sock = PyRwLockReadGuard::try_map(self.sock.read(), |sock| sock.as_ref());
824            sock.ok()
825        }
826
827        pub fn sock(&self) -> io::Result<PyMappedRwLockReadGuard<'_, Socket>> {
828            self.sock_opt()
829                .ok_or_else(|| io::Error::from_raw_os_error(CLOSED_ERR))
830        }
831
832        fn init_inner(
833            &self,
834            family: i32,
835            socket_kind: i32,
836            proto: i32,
837            sock: Socket,
838        ) -> io::Result<()> {
839            self.family.store(family);
840            self.kind.store(socket_kind);
841            self.proto.store(proto);
842            let mut s = self.sock.write();
843            let sock = s.insert(sock);
844            let timeout = DEFAULT_TIMEOUT.load();
845            self.timeout.store(timeout);
846            if timeout >= 0.0 {
847                sock.set_nonblocking(true)?;
848            }
849            Ok(())
850        }
851
852        /// returns Err(blocking)
853        pub fn get_timeout(&self) -> Result<Duration, bool> {
854            let timeout = self.timeout.load();
855            if timeout > 0.0 {
856                Ok(Duration::from_secs_f64(timeout))
857            } else {
858                Err(timeout != 0.0)
859            }
860        }
861
862        fn sock_op<F, R>(
863            &self,
864            vm: &VirtualMachine,
865            select: SelectKind,
866            f: F,
867        ) -> Result<R, IoOrPyException>
868        where
869            F: FnMut() -> io::Result<R>,
870        {
871            let timeout = self.get_timeout().ok();
872            self.sock_op_timeout_err(vm, select, timeout, f)
873        }
874
875        fn sock_op_timeout_err<F, R>(
876            &self,
877            vm: &VirtualMachine,
878            select: SelectKind,
879            timeout: Option<Duration>,
880            mut f: F,
881        ) -> Result<R, IoOrPyException>
882        where
883            F: FnMut() -> io::Result<R>,
884        {
885            let deadline = timeout.map(Deadline::new);
886
887            loop {
888                if deadline.is_some() || matches!(select, SelectKind::Connect) {
889                    let interval = deadline.as_ref().map(|d| d.time_until()).transpose()?;
890                    let res = sock_select(&*self.sock()?, select, interval);
891                    match res {
892                        Ok(true) => return Err(IoOrPyException::Timeout),
893                        Err(e) if e.kind() == io::ErrorKind::Interrupted => {
894                            vm.check_signals()?;
895                            continue;
896                        }
897                        Err(e) => return Err(e.into()),
898                        Ok(false) => {} // no timeout, continue as normal
899                    }
900                }
901
902                let err = loop {
903                    // loop on interrupt
904                    match f() {
905                        Ok(x) => return Ok(x),
906                        Err(e) if e.kind() == io::ErrorKind::Interrupted => vm.check_signals()?,
907                        Err(e) => break e,
908                    }
909                };
910                if timeout.is_some() && err.kind() == io::ErrorKind::WouldBlock {
911                    continue;
912                }
913                return Err(err.into());
914            }
915        }
916
917        fn extract_address(
918            &self,
919            addr: PyObjectRef,
920            caller: &str,
921            vm: &VirtualMachine,
922        ) -> Result<socket2::SockAddr, IoOrPyException> {
923            let family = self.family.load();
924            match family {
925                #[cfg(unix)]
926                c::AF_UNIX => {
927                    use std::os::unix::ffi::OsStrExt;
928                    let buf = crate::vm::function::ArgStrOrBytesLike::try_from_object(vm, addr)?;
929                    let path = &*buf.borrow_bytes();
930                    socket2::SockAddr::unix(ffi::OsStr::from_bytes(path))
931                        .map_err(|_| vm.new_os_error("AF_UNIX path too long".to_owned()).into())
932                }
933                c::AF_INET => {
934                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
935                        vm.new_type_error(format!(
936                            "{}(): AF_INET address must be tuple, not {}",
937                            caller,
938                            obj.class().name()
939                        ))
940                    })?;
941                    if tuple.len() != 2 {
942                        return Err(vm
943                            .new_type_error(
944                                "AF_INET address must be a pair (host, post)".to_owned(),
945                            )
946                            .into());
947                    }
948                    let addr = Address::from_tuple(&tuple, vm)?;
949                    let mut addr4 = get_addr(vm, addr.host, c::AF_INET)?;
950                    match &mut addr4 {
951                        SocketAddr::V4(addr4) => {
952                            addr4.set_port(addr.port);
953                        }
954                        SocketAddr::V6(_) => unreachable!(),
955                    }
956                    Ok(addr4.into())
957                }
958                c::AF_INET6 => {
959                    let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
960                        vm.new_type_error(format!(
961                            "{}(): AF_INET6 address must be tuple, not {}",
962                            caller,
963                            obj.class().name()
964                        ))
965                    })?;
966                    match tuple.len() {
967                        2..=4 => {}
968                        _ => return Err(vm.new_type_error(
969                            "AF_INET6 address must be a tuple (host, port[, flowinfo[, scopeid]])"
970                                .to_owned(),
971                        ).into()),
972                    }
973                    let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&tuple, vm)?;
974                    let mut addr6 = get_addr(vm, addr.host, c::AF_INET6)?;
975                    match &mut addr6 {
976                        SocketAddr::V6(addr6) => {
977                            addr6.set_port(addr.port);
978                            addr6.set_flowinfo(flowinfo);
979                            addr6.set_scope_id(scopeid);
980                        }
981                        SocketAddr::V4(_) => unreachable!(),
982                    }
983                    Ok(addr6.into())
984                }
985                _ => Err(vm.new_os_error(format!("{caller}(): bad family")).into()),
986            }
987        }
988
989        fn connect_inner(
990            &self,
991            address: PyObjectRef,
992            caller: &str,
993            vm: &VirtualMachine,
994        ) -> Result<(), IoOrPyException> {
995            let sock_addr = self.extract_address(address, caller, vm)?;
996
997            let err = match self.sock()?.connect(&sock_addr) {
998                Ok(()) => return Ok(()),
999                Err(e) => e,
1000            };
1001
1002            let wait_connect = if err.kind() == io::ErrorKind::Interrupted {
1003                vm.check_signals()?;
1004                self.timeout.load() != 0.0
1005            } else {
1006                #[cfg(unix)]
1007                use c::EINPROGRESS;
1008                #[cfg(windows)]
1009                use c::WSAEWOULDBLOCK as EINPROGRESS;
1010
1011                self.timeout.load() > 0.0 && err.raw_os_error() == Some(EINPROGRESS)
1012            };
1013
1014            if wait_connect {
1015                // basically, connect() is async, and it registers an "error" on the socket when it's
1016                // done connecting. SelectKind::Connect fills the errorfds fd_set, so if we wake up
1017                // from poll and the error is EISCONN then we know that the connect is done
1018                self.sock_op(vm, SelectKind::Connect, || {
1019                    let sock = self.sock()?;
1020                    let err = sock.take_error()?;
1021                    match err {
1022                        Some(e) if e.posix_errno() == libc::EISCONN => Ok(()),
1023                        Some(e) => Err(e),
1024                        // TODO: is this accurate?
1025                        None => Ok(()),
1026                    }
1027                })
1028            } else {
1029                Err(err.into())
1030            }
1031        }
1032    }
1033
1034    impl DefaultConstructor for PySocket {}
1035
1036    impl Initializer for PySocket {
1037        type Args = (
1038            OptionalArg<i32>,
1039            OptionalArg<i32>,
1040            OptionalArg<i32>,
1041            OptionalOption<PyObjectRef>,
1042        );
1043
1044        fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
1045            Self::_init(zelf, args, vm).map_err(|e| e.into_pyexception(vm))
1046        }
1047    }
1048
1049    impl Representable for PySocket {
1050        #[inline]
1051        fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1052            Ok(format!(
1053                "<socket object, fd={}, family={}, type={}, proto={}>",
1054                // cast because INVALID_SOCKET is unsigned, so would show usize::MAX instead of -1
1055                zelf.fileno() as i64,
1056                zelf.family.load(),
1057                zelf.kind.load(),
1058                zelf.proto.load(),
1059            ))
1060        }
1061    }
1062
1063    #[pyclass(with(Constructor, Initializer, Representable), flags(BASETYPE))]
1064    impl PySocket {
1065        fn _init(
1066            zelf: PyRef<Self>,
1067            (family, socket_kind, proto, fileno): <Self as Initializer>::Args,
1068            vm: &VirtualMachine,
1069        ) -> Result<(), IoOrPyException> {
1070            let mut family = family.unwrap_or(-1);
1071            let mut socket_kind = socket_kind.unwrap_or(-1);
1072            let mut proto = proto.unwrap_or(-1);
1073            let fileno = fileno
1074                .flatten()
1075                .map(|obj| get_raw_sock(obj, vm))
1076                .transpose()?;
1077            let sock;
1078            if let Some(fileno) = fileno {
1079                sock = sock_from_raw(fileno, vm)?;
1080                match sock.local_addr() {
1081                    Ok(addr) if family == -1 => family = addr.family() as i32,
1082                    Err(e)
1083                        if family == -1
1084                            || matches!(
1085                                e.raw_os_error(),
1086                                Some(errcode!(ENOTSOCK)) | Some(errcode!(EBADF))
1087                            ) =>
1088                    {
1089                        std::mem::forget(sock);
1090                        return Err(e.into());
1091                    }
1092                    _ => {}
1093                }
1094                if socket_kind == -1 {
1095                    socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into();
1096                }
1097                cfg_if::cfg_if! {
1098                    if #[cfg(any(
1099                        target_os = "android",
1100                        target_os = "freebsd",
1101                        target_os = "fuchsia",
1102                        target_os = "linux",
1103                    ))] {
1104                        if proto == -1 {
1105                            proto = sock.protocol()?.map_or(0, Into::into);
1106                        }
1107                    } else {
1108                        proto = 0;
1109                    }
1110                }
1111            } else {
1112                if family == -1 {
1113                    family = c::AF_INET as _
1114                }
1115                if socket_kind == -1 {
1116                    socket_kind = c::SOCK_STREAM
1117                }
1118                if proto == -1 {
1119                    proto = 0
1120                }
1121                sock = Socket::new(family.into(), socket_kind.into(), Some(proto.into()))?;
1122            };
1123            Ok(zelf.init_inner(family, socket_kind, proto, sock)?)
1124        }
1125
1126        #[pymethod]
1127        fn connect(
1128            &self,
1129            address: PyObjectRef,
1130            vm: &VirtualMachine,
1131        ) -> Result<(), IoOrPyException> {
1132            self.connect_inner(address, "connect", vm)
1133        }
1134
1135        #[pymethod]
1136        fn connect_ex(&self, address: PyObjectRef, vm: &VirtualMachine) -> PyResult<i32> {
1137            match self.connect_inner(address, "connect_ex", vm) {
1138                Ok(()) => Ok(0),
1139                Err(err) => err.errno(),
1140            }
1141        }
1142
1143        #[pymethod]
1144        fn bind(&self, address: PyObjectRef, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
1145            let sock_addr = self.extract_address(address, "bind", vm)?;
1146            Ok(self.sock()?.bind(&sock_addr)?)
1147        }
1148
1149        #[pymethod]
1150        fn listen(&self, backlog: OptionalArg<i32>) -> io::Result<()> {
1151            let backlog = backlog.unwrap_or(128);
1152            let backlog = if backlog < 0 { 0 } else { backlog };
1153            self.sock()?.listen(backlog)
1154        }
1155
1156        #[pymethod]
1157        fn _accept(
1158            &self,
1159            vm: &VirtualMachine,
1160        ) -> Result<(RawSocket, PyObjectRef), IoOrPyException> {
1161            let (sock, addr) = self.sock_op(vm, SelectKind::Read, || self.sock()?.accept())?;
1162            let fd = into_sock_fileno(sock);
1163            Ok((fd, get_addr_tuple(&addr, vm)))
1164        }
1165
1166        #[pymethod]
1167        fn recv(
1168            &self,
1169            bufsize: usize,
1170            flags: OptionalArg<i32>,
1171            vm: &VirtualMachine,
1172        ) -> Result<Vec<u8>, IoOrPyException> {
1173            let flags = flags.unwrap_or(0);
1174            let mut buffer = Vec::with_capacity(bufsize);
1175            let sock = self.sock()?;
1176            let n = self.sock_op(vm, SelectKind::Read, || {
1177                sock.recv_with_flags(buffer.spare_capacity_mut(), flags)
1178            })?;
1179            unsafe { buffer.set_len(n) };
1180            Ok(buffer)
1181        }
1182
1183        #[pymethod]
1184        fn recv_into(
1185            &self,
1186            buf: ArgMemoryBuffer,
1187            flags: OptionalArg<i32>,
1188            vm: &VirtualMachine,
1189        ) -> Result<usize, IoOrPyException> {
1190            let flags = flags.unwrap_or(0);
1191            let sock = self.sock()?;
1192            let mut buf = buf.borrow_buf_mut();
1193            let buf = &mut *buf;
1194            self.sock_op(vm, SelectKind::Read, || {
1195                sock.recv_with_flags(unsafe { slice_as_uninit(buf) }, flags)
1196            })
1197        }
1198
1199        #[pymethod]
1200        fn recvfrom(
1201            &self,
1202            bufsize: isize,
1203            flags: OptionalArg<i32>,
1204            vm: &VirtualMachine,
1205        ) -> Result<(Vec<u8>, PyObjectRef), IoOrPyException> {
1206            let flags = flags.unwrap_or(0);
1207            let bufsize = bufsize
1208                .to_usize()
1209                .ok_or_else(|| vm.new_value_error("negative buffersize in recvfrom".to_owned()))?;
1210            let mut buffer = Vec::with_capacity(bufsize);
1211            let (n, addr) = self.sock_op(vm, SelectKind::Read, || {
1212                self.sock()?
1213                    .recv_from_with_flags(buffer.spare_capacity_mut(), flags)
1214            })?;
1215            unsafe { buffer.set_len(n) };
1216            Ok((buffer, get_addr_tuple(&addr, vm)))
1217        }
1218
1219        #[pymethod]
1220        fn recvfrom_into(
1221            &self,
1222            buf: ArgMemoryBuffer,
1223            nbytes: OptionalArg<isize>,
1224            flags: OptionalArg<i32>,
1225            vm: &VirtualMachine,
1226        ) -> Result<(usize, PyObjectRef), IoOrPyException> {
1227            let mut buf = buf.borrow_buf_mut();
1228            let buf = &mut *buf;
1229            let buf = match nbytes {
1230                OptionalArg::Present(i) => {
1231                    let i = i.to_usize().ok_or_else(|| {
1232                        vm.new_value_error("negative buffersize in recvfrom_into".to_owned())
1233                    })?;
1234                    buf.get_mut(..i).ok_or_else(|| {
1235                        vm.new_value_error(
1236                            "nbytes is greater than the length of the buffer".to_owned(),
1237                        )
1238                    })?
1239                }
1240                OptionalArg::Missing => buf,
1241            };
1242            let flags = flags.unwrap_or(0);
1243            let sock = self.sock()?;
1244            let (n, addr) = self.sock_op(vm, SelectKind::Read, || {
1245                sock.recv_from_with_flags(unsafe { slice_as_uninit(buf) }, flags)
1246            })?;
1247            Ok((n, get_addr_tuple(&addr, vm)))
1248        }
1249
1250        #[pymethod]
1251        fn send(
1252            &self,
1253            bytes: ArgBytesLike,
1254            flags: OptionalArg<i32>,
1255            vm: &VirtualMachine,
1256        ) -> Result<usize, IoOrPyException> {
1257            let flags = flags.unwrap_or(0);
1258            let buf = bytes.borrow_buf();
1259            let buf = &*buf;
1260            self.sock_op(vm, SelectKind::Write, || {
1261                self.sock()?.send_with_flags(buf, flags)
1262            })
1263        }
1264
1265        #[pymethod]
1266        fn sendall(
1267            &self,
1268            bytes: ArgBytesLike,
1269            flags: OptionalArg<i32>,
1270            vm: &VirtualMachine,
1271        ) -> Result<(), IoOrPyException> {
1272            let flags = flags.unwrap_or(0);
1273
1274            let timeout = self.get_timeout().ok();
1275
1276            let deadline = timeout.map(Deadline::new);
1277
1278            let buf = bytes.borrow_buf();
1279            let buf = &*buf;
1280            let mut buf_offset = 0;
1281            // now we have like 3 layers of interrupt loop :)
1282            while buf_offset < buf.len() {
1283                let interval = deadline.as_ref().map(|d| d.time_until()).transpose()?;
1284                self.sock_op_timeout_err(vm, SelectKind::Write, interval, || {
1285                    let subbuf = &buf[buf_offset..];
1286                    buf_offset += self.sock()?.send_with_flags(subbuf, flags)?;
1287                    Ok(())
1288                })?;
1289                vm.check_signals()?;
1290            }
1291            Ok(())
1292        }
1293
1294        #[pymethod]
1295        fn sendto(
1296            &self,
1297            bytes: ArgBytesLike,
1298            arg2: PyObjectRef,
1299            arg3: OptionalArg<PyObjectRef>,
1300            vm: &VirtualMachine,
1301        ) -> Result<usize, IoOrPyException> {
1302            // signature is bytes[, flags], address
1303            let (flags, address) = match arg3 {
1304                OptionalArg::Present(arg3) => {
1305                    // should just be i32::try_from_obj but tests check for error message
1306                    let int = arg2.try_index_opt(vm).unwrap_or_else(|| {
1307                        Err(vm.new_type_error("an integer is required".to_owned()))
1308                    })?;
1309                    let flags = int.try_to_primitive::<i32>(vm)?;
1310                    (flags, arg3)
1311                }
1312                OptionalArg::Missing => (0, arg2),
1313            };
1314            let addr = self.extract_address(address, "sendto", vm)?;
1315            let buf = bytes.borrow_buf();
1316            let buf = &*buf;
1317            self.sock_op(vm, SelectKind::Write, || {
1318                self.sock()?.send_to_with_flags(buf, &addr, flags)
1319            })
1320        }
1321
1322        #[cfg(all(unix, not(target_os = "redox")))]
1323        #[pymethod]
1324        fn sendmsg(
1325            &self,
1326            buffers: Vec<ArgBytesLike>,
1327            ancdata: OptionalArg,
1328            flags: OptionalArg<i32>,
1329            addr: OptionalOption,
1330            vm: &VirtualMachine,
1331        ) -> PyResult<usize> {
1332            let flags = flags.unwrap_or(0);
1333            let mut msg = socket2::MsgHdr::new();
1334
1335            let sockaddr;
1336            if let Some(addr) = addr.flatten() {
1337                sockaddr = self
1338                    .extract_address(addr, "sendmsg", vm)
1339                    .map_err(|e| e.into_pyexception(vm))?;
1340                msg = msg.with_addr(&sockaddr);
1341            }
1342
1343            let buffers = buffers
1344                .iter()
1345                .map(|buf| buf.borrow_buf())
1346                .collect::<Vec<_>>();
1347            let buffers = buffers
1348                .iter()
1349                .map(|buf| io::IoSlice::new(buf))
1350                .collect::<Vec<_>>();
1351            msg = msg.with_buffers(&buffers);
1352
1353            let control_buf;
1354            if let OptionalArg::Present(ancdata) = ancdata {
1355                let cmsgs = vm.extract_elements_with(
1356                    &ancdata,
1357                    |obj| -> PyResult<(i32, i32, ArgBytesLike)> {
1358                        let seq: Vec<PyObjectRef> = obj.try_into_value(vm)?;
1359                        let [lvl, typ, data]: [PyObjectRef; 3] = seq.try_into().map_err(|_| {
1360                            vm.new_type_error("expected a sequence of length 3".to_owned())
1361                        })?;
1362                        Ok((
1363                            lvl.try_into_value(vm)?,
1364                            typ.try_into_value(vm)?,
1365                            data.try_into_value(vm)?,
1366                        ))
1367                    },
1368                )?;
1369                control_buf = Self::pack_cmsgs_to_send(&cmsgs, vm)?;
1370                if !control_buf.is_empty() {
1371                    msg = msg.with_control(&control_buf);
1372                }
1373            }
1374
1375            self.sock_op(vm, SelectKind::Write, || {
1376                let sock = self.sock()?;
1377                sock.sendmsg(&msg, flags)
1378            })
1379            .map_err(|e| e.into_pyexception(vm))
1380        }
1381
1382        // based on nix's implementation
1383        #[cfg(all(unix, not(target_os = "redox")))]
1384        fn pack_cmsgs_to_send(
1385            cmsgs: &[(i32, i32, ArgBytesLike)],
1386            vm: &VirtualMachine,
1387        ) -> PyResult<Vec<u8>> {
1388            use std::{mem, ptr};
1389
1390            if cmsgs.is_empty() {
1391                return Ok(vec![]);
1392            }
1393
1394            let capacity = cmsgs
1395                .iter()
1396                .map(|(_, _, buf)| buf.len())
1397                .try_fold(0, |sum, len| {
1398                    let space = checked_cmsg_space(len).ok_or_else(|| {
1399                        vm.new_os_error("ancillary data item too large".to_owned())
1400                    })?;
1401                    usize::checked_add(sum, space)
1402                        .ok_or_else(|| vm.new_os_error("too much ancillary data".to_owned()))
1403                })?;
1404
1405            let mut cmsg_buffer = vec![0u8; capacity];
1406
1407            // make a dummy msghdr so we can use the CMSG_* apis
1408            let mut mhdr = unsafe { mem::zeroed::<libc::msghdr>() };
1409            mhdr.msg_control = cmsg_buffer.as_mut_ptr().cast();
1410            mhdr.msg_controllen = capacity as _;
1411
1412            let mut pmhdr: *mut libc::cmsghdr = unsafe { libc::CMSG_FIRSTHDR(&mhdr) };
1413            for (lvl, typ, buf) in cmsgs {
1414                if pmhdr.is_null() {
1415                    return Err(vm.new_runtime_error(
1416                        "unexpected NULL result from CMSG_FIRSTHDR/CMSG_NXTHDR".to_owned(),
1417                    ));
1418                }
1419                let data = &*buf.borrow_buf();
1420                assert_eq!(data.len(), buf.len());
1421                // Safe because we know that pmhdr is valid, and we initialized it with
1422                // sufficient space
1423                unsafe {
1424                    (*pmhdr).cmsg_level = *lvl;
1425                    (*pmhdr).cmsg_type = *typ;
1426                    (*pmhdr).cmsg_len = data.len() as _;
1427                    ptr::copy_nonoverlapping(data.as_ptr(), libc::CMSG_DATA(pmhdr), data.len());
1428                }
1429
1430                // Safe because mhdr is valid
1431                pmhdr = unsafe { libc::CMSG_NXTHDR(&mhdr, pmhdr) };
1432            }
1433
1434            Ok(cmsg_buffer)
1435        }
1436
1437        #[pymethod]
1438        fn close(&self) -> io::Result<()> {
1439            let sock = self.detach();
1440            if sock != INVALID_SOCKET {
1441                close_inner(sock)?;
1442            }
1443            Ok(())
1444        }
1445        #[pymethod]
1446        #[inline]
1447        fn detach(&self) -> RawSocket {
1448            let sock = self.sock.write().take();
1449            sock.map_or(INVALID_SOCKET, into_sock_fileno)
1450        }
1451
1452        #[pymethod]
1453        fn fileno(&self) -> RawSocket {
1454            self.sock
1455                .read()
1456                .as_ref()
1457                .map_or(INVALID_SOCKET, sock_fileno)
1458        }
1459
1460        #[pymethod]
1461        fn getsockname(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
1462            let addr = self.sock()?.local_addr()?;
1463
1464            Ok(get_addr_tuple(&addr, vm))
1465        }
1466        #[pymethod]
1467        fn getpeername(&self, vm: &VirtualMachine) -> std::io::Result<PyObjectRef> {
1468            let addr = self.sock()?.peer_addr()?;
1469
1470            Ok(get_addr_tuple(&addr, vm))
1471        }
1472
1473        #[pymethod]
1474        fn gettimeout(&self) -> Option<f64> {
1475            let timeout = self.timeout.load();
1476            if timeout >= 0.0 {
1477                Some(timeout)
1478            } else {
1479                None
1480            }
1481        }
1482
1483        #[pymethod]
1484        fn setblocking(&self, block: bool) -> io::Result<()> {
1485            self.timeout.store(if block { -1.0 } else { 0.0 });
1486            self.sock()?.set_nonblocking(!block)
1487        }
1488
1489        #[pymethod]
1490        fn getblocking(&self) -> bool {
1491            self.timeout.load() != 0.0
1492        }
1493
1494        #[pymethod]
1495        fn settimeout(&self, timeout: Option<Duration>) -> io::Result<()> {
1496            self.timeout
1497                .store(timeout.map_or(-1.0, |d| d.as_secs_f64()));
1498            // even if timeout is > 0 the socket needs to be nonblocking in order for us to select() on
1499            // it
1500            self.sock()?.set_nonblocking(timeout.is_some())
1501        }
1502
1503        #[pymethod]
1504        fn getsockopt(
1505            &self,
1506            level: i32,
1507            name: i32,
1508            buflen: OptionalArg<i32>,
1509            vm: &VirtualMachine,
1510        ) -> Result<PyObjectRef, IoOrPyException> {
1511            let sock = self.sock()?;
1512            let fd = sock_fileno(&sock);
1513            let buflen = buflen.unwrap_or(0);
1514            if buflen == 0 {
1515                let mut flag: libc::c_int = 0;
1516                let mut flagsize = std::mem::size_of::<libc::c_int>() as _;
1517                let ret = unsafe {
1518                    c::getsockopt(
1519                        fd as _,
1520                        level,
1521                        name,
1522                        &mut flag as *mut libc::c_int as *mut _,
1523                        &mut flagsize,
1524                    )
1525                };
1526                if ret < 0 {
1527                    return Err(crate::common::os::last_os_error().into());
1528                }
1529                Ok(vm.ctx.new_int(flag).into())
1530            } else {
1531                if buflen <= 0 || buflen > 1024 {
1532                    return Err(vm
1533                        .new_os_error("getsockopt buflen out of range".to_owned())
1534                        .into());
1535                }
1536                let mut buf = vec![0u8; buflen as usize];
1537                let mut buflen = buflen as _;
1538                let ret = unsafe {
1539                    c::getsockopt(
1540                        fd as _,
1541                        level,
1542                        name,
1543                        buf.as_mut_ptr() as *mut _,
1544                        &mut buflen,
1545                    )
1546                };
1547                if ret < 0 {
1548                    return Err(crate::common::os::last_os_error().into());
1549                }
1550                buf.truncate(buflen as usize);
1551                Ok(vm.ctx.new_bytes(buf).into())
1552            }
1553        }
1554
1555        #[pymethod]
1556        fn setsockopt(
1557            &self,
1558            level: i32,
1559            name: i32,
1560            value: Option<Either<ArgBytesLike, i32>>,
1561            optlen: OptionalArg<u32>,
1562            vm: &VirtualMachine,
1563        ) -> Result<(), IoOrPyException> {
1564            let sock = self.sock()?;
1565            let fd = sock_fileno(&sock);
1566            let ret = match (value, optlen) {
1567                (Some(Either::A(b)), OptionalArg::Missing) => b.with_ref(|b| unsafe {
1568                    c::setsockopt(fd as _, level, name, b.as_ptr() as *const _, b.len() as _)
1569                }),
1570                (Some(Either::B(ref val)), OptionalArg::Missing) => unsafe {
1571                    c::setsockopt(
1572                        fd as _,
1573                        level,
1574                        name,
1575                        val as *const i32 as *const _,
1576                        std::mem::size_of::<i32>() as _,
1577                    )
1578                },
1579                (None, OptionalArg::Present(optlen)) => unsafe {
1580                    c::setsockopt(fd as _, level, name, std::ptr::null(), optlen as _)
1581                },
1582                _ => {
1583                    return Err(vm
1584                        .new_type_error("expected the value arg xor the optlen arg".to_owned())
1585                        .into());
1586                }
1587            };
1588            if ret < 0 {
1589                Err(crate::common::os::last_os_error().into())
1590            } else {
1591                Ok(())
1592            }
1593        }
1594
1595        #[pymethod]
1596        fn shutdown(&self, how: i32, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
1597            let how = match how {
1598                c::SHUT_RD => Shutdown::Read,
1599                c::SHUT_WR => Shutdown::Write,
1600                c::SHUT_RDWR => Shutdown::Both,
1601                _ => {
1602                    return Err(vm
1603                        .new_value_error("`how` must be SHUT_RD, SHUT_WR, or SHUT_RDWR".to_owned())
1604                        .into())
1605                }
1606            };
1607            Ok(self.sock()?.shutdown(how)?)
1608        }
1609
1610        #[pygetset(name = "type")]
1611        fn kind(&self) -> i32 {
1612            self.kind.load()
1613        }
1614        #[pygetset]
1615        fn family(&self) -> i32 {
1616            self.family.load()
1617        }
1618        #[pygetset]
1619        fn proto(&self) -> i32 {
1620            self.proto.load()
1621        }
1622    }
1623
1624    struct Address {
1625        host: PyStrRef,
1626        port: u16,
1627    }
1628
1629    impl ToSocketAddrs for Address {
1630        type Iter = std::vec::IntoIter<SocketAddr>;
1631        fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
1632            (self.host.as_str(), self.port).to_socket_addrs()
1633        }
1634    }
1635
1636    impl TryFromObject for Address {
1637        fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1638            let tuple = PyTupleRef::try_from_object(vm, obj)?;
1639            if tuple.len() != 2 {
1640                Err(vm.new_type_error("Address tuple should have only 2 values".to_owned()))
1641            } else {
1642                Self::from_tuple(&tuple, vm)
1643            }
1644        }
1645    }
1646
1647    impl Address {
1648        fn from_tuple(tuple: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<Self> {
1649            let host = PyStrRef::try_from_object(vm, tuple[0].clone())?;
1650            let port = i32::try_from_borrowed_object(vm, &tuple[1])?;
1651            let port = port
1652                .to_u16()
1653                .ok_or_else(|| vm.new_overflow_error("port must be 0-65535.".to_owned()))?;
1654            Ok(Address { host, port })
1655        }
1656        fn from_tuple_ipv6(
1657            tuple: &[PyObjectRef],
1658            vm: &VirtualMachine,
1659        ) -> PyResult<(Self, u32, u32)> {
1660            let addr = Address::from_tuple(tuple, vm)?;
1661            let flowinfo = tuple
1662                .get(2)
1663                .map(|obj| u32::try_from_borrowed_object(vm, obj))
1664                .transpose()?
1665                .unwrap_or(0);
1666            let scopeid = tuple
1667                .get(3)
1668                .map(|obj| u32::try_from_borrowed_object(vm, obj))
1669                .transpose()?
1670                .unwrap_or(0);
1671            if flowinfo > 0xfffff {
1672                return Err(vm.new_overflow_error("flowinfo must be 0-1048575.".to_owned()));
1673            }
1674            Ok((addr, flowinfo, scopeid))
1675        }
1676    }
1677
1678    fn get_ip_addr_tuple(addr: &SocketAddr, vm: &VirtualMachine) -> PyObjectRef {
1679        match addr {
1680            SocketAddr::V4(addr) => (addr.ip().to_string(), addr.port()).to_pyobject(vm),
1681            SocketAddr::V6(addr) => (
1682                addr.ip().to_string(),
1683                addr.port(),
1684                addr.flowinfo(),
1685                addr.scope_id(),
1686            )
1687                .to_pyobject(vm),
1688        }
1689    }
1690
1691    fn get_addr_tuple(addr: &socket2::SockAddr, vm: &VirtualMachine) -> PyObjectRef {
1692        if let Some(addr) = addr.as_socket() {
1693            return get_ip_addr_tuple(&addr, vm);
1694        }
1695        #[cfg(unix)]
1696        if addr.is_unix() {
1697            use std::os::unix::ffi::OsStrExt;
1698            if let Some(abstractpath) = addr.as_abstract_namespace() {
1699                return vm.ctx.new_bytes([b"\0", abstractpath].concat()).into();
1700            }
1701            // necessary on macos
1702            let path = ffi::OsStr::as_bytes(addr.as_pathname().unwrap_or("".as_ref()).as_ref());
1703            let nul_pos = memchr::memchr(b'\0', path).unwrap_or(path.len());
1704            let path = ffi::OsStr::from_bytes(&path[..nul_pos]);
1705            return vm.ctx.new_str(path.to_string_lossy()).into();
1706        }
1707        // TODO: support more address families
1708        (String::new(), 0).to_pyobject(vm)
1709    }
1710
1711    #[pyfunction]
1712    fn gethostname(vm: &VirtualMachine) -> PyResult<PyStrRef> {
1713        gethostname::gethostname()
1714            .into_string()
1715            .map(|hostname| vm.ctx.new_str(hostname))
1716            .map_err(|err| vm.new_os_error(err.into_string().unwrap()))
1717    }
1718
1719    #[cfg(all(unix, not(target_os = "redox")))]
1720    #[pyfunction]
1721    fn sethostname(hostname: PyStrRef) -> nix::Result<()> {
1722        nix::unistd::sethostname(hostname.as_str())
1723    }
1724
1725    #[pyfunction]
1726    fn inet_aton(ip_string: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
1727        ip_string
1728            .as_str()
1729            .parse::<Ipv4Addr>()
1730            .map(|ip_addr| Vec::<u8>::from(ip_addr.octets()))
1731            .map_err(|_| {
1732                vm.new_os_error("illegal IP address string passed to inet_aton".to_owned())
1733            })
1734    }
1735
1736    #[pyfunction]
1737    fn inet_ntoa(packed_ip: ArgBytesLike, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1738        let packed_ip = packed_ip.borrow_buf();
1739        let packed_ip = <&[u8; 4]>::try_from(&*packed_ip)
1740            .map_err(|_| vm.new_os_error("packed IP wrong length for inet_ntoa".to_owned()))?;
1741        Ok(vm.ctx.new_str(Ipv4Addr::from(*packed_ip).to_string()))
1742    }
1743
1744    fn cstr_opt_as_ptr(x: &OptionalArg<ffi::CString>) -> *const libc::c_char {
1745        x.as_ref().map_or_else(std::ptr::null, |s| s.as_ptr())
1746    }
1747
1748    #[pyfunction]
1749    fn getservbyname(
1750        servicename: PyStrRef,
1751        protocolname: OptionalArg<PyStrRef>,
1752        vm: &VirtualMachine,
1753    ) -> PyResult<u16> {
1754        let cstr_name = servicename.to_cstring(vm)?;
1755        let cstr_proto = protocolname
1756            .as_ref()
1757            .map(|s| s.to_cstring(vm))
1758            .transpose()?;
1759        let cstr_proto = cstr_opt_as_ptr(&cstr_proto);
1760        let serv = unsafe { c::getservbyname(cstr_name.as_ptr(), cstr_proto) };
1761        if serv.is_null() {
1762            return Err(vm.new_os_error("service/proto not found".to_owned()));
1763        }
1764        let port = unsafe { (*serv).s_port };
1765        Ok(u16::from_be(port as u16))
1766    }
1767
1768    #[pyfunction]
1769    fn getservbyport(
1770        port: i32,
1771        protocolname: OptionalArg<PyStrRef>,
1772        vm: &VirtualMachine,
1773    ) -> PyResult<String> {
1774        let port = port.to_u16().ok_or_else(|| {
1775            vm.new_overflow_error("getservbyport: port must be 0-65535.".to_owned())
1776        })?;
1777        let cstr_proto = protocolname
1778            .as_ref()
1779            .map(|s| s.to_cstring(vm))
1780            .transpose()?;
1781        let cstr_proto = cstr_opt_as_ptr(&cstr_proto);
1782        let serv = unsafe { c::getservbyport(port.to_be() as _, cstr_proto) };
1783        if serv.is_null() {
1784            return Err(vm.new_os_error("port/proto not found".to_owned()));
1785        }
1786        let s = unsafe { ffi::CStr::from_ptr((*serv).s_name) };
1787        Ok(s.to_string_lossy().into_owned())
1788    }
1789
1790    unsafe fn slice_as_uninit<T>(v: &mut [T]) -> &mut [MaybeUninit<T>] {
1791        &mut *(v as *mut [T] as *mut [MaybeUninit<T>])
1792    }
1793
1794    enum IoOrPyException {
1795        Timeout,
1796        Py(PyBaseExceptionRef),
1797        Io(io::Error),
1798    }
1799    impl From<PyBaseExceptionRef> for IoOrPyException {
1800        fn from(exc: PyBaseExceptionRef) -> Self {
1801            Self::Py(exc)
1802        }
1803    }
1804    impl From<io::Error> for IoOrPyException {
1805        fn from(err: io::Error) -> Self {
1806            Self::Io(err)
1807        }
1808    }
1809    impl IoOrPyException {
1810        fn errno(self) -> PyResult<i32> {
1811            match self {
1812                Self::Timeout => Ok(errcode!(EWOULDBLOCK)),
1813                Self::Io(err) => Ok(err.posix_errno()),
1814                Self::Py(exc) => Err(exc),
1815            }
1816        }
1817    }
1818    impl IntoPyException for IoOrPyException {
1819        #[inline]
1820        fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1821            match self {
1822                Self::Timeout => timeout_error(vm),
1823                Self::Py(exc) => exc,
1824                Self::Io(err) => err.into_pyexception(vm),
1825            }
1826        }
1827    }
1828
1829    #[derive(Copy, Clone)]
1830    pub(crate) enum SelectKind {
1831        Read,
1832        Write,
1833        Connect,
1834    }
1835
1836    /// returns true if timed out
1837    pub(crate) fn sock_select(
1838        sock: &Socket,
1839        kind: SelectKind,
1840        interval: Option<Duration>,
1841    ) -> io::Result<bool> {
1842        #[cfg(unix)]
1843        {
1844            use nix::poll::*;
1845            let events = match kind {
1846                SelectKind::Read => PollFlags::POLLIN,
1847                SelectKind::Write => PollFlags::POLLOUT,
1848                SelectKind::Connect => PollFlags::POLLOUT | PollFlags::POLLERR,
1849            };
1850            let mut pollfd = [PollFd::new(sock, events)];
1851            let timeout = match interval {
1852                Some(d) => d.as_millis() as _,
1853                None => -1,
1854            };
1855            let ret = poll(&mut pollfd, timeout)?;
1856            Ok(ret == 0)
1857        }
1858        #[cfg(windows)]
1859        {
1860            use crate::select;
1861
1862            let fd = sock_fileno(sock);
1863
1864            let mut reads = select::FdSet::new();
1865            let mut writes = select::FdSet::new();
1866            let mut errs = select::FdSet::new();
1867
1868            let fd = fd as usize;
1869            match kind {
1870                SelectKind::Read => reads.insert(fd),
1871                SelectKind::Write => writes.insert(fd),
1872                SelectKind::Connect => {
1873                    writes.insert(fd);
1874                    errs.insert(fd);
1875                }
1876            }
1877
1878            let mut interval = interval.map(|dur| select::timeval {
1879                tv_sec: dur.as_secs() as _,
1880                tv_usec: dur.subsec_micros() as _,
1881            });
1882
1883            select::select(
1884                fd as i32 + 1,
1885                &mut reads,
1886                &mut writes,
1887                &mut errs,
1888                interval.as_mut(),
1889            )
1890            .map(|ret| ret == 0)
1891        }
1892    }
1893
1894    #[derive(FromArgs)]
1895    struct GAIOptions {
1896        #[pyarg(positional)]
1897        host: Option<PyStrRef>,
1898        #[pyarg(positional)]
1899        port: Option<Either<PyStrRef, i32>>,
1900
1901        #[pyarg(positional, default = "c::AF_UNSPEC")]
1902        family: i32,
1903        #[pyarg(positional, default = "0")]
1904        ty: i32,
1905        #[pyarg(positional, default = "0")]
1906        proto: i32,
1907        #[pyarg(positional, default = "0")]
1908        flags: i32,
1909    }
1910
1911    #[pyfunction]
1912    fn getaddrinfo(
1913        opts: GAIOptions,
1914        vm: &VirtualMachine,
1915    ) -> Result<Vec<PyObjectRef>, IoOrPyException> {
1916        let hints = dns_lookup::AddrInfoHints {
1917            socktype: opts.ty,
1918            protocol: opts.proto,
1919            address: opts.family,
1920            flags: opts.flags,
1921        };
1922
1923        let host = opts.host.as_ref().map(|s| s.as_str());
1924        let port = opts.port.as_ref().map(|p| -> std::borrow::Cow<str> {
1925            match p {
1926                Either::A(ref s) => s.as_str().into(),
1927                Either::B(i) => i.to_string().into(),
1928            }
1929        });
1930        let port = port.as_ref().map(|p| p.as_ref());
1931
1932        let addrs = dns_lookup::getaddrinfo(host, port, Some(hints))
1933            .map_err(|err| convert_socket_error(vm, err, SocketError::GaiError))?;
1934
1935        let list = addrs
1936            .map(|ai| {
1937                ai.map(|ai| {
1938                    vm.new_tuple((
1939                        ai.address,
1940                        ai.socktype,
1941                        ai.protocol,
1942                        ai.canonname,
1943                        get_ip_addr_tuple(&ai.sockaddr, vm),
1944                    ))
1945                    .into()
1946                })
1947            })
1948            .collect::<io::Result<Vec<_>>>()?;
1949        Ok(list)
1950    }
1951
1952    #[pyfunction]
1953    fn gethostbyaddr(
1954        addr: PyStrRef,
1955        vm: &VirtualMachine,
1956    ) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
1957        let addr = get_addr(vm, addr, c::AF_UNSPEC)?;
1958        let (hostname, _) = dns_lookup::getnameinfo(&addr, 0)
1959            .map_err(|e| convert_socket_error(vm, e, SocketError::HError))?;
1960        Ok((
1961            hostname,
1962            vm.ctx.new_list(vec![]),
1963            vm.ctx
1964                .new_list(vec![vm.ctx.new_str(addr.ip().to_string()).into()]),
1965        ))
1966    }
1967
1968    #[pyfunction]
1969    fn gethostbyname(name: PyStrRef, vm: &VirtualMachine) -> Result<String, IoOrPyException> {
1970        let addr = get_addr(vm, name, c::AF_INET)?;
1971        match addr {
1972            SocketAddr::V4(ip) => Ok(ip.ip().to_string()),
1973            _ => unreachable!(),
1974        }
1975    }
1976
1977    #[pyfunction]
1978    fn gethostbyname_ex(
1979        name: PyStrRef,
1980        vm: &VirtualMachine,
1981    ) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
1982        let addr = get_addr(vm, name, c::AF_INET)?;
1983        let (hostname, _) = dns_lookup::getnameinfo(&addr, 0)
1984            .map_err(|e| convert_socket_error(vm, e, SocketError::HError))?;
1985        Ok((
1986            hostname,
1987            vm.ctx.new_list(vec![]),
1988            vm.ctx
1989                .new_list(vec![vm.ctx.new_str(addr.ip().to_string()).into()]),
1990        ))
1991    }
1992
1993    #[pyfunction]
1994    fn inet_pton(af_inet: i32, ip_string: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
1995        static ERROR_MSG: &str = "illegal IP address string passed to inet_pton";
1996        let ip_addr = match af_inet {
1997            c::AF_INET => ip_string
1998                .as_str()
1999                .parse::<Ipv4Addr>()
2000                .map_err(|_| vm.new_os_error(ERROR_MSG.to_owned()))?
2001                .octets()
2002                .to_vec(),
2003            c::AF_INET6 => ip_string
2004                .as_str()
2005                .parse::<Ipv6Addr>()
2006                .map_err(|_| vm.new_os_error(ERROR_MSG.to_owned()))?
2007                .octets()
2008                .to_vec(),
2009            _ => return Err(vm.new_os_error("Address family not supported by protocol".to_owned())),
2010        };
2011        Ok(ip_addr)
2012    }
2013
2014    #[pyfunction]
2015    fn inet_ntop(af_inet: i32, packed_ip: ArgBytesLike, vm: &VirtualMachine) -> PyResult<String> {
2016        let packed_ip = packed_ip.borrow_buf();
2017        match af_inet {
2018            c::AF_INET => {
2019                let packed_ip = <&[u8; 4]>::try_from(&*packed_ip).map_err(|_| {
2020                    vm.new_value_error("invalid length of packed IP address string".to_owned())
2021                })?;
2022                Ok(Ipv4Addr::from(*packed_ip).to_string())
2023            }
2024            c::AF_INET6 => {
2025                let packed_ip = <&[u8; 16]>::try_from(&*packed_ip).map_err(|_| {
2026                    vm.new_value_error("invalid length of packed IP address string".to_owned())
2027                })?;
2028                Ok(get_ipv6_addr_str(Ipv6Addr::from(*packed_ip)))
2029            }
2030            _ => Err(vm.new_value_error(format!("unknown address family {af_inet}"))),
2031        }
2032    }
2033
2034    #[pyfunction]
2035    fn getprotobyname(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
2036        let cstr = name.to_cstring(vm)?;
2037        let proto = unsafe { c::getprotobyname(cstr.as_ptr()) };
2038        if proto.is_null() {
2039            return Err(vm.new_os_error("protocol not found".to_owned()));
2040        }
2041        let num = unsafe { (*proto).p_proto };
2042        Ok(vm.ctx.new_int(num).into())
2043    }
2044
2045    #[pyfunction]
2046    fn getnameinfo(
2047        address: PyTupleRef,
2048        flags: i32,
2049        vm: &VirtualMachine,
2050    ) -> Result<(String, String), IoOrPyException> {
2051        match address.len() {
2052            2..=4 => {}
2053            _ => {
2054                return Err(vm
2055                    .new_type_error("illegal sockaddr argument".to_owned())
2056                    .into())
2057            }
2058        }
2059        let (addr, flowinfo, scopeid) = Address::from_tuple_ipv6(&address, vm)?;
2060        let hints = dns_lookup::AddrInfoHints {
2061            address: c::AF_UNSPEC,
2062            socktype: c::SOCK_DGRAM,
2063            flags: c::AI_NUMERICHOST,
2064            protocol: 0,
2065        };
2066        let service = addr.port.to_string();
2067        let mut res =
2068            dns_lookup::getaddrinfo(Some(addr.host.as_str()), Some(&service), Some(hints))
2069                .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?
2070                .filter_map(Result::ok);
2071        let mut ainfo = res.next().unwrap();
2072        if res.next().is_some() {
2073            return Err(vm
2074                .new_os_error("sockaddr resolved to multiple addresses".to_owned())
2075                .into());
2076        }
2077        match &mut ainfo.sockaddr {
2078            SocketAddr::V4(_) => {
2079                if address.len() != 2 {
2080                    return Err(vm
2081                        .new_os_error("IPv4 sockaddr must be 2 tuple".to_owned())
2082                        .into());
2083                }
2084            }
2085            SocketAddr::V6(addr) => {
2086                addr.set_flowinfo(flowinfo);
2087                addr.set_scope_id(scopeid);
2088            }
2089        }
2090        dns_lookup::getnameinfo(&ainfo.sockaddr, flags)
2091            .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))
2092    }
2093
2094    #[cfg(unix)]
2095    #[pyfunction]
2096    fn socketpair(
2097        family: OptionalArg<i32>,
2098        socket_kind: OptionalArg<i32>,
2099        proto: OptionalArg<i32>,
2100    ) -> Result<(PySocket, PySocket), IoOrPyException> {
2101        let family = family.unwrap_or(libc::AF_UNIX);
2102        let socket_kind = socket_kind.unwrap_or(libc::SOCK_STREAM);
2103        let proto = proto.unwrap_or(0);
2104        let (a, b) = Socket::pair(family.into(), socket_kind.into(), Some(proto.into()))?;
2105        let py_a = PySocket::default();
2106        py_a.init_inner(family, socket_kind, proto, a)?;
2107        let py_b = PySocket::default();
2108        py_b.init_inner(family, socket_kind, proto, b)?;
2109        Ok((py_a, py_b))
2110    }
2111
2112    #[cfg(all(unix, not(target_os = "redox")))]
2113    type IfIndex = c::c_uint;
2114    #[cfg(windows)]
2115    type IfIndex = winapi::shared::ifdef::NET_IFINDEX;
2116
2117    #[cfg(not(target_os = "redox"))]
2118    #[pyfunction]
2119    fn if_nametoindex(name: FsPath, vm: &VirtualMachine) -> PyResult<IfIndex> {
2120        let name = name.to_cstring(vm)?;
2121
2122        let ret = unsafe { c::if_nametoindex(name.as_ptr()) };
2123        if ret == 0 {
2124            Err(vm.new_os_error("no interface with this name".to_owned()))
2125        } else {
2126            Ok(ret)
2127        }
2128    }
2129
2130    #[cfg(not(target_os = "redox"))]
2131    #[pyfunction]
2132    fn if_indextoname(index: IfIndex, vm: &VirtualMachine) -> PyResult<String> {
2133        let mut buf = [0; c::IF_NAMESIZE + 1];
2134        let ret = unsafe { c::if_indextoname(index, buf.as_mut_ptr()) };
2135        if ret.is_null() {
2136            Err(crate::vm::stdlib::os::errno_err(vm))
2137        } else {
2138            let buf = unsafe { ffi::CStr::from_ptr(buf.as_ptr()) };
2139            Ok(buf.to_string_lossy().into_owned())
2140        }
2141    }
2142
2143    #[cfg(any(
2144        windows,
2145        target_os = "dragonfly",
2146        target_os = "freebsd",
2147        target_os = "fuchsia",
2148        target_os = "ios",
2149        target_os = "linux",
2150        target_os = "macos",
2151        target_os = "netbsd",
2152        target_os = "openbsd",
2153    ))]
2154    #[pyfunction]
2155    fn if_nameindex(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
2156        #[cfg(not(windows))]
2157        {
2158            let list = nix::net::if_::if_nameindex()
2159                .map_err(|err| err.into_pyexception(vm))?
2160                .to_slice()
2161                .iter()
2162                .map(|iface| {
2163                    let tup: (u32, String) =
2164                        (iface.index(), iface.name().to_string_lossy().into_owned());
2165                    tup.to_pyobject(vm)
2166                })
2167                .collect();
2168
2169            Ok(list)
2170        }
2171        #[cfg(windows)]
2172        {
2173            use std::ptr;
2174
2175            let table = MibTable::get_raw().map_err(|err| err.into_pyexception(vm))?;
2176            let list = table.as_slice().iter().map(|entry| {
2177                let name =
2178                    get_name(&entry.InterfaceLuid).map_err(|err| err.into_pyexception(vm))?;
2179                let tup = (entry.InterfaceIndex, name.to_string_lossy());
2180                Ok(tup.to_pyobject(vm))
2181            });
2182            let list = list.collect::<PyResult<_>>()?;
2183            return Ok(list);
2184
2185            fn get_name(
2186                luid: &winapi::shared::ifdef::NET_LUID,
2187            ) -> io::Result<widestring::WideCString> {
2188                let mut buf = [0; c::IF_NAMESIZE + 1];
2189                let ret = unsafe {
2190                    netioapi::ConvertInterfaceLuidToNameW(luid, buf.as_mut_ptr(), buf.len())
2191                };
2192                if ret == 0 {
2193                    Ok(widestring::WideCString::from_ustr_truncate(
2194                        widestring::WideStr::from_slice(&buf[..]),
2195                    ))
2196                } else {
2197                    Err(io::Error::from_raw_os_error(ret as i32))
2198                }
2199            }
2200            struct MibTable {
2201                ptr: ptr::NonNull<netioapi::MIB_IF_TABLE2>,
2202            }
2203            impl MibTable {
2204                fn get_raw() -> io::Result<Self> {
2205                    let mut ptr = ptr::null_mut();
2206                    let ret = unsafe { netioapi::GetIfTable2Ex(netioapi::MibIfTableRaw, &mut ptr) };
2207                    if ret == 0 {
2208                        let ptr = unsafe { ptr::NonNull::new_unchecked(ptr) };
2209                        Ok(Self { ptr })
2210                    } else {
2211                        Err(io::Error::from_raw_os_error(ret as i32))
2212                    }
2213                }
2214            }
2215            impl MibTable {
2216                fn as_slice(&self) -> &[netioapi::MIB_IF_ROW2] {
2217                    unsafe {
2218                        let p = self.ptr.as_ptr();
2219                        let ptr = ptr::addr_of!((*p).Table) as *const netioapi::MIB_IF_ROW2;
2220                        std::slice::from_raw_parts(ptr, (*p).NumEntries as usize)
2221                    }
2222                }
2223            }
2224            impl Drop for MibTable {
2225                fn drop(&mut self) {
2226                    unsafe { netioapi::FreeMibTable(self.ptr.as_ptr() as *mut _) }
2227                }
2228            }
2229        }
2230    }
2231
2232    fn get_addr(
2233        vm: &VirtualMachine,
2234        pyname: PyStrRef,
2235        af: i32,
2236    ) -> Result<SocketAddr, IoOrPyException> {
2237        let name = pyname.as_str();
2238        if name.is_empty() {
2239            let hints = dns_lookup::AddrInfoHints {
2240                address: af,
2241                socktype: c::SOCK_DGRAM,
2242                flags: c::AI_PASSIVE,
2243                protocol: 0,
2244            };
2245            let mut res = dns_lookup::getaddrinfo(None, Some("0"), Some(hints))
2246                .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?;
2247            let ainfo = res.next().unwrap()?;
2248            if res.next().is_some() {
2249                return Err(vm
2250                    .new_os_error("wildcard resolved to multiple address".to_owned())
2251                    .into());
2252            }
2253            return Ok(ainfo.sockaddr);
2254        }
2255        if name == "255.255.255.255" || name == "<broadcast>" {
2256            match af {
2257                c::AF_INET | c::AF_UNSPEC => {}
2258                _ => {
2259                    return Err(vm
2260                        .new_os_error("address family mismatched".to_owned())
2261                        .into())
2262                }
2263            }
2264            return Ok(SocketAddr::V4(net::SocketAddrV4::new(
2265                c::INADDR_BROADCAST.into(),
2266                0,
2267            )));
2268        }
2269        if let c::AF_INET | c::AF_UNSPEC = af {
2270            if let Ok(addr) = name.parse::<Ipv4Addr>() {
2271                return Ok(SocketAddr::V4(net::SocketAddrV4::new(addr, 0)));
2272            }
2273        }
2274        if matches!(af, c::AF_INET | c::AF_UNSPEC) && !name.contains('%') {
2275            if let Ok(addr) = name.parse::<Ipv6Addr>() {
2276                return Ok(SocketAddr::V6(net::SocketAddrV6::new(addr, 0, 0, 0)));
2277            }
2278        }
2279        let hints = dns_lookup::AddrInfoHints {
2280            address: af,
2281            ..Default::default()
2282        };
2283        let name = vm
2284            .state
2285            .codec_registry
2286            .encode_text(pyname, "idna", None, vm)?;
2287        let name = std::str::from_utf8(name.as_bytes())
2288            .map_err(|_| vm.new_runtime_error("idna output is not utf8".to_owned()))?;
2289        let mut res = dns_lookup::getaddrinfo(Some(name), None, Some(hints))
2290            .map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?;
2291        Ok(res.next().unwrap().map(|ainfo| ainfo.sockaddr)?)
2292    }
2293
2294    fn sock_from_raw(fileno: RawSocket, vm: &VirtualMachine) -> PyResult<Socket> {
2295        let invalid = {
2296            cfg_if::cfg_if! {
2297                if #[cfg(windows)] {
2298                    fileno == INVALID_SOCKET
2299                } else {
2300                    fileno < 0
2301                }
2302            }
2303        };
2304        if invalid {
2305            return Err(vm.new_value_error("negative file descriptor".to_owned()));
2306        }
2307        Ok(unsafe { sock_from_raw_unchecked(fileno) })
2308    }
2309    /// SAFETY: fileno must not be equal to INVALID_SOCKET
2310    unsafe fn sock_from_raw_unchecked(fileno: RawSocket) -> Socket {
2311        #[cfg(unix)]
2312        {
2313            use std::os::unix::io::FromRawFd;
2314            Socket::from_raw_fd(fileno)
2315        }
2316        #[cfg(windows)]
2317        {
2318            use std::os::windows::io::FromRawSocket;
2319            Socket::from_raw_socket(fileno)
2320        }
2321    }
2322    pub(super) fn sock_fileno(sock: &Socket) -> RawSocket {
2323        #[cfg(unix)]
2324        {
2325            use std::os::unix::io::AsRawFd;
2326            sock.as_raw_fd()
2327        }
2328        #[cfg(windows)]
2329        {
2330            use std::os::windows::io::AsRawSocket;
2331            sock.as_raw_socket()
2332        }
2333    }
2334    fn into_sock_fileno(sock: Socket) -> RawSocket {
2335        #[cfg(unix)]
2336        {
2337            use std::os::unix::io::IntoRawFd;
2338            sock.into_raw_fd()
2339        }
2340        #[cfg(windows)]
2341        {
2342            use std::os::windows::io::IntoRawSocket;
2343            sock.into_raw_socket()
2344        }
2345    }
2346
2347    pub(super) const INVALID_SOCKET: RawSocket = {
2348        #[cfg(unix)]
2349        {
2350            -1
2351        }
2352        #[cfg(windows)]
2353        {
2354            windows_sys::Win32::Networking::WinSock::INVALID_SOCKET as RawSocket
2355        }
2356    };
2357
2358    fn convert_socket_error(
2359        vm: &VirtualMachine,
2360        err: dns_lookup::LookupError,
2361        err_kind: SocketError,
2362    ) -> IoOrPyException {
2363        if let dns_lookup::LookupErrorKind::System = err.kind() {
2364            return io::Error::from(err).into();
2365        }
2366        let strerr = {
2367            #[cfg(unix)]
2368            {
2369                let s = match err_kind {
2370                    SocketError::GaiError => unsafe {
2371                        ffi::CStr::from_ptr(libc::gai_strerror(err.error_num()))
2372                    },
2373                    SocketError::HError => unsafe {
2374                        ffi::CStr::from_ptr(libc::hstrerror(err.error_num()))
2375                    },
2376                };
2377                s.to_str().unwrap()
2378            }
2379            #[cfg(windows)]
2380            {
2381                "getaddrinfo failed"
2382            }
2383        };
2384        let exception_cls = match err_kind {
2385            SocketError::GaiError => gaierror(vm),
2386            SocketError::HError => herror(vm),
2387        };
2388        vm.new_exception(
2389            exception_cls,
2390            vec![vm.new_pyobj(err.error_num()), vm.ctx.new_str(strerr).into()],
2391        )
2392        .into()
2393    }
2394
2395    fn timeout_error(vm: &VirtualMachine) -> PyBaseExceptionRef {
2396        timeout_error_msg(vm, "timed out".to_owned())
2397    }
2398    pub(crate) fn timeout_error_msg(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef {
2399        vm.new_exception_msg(timeout(vm), msg)
2400    }
2401
2402    fn get_ipv6_addr_str(ipv6: Ipv6Addr) -> String {
2403        match ipv6.to_ipv4() {
2404            // instead of "::0.0.ddd.ddd" it's "::xxxx"
2405            Some(v4) if !ipv6.is_unspecified() && matches!(v4.octets(), [0, 0, _, _]) => {
2406                format!("::{:x}", u32::from(v4))
2407            }
2408            _ => ipv6.to_string(),
2409        }
2410    }
2411
2412    pub(crate) struct Deadline {
2413        deadline: Instant,
2414    }
2415
2416    impl Deadline {
2417        fn new(timeout: Duration) -> Self {
2418            Self {
2419                deadline: Instant::now() + timeout,
2420            }
2421        }
2422        fn time_until(&self) -> Result<Duration, IoOrPyException> {
2423            self.deadline
2424                .checked_duration_since(Instant::now())
2425                // past the deadline already
2426                .ok_or(IoOrPyException::Timeout)
2427        }
2428    }
2429
2430    static DEFAULT_TIMEOUT: AtomicCell<f64> = AtomicCell::new(-1.0);
2431
2432    #[pyfunction]
2433    fn getdefaulttimeout() -> Option<f64> {
2434        let timeout = DEFAULT_TIMEOUT.load();
2435        if timeout >= 0.0 {
2436            Some(timeout)
2437        } else {
2438            None
2439        }
2440    }
2441
2442    #[pyfunction]
2443    fn setdefaulttimeout(timeout: Option<Duration>) {
2444        DEFAULT_TIMEOUT.store(timeout.map_or(-1.0, |d| d.as_secs_f64()));
2445    }
2446
2447    #[pyfunction]
2448    fn dup(x: PyObjectRef, vm: &VirtualMachine) -> Result<RawSocket, IoOrPyException> {
2449        let sock = get_raw_sock(x, vm)?;
2450        let sock = std::mem::ManuallyDrop::new(sock_from_raw(sock, vm)?);
2451        let newsock = sock.try_clone()?;
2452        let fd = into_sock_fileno(newsock);
2453        #[cfg(windows)]
2454        crate::vm::stdlib::nt::raw_set_handle_inheritable(fd as _, false)?;
2455        Ok(fd)
2456    }
2457
2458    #[pyfunction]
2459    fn close(x: PyObjectRef, vm: &VirtualMachine) -> Result<(), IoOrPyException> {
2460        Ok(close_inner(get_raw_sock(x, vm)?)?)
2461    }
2462
2463    fn close_inner(x: RawSocket) -> io::Result<()> {
2464        #[cfg(unix)]
2465        use libc::close;
2466        #[cfg(windows)]
2467        use windows_sys::Win32::Networking::WinSock::closesocket as close;
2468        let ret = unsafe { close(x as _) };
2469        if ret < 0 {
2470            let err = crate::common::os::last_os_error();
2471            if err.raw_os_error() != Some(errcode!(ECONNRESET)) {
2472                return Err(err);
2473            }
2474        }
2475        Ok(())
2476    }
2477
2478    enum SocketError {
2479        HError,
2480        GaiError,
2481    }
2482
2483    #[cfg(all(unix, not(target_os = "redox")))]
2484    fn checked_cmsg_len(len: usize) -> Option<usize> {
2485        // SAFETY: CMSG_LEN is always safe
2486        let cmsg_len = |length| unsafe { libc::CMSG_LEN(length) };
2487        if len as u64 > (i32::MAX as u64 - cmsg_len(0) as u64) {
2488            return None;
2489        }
2490        let res = cmsg_len(len as _) as usize;
2491        if res > i32::MAX as usize || res < len {
2492            return None;
2493        }
2494        Some(res)
2495    }
2496
2497    #[cfg(all(unix, not(target_os = "redox")))]
2498    fn checked_cmsg_space(len: usize) -> Option<usize> {
2499        // SAFETY: CMSG_SPACE is always safe
2500        let cmsg_space = |length| unsafe { libc::CMSG_SPACE(length) };
2501        if len as u64 > (i32::MAX as u64 - cmsg_space(1) as u64) {
2502            return None;
2503        }
2504        let res = cmsg_space(len as _) as usize;
2505        if res > i32::MAX as usize || res < len {
2506            return None;
2507        }
2508        Some(res)
2509    }
2510
2511    #[cfg(all(unix, not(target_os = "redox")))]
2512    #[pyfunction(name = "CMSG_LEN")]
2513    fn cmsg_len(length: usize, vm: &VirtualMachine) -> PyResult<usize> {
2514        checked_cmsg_len(length)
2515            .ok_or_else(|| vm.new_overflow_error("CMSG_LEN() argument out of range".to_owned()))
2516    }
2517
2518    #[cfg(all(unix, not(target_os = "redox")))]
2519    #[pyfunction(name = "CMSG_SPACE")]
2520    fn cmsg_space(length: usize, vm: &VirtualMachine) -> PyResult<usize> {
2521        checked_cmsg_space(length)
2522            .ok_or_else(|| vm.new_overflow_error("CMSG_SPACE() argument out of range".to_owned()))
2523    }
2524}