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 #[pyattr(name = "has_ipv6")]
81 const HAS_IPV6: bool = true;
82 #[pyattr]
83 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 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
223 #[pyattr]
224 const SO_VM_SOCKETS_BUFFER_SIZE: u32 = 0;
225
226 #[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 #[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 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
238 #[pyattr]
239 const VMADDR_CID_ANY: u32 = 0xffffffff; #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
243 #[pyattr]
244 const VMADDR_PORT_ANY: u32 = 0xffffffff; #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
248 #[pyattr]
249 const VMADDR_CID_HOST: u32 = 2;
250
251 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
253 #[pyattr]
254 const VM_SOCKETS_INVALID_VERSION: u32 = 0xffffffff; #[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; #[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; #[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 #[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 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 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) => {} }
900 }
901
902 let err = loop {
903 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 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 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 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 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 let (flags, address) = match arg3 {
1304 OptionalArg::Present(arg3) => {
1305 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 #[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 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 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 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 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 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 (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 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 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 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 .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 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 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}