1#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
14compile_error!("air-sys-syscall::net supporte uniquement x86_64 et aarch64 (ADR-014).");
15
16use air_sys_types::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
17use alloc::ffi::CString;
18use alloc::vec::Vec;
19use core::ffi::CStr;
20use core::num::NonZeroI32;
21
22use air_sys_types::net::{
23 AcceptFlags, AcceptResult, IoSlice, IoSliceMut, IpMembershipV4, IpMembershipV6, Ipv4SocketAddr,
24 Ipv6SocketAddr, LingerOption, MessageFlags, ReceiveMessageRequest, ReceiveMessageResult,
25 SendMessageRequest, SendMessageResult, ShutdownMode, SocketAddr, SocketDomain, SocketType,
26 UnixCredentials, UnixSocketAddr,
27};
28use air_sys_types::process::Pid;
29use air_sys_types::{Errno, OwnedFd as AirOwnedFd, Timeval};
30
31fn errno_from_negative_syscall_ret(ret: i64) -> Errno {
36 debug_assert!(ret < 0 && ret > -4096);
37 #[allow(clippy::cast_possible_truncation)]
38 let raw = ret.wrapping_neg() as i32;
39 let nz = NonZeroI32::new(raw).expect("errno strictement positif par construction");
40 Errno::from_nonzero(nz)
41}
42
43const SOCK_CLOEXEC: i32 = 0x0008_0000;
49
50const MSG_NOSIGNAL: i32 = 0x4000;
52
53const SOL_SOCKET: i32 = 1;
58const SO_KEEPALIVE: i32 = 9;
59const SO_REUSEADDR: i32 = 2;
60const SO_REUSEPORT: i32 = 15;
61const SO_SNDBUF: i32 = 7;
62const SO_RCVBUF: i32 = 8;
63const SO_ERROR: i32 = 4;
64const SO_LINGER: i32 = 13;
65const SO_PEERCRED: i32 = 17;
66const SO_BINDTODEVICE: i32 = 25;
67const SO_RCVTIMEO: i32 = 20;
71const SO_SNDTIMEO: i32 = 21;
72
73const IPPROTO_TCP: i32 = 6;
74const TCP_NODELAY: i32 = 1;
75const TCP_KEEPIDLE: i32 = 4;
76const TCP_KEEPINTVL: i32 = 5;
77const TCP_KEEPCNT: i32 = 6;
78
79const IPPROTO_IP: i32 = 0;
80const IP_TTL: i32 = 2;
81const IP_ADD_MEMBERSHIP: i32 = 35;
82const IP_DROP_MEMBERSHIP: i32 = 36;
83
84const IPPROTO_IPV6: i32 = 41;
85const IPV6_V6ONLY: i32 = 26;
86const IPV6_ADD_MEMBERSHIP: i32 = 20;
88const IPV6_DROP_MEMBERSHIP: i32 = 21;
89
90const TIMEVAL_LEN: u32 = 16;
92const IP_MREQ_LEN: u32 = 8;
94const IPV6_MREQ_LEN: u32 = 20;
96
97const _: () = assert!(core::mem::size_of::<Timeval>() == TIMEVAL_LEN as usize);
100const _: () = assert!(core::mem::size_of::<IpMembershipV4>() == IP_MREQ_LEN as usize);
101const _: () = assert!(core::mem::size_of::<IpMembershipV6>() == IPV6_MREQ_LEN as usize);
102
103pub(crate) const MAX_SOCKADDR_LEN: usize = 128;
108
109#[repr(C, align(8))]
111#[derive(Debug)]
112pub(crate) struct RawSockaddrStorage {
113 pub(crate) bytes: [u8; MAX_SOCKADDR_LEN],
114}
115
116pub(crate) fn socket_addr_to_raw(address: &SocketAddr) -> (RawSockaddrStorage, u32) {
119 let mut storage = RawSockaddrStorage {
120 bytes: [0u8; MAX_SOCKADDR_LEN],
121 };
122 match address {
123 SocketAddr::Unix(unix) => {
124 storage.bytes[0..2].copy_from_slice(&1u16.to_ne_bytes());
126 match unix {
127 UnixSocketAddr::Path(path) => {
128 let bytes = path.as_bytes();
129 let length = bytes.len().min(107);
130 storage.bytes[2..2_usize.saturating_add(length)]
131 .copy_from_slice(&bytes[..length]);
132 storage.bytes[2_usize.saturating_add(length)] = 0; #[allow(clippy::cast_possible_truncation)]
135 let addrlen = (2_usize.saturating_add(length).saturating_add(1)) as u32;
136 (storage, addrlen)
137 }
138 UnixSocketAddr::Abstract(name) => {
139 storage.bytes[2] = 0;
141 let length = name.len().min(107);
142 storage.bytes[3..3_usize.saturating_add(length)]
143 .copy_from_slice(&name[..length]);
144 #[allow(clippy::cast_possible_truncation)]
146 let addrlen = (2_usize.saturating_add(1).saturating_add(length)) as u32;
147 (storage, addrlen)
148 }
149 UnixSocketAddr::Unnamed => (storage, 2),
150 }
151 }
152 SocketAddr::Ipv4(ipv4) => {
153 storage.bytes[0..2].copy_from_slice(&2u16.to_ne_bytes());
155 let port_be = ipv4.port.to_be();
157 storage.bytes[2..4].copy_from_slice(&port_be.to_ne_bytes());
158 storage.bytes[4..8].copy_from_slice(&ipv4.address);
159 (storage, 16)
161 }
162 SocketAddr::Ipv6(ipv6) => {
163 storage.bytes[0..2].copy_from_slice(&10u16.to_ne_bytes());
165 let port_be = ipv6.port.to_be();
166 storage.bytes[2..4].copy_from_slice(&port_be.to_ne_bytes());
167 storage.bytes[4..8].copy_from_slice(&ipv6.flowinfo.to_ne_bytes());
168 storage.bytes[8..24].copy_from_slice(&ipv6.address);
169 storage.bytes[24..28].copy_from_slice(&ipv6.scope_id.to_ne_bytes());
170 (storage, 28)
171 }
172 }
173}
174
175pub(crate) fn raw_to_socket_addr(buffer: &[u8], length: u32) -> Option<SocketAddr> {
178 if length < 2 {
179 return None;
180 }
181 let len_usize = length as usize;
182 if buffer.len() < len_usize {
183 return None;
184 }
185 let family = u16::from_ne_bytes([buffer[0], buffer[1]]);
186 match family {
187 1 => {
188 if length == 2 {
190 return Some(SocketAddr::Unix(UnixSocketAddr::Unnamed));
191 }
192 if length < 3 {
193 return None;
194 }
195 let path_bytes = buffer.get(2..len_usize)?;
196 if path_bytes.first() == Some(&0) {
197 let name = path_bytes.get(1..).unwrap_or(&[]).to_vec();
199 Some(SocketAddr::Unix(UnixSocketAddr::Abstract(name)))
200 } else {
201 let nul = path_bytes
203 .iter()
204 .position(|&b| b == 0)
205 .unwrap_or(path_bytes.len());
206 let cstr = CString::new(&path_bytes[..nul]).ok()?;
207 Some(SocketAddr::Unix(UnixSocketAddr::Path(cstr)))
208 }
209 }
210 2 => {
211 if length < 16 {
213 return None;
214 }
215 let port = u16::from_be_bytes([buffer[2], buffer[3]]);
216 let address = [buffer[4], buffer[5], buffer[6], buffer[7]];
217 Some(SocketAddr::Ipv4(Ipv4SocketAddr { address, port }))
218 }
219 10 => {
220 if length < 28 {
222 return None;
223 }
224 let port = u16::from_be_bytes([buffer[2], buffer[3]]);
225 let flowinfo = u32::from_ne_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
226 let mut address = [0u8; 16];
227 address.copy_from_slice(buffer.get(8..24)?);
228 let scope_id = u32::from_ne_bytes([buffer[24], buffer[25], buffer[26], buffer[27]]);
229 Some(SocketAddr::Ipv6(Ipv6SocketAddr {
230 address,
231 port,
232 flowinfo,
233 scope_id,
234 }))
235 }
236 _ => None,
237 }
238}
239
240#[must_use]
252pub fn socket_addr_from_bytes(bytes: &[u8], length: u32) -> Option<SocketAddr> {
253 raw_to_socket_addr(bytes, length)
254}
255
256#[must_use]
261pub fn socket_addr_to_bytes(address: &SocketAddr, out: &mut [u8]) -> Option<u32> {
262 let (storage, length) = socket_addr_to_raw(address);
263 let length_usize = usize::try_from(length).ok()?;
264 let source = storage.bytes.get(..length_usize)?;
265 let target = out.get_mut(..length_usize)?;
266 target.copy_from_slice(source);
267 Some(length)
268}
269
270pub(crate) fn build_scm_rights_cmsg(fds: &[BorrowedFd<'_>]) -> Vec<u8> {
282 if fds.is_empty() {
283 return Vec::new();
284 }
285 let data_len = fds.len().saturating_mul(4); let cmsg_len = 16_usize.saturating_add(data_len); let total = cmsg_len.saturating_add(7) & !7_usize; let mut buffer = vec![0u8; total];
289 #[allow(clippy::cast_possible_truncation)]
291 buffer[0..8].copy_from_slice(&(cmsg_len as u64).to_ne_bytes());
292 buffer[8..12].copy_from_slice(&1i32.to_ne_bytes());
294 buffer[12..16].copy_from_slice(&1i32.to_ne_bytes());
296 for (i, fd) in fds.iter().enumerate() {
298 let raw = fd.as_raw_fd();
299 let offset = 16_usize.saturating_add(i.saturating_mul(4));
300 buffer[offset..offset.saturating_add(4)].copy_from_slice(&raw.to_ne_bytes());
301 }
302 buffer
303}
304
305pub(crate) fn parse_scm_rights(cmsg_buf: &[u8], cmsg_len: usize) -> Vec<OwnedFd> {
307 let mut fds = Vec::new();
308 let mut offset = 0_usize;
309 while offset.saturating_add(16) <= cmsg_len && offset.saturating_add(16) <= cmsg_buf.len() {
310 let len_bytes: [u8; 8] = match cmsg_buf[offset..offset.saturating_add(8)].try_into() {
311 Ok(b) => b,
312 Err(_) => break,
313 };
314 #[allow(clippy::cast_possible_truncation)]
318 let length = u64::from_ne_bytes(len_bytes) as usize;
319 let level_bytes: [u8; 4] =
320 match cmsg_buf[offset.saturating_add(8)..offset.saturating_add(12)].try_into() {
321 Ok(b) => b,
322 Err(_) => break,
323 };
324 let level = i32::from_ne_bytes(level_bytes);
325 let ty_bytes: [u8; 4] =
326 match cmsg_buf[offset.saturating_add(12)..offset.saturating_add(16)].try_into() {
327 Ok(b) => b,
328 Err(_) => break,
329 };
330 let ty = i32::from_ne_bytes(ty_bytes);
331
332 if level == 1 && ty == 1 && length >= 16 {
334 let mut fd_off = offset.saturating_add(16);
335 let end = offset.saturating_add(length);
336 while fd_off.saturating_add(4) <= end && fd_off.saturating_add(4) <= cmsg_buf.len() {
337 let raw_bytes: [u8; 4] = match cmsg_buf[fd_off..fd_off.saturating_add(4)].try_into()
338 {
339 Ok(b) => b,
340 Err(_) => break,
341 };
342 let raw = i32::from_ne_bytes(raw_bytes);
343 fds.push(unsafe { OwnedFd::from_raw_fd(raw) });
346 fd_off = fd_off.saturating_add(4);
347 }
348 }
349 let aligned = length.saturating_add(7) & !7_usize;
350 if aligned == 0 {
351 break;
352 }
353 offset = offset.saturating_add(aligned);
354 }
355 fds
356}
357
358#[repr(C)]
367struct KernelMsghdr {
368 msg_name: u64, msg_namelen: u32, _pad: u32, msg_iov: u64, msg_iovlen: u64, msg_control: u64, msg_controllen: u64, msg_flags: i32, _pad2: u32,
377}
378
379fn getsockopt_i32(sock: BorrowedFd<'_>, level: i32, optname: i32) -> Result<i32, Errno> {
385 let mut val: i32 = 0;
386 let mut length: u32 = 4;
387 let val_ptr: *mut i32 = &mut val;
388 let len_ptr: *mut u32 = &mut length;
389 let ret = unsafe {
394 raw_syscall_getsockopt(
395 sock.as_raw_fd(),
396 level,
397 optname,
398 val_ptr as u64,
399 len_ptr as u64,
400 )
401 };
402 if ret < 0 {
403 return Err(errno_from_negative_syscall_ret(ret));
404 }
405 Ok(val)
406}
407
408fn setsockopt_i32(sock: BorrowedFd<'_>, level: i32, optname: i32, val: i32) -> Result<(), Errno> {
410 let val_ref: &i32 = &val;
411 let val_ptr: *const i32 = val_ref;
412 let length: u32 = 4;
413 let ret =
418 unsafe { raw_syscall_setsockopt(sock.as_raw_fd(), level, optname, val_ptr as u64, length) };
419 if ret < 0 {
420 return Err(errno_from_negative_syscall_ret(ret));
421 }
422 Ok(())
423}
424
425pub fn set_socket_option_int(
438 sock: BorrowedFd<'_>,
439 level: i32,
440 optname: i32,
441 value: i32,
442) -> Result<(), Errno> {
443 setsockopt_i32(sock, level, optname, value)
444}
445
446pub fn get_socket_option_int(sock: BorrowedFd<'_>, level: i32, optname: i32) -> Result<i32, Errno> {
452 getsockopt_i32(sock, level, optname)
453}
454
455pub fn socket(domain: SocketDomain, ty: SocketType, protocol: i32) -> Result<AirOwnedFd, Errno> {
489 let domain_raw = domain as i32;
490 let ty_raw = (ty as i32) | SOCK_CLOEXEC;
491 let ret = unsafe { raw_syscall_socket(domain_raw, ty_raw, protocol) };
496 if ret < 0 {
497 return Err(errno_from_negative_syscall_ret(ret));
498 }
499 #[allow(clippy::cast_possible_truncation)]
500 let raw_fd = ret as i32;
501 Ok(unsafe { AirOwnedFd::from_raw_fd(raw_fd) })
503}
504
505pub fn bind(sock: BorrowedFd<'_>, address: &SocketAddr) -> Result<(), Errno> {
537 let (raw, addrlen) = socket_addr_to_raw(address);
538 let addr_ptr: *const u8 = raw.bytes.as_ptr();
539 let ret = unsafe { raw_syscall_bind(sock.as_raw_fd(), addr_ptr as u64, addrlen) };
544 if ret < 0 {
545 return Err(errno_from_negative_syscall_ret(ret));
546 }
547 Ok(())
548}
549
550pub fn listen(sock: BorrowedFd<'_>, backlog: u32) -> Result<(), Errno> {
575 #[allow(clippy::cast_possible_truncation)]
577 let backlog_i32 = if backlog > i32::MAX as u32 {
578 i32::MAX
579 } else {
580 backlog as i32
581 };
582 let ret = unsafe { raw_syscall_listen(sock.as_raw_fd(), backlog_i32) };
586 if ret < 0 {
587 return Err(errno_from_negative_syscall_ret(ret));
588 }
589 Ok(())
590}
591
592pub fn connect(sock: BorrowedFd<'_>, address: &SocketAddr) -> Result<(), Errno> {
628 let (raw, addrlen) = socket_addr_to_raw(address);
629 let addr_ptr: *const u8 = raw.bytes.as_ptr();
630 let ret = unsafe { raw_syscall_connect(sock.as_raw_fd(), addr_ptr as u64, addrlen) };
635 if ret < 0 {
636 return Err(errno_from_negative_syscall_ret(ret));
637 }
638 Ok(())
639}
640
641pub fn accept4(listener: BorrowedFd<'_>, flags: AcceptFlags) -> Result<AcceptResult, Errno> {
670 let mut addr_buf = RawSockaddrStorage {
671 bytes: [0u8; MAX_SOCKADDR_LEN],
672 };
673 #[allow(clippy::cast_possible_truncation)]
675 let mut addrlen: u32 = MAX_SOCKADDR_LEN as u32;
676 let addr_ptr: *mut u8 = addr_buf.bytes.as_mut_ptr();
677 let addrlen_ptr: *mut u32 = &mut addrlen;
678 let effective_flags = (flags | AcceptFlags::CLOEXEC).bits();
680 let ret = unsafe {
687 raw_syscall_accept4(
688 listener.as_raw_fd(),
689 addr_ptr as u64,
690 addrlen_ptr as u64,
691 effective_flags,
692 )
693 };
694 if ret < 0 {
695 return Err(errno_from_negative_syscall_ret(ret));
696 }
697 #[allow(clippy::cast_possible_truncation)]
698 let raw_fd = ret as i32;
699 let fd = unsafe { AirOwnedFd::from_raw_fd(raw_fd) };
701 let address = raw_to_socket_addr(&addr_buf.bytes, addrlen);
702 Ok(AcceptResult { fd, address })
703}
704
705pub fn send(sock: BorrowedFd<'_>, buffer: &[u8], flags: MessageFlags) -> Result<usize, Errno> {
735 let buf_ptr: *const u8 = buffer.as_ptr();
736 #[allow(clippy::cast_sign_loss)]
738 let effective_flags = (flags.bits() | MSG_NOSIGNAL) as u64;
739 let ret = unsafe {
744 raw_syscall_sendto(
745 sock.as_raw_fd(),
746 buf_ptr as u64,
747 buffer.len(),
748 effective_flags,
749 0,
750 0,
751 )
752 };
753 if ret < 0 {
754 return Err(errno_from_negative_syscall_ret(ret));
755 }
756 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
757 Ok(ret as usize)
758}
759
760pub fn recv(sock: BorrowedFd<'_>, buffer: &mut [u8], flags: MessageFlags) -> Result<usize, Errno> {
790 let buf_ptr: *mut u8 = buffer.as_mut_ptr();
791 #[allow(clippy::cast_sign_loss)]
792 let flags_u64 = flags.bits() as u64;
793 let ret = unsafe {
799 raw_syscall_recvfrom(
800 sock.as_raw_fd(),
801 buf_ptr as u64,
802 buffer.len(),
803 flags_u64,
804 0,
805 0,
806 )
807 };
808 if ret < 0 {
809 return Err(errno_from_negative_syscall_ret(ret));
810 }
811 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
812 Ok(ret as usize)
813}
814
815pub fn sendmsg(
850 sock: BorrowedFd<'_>,
851 request: &SendMessageRequest<'_>,
852) -> Result<SendMessageResult, Errno> {
853 let (addr_storage, addr_storage_len) = if let Some(address) = request.address {
855 let (s, l) = socket_addr_to_raw(address);
856 (Some(s), l)
857 } else {
858 (None, 0u32)
859 };
860
861 let cmsg_buf = build_scm_rights_cmsg(request.fds);
863
864 let iov_ptr: *const IoSlice<'_> = request.iov.as_ptr();
865
866 let (name_ptr, name_len) = if let Some(ref s) = addr_storage {
867 (s.bytes.as_ptr() as u64, addr_storage_len)
868 } else {
869 (0u64, 0u32)
870 };
871
872 let (ctrl_ptr, ctrl_len) = if cmsg_buf.is_empty() {
873 (0u64, 0u64)
874 } else {
875 (cmsg_buf.as_ptr() as u64, cmsg_buf.len() as u64)
876 };
877
878 let mut msghdr = KernelMsghdr {
879 msg_name: name_ptr,
880 msg_namelen: name_len,
881 _pad: 0,
882 msg_iov: iov_ptr as u64,
883 msg_iovlen: request.iov.len() as u64,
884 msg_control: ctrl_ptr,
885 msg_controllen: ctrl_len,
886 msg_flags: 0,
887 _pad2: 0,
888 };
889
890 #[allow(clippy::cast_sign_loss)]
891 let effective_flags = (request.flags.bits() | MSG_NOSIGNAL) as u64;
892 let hdr_ptr: *mut KernelMsghdr = &mut msghdr;
893
894 let ret = unsafe { raw_syscall_sendmsg(sock.as_raw_fd(), hdr_ptr as u64, effective_flags) };
901 if ret < 0 {
902 return Err(errno_from_negative_syscall_ret(ret));
903 }
904 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
905 Ok(SendMessageResult {
906 bytes_sent: ret as usize,
907 })
908}
909
910pub fn recvmsg(
944 sock: BorrowedFd<'_>,
945 request: &mut ReceiveMessageRequest<'_>,
946) -> Result<ReceiveMessageResult, Errno> {
947 let mut cmsg_buf = [0u8; 1024];
949 let mut addr_buf = RawSockaddrStorage {
950 bytes: [0u8; MAX_SOCKADDR_LEN],
951 };
952 #[allow(clippy::cast_possible_truncation)]
954 let mut addrlen: u32 = MAX_SOCKADDR_LEN as u32;
955
956 let iov_ptr: *mut IoSliceMut<'_> = request.iov.as_mut_ptr();
957 let addr_ptr: *mut u8 = addr_buf.bytes.as_mut_ptr();
958 let addrlen_ptr: *mut u32 = &mut addrlen;
959 let cmsg_ptr: *mut u8 = cmsg_buf.as_mut_ptr();
960
961 let mut msghdr = KernelMsghdr {
962 msg_name: addr_ptr as u64,
963 msg_namelen: addrlen,
964 _pad: 0,
965 msg_iov: iov_ptr as u64,
966 msg_iovlen: request.iov.len() as u64,
967 msg_control: cmsg_ptr as u64,
968 msg_controllen: cmsg_buf.len() as u64,
969 msg_flags: 0,
970 _pad2: 0,
971 };
972
973 #[allow(clippy::cast_sign_loss)]
974 let flags_u64 = request.flags.bits() as u64;
975 let hdr_ptr: *mut KernelMsghdr = &mut msghdr;
976
977 let ret = unsafe { raw_syscall_recvmsg(sock.as_raw_fd(), hdr_ptr as u64, flags_u64) };
986 if ret < 0 {
987 return Err(errno_from_negative_syscall_ret(ret));
988 }
989
990 let returned_addrlen = msghdr.msg_namelen;
992 #[allow(clippy::cast_possible_truncation)]
994 let returned_cmsg_len = msghdr.msg_controllen as usize;
995 let returned_flags_bits = msghdr.msg_flags;
997
998 let _ = addrlen_ptr; let address = raw_to_socket_addr(&addr_buf.bytes, returned_addrlen);
1001 let fds = parse_scm_rights(&cmsg_buf, returned_cmsg_len);
1002
1003 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1004 let bytes_received = ret as usize;
1005
1006 let flags = MessageFlags::from_bits_truncate(returned_flags_bits);
1007
1008 Ok(ReceiveMessageResult {
1009 bytes_received,
1010 address,
1011 fds,
1012 flags,
1013 })
1014}
1015
1016pub fn shutdown(sock: BorrowedFd<'_>, mode: ShutdownMode) -> Result<(), Errno> {
1043 let how = mode as i32;
1044 let ret = unsafe { raw_syscall_shutdown(sock.as_raw_fd(), how) };
1048 if ret < 0 {
1049 return Err(errno_from_negative_syscall_ret(ret));
1050 }
1051 Ok(())
1052}
1053
1054pub fn getsockname(sock: BorrowedFd<'_>) -> Result<SocketAddr, Errno> {
1076 let mut addr_buf = RawSockaddrStorage {
1077 bytes: [0u8; MAX_SOCKADDR_LEN],
1078 };
1079 #[allow(clippy::cast_possible_truncation)]
1081 let mut addrlen: u32 = MAX_SOCKADDR_LEN as u32;
1082 let addr_ptr: *mut u8 = addr_buf.bytes.as_mut_ptr();
1083 let addrlen_ptr: *mut u32 = &mut addrlen;
1084 let ret =
1090 unsafe { raw_syscall_getsockname(sock.as_raw_fd(), addr_ptr as u64, addrlen_ptr as u64) };
1091 if ret < 0 {
1092 return Err(errno_from_negative_syscall_ret(ret));
1093 }
1094 raw_to_socket_addr(&addr_buf.bytes, addrlen).ok_or_else(|| {
1095 #[allow(clippy::unwrap_used)]
1097 Errno::from_nonzero(NonZeroI32::new(97).unwrap()) })
1099}
1100
1101pub fn getpeername(sock: BorrowedFd<'_>) -> Result<SocketAddr, Errno> {
1119 let mut addr_buf = RawSockaddrStorage {
1120 bytes: [0u8; MAX_SOCKADDR_LEN],
1121 };
1122 #[allow(clippy::cast_possible_truncation)]
1124 let mut addrlen: u32 = MAX_SOCKADDR_LEN as u32;
1125 let addr_ptr: *mut u8 = addr_buf.bytes.as_mut_ptr();
1126 let addrlen_ptr: *mut u32 = &mut addrlen;
1127 let ret =
1132 unsafe { raw_syscall_getpeername(sock.as_raw_fd(), addr_ptr as u64, addrlen_ptr as u64) };
1133 if ret < 0 {
1134 return Err(errno_from_negative_syscall_ret(ret));
1135 }
1136 raw_to_socket_addr(&addr_buf.bytes, addrlen).ok_or_else(|| {
1143 #[allow(clippy::unwrap_used)]
1144 Errno::from_nonzero(NonZeroI32::new(97).unwrap()) })
1146}
1147
1148pub fn socketpair(
1177 domain: SocketDomain,
1178 ty: SocketType,
1179 protocol: i32,
1180) -> Result<(AirOwnedFd, AirOwnedFd), Errno> {
1181 let mut sv = [0i32; 2];
1182 let sv_ptr: *mut i32 = sv.as_mut_ptr();
1183 let domain_raw = domain as i32;
1184 let ty_raw = (ty as i32) | SOCK_CLOEXEC;
1185 let ret = unsafe { raw_syscall_socketpair(domain_raw, ty_raw, protocol, sv_ptr as u64) };
1190 if ret < 0 {
1191 return Err(errno_from_negative_syscall_ret(ret));
1192 }
1193 let fd_a = unsafe { AirOwnedFd::from_raw_fd(sv[0]) };
1195 let fd_b = unsafe { AirOwnedFd::from_raw_fd(sv[1]) };
1196 Ok((fd_a, fd_b))
1197}
1198
1199pub fn get_so_keepalive(sock: BorrowedFd<'_>) -> Result<bool, Errno> {
1205 getsockopt_i32(sock, SOL_SOCKET, SO_KEEPALIVE).map(|v| v != 0)
1206}
1207
1208pub fn set_so_keepalive(sock: BorrowedFd<'_>, enable: bool) -> Result<(), Errno> {
1210 setsockopt_i32(sock, SOL_SOCKET, SO_KEEPALIVE, i32::from(enable))
1211}
1212
1213pub fn get_so_reuseaddr(sock: BorrowedFd<'_>) -> Result<bool, Errno> {
1215 getsockopt_i32(sock, SOL_SOCKET, SO_REUSEADDR).map(|v| v != 0)
1216}
1217
1218pub fn set_so_reuseaddr(sock: BorrowedFd<'_>, enable: bool) -> Result<(), Errno> {
1220 setsockopt_i32(sock, SOL_SOCKET, SO_REUSEADDR, i32::from(enable))
1221}
1222
1223pub fn get_so_reuseport(sock: BorrowedFd<'_>) -> Result<bool, Errno> {
1225 getsockopt_i32(sock, SOL_SOCKET, SO_REUSEPORT).map(|v| v != 0)
1226}
1227
1228pub fn set_so_reuseport(sock: BorrowedFd<'_>, enable: bool) -> Result<(), Errno> {
1230 setsockopt_i32(sock, SOL_SOCKET, SO_REUSEPORT, i32::from(enable))
1231}
1232
1233pub fn get_tcp_nodelay(sock: BorrowedFd<'_>) -> Result<bool, Errno> {
1235 getsockopt_i32(sock, IPPROTO_TCP, TCP_NODELAY).map(|v| v != 0)
1236}
1237
1238pub fn set_tcp_nodelay(sock: BorrowedFd<'_>, enable: bool) -> Result<(), Errno> {
1240 setsockopt_i32(sock, IPPROTO_TCP, TCP_NODELAY, i32::from(enable))
1241}
1242
1243pub fn get_so_rcvbuf(sock: BorrowedFd<'_>) -> Result<u32, Errno> {
1245 getsockopt_i32(sock, SOL_SOCKET, SO_RCVBUF).map(|v| {
1246 #[allow(clippy::cast_sign_loss)]
1248 let u = v as u32;
1249 u
1250 })
1251}
1252
1253pub fn set_so_rcvbuf(sock: BorrowedFd<'_>, size: u32) -> Result<(), Errno> {
1255 #[allow(clippy::cast_possible_truncation)]
1257 let val = if size > i32::MAX as u32 {
1258 i32::MAX
1259 } else {
1260 size as i32
1261 };
1262 setsockopt_i32(sock, SOL_SOCKET, SO_RCVBUF, val)
1263}
1264
1265pub fn get_so_sndbuf(sock: BorrowedFd<'_>) -> Result<u32, Errno> {
1267 getsockopt_i32(sock, SOL_SOCKET, SO_SNDBUF).map(|v| {
1268 #[allow(clippy::cast_sign_loss)]
1269 let u = v as u32;
1270 u
1271 })
1272}
1273
1274pub fn set_so_sndbuf(sock: BorrowedFd<'_>, size: u32) -> Result<(), Errno> {
1276 #[allow(clippy::cast_possible_truncation)]
1277 let val = if size > i32::MAX as u32 {
1278 i32::MAX
1279 } else {
1280 size as i32
1281 };
1282 setsockopt_i32(sock, SOL_SOCKET, SO_SNDBUF, val)
1283}
1284
1285pub fn get_so_error(sock: BorrowedFd<'_>) -> Result<Option<Errno>, Errno> {
1289 let val = getsockopt_i32(sock, SOL_SOCKET, SO_ERROR)?;
1290 if val == 0 {
1291 Ok(None)
1292 } else {
1293 let nz = NonZeroI32::new(val).ok_or_else(|| {
1294 #[allow(clippy::unwrap_used)]
1296 Errno::from_nonzero(NonZeroI32::new(22).unwrap()) })?;
1298 Ok(Some(Errno::from_nonzero(nz)))
1299 }
1300}
1301
1302pub fn get_so_peercred(sock: BorrowedFd<'_>) -> Result<UnixCredentials, Errno> {
1306 let mut cred = [0i32; 3];
1308 let mut length: u32 = 12;
1309 let cred_ptr: *mut i32 = cred.as_mut_ptr();
1310 let len_ptr: *mut u32 = &mut length;
1311 let ret = unsafe {
1316 raw_syscall_getsockopt(
1317 sock.as_raw_fd(),
1318 SOL_SOCKET,
1319 SO_PEERCRED,
1320 cred_ptr as u64,
1321 len_ptr as u64,
1322 )
1323 };
1324 if ret < 0 {
1325 return Err(errno_from_negative_syscall_ret(ret));
1326 }
1327 let pid_raw = cred[0];
1329 #[allow(clippy::cast_sign_loss)]
1330 let uid = cred[1] as u32;
1331 #[allow(clippy::cast_sign_loss)]
1332 let gid = cred[2] as u32;
1333 let nz = NonZeroI32::new(pid_raw).ok_or_else(|| {
1334 #[allow(clippy::unwrap_used)]
1335 Errno::from_nonzero(NonZeroI32::new(22).unwrap()) })?;
1337 Ok(UnixCredentials {
1338 pid: Pid::from_nonzero(nz),
1339 uid,
1340 gid,
1341 })
1342}
1343
1344pub fn get_so_linger(sock: BorrowedFd<'_>) -> Result<LingerOption, Errno> {
1346 let mut linger = [0i32; 2];
1348 let mut length: u32 = 8;
1349 let linger_ptr: *mut i32 = linger.as_mut_ptr();
1350 let len_ptr: *mut u32 = &mut length;
1351 let ret = unsafe {
1356 raw_syscall_getsockopt(
1357 sock.as_raw_fd(),
1358 SOL_SOCKET,
1359 SO_LINGER,
1360 linger_ptr as u64,
1361 len_ptr as u64,
1362 )
1363 };
1364 if ret < 0 {
1365 return Err(errno_from_negative_syscall_ret(ret));
1366 }
1367 if linger[0] == 0 {
1368 Ok(LingerOption::Disabled)
1369 } else {
1370 #[allow(clippy::cast_sign_loss)]
1371 let secs = linger[1] as u64;
1372 Ok(LingerOption::Enabled(core::time::Duration::from_secs(secs)))
1373 }
1374}
1375
1376pub fn set_so_linger(sock: BorrowedFd<'_>, linger: LingerOption) -> Result<(), Errno> {
1378 let (l_onoff, l_linger): (i32, i32) = match linger {
1379 LingerOption::Disabled => (0, 0),
1380 LingerOption::Enabled(d) => {
1381 let secs = d.as_secs();
1383 #[allow(clippy::cast_possible_truncation)]
1384 let secs_i32 = if secs > i32::MAX as u64 {
1385 i32::MAX
1386 } else {
1387 secs as i32
1388 };
1389 (1, secs_i32)
1390 }
1391 };
1392 let buffer = [l_onoff, l_linger];
1393 let buf_ptr: *const i32 = buffer.as_ptr();
1394 let length: u32 = 8;
1395 let ret = unsafe {
1400 raw_syscall_setsockopt(
1401 sock.as_raw_fd(),
1402 SOL_SOCKET,
1403 SO_LINGER,
1404 buf_ptr as u64,
1405 length,
1406 )
1407 };
1408 if ret < 0 {
1409 return Err(errno_from_negative_syscall_ret(ret));
1410 }
1411 Ok(())
1412}
1413
1414pub fn set_so_bindtodevice(sock: BorrowedFd<'_>, ifname: &CStr) -> Result<(), Errno> {
1416 let bytes = ifname.to_bytes_with_nul();
1417 let ptr: *const u8 = bytes.as_ptr();
1418 #[allow(clippy::cast_possible_truncation)]
1419 let length = bytes.len() as u32;
1420 let ret = unsafe {
1425 raw_syscall_setsockopt(
1426 sock.as_raw_fd(),
1427 SOL_SOCKET,
1428 SO_BINDTODEVICE,
1429 ptr as u64,
1430 length,
1431 )
1432 };
1433 if ret < 0 {
1434 return Err(errno_from_negative_syscall_ret(ret));
1435 }
1436 Ok(())
1437}
1438
1439pub fn get_so_bindtodevice(sock: BorrowedFd<'_>) -> Result<CString, Errno> {
1441 let mut buffer = [0u8; 16];
1443 let mut length: u32 = 16;
1444 let buf_ptr: *mut u8 = buffer.as_mut_ptr();
1445 let len_ptr: *mut u32 = &mut length;
1446 let ret = unsafe {
1451 raw_syscall_getsockopt(
1452 sock.as_raw_fd(),
1453 SOL_SOCKET,
1454 SO_BINDTODEVICE,
1455 buf_ptr as u64,
1456 len_ptr as u64,
1457 )
1458 };
1459 if ret < 0 {
1460 return Err(errno_from_negative_syscall_ret(ret));
1461 }
1462 let nul_pos = buffer
1464 .iter()
1465 .position(|&b| b == 0)
1466 .unwrap_or(length as usize);
1467 CString::new(&buffer[..nul_pos]).map_err(|_| {
1473 #[allow(clippy::unwrap_used)]
1474 Errno::from_nonzero(NonZeroI32::new(22).unwrap()) })
1476}
1477
1478pub fn set_tcp_keepidle(sock: BorrowedFd<'_>, seconds: u32) -> Result<(), Errno> {
1480 #[allow(clippy::cast_possible_truncation)]
1481 let val = if seconds > i32::MAX as u32 {
1482 i32::MAX
1483 } else {
1484 seconds as i32
1485 };
1486 setsockopt_i32(sock, IPPROTO_TCP, TCP_KEEPIDLE, val)
1487}
1488
1489pub fn set_tcp_keepintvl(sock: BorrowedFd<'_>, seconds: u32) -> Result<(), Errno> {
1491 #[allow(clippy::cast_possible_truncation)]
1492 let val = if seconds > i32::MAX as u32 {
1493 i32::MAX
1494 } else {
1495 seconds as i32
1496 };
1497 setsockopt_i32(sock, IPPROTO_TCP, TCP_KEEPINTVL, val)
1498}
1499
1500pub fn set_tcp_keepcnt(sock: BorrowedFd<'_>, count: u32) -> Result<(), Errno> {
1502 #[allow(clippy::cast_possible_truncation)]
1503 let val = if count > i32::MAX as u32 {
1504 i32::MAX
1505 } else {
1506 count as i32
1507 };
1508 setsockopt_i32(sock, IPPROTO_TCP, TCP_KEEPCNT, val)
1509}
1510
1511pub fn set_ip_ttl(sock: BorrowedFd<'_>, ttl: u8) -> Result<(), Errno> {
1513 setsockopt_i32(sock, IPPROTO_IP, IP_TTL, i32::from(ttl))
1514}
1515
1516pub fn set_ipv6_v6only(sock: BorrowedFd<'_>, v6only: bool) -> Result<(), Errno> {
1518 setsockopt_i32(sock, IPPROTO_IPV6, IPV6_V6ONLY, i32::from(v6only))
1519}
1520
1521fn decode_timeout(tv: Timeval) -> Option<Timeval> {
1535 if tv.tv_sec == 0 && tv.tv_usec == 0 {
1536 None
1537 } else {
1538 Some(tv)
1539 }
1540}
1541
1542fn set_socket_timeout(
1548 sock: BorrowedFd<'_>,
1549 optname: i32,
1550 timeout: Option<Timeval>,
1551) -> Result<(), Errno> {
1552 let tv = timeout.unwrap_or(Timeval {
1553 tv_sec: 0,
1554 tv_usec: 0,
1555 });
1556 let tv_ptr: *const Timeval = &tv;
1557 let ret = unsafe {
1563 raw_syscall_setsockopt(
1564 sock.as_raw_fd(),
1565 SOL_SOCKET,
1566 optname,
1567 tv_ptr as u64,
1568 TIMEVAL_LEN,
1569 )
1570 };
1571 if ret < 0 {
1572 return Err(errno_from_negative_syscall_ret(ret));
1573 }
1574 Ok(())
1575}
1576
1577fn get_socket_timeout(sock: BorrowedFd<'_>, optname: i32) -> Result<Option<Timeval>, Errno> {
1580 let mut tv = Timeval {
1581 tv_sec: 0,
1582 tv_usec: 0,
1583 };
1584 let mut length: u32 = TIMEVAL_LEN;
1585 let tv_ptr: *mut Timeval = &mut tv;
1586 let len_ptr: *mut u32 = &mut length;
1587 let ret = unsafe {
1593 raw_syscall_getsockopt(
1594 sock.as_raw_fd(),
1595 SOL_SOCKET,
1596 optname,
1597 tv_ptr as u64,
1598 len_ptr as u64,
1599 )
1600 };
1601 if ret < 0 {
1602 return Err(errno_from_negative_syscall_ret(ret));
1603 }
1604 Ok(decode_timeout(tv))
1605}
1606
1607pub fn set_receive_timeout(sock: BorrowedFd<'_>, timeout: Option<Timeval>) -> Result<(), Errno> {
1609 set_socket_timeout(sock, SO_RCVTIMEO, timeout)
1610}
1611
1612pub fn receive_timeout(sock: BorrowedFd<'_>) -> Result<Option<Timeval>, Errno> {
1614 get_socket_timeout(sock, SO_RCVTIMEO)
1615}
1616
1617pub fn set_send_timeout(sock: BorrowedFd<'_>, timeout: Option<Timeval>) -> Result<(), Errno> {
1619 set_socket_timeout(sock, SO_SNDTIMEO, timeout)
1620}
1621
1622pub fn send_timeout(sock: BorrowedFd<'_>) -> Result<Option<Timeval>, Errno> {
1624 get_socket_timeout(sock, SO_SNDTIMEO)
1625}
1626
1627fn set_ip_membership_v4(
1635 sock: BorrowedFd<'_>,
1636 optname: i32,
1637 membership: &IpMembershipV4,
1638) -> Result<(), Errno> {
1639 let ptr: *const IpMembershipV4 = membership;
1640 let ret = unsafe {
1646 raw_syscall_setsockopt(
1647 sock.as_raw_fd(),
1648 IPPROTO_IP,
1649 optname,
1650 ptr as u64,
1651 IP_MREQ_LEN,
1652 )
1653 };
1654 if ret < 0 {
1655 return Err(errno_from_negative_syscall_ret(ret));
1656 }
1657 Ok(())
1658}
1659
1660fn set_ip_membership_v6(
1663 sock: BorrowedFd<'_>,
1664 optname: i32,
1665 membership: &IpMembershipV6,
1666) -> Result<(), Errno> {
1667 let ptr: *const IpMembershipV6 = membership;
1668 let ret = unsafe {
1674 raw_syscall_setsockopt(
1675 sock.as_raw_fd(),
1676 IPPROTO_IPV6,
1677 optname,
1678 ptr as u64,
1679 IPV6_MREQ_LEN,
1680 )
1681 };
1682 if ret < 0 {
1683 return Err(errno_from_negative_syscall_ret(ret));
1684 }
1685 Ok(())
1686}
1687
1688pub fn join_multicast_v4(sock: BorrowedFd<'_>, membership: &IpMembershipV4) -> Result<(), Errno> {
1690 set_ip_membership_v4(sock, IP_ADD_MEMBERSHIP, membership)
1691}
1692
1693pub fn leave_multicast_v4(sock: BorrowedFd<'_>, membership: &IpMembershipV4) -> Result<(), Errno> {
1695 set_ip_membership_v4(sock, IP_DROP_MEMBERSHIP, membership)
1696}
1697
1698pub fn join_multicast_v6(sock: BorrowedFd<'_>, membership: &IpMembershipV6) -> Result<(), Errno> {
1700 set_ip_membership_v6(sock, IPV6_ADD_MEMBERSHIP, membership)
1701}
1702
1703pub fn leave_multicast_v6(sock: BorrowedFd<'_>, membership: &IpMembershipV6) -> Result<(), Errno> {
1705 set_ip_membership_v6(sock, IPV6_DROP_MEMBERSHIP, membership)
1706}
1707
1708#[cfg(target_arch = "x86_64")]
1713#[inline]
1714unsafe fn raw_syscall_socket(domain: i32, ty: i32, protocol: i32) -> i64 {
1715 let ret: i64;
1716 unsafe {
1718 core::arch::asm!(
1719 "syscall",
1720 in("rax") 41_i64,
1721 in("rdi") i64::from(domain),
1722 in("rsi") i64::from(ty),
1723 in("rdx") i64::from(protocol),
1724 lateout("rax") ret,
1725 lateout("rcx") _,
1726 lateout("r11") _,
1727 options(nostack, preserves_flags),
1728 );
1729 }
1730 ret
1731}
1732
1733#[cfg(target_arch = "x86_64")]
1734#[inline]
1735unsafe fn raw_syscall_bind(fd: i32, address: u64, addrlen: u32) -> i64 {
1736 let ret: i64;
1737 unsafe {
1739 core::arch::asm!(
1740 "syscall",
1741 in("rax") 49_i64,
1742 in("rdi") i64::from(fd),
1743 in("rsi") address,
1744 in("rdx") i64::from(addrlen),
1745 lateout("rax") ret,
1746 lateout("rcx") _,
1747 lateout("r11") _,
1748 options(nostack, preserves_flags),
1749 );
1750 }
1751 ret
1752}
1753
1754#[cfg(target_arch = "x86_64")]
1755#[inline]
1756unsafe fn raw_syscall_listen(fd: i32, backlog: i32) -> i64 {
1757 let ret: i64;
1758 unsafe {
1760 core::arch::asm!(
1761 "syscall",
1762 in("rax") 50_i64,
1763 in("rdi") i64::from(fd),
1764 in("rsi") i64::from(backlog),
1765 lateout("rax") ret,
1766 lateout("rcx") _,
1767 lateout("r11") _,
1768 options(nostack, preserves_flags),
1769 );
1770 }
1771 ret
1772}
1773
1774#[cfg(target_arch = "x86_64")]
1775#[inline]
1776unsafe fn raw_syscall_connect(fd: i32, address: u64, addrlen: u32) -> i64 {
1777 let ret: i64;
1778 unsafe {
1780 core::arch::asm!(
1781 "syscall",
1782 in("rax") 42_i64,
1783 in("rdi") i64::from(fd),
1784 in("rsi") address,
1785 in("rdx") i64::from(addrlen),
1786 lateout("rax") ret,
1787 lateout("rcx") _,
1788 lateout("r11") _,
1789 options(nostack, preserves_flags),
1790 );
1791 }
1792 ret
1793}
1794
1795#[cfg(target_arch = "x86_64")]
1796#[inline]
1797unsafe fn raw_syscall_accept4(fd: i32, address: u64, addrlen: u64, flags: i32) -> i64 {
1798 let ret: i64;
1799 unsafe {
1801 core::arch::asm!(
1802 "syscall",
1803 in("rax") 288_i64,
1804 in("rdi") i64::from(fd),
1805 in("rsi") address,
1806 in("rdx") addrlen,
1807 in("r10") i64::from(flags),
1808 lateout("rax") ret,
1809 lateout("rcx") _,
1810 lateout("r11") _,
1811 options(nostack, preserves_flags),
1812 );
1813 }
1814 ret
1815}
1816
1817#[cfg(target_arch = "x86_64")]
1818#[inline]
1819unsafe fn raw_syscall_sendto(
1820 fd: i32,
1821 buffer: u64,
1822 length: usize,
1823 flags: u64,
1824 address: u64,
1825 addrlen: u32,
1826) -> i64 {
1827 let ret: i64;
1828 unsafe {
1830 core::arch::asm!(
1831 "syscall",
1832 in("rax") 44_i64,
1833 in("rdi") i64::from(fd),
1834 in("rsi") buffer,
1835 in("rdx") length,
1836 in("r10") flags,
1837 in("r8") address,
1838 in("r9") i64::from(addrlen),
1839 lateout("rax") ret,
1840 lateout("rcx") _,
1841 lateout("r11") _,
1842 options(nostack, preserves_flags),
1843 );
1844 }
1845 ret
1846}
1847
1848#[cfg(target_arch = "x86_64")]
1849#[inline]
1850unsafe fn raw_syscall_recvfrom(
1851 fd: i32,
1852 buffer: u64,
1853 length: usize,
1854 flags: u64,
1855 address: u64,
1856 addrlen: u64,
1857) -> i64 {
1858 let ret: i64;
1859 unsafe {
1861 core::arch::asm!(
1862 "syscall",
1863 in("rax") 45_i64,
1864 in("rdi") i64::from(fd),
1865 in("rsi") buffer,
1866 in("rdx") length,
1867 in("r10") flags,
1868 in("r8") address,
1869 in("r9") addrlen,
1870 lateout("rax") ret,
1871 lateout("rcx") _,
1872 lateout("r11") _,
1873 options(nostack, preserves_flags),
1874 );
1875 }
1876 ret
1877}
1878
1879#[cfg(target_arch = "x86_64")]
1880#[inline]
1881unsafe fn raw_syscall_sendmsg(fd: i32, msghdr: u64, flags: u64) -> i64 {
1882 let ret: i64;
1883 unsafe {
1885 core::arch::asm!(
1886 "syscall",
1887 in("rax") 46_i64,
1888 in("rdi") i64::from(fd),
1889 in("rsi") msghdr,
1890 in("rdx") flags,
1891 lateout("rax") ret,
1892 lateout("rcx") _,
1893 lateout("r11") _,
1894 options(nostack, preserves_flags),
1895 );
1896 }
1897 ret
1898}
1899
1900#[cfg(target_arch = "x86_64")]
1901#[inline]
1902unsafe fn raw_syscall_recvmsg(fd: i32, msghdr: u64, flags: u64) -> i64 {
1903 let ret: i64;
1904 unsafe {
1906 core::arch::asm!(
1907 "syscall",
1908 in("rax") 47_i64,
1909 in("rdi") i64::from(fd),
1910 in("rsi") msghdr,
1911 in("rdx") flags,
1912 lateout("rax") ret,
1913 lateout("rcx") _,
1914 lateout("r11") _,
1915 options(nostack, preserves_flags),
1916 );
1917 }
1918 ret
1919}
1920
1921#[cfg(target_arch = "x86_64")]
1922#[inline]
1923unsafe fn raw_syscall_shutdown(fd: i32, how: i32) -> i64 {
1924 let ret: i64;
1925 unsafe {
1927 core::arch::asm!(
1928 "syscall",
1929 in("rax") 48_i64,
1930 in("rdi") i64::from(fd),
1931 in("rsi") i64::from(how),
1932 lateout("rax") ret,
1933 lateout("rcx") _,
1934 lateout("r11") _,
1935 options(nostack, preserves_flags),
1936 );
1937 }
1938 ret
1939}
1940
1941#[cfg(target_arch = "x86_64")]
1942#[inline]
1943unsafe fn raw_syscall_getsockname(fd: i32, address: u64, addrlen: u64) -> i64 {
1944 let ret: i64;
1945 unsafe {
1947 core::arch::asm!(
1948 "syscall",
1949 in("rax") 51_i64,
1950 in("rdi") i64::from(fd),
1951 in("rsi") address,
1952 in("rdx") addrlen,
1953 lateout("rax") ret,
1954 lateout("rcx") _,
1955 lateout("r11") _,
1956 options(nostack, preserves_flags),
1957 );
1958 }
1959 ret
1960}
1961
1962#[cfg(target_arch = "x86_64")]
1963#[inline]
1964unsafe fn raw_syscall_getpeername(fd: i32, address: u64, addrlen: u64) -> i64 {
1965 let ret: i64;
1966 unsafe {
1968 core::arch::asm!(
1969 "syscall",
1970 in("rax") 52_i64,
1971 in("rdi") i64::from(fd),
1972 in("rsi") address,
1973 in("rdx") addrlen,
1974 lateout("rax") ret,
1975 lateout("rcx") _,
1976 lateout("r11") _,
1977 options(nostack, preserves_flags),
1978 );
1979 }
1980 ret
1981}
1982
1983#[cfg(target_arch = "x86_64")]
1984#[inline]
1985unsafe fn raw_syscall_socketpair(domain: i32, ty: i32, protocol: i32, sv: u64) -> i64 {
1986 let ret: i64;
1987 unsafe {
1989 core::arch::asm!(
1990 "syscall",
1991 in("rax") 53_i64,
1992 in("rdi") i64::from(domain),
1993 in("rsi") i64::from(ty),
1994 in("rdx") i64::from(protocol),
1995 in("r10") sv,
1996 lateout("rax") ret,
1997 lateout("rcx") _,
1998 lateout("r11") _,
1999 options(nostack, preserves_flags),
2000 );
2001 }
2002 ret
2003}
2004
2005#[cfg(target_arch = "x86_64")]
2006#[inline]
2007unsafe fn raw_syscall_getsockopt(
2008 fd: i32,
2009 level: i32,
2010 optname: i32,
2011 optval: u64,
2012 optlen: u64,
2013) -> i64 {
2014 let ret: i64;
2015 unsafe {
2017 core::arch::asm!(
2018 "syscall",
2019 in("rax") 55_i64,
2020 in("rdi") i64::from(fd),
2021 in("rsi") i64::from(level),
2022 in("rdx") i64::from(optname),
2023 in("r10") optval,
2024 in("r8") optlen,
2025 lateout("rax") ret,
2026 lateout("rcx") _,
2027 lateout("r11") _,
2028 options(nostack, preserves_flags),
2029 );
2030 }
2031 ret
2032}
2033
2034#[cfg(target_arch = "x86_64")]
2035#[inline]
2036unsafe fn raw_syscall_setsockopt(
2037 fd: i32,
2038 level: i32,
2039 optname: i32,
2040 optval: u64,
2041 optlen: u32,
2042) -> i64 {
2043 let ret: i64;
2044 unsafe {
2046 core::arch::asm!(
2047 "syscall",
2048 in("rax") 54_i64,
2049 in("rdi") i64::from(fd),
2050 in("rsi") i64::from(level),
2051 in("rdx") i64::from(optname),
2052 in("r10") optval,
2053 in("r8") i64::from(optlen),
2054 lateout("rax") ret,
2055 lateout("rcx") _,
2056 lateout("r11") _,
2057 options(nostack, preserves_flags),
2058 );
2059 }
2060 ret
2061}
2062
2063#[cfg(target_arch = "aarch64")]
2068#[inline]
2069unsafe fn raw_syscall_socket(domain: i32, ty: i32, protocol: i32) -> i64 {
2070 let ret: i64;
2071 unsafe {
2073 core::arch::asm!(
2074 "svc 0",
2075 in("x8") 198_i64,
2076 inout("x0") i64::from(domain) => ret,
2077 in("x1") i64::from(ty),
2078 in("x2") i64::from(protocol),
2079 options(nostack, preserves_flags),
2080 );
2081 }
2082 ret
2083}
2084
2085#[cfg(target_arch = "aarch64")]
2086#[inline]
2087unsafe fn raw_syscall_bind(fd: i32, address: u64, addrlen: u32) -> i64 {
2088 let ret: i64;
2089 unsafe {
2091 core::arch::asm!(
2092 "svc 0",
2093 in("x8") 200_i64,
2094 inout("x0") i64::from(fd) => ret,
2095 in("x1") address,
2096 in("x2") i64::from(addrlen),
2097 options(nostack, preserves_flags),
2098 );
2099 }
2100 ret
2101}
2102
2103#[cfg(target_arch = "aarch64")]
2104#[inline]
2105unsafe fn raw_syscall_listen(fd: i32, backlog: i32) -> i64 {
2106 let ret: i64;
2107 unsafe {
2109 core::arch::asm!(
2110 "svc 0",
2111 in("x8") 201_i64,
2112 inout("x0") i64::from(fd) => ret,
2113 in("x1") i64::from(backlog),
2114 options(nostack, preserves_flags),
2115 );
2116 }
2117 ret
2118}
2119
2120#[cfg(target_arch = "aarch64")]
2121#[inline]
2122unsafe fn raw_syscall_connect(fd: i32, address: u64, addrlen: u32) -> i64 {
2123 let ret: i64;
2124 unsafe {
2126 core::arch::asm!(
2127 "svc 0",
2128 in("x8") 203_i64,
2129 inout("x0") i64::from(fd) => ret,
2130 in("x1") address,
2131 in("x2") i64::from(addrlen),
2132 options(nostack, preserves_flags),
2133 );
2134 }
2135 ret
2136}
2137
2138#[cfg(target_arch = "aarch64")]
2139#[inline]
2140unsafe fn raw_syscall_accept4(fd: i32, address: u64, addrlen: u64, flags: i32) -> i64 {
2141 let ret: i64;
2142 unsafe {
2144 core::arch::asm!(
2145 "svc 0",
2146 in("x8") 242_i64,
2147 inout("x0") i64::from(fd) => ret,
2148 in("x1") address,
2149 in("x2") addrlen,
2150 in("x3") i64::from(flags),
2151 options(nostack, preserves_flags),
2152 );
2153 }
2154 ret
2155}
2156
2157#[cfg(target_arch = "aarch64")]
2158#[inline]
2159unsafe fn raw_syscall_sendto(
2160 fd: i32,
2161 buffer: u64,
2162 length: usize,
2163 flags: u64,
2164 address: u64,
2165 addrlen: u32,
2166) -> i64 {
2167 let ret: i64;
2168 unsafe {
2170 core::arch::asm!(
2171 "svc 0",
2172 in("x8") 206_i64,
2173 inout("x0") i64::from(fd) => ret,
2174 in("x1") buffer,
2175 in("x2") length,
2176 in("x3") flags,
2177 in("x4") address,
2178 in("x5") i64::from(addrlen),
2179 options(nostack, preserves_flags),
2180 );
2181 }
2182 ret
2183}
2184
2185#[cfg(target_arch = "aarch64")]
2186#[inline]
2187unsafe fn raw_syscall_recvfrom(
2188 fd: i32,
2189 buffer: u64,
2190 length: usize,
2191 flags: u64,
2192 address: u64,
2193 addrlen: u64,
2194) -> i64 {
2195 let ret: i64;
2196 unsafe {
2198 core::arch::asm!(
2199 "svc 0",
2200 in("x8") 207_i64,
2201 inout("x0") i64::from(fd) => ret,
2202 in("x1") buffer,
2203 in("x2") length,
2204 in("x3") flags,
2205 in("x4") address,
2206 in("x5") addrlen,
2207 options(nostack, preserves_flags),
2208 );
2209 }
2210 ret
2211}
2212
2213#[cfg(target_arch = "aarch64")]
2214#[inline]
2215unsafe fn raw_syscall_sendmsg(fd: i32, msghdr: u64, flags: u64) -> i64 {
2216 let ret: i64;
2217 unsafe {
2219 core::arch::asm!(
2220 "svc 0",
2221 in("x8") 211_i64,
2222 inout("x0") i64::from(fd) => ret,
2223 in("x1") msghdr,
2224 in("x2") flags,
2225 options(nostack, preserves_flags),
2226 );
2227 }
2228 ret
2229}
2230
2231#[cfg(target_arch = "aarch64")]
2232#[inline]
2233unsafe fn raw_syscall_recvmsg(fd: i32, msghdr: u64, flags: u64) -> i64 {
2234 let ret: i64;
2235 unsafe {
2237 core::arch::asm!(
2238 "svc 0",
2239 in("x8") 212_i64,
2240 inout("x0") i64::from(fd) => ret,
2241 in("x1") msghdr,
2242 in("x2") flags,
2243 options(nostack, preserves_flags),
2244 );
2245 }
2246 ret
2247}
2248
2249#[cfg(target_arch = "aarch64")]
2250#[inline]
2251unsafe fn raw_syscall_shutdown(fd: i32, how: i32) -> i64 {
2252 let ret: i64;
2253 unsafe {
2255 core::arch::asm!(
2256 "svc 0",
2257 in("x8") 210_i64,
2258 inout("x0") i64::from(fd) => ret,
2259 in("x1") i64::from(how),
2260 options(nostack, preserves_flags),
2261 );
2262 }
2263 ret
2264}
2265
2266#[cfg(target_arch = "aarch64")]
2267#[inline]
2268unsafe fn raw_syscall_getsockname(fd: i32, address: u64, addrlen: u64) -> i64 {
2269 let ret: i64;
2270 unsafe {
2272 core::arch::asm!(
2273 "svc 0",
2274 in("x8") 204_i64,
2275 inout("x0") i64::from(fd) => ret,
2276 in("x1") address,
2277 in("x2") addrlen,
2278 options(nostack, preserves_flags),
2279 );
2280 }
2281 ret
2282}
2283
2284#[cfg(target_arch = "aarch64")]
2285#[inline]
2286unsafe fn raw_syscall_getpeername(fd: i32, address: u64, addrlen: u64) -> i64 {
2287 let ret: i64;
2288 unsafe {
2290 core::arch::asm!(
2291 "svc 0",
2292 in("x8") 205_i64,
2293 inout("x0") i64::from(fd) => ret,
2294 in("x1") address,
2295 in("x2") addrlen,
2296 options(nostack, preserves_flags),
2297 );
2298 }
2299 ret
2300}
2301
2302#[cfg(target_arch = "aarch64")]
2303#[inline]
2304unsafe fn raw_syscall_socketpair(domain: i32, ty: i32, protocol: i32, sv: u64) -> i64 {
2305 let ret: i64;
2306 unsafe {
2308 core::arch::asm!(
2309 "svc 0",
2310 in("x8") 199_i64,
2311 inout("x0") i64::from(domain) => ret,
2312 in("x1") i64::from(ty),
2313 in("x2") i64::from(protocol),
2314 in("x3") sv,
2315 options(nostack, preserves_flags),
2316 );
2317 }
2318 ret
2319}
2320
2321#[cfg(target_arch = "aarch64")]
2322#[inline]
2323unsafe fn raw_syscall_getsockopt(
2324 fd: i32,
2325 level: i32,
2326 optname: i32,
2327 optval: u64,
2328 optlen: u64,
2329) -> i64 {
2330 let ret: i64;
2331 unsafe {
2333 core::arch::asm!(
2334 "svc 0",
2335 in("x8") 209_i64,
2336 inout("x0") i64::from(fd) => ret,
2337 in("x1") i64::from(level),
2338 in("x2") i64::from(optname),
2339 in("x3") optval,
2340 in("x4") optlen,
2341 options(nostack, preserves_flags),
2342 );
2343 }
2344 ret
2345}
2346
2347#[cfg(target_arch = "aarch64")]
2348#[inline]
2349unsafe fn raw_syscall_setsockopt(
2350 fd: i32,
2351 level: i32,
2352 optname: i32,
2353 optval: u64,
2354 optlen: u32,
2355) -> i64 {
2356 let ret: i64;
2357 unsafe {
2359 core::arch::asm!(
2360 "svc 0",
2361 in("x8") 208_i64,
2362 inout("x0") i64::from(fd) => ret,
2363 in("x1") i64::from(level),
2364 in("x2") i64::from(optname),
2365 in("x3") optval,
2366 in("x4") i64::from(optlen),
2367 options(nostack, preserves_flags),
2368 );
2369 }
2370 ret
2371}
2372
2373#[cfg(all(test, target_os = "linux"))]
2378mod tests {
2379 use super::*;
2380 use air_sys_types::fd::AsFd;
2381
2382 const EADDRINUSE: Errno = Errno::try_from_raw(98).unwrap();
2385 const ECONNREFUSED: Errno = Errno::try_from_raw(111).unwrap();
2386 const ENOTCONN: Errno = Errno::try_from_raw(107).unwrap();
2387 const ESOCKTNOSUPPORT: Errno = Errno::try_from_raw(94).unwrap();
2388
2389 fn invalid_fd() -> BorrowedFd<'static> {
2395 unsafe { BorrowedFd::borrow_raw(i32::MAX) }
2398 }
2399
2400 #[test]
2402 fn socket_creates_valid_fd() {
2403 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket Unix stream");
2404 let _borrowed = fd.as_fd();
2406 }
2407
2408 #[test]
2410 fn socketpair_creates_two_valid_fds() {
2411 let (fd_a, fd_b) =
2412 socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
2413 let raw_a = fd_a.as_raw_fd();
2414 let raw_b = fd_b.as_raw_fd();
2415 assert_ne!(raw_a, raw_b, "les deux FDs doivent être distincts");
2416 }
2417
2418 #[test]
2420 fn socketpair_send_recv_roundtrip() {
2421 let (fd_a, fd_b) =
2422 socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
2423
2424 let data = b"bonjour";
2425 let n = send(fd_a.as_fd(), data, MessageFlags::empty()).expect("send");
2426 assert_eq!(n, data.len(), "send doit envoyer tous les octets");
2427
2428 let mut buffer = [0u8; 16];
2429 let m = recv(fd_b.as_fd(), &mut buffer, MessageFlags::empty()).expect("recv");
2430 assert_eq!(
2431 m,
2432 data.len(),
2433 "recv doit recevoir autant d'octets qu'envoyés"
2434 );
2435 assert_eq!(
2436 &buffer[..m],
2437 data,
2438 "les données reçues doivent être identiques"
2439 );
2440 }
2441
2442 #[test]
2444 fn socketpair_sendmsg_recvmsg_roundtrip() {
2445 let (fd_a, fd_b) =
2446 socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
2447
2448 let data1 = b"hello";
2449 let data2 = b" world";
2450 let iov_send = [IoSlice::new(data1), IoSlice::new(data2)];
2451 let req_send = SendMessageRequest {
2452 iov: &iov_send,
2453 address: None,
2454 fds: &[],
2455 flags: MessageFlags::empty(),
2456 };
2457 let result = sendmsg(fd_a.as_fd(), &req_send).expect("sendmsg");
2458 assert_eq!(
2459 result.bytes_sent,
2460 data1.len() + data2.len(),
2461 "sendmsg doit envoyer 11 octets"
2462 );
2463
2464 let mut buffer = [0u8; 32];
2465 let mut iov_recv = [IoSliceMut::new(&mut buffer)];
2466 let mut req_recv = ReceiveMessageRequest {
2467 iov: &mut iov_recv,
2468 flags: MessageFlags::empty(),
2469 };
2470 let result_recv = recvmsg(fd_b.as_fd(), &mut req_recv).expect("recvmsg");
2471 assert_eq!(
2472 result_recv.bytes_received,
2473 data1.len() + data2.len(),
2474 "recvmsg doit recevoir 11 octets"
2475 );
2476 assert_eq!(&buffer[..result_recv.bytes_received], b"hello world");
2477 }
2478
2479 #[test]
2481 fn unix_bind_listen_connect_accept4() {
2482 let path = format!("/tmp/air-net-test-{}.sock", std::process::id());
2484 let cpath = CString::new(path.as_str()).expect("CString");
2485
2486 let _ = std::fs::remove_file(&path);
2488
2489 let server = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket serveur");
2490 bind(
2491 server.as_fd(),
2492 &SocketAddr::Unix(UnixSocketAddr::Path(cpath.clone())),
2493 )
2494 .expect("bind");
2495 listen(server.as_fd(), 4).expect("listen");
2496
2497 let client = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket client");
2498 connect(
2499 client.as_fd(),
2500 &SocketAddr::Unix(UnixSocketAddr::Path(cpath.clone())),
2501 )
2502 .expect("connect");
2503
2504 let accepted = accept4(server.as_fd(), AcceptFlags::CLOEXEC).expect("accept4");
2505 let _borrowed = accepted.fd.as_fd();
2507
2508 let _ = std::fs::remove_file(&path);
2510 }
2511
2512 #[test]
2514 fn getsockname_unix_path() {
2515 let path = format!("/tmp/air-net-gsn-{}.sock", std::process::id());
2516 let cpath = CString::new(path.as_str()).expect("CString");
2517 let _ = std::fs::remove_file(&path);
2518
2519 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket");
2520 bind(
2521 fd.as_fd(),
2522 &SocketAddr::Unix(UnixSocketAddr::Path(cpath.clone())),
2523 )
2524 .expect("bind");
2525
2526 let address = getsockname(fd.as_fd()).expect("getsockname");
2527 match address {
2528 SocketAddr::Unix(UnixSocketAddr::Path(returned)) => {
2529 assert_eq!(returned, cpath, "getsockname doit retourner le chemin lié");
2530 }
2531 other => panic!("adresse inattendue : {other:?}"),
2532 }
2533
2534 let _ = std::fs::remove_file(&path);
2535 }
2536
2537 #[test]
2539 fn getpeername_unix_abstract() {
2540 let path = format!("/tmp/air-net-gpn-{}.sock", std::process::id());
2542 let cpath = CString::new(path.as_str()).expect("CString");
2543 let _ = std::fs::remove_file(&path);
2544
2545 let server = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket serveur");
2546 bind(
2547 server.as_fd(),
2548 &SocketAddr::Unix(UnixSocketAddr::Path(cpath.clone())),
2549 )
2550 .expect("bind");
2551 listen(server.as_fd(), 4).expect("listen");
2552
2553 let client = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket client");
2554 connect(
2555 client.as_fd(),
2556 &SocketAddr::Unix(UnixSocketAddr::Path(cpath.clone())),
2557 )
2558 .expect("connect");
2559
2560 let accepted = accept4(server.as_fd(), AcceptFlags::CLOEXEC).expect("accept4");
2561
2562 let peer = getpeername(client.as_fd()).expect("getpeername");
2564 match peer {
2565 SocketAddr::Unix(_) => { }
2566 other => panic!("adresse inattendue : {other:?}"),
2567 }
2568
2569 drop(accepted);
2570 let _ = std::fs::remove_file(&path);
2571 }
2572
2573 #[test]
2575 fn shutdown_on_connected_socket() {
2576 let (fd_a, _fd_b) =
2577 socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
2578 shutdown(fd_a.as_fd(), ShutdownMode::Write).expect("shutdown write");
2579 }
2580
2581 #[test]
2583 fn sockopt_reuseaddr_roundtrip() {
2584 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2585 set_so_reuseaddr(fd.as_fd(), true).expect("set_so_reuseaddr(true)");
2586 let val = get_so_reuseaddr(fd.as_fd()).expect("get_so_reuseaddr");
2587 assert!(val, "SO_REUSEADDR doit être activé");
2588
2589 set_so_reuseaddr(fd.as_fd(), false).expect("set_so_reuseaddr(false)");
2590 let val2 = get_so_reuseaddr(fd.as_fd()).expect("get_so_reuseaddr");
2591 assert!(!val2, "SO_REUSEADDR doit être désactivé");
2592 }
2593
2594 #[test]
2596 fn sockopt_keepalive_roundtrip() {
2597 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2598 set_so_keepalive(fd.as_fd(), true).expect("set_so_keepalive");
2599 let val = get_so_keepalive(fd.as_fd()).expect("get_so_keepalive");
2600 assert!(val, "SO_KEEPALIVE doit être activé");
2601 }
2602
2603 #[test]
2605 fn get_so_error_none_on_healthy_socket() {
2606 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket");
2607 let err = get_so_error(fd.as_fd()).expect("get_so_error");
2608 assert!(err.is_none(), "SO_ERROR doit être None sur un socket sain");
2609 }
2610
2611 #[test]
2618 #[cfg_attr(miri, ignore = "sockets non supportés par Miri")]
2619 fn get_so_error_some_on_refused_nonblocking_connect() {
2620 use air_sys_types::net::Ipv4SocketAddr;
2621
2622 let probe = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket probe");
2624 bind(
2625 probe.as_fd(),
2626 &SocketAddr::Ipv4(Ipv4SocketAddr {
2627 address: [127, 0, 0, 1],
2628 port: 0,
2629 }),
2630 )
2631 .expect("bind probe");
2632 let port = match getsockname(probe.as_fd()).expect("getsockname") {
2633 SocketAddr::Ipv4(v4) => v4.port,
2634 other => panic!("adresse IPv4 attendue : {other:?}"),
2635 };
2636 drop(probe); let sock = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
2639 let ret = unsafe { raw_fcntl_setfl(sock.as_raw_fd(), 4, 0x800) };
2643 assert!(ret >= 0, "fcntl F_SETFL O_NONBLOCK");
2644 let target = SocketAddr::Ipv4(Ipv4SocketAddr {
2645 address: [127, 0, 0, 1],
2646 port,
2647 });
2648 let _ = connect(sock.as_fd(), &target);
2651
2652 let mut observed = None;
2654 for _ in 0..2_000_000 {
2655 if let Some(error) = get_so_error(sock.as_fd()).expect("get_so_error") {
2656 observed = Some(error);
2657 break;
2658 }
2659 }
2660 let error = observed.expect("SO_ERROR doit devenir disponible (connexion refusée)");
2661 assert_eq!(
2662 error, ECONNREFUSED,
2663 "port fermé → ECONNREFUSED via SO_ERROR"
2664 );
2665 assert!(
2667 get_so_error(sock.as_fd())
2668 .expect("get_so_error 2")
2669 .is_none(),
2670 "SO_ERROR effacé après lecture"
2671 );
2672 }
2673
2674 #[test]
2676 fn sockopt_linger_disabled_roundtrip() {
2677 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2678 set_so_linger(fd.as_fd(), LingerOption::Disabled).expect("set_so_linger(Disabled)");
2679 let val = get_so_linger(fd.as_fd()).expect("get_so_linger");
2680 assert_eq!(val, LingerOption::Disabled);
2681 }
2682
2683 #[test]
2685 fn sockopt_linger_enabled_roundtrip() {
2686 use core::time::Duration;
2687 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2688 let linger = LingerOption::Enabled(Duration::from_secs(5));
2689 set_so_linger(fd.as_fd(), linger).expect("set_so_linger(Enabled(5s))");
2690 let val = get_so_linger(fd.as_fd()).expect("get_so_linger");
2691 assert_eq!(val, LingerOption::Enabled(Duration::from_secs(5)));
2692 }
2693
2694 #[test]
2698 fn sockopt_peercred_on_socketpair_is_self() {
2699 let (a, _b) = socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
2700 let creds = get_so_peercred(a.as_fd()).expect("get_so_peercred");
2701 assert_eq!(creds.pid.as_raw(), crate::process::getpid().as_raw());
2703 }
2704
2705 #[test]
2711 fn sockopt_peercred_einval_on_unconnected_unix_socket() {
2712 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket Unix");
2713 assert_eq!(
2714 get_so_peercred(fd.as_fd()).unwrap_err(),
2715 Errno::EINVAL,
2716 "un socket Unix non connecté rapporte pid=0 → EINVAL"
2717 );
2718 }
2719
2720 #[test]
2722 fn sockopt_ip_ttl_set_on_ipv4_socket() {
2723 let fd = socket(SocketDomain::Ipv4, SocketType::Datagram, 0).expect("socket IPv4 UDP");
2724 set_ip_ttl(fd.as_fd(), 64).expect("set_ip_ttl(64)");
2725 }
2726
2727 #[test]
2730 fn sockopt_ipv6_v6only_set_on_ipv6_socket() {
2731 if let Ok(fd) = socket(SocketDomain::Ipv6, SocketType::Stream, 0) {
2733 set_ipv6_v6only(fd.as_fd(), true).expect("set_ipv6_v6only(true)");
2734 }
2735 }
2736
2737 #[test]
2742 fn sockopt_receive_timeout_some_roundtrip() {
2743 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2744 let tv = Timeval {
2745 tv_sec: 0,
2746 tv_usec: 200_000,
2747 };
2748 set_receive_timeout(fd.as_fd(), Some(tv)).expect("set_receive_timeout(Some)");
2749 assert_eq!(
2750 receive_timeout(fd.as_fd()).expect("receive_timeout"),
2751 Some(tv)
2752 );
2753 }
2754
2755 #[test]
2758 fn sockopt_receive_timeout_none_clears() {
2759 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2760 set_receive_timeout(
2761 fd.as_fd(),
2762 Some(Timeval {
2763 tv_sec: 1,
2764 tv_usec: 0,
2765 }),
2766 )
2767 .expect("armer d'abord");
2768 set_receive_timeout(fd.as_fd(), None).expect("set_receive_timeout(None)");
2769 assert_eq!(receive_timeout(fd.as_fd()).expect("receive_timeout"), None);
2770 }
2771
2772 #[test]
2775 fn sockopt_send_timeout_roundtrip() {
2776 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2777 let tv = Timeval {
2778 tv_sec: 2,
2779 tv_usec: 500_000,
2780 };
2781 set_send_timeout(fd.as_fd(), Some(tv)).expect("set_send_timeout");
2782 assert_eq!(send_timeout(fd.as_fd()).expect("send_timeout"), Some(tv));
2783 }
2784
2785 #[test]
2787 fn sockopt_timeout_ebadf_on_invalid_fd() {
2788 let tv = Timeval {
2789 tv_sec: 0,
2790 tv_usec: 1,
2791 };
2792 assert_eq!(
2793 set_receive_timeout(invalid_fd(), Some(tv)).unwrap_err(),
2794 Errno::EBADF
2795 );
2796 assert_eq!(receive_timeout(invalid_fd()).unwrap_err(), Errno::EBADF);
2797 assert_eq!(
2798 set_send_timeout(invalid_fd(), Some(tv)).unwrap_err(),
2799 Errno::EBADF
2800 );
2801 assert_eq!(send_timeout(invalid_fd()).unwrap_err(), Errno::EBADF);
2802 }
2803
2804 #[test]
2813 fn sockopt_multicast_v4_join_leave_on_loopback() {
2814 let fd = socket(SocketDomain::Ipv4, SocketType::Datagram, 0).expect("socket IPv4 UDP");
2815 let membership = IpMembershipV4 {
2816 multiaddr: [224, 0, 0, 1],
2817 interface: [127, 0, 0, 1],
2818 };
2819 join_multicast_v4(fd.as_fd(), &membership).expect("join_multicast_v4(224.0.0.1@lo)");
2820 leave_multicast_v4(fd.as_fd(), &membership).expect("leave_multicast_v4");
2821 }
2822
2823 #[test]
2826 fn sockopt_multicast_v6_join_leave_on_loopback() {
2827 let fd = socket(SocketDomain::Ipv6, SocketType::Datagram, 0).expect("socket IPv6 UDP");
2828 let membership = IpMembershipV6 {
2829 multiaddr: [0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
2830 interface: 1, };
2832 join_multicast_v6(fd.as_fd(), &membership).expect("join_multicast_v6(ff02::1@lo)");
2833 leave_multicast_v6(fd.as_fd(), &membership).expect("leave_multicast_v6");
2834 }
2835
2836 #[test]
2838 fn sockopt_multicast_ebadf_on_invalid_fd() {
2839 let v4 = IpMembershipV4 {
2840 multiaddr: [224, 0, 0, 1],
2841 interface: [0, 0, 0, 0],
2842 };
2843 let v6 = IpMembershipV6 {
2844 multiaddr: [0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
2845 interface: 0,
2846 };
2847 assert_eq!(
2848 join_multicast_v4(invalid_fd(), &v4).unwrap_err(),
2849 Errno::EBADF
2850 );
2851 assert_eq!(
2852 leave_multicast_v4(invalid_fd(), &v4).unwrap_err(),
2853 Errno::EBADF
2854 );
2855 assert_eq!(
2856 join_multicast_v6(invalid_fd(), &v6).unwrap_err(),
2857 Errno::EBADF
2858 );
2859 assert_eq!(
2860 leave_multicast_v6(invalid_fd(), &v6).unwrap_err(),
2861 Errno::EBADF
2862 );
2863 }
2864
2865 proptest::proptest! {
2866 #[test]
2871 fn decode_timeout_is_none_iff_zero(sec in i64::MIN..=i64::MAX, usec in i64::MIN..=i64::MAX) {
2872 let tv = Timeval { tv_sec: sec, tv_usec: usec };
2873 let decoded = decode_timeout(tv);
2874 if sec == 0 && usec == 0 {
2875 proptest::prop_assert_eq!(decoded, None);
2876 } else {
2877 proptest::prop_assert_eq!(decoded, Some(tv));
2878 }
2879 }
2880 }
2881
2882 #[test]
2884 fn sockopt_reuseport_roundtrip() {
2885 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2886 set_so_reuseport(fd.as_fd(), true).expect("set_so_reuseport(true)");
2887 assert!(get_so_reuseport(fd.as_fd()).expect("get_so_reuseport"));
2888 }
2889
2890 #[test]
2892 fn sockopt_tcp_nodelay_roundtrip() {
2893 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket TCP");
2894 set_tcp_nodelay(fd.as_fd(), true).expect("set_tcp_nodelay(true)");
2895 assert!(get_tcp_nodelay(fd.as_fd()).expect("get_tcp_nodelay"));
2896 }
2897
2898 #[test]
2901 fn sockopt_rcvbuf_sndbuf_roundtrip() {
2902 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2903 set_so_rcvbuf(fd.as_fd(), 8192).expect("set_so_rcvbuf");
2904 assert!(get_so_rcvbuf(fd.as_fd()).expect("get_so_rcvbuf") > 0);
2905 set_so_sndbuf(fd.as_fd(), 8192).expect("set_so_sndbuf");
2906 assert!(get_so_sndbuf(fd.as_fd()).expect("get_so_sndbuf") > 0);
2907 }
2908
2909 #[test]
2911 fn sockopt_tcp_keepalive_params() {
2912 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket TCP");
2913 set_tcp_keepidle(fd.as_fd(), 60).expect("keepidle");
2914 set_tcp_keepintvl(fd.as_fd(), 10).expect("keepintvl");
2915 set_tcp_keepcnt(fd.as_fd(), 5).expect("keepcnt");
2916 }
2917
2918 #[test]
2921 fn bind_ipv4_loopback_and_getsockname() {
2922 use air_sys_types::net::Ipv4SocketAddr;
2923 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket IPv4");
2924 set_so_reuseaddr(fd.as_fd(), true).expect("reuseaddr");
2925 let address = SocketAddr::Ipv4(Ipv4SocketAddr {
2926 address: [127, 0, 0, 1],
2927 port: 0, });
2929 bind(fd.as_fd(), &address).expect("bind IPv4 loopback");
2930 let name = getsockname(fd.as_fd()).expect("getsockname IPv4");
2931 if let SocketAddr::Ipv4(v4) = name {
2932 assert_eq!(v4.address, [127, 0, 0, 1]);
2933 assert!(v4.port > 0, "port assigné doit être > 0");
2934 } else {
2935 panic!("getsockname doit retourner IPv4, got {name:?}");
2936 }
2937 }
2938
2939 #[test]
2942 fn bind_ipv6_loopback_and_getsockname() {
2943 use air_sys_types::net::Ipv6SocketAddr;
2944 let fd = socket(SocketDomain::Ipv6, SocketType::Stream, 0).expect("socket IPv6");
2945 set_so_reuseaddr(fd.as_fd(), true).expect("reuseaddr");
2946 let address = SocketAddr::Ipv6(Ipv6SocketAddr {
2947 address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], port: 0,
2949 flowinfo: 0,
2950 scope_id: 0,
2951 });
2952 bind(fd.as_fd(), &address).expect("bind IPv6 loopback");
2953 let name = getsockname(fd.as_fd()).expect("getsockname IPv6");
2954 if let SocketAddr::Ipv6(v6) = name {
2955 assert_eq!(v6.address, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
2956 } else {
2957 panic!("getsockname doit retourner IPv6, got {name:?}");
2958 }
2959 }
2960
2961 #[test]
2964 fn bind_unix_abstract_and_getsockname() {
2965 use air_sys_types::net::UnixSocketAddr;
2966 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket Unix");
2967 let address = SocketAddr::Unix(UnixSocketAddr::Abstract(b"air_test_abstract_42".to_vec()));
2969 bind(fd.as_fd(), &address).expect("bind Unix abstrait");
2970 let name = getsockname(fd.as_fd()).expect("getsockname abstrait");
2971 if let SocketAddr::Unix(UnixSocketAddr::Abstract(_)) = name {
2972 } else {
2974 panic!("getsockname doit retourner Unix abstrait, got {name:?}");
2975 }
2976 }
2977
2978 #[test]
2981 fn getsockname_unbound_unix_returns_unnamed_or_empty() {
2982 let fd = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket Unix");
2983 match getsockname(fd.as_fd()) {
2985 Ok(SocketAddr::Unix(_)) => {}
2986 Ok(other) => panic!("attendu Unix, got {other:?}"),
2987 Err(_) => {} }
2989 }
2990
2991 #[test]
2994 fn bind_ipv4_eaddrinuse() {
2995 use air_sys_types::net::Ipv4SocketAddr;
2996 let fd1 = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket 1");
2997 let fd2 = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket 2");
2998 set_so_reuseaddr(fd1.as_fd(), true).expect("reuseaddr 1");
2999 let address = SocketAddr::Ipv4(Ipv4SocketAddr {
3000 address: [127, 0, 0, 1],
3001 port: 0,
3002 });
3003 bind(fd1.as_fd(), &address).expect("bind 1");
3004 let name = getsockname(fd1.as_fd()).expect("getsockname 1");
3006 let port = if let SocketAddr::Ipv4(v4) = name {
3007 v4.port
3008 } else {
3009 panic!()
3010 };
3011 let addr2 = SocketAddr::Ipv4(Ipv4SocketAddr {
3013 address: [127, 0, 0, 1],
3014 port,
3015 });
3016 let err = bind(fd2.as_fd(), &addr2).unwrap_err();
3017 assert_eq!(err, EADDRINUSE);
3018 }
3019
3020 #[test]
3023 fn connect_ipv4_econnrefused() {
3024 use air_sys_types::net::Ipv4SocketAddr;
3025 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket TCP");
3027 let address = SocketAddr::Ipv4(Ipv4SocketAddr {
3028 address: [127, 0, 0, 1],
3029 port: 1, });
3031 let err = connect(fd.as_fd(), &address).unwrap_err();
3032 assert!(
3033 err == ECONNREFUSED || err == Errno::EACCES,
3034 "attendu ECONNREFUSED ou EACCES, got {err:?}"
3035 );
3036 }
3037
3038 #[test]
3041 fn send_ebadf() {
3042 let invalid = invalid_fd();
3043 let err = send(invalid, b"data", MessageFlags::empty()).unwrap_err();
3044 assert_eq!(err, Errno::EBADF);
3045 }
3046
3047 #[test]
3048 fn recv_ebadf() {
3049 let invalid = invalid_fd();
3050 let mut buffer = [0u8; 4];
3051 let err = recv(invalid, &mut buffer, MessageFlags::empty()).unwrap_err();
3052 assert_eq!(err, Errno::EBADF);
3053 }
3054
3055 #[test]
3058 fn get_so_bindtodevice_unbound_returns_empty() {
3059 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3060 let device = get_so_bindtodevice(fd.as_fd()).expect("get_so_bindtodevice");
3061 assert!(
3062 device.to_bytes().is_empty(),
3063 "socket non bindé à un device doit retourner vide"
3064 );
3065 }
3066
3067 #[test]
3068 fn set_so_bindtodevice_lo_or_eperm() {
3069 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3070 match set_so_bindtodevice(fd.as_fd(), c"lo") {
3072 Ok(()) => {
3073 let dev = get_so_bindtodevice(fd.as_fd()).expect("get après set");
3075 assert_eq!(dev.to_bytes(), b"lo");
3076 }
3077 Err(Errno::EPERM) | Err(Errno::ENODEV) => {
3078 }
3080 Err(e) => panic!("set_so_bindtodevice erreur inattendue : {e:?}"),
3081 }
3082 }
3083
3084 #[test]
3087 fn socket_esocktnosupport_on_unsupported_type() {
3088 let err = socket(SocketDomain::Ipv4, SocketType::SeqPacket, 0).unwrap_err();
3092 assert_eq!(err, ESOCKTNOSUPPORT);
3093 }
3094
3095 #[test]
3098 fn accept4_eagain_on_nonblocking_without_connections() {
3099 use air_sys_types::net::UnixSocketAddr;
3100 let listener = socket(SocketDomain::Unix, SocketType::Stream, 0).expect("socket");
3101 let path = c"/tmp/air_net_accept4_eagain_42.sock";
3102 let _ = std::fs::remove_file("/tmp/air_net_accept4_eagain_42.sock");
3103 bind(
3104 listener.as_fd(),
3105 &SocketAddr::Unix(UnixSocketAddr::Path(path.to_owned())),
3106 )
3107 .expect("bind");
3108 listen(listener.as_fd(), 1).expect("listen");
3109 let o_nonblock: u64 = 0x800;
3112 let ret = unsafe { raw_fcntl_setfl(listener.as_raw_fd(), 4, o_nonblock) };
3115 if ret < 0 {
3116 let _ = std::fs::remove_file("/tmp/air_net_accept4_eagain_42.sock");
3118 return;
3119 }
3120 let err = accept4(listener.as_fd(), AcceptFlags::CLOEXEC).unwrap_err();
3121 assert_eq!(
3123 err,
3124 Errno::EAGAIN,
3125 "attendu EAGAIN sur socket non-bloquant vide"
3126 );
3127 let _ = std::fs::remove_file("/tmp/air_net_accept4_eagain_42.sock");
3128 }
3129
3130 #[cfg(target_arch = "x86_64")]
3131 unsafe fn raw_fcntl_setfl(fd: i32, cmd: i64, flags: u64) -> i64 {
3132 let ret: i64;
3133 unsafe {
3136 core::arch::asm!(
3137 "syscall",
3138 in("rax") 72_i64, in("rdi") i64::from(fd),
3140 in("rsi") cmd,
3141 in("rdx") flags,
3142 lateout("rax") ret,
3143 lateout("rcx") _,
3144 lateout("r11") _,
3145 options(nostack, preserves_flags),
3146 );
3147 }
3148 ret
3149 }
3150
3151 #[cfg(target_arch = "aarch64")]
3152 unsafe fn raw_fcntl_setfl(fd: i32, cmd: i64, flags: u64) -> i64 {
3153 let ret: i64;
3154 unsafe {
3157 core::arch::asm!(
3158 "svc 0",
3159 in("x8") 25_i64, inout("x0") i64::from(fd) => ret,
3161 in("x1") cmd,
3162 in("x2") flags,
3163 options(nostack, preserves_flags),
3164 );
3165 }
3166 ret
3167 }
3168
3169 #[test]
3172 fn shutdown_enotconn_on_unconnected() {
3173 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3174 let err = shutdown(fd.as_fd(), ShutdownMode::Both).unwrap_err();
3175 assert_eq!(err, ENOTCONN);
3176 }
3177
3178 #[test]
3182 #[should_panic(expected = "ret < 0 && ret > -4096")]
3183 fn errno_from_negative_syscall_ret_panics_on_non_negative() {
3184 let _ = errno_from_negative_syscall_ret(0);
3185 }
3186
3187 #[test]
3188 #[should_panic(expected = "ret < 0 && ret > -4096")]
3189 fn errno_from_negative_syscall_ret_panics_below_errno_range() {
3190 let _ = errno_from_negative_syscall_ret(-5000);
3191 }
3192
3193 #[test]
3196 fn listen_ebadf_on_invalid_fd() {
3197 assert_eq!(listen(invalid_fd(), 1).unwrap_err(), Errno::EBADF);
3198 }
3199
3200 #[test]
3201 fn getsockname_ebadf_on_invalid_fd() {
3202 assert_eq!(getsockname(invalid_fd()).unwrap_err(), Errno::EBADF);
3203 }
3204
3205 #[test]
3206 fn getpeername_ebadf_on_invalid_fd() {
3207 assert_eq!(getpeername(invalid_fd()).unwrap_err(), Errno::EBADF);
3208 }
3209
3210 #[test]
3211 fn get_so_peercred_ebadf_on_invalid_fd() {
3212 assert_eq!(get_so_peercred(invalid_fd()).unwrap_err(), Errno::EBADF);
3213 }
3214
3215 #[test]
3216 fn get_so_linger_ebadf_on_invalid_fd() {
3217 assert_eq!(get_so_linger(invalid_fd()).unwrap_err(), Errno::EBADF);
3218 }
3219
3220 #[test]
3221 fn set_so_linger_ebadf_on_invalid_fd() {
3222 use core::time::Duration;
3223 let err =
3224 set_so_linger(invalid_fd(), LingerOption::Enabled(Duration::from_secs(1))).unwrap_err();
3225 assert_eq!(err, Errno::EBADF);
3226 }
3227
3228 #[test]
3229 fn set_so_bindtodevice_ebadf_on_invalid_fd() {
3230 assert_eq!(
3231 set_so_bindtodevice(invalid_fd(), c"lo").unwrap_err(),
3232 Errno::EBADF
3233 );
3234 }
3235
3236 #[test]
3237 fn get_so_bindtodevice_ebadf_on_invalid_fd() {
3238 assert_eq!(get_so_bindtodevice(invalid_fd()).unwrap_err(), Errno::EBADF);
3239 }
3240
3241 #[test]
3242 fn sendmsg_ebadf_on_invalid_fd() {
3243 let data = *b"x";
3244 let iov = [IoSlice::new(&data)];
3245 let req = SendMessageRequest {
3246 iov: &iov,
3247 address: None,
3248 fds: &[],
3249 flags: MessageFlags::empty(),
3250 };
3251 assert_eq!(sendmsg(invalid_fd(), &req).unwrap_err(), Errno::EBADF);
3252 }
3253
3254 #[test]
3255 fn recvmsg_ebadf_on_invalid_fd() {
3256 let mut buffer = [0u8; 8];
3257 let mut iov = [IoSliceMut::new(&mut buffer)];
3258 let mut req = ReceiveMessageRequest {
3259 iov: &mut iov,
3260 flags: MessageFlags::empty(),
3261 };
3262 assert_eq!(recvmsg(invalid_fd(), &mut req).unwrap_err(), Errno::EBADF);
3263 }
3264
3265 #[test]
3266 fn socketpair_eopnotsupp_on_inet_domain() {
3267 let eopnotsupp = Errno::try_from_raw(95).unwrap();
3270 let err = socketpair(SocketDomain::Ipv4, SocketType::Stream, 0).unwrap_err();
3271 assert_eq!(err, eopnotsupp);
3272 }
3273
3274 #[test]
3277 fn set_so_rcvbuf_clamps_oversized() {
3278 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3279 set_so_rcvbuf(fd.as_fd(), 0x8000_0000).expect("set_so_rcvbuf clamp");
3280 }
3281
3282 #[test]
3283 fn set_so_sndbuf_clamps_oversized() {
3284 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3285 set_so_sndbuf(fd.as_fd(), 0x8000_0000).expect("set_so_sndbuf clamp");
3286 }
3287
3288 #[test]
3289 fn set_so_linger_clamps_oversized_secs() {
3290 use core::time::Duration;
3291 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3292 let _ = set_so_linger(
3295 fd.as_fd(),
3296 LingerOption::Enabled(Duration::from_secs(u64::from(u32::MAX))),
3297 );
3298 }
3299
3300 #[test]
3301 fn set_tcp_keepalive_params_clamp_oversized() {
3302 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket TCP");
3303 let _ = set_tcp_keepidle(fd.as_fd(), 0x8000_0000);
3305 let _ = set_tcp_keepintvl(fd.as_fd(), 0x8000_0000);
3306 let _ = set_tcp_keepcnt(fd.as_fd(), 0x8000_0000);
3307 }
3308
3309 #[test]
3312 fn raw_to_socket_addr_rejects_buffer_shorter_than_len() {
3313 assert!(raw_to_socket_addr(&[2, 0], 16).is_none());
3315 }
3316
3317 #[test]
3318 fn raw_to_socket_addr_rejects_truncated_ipv4() {
3319 let mut buffer = [0u8; 16];
3321 buffer[0..2].copy_from_slice(&2u16.to_ne_bytes());
3322 assert!(raw_to_socket_addr(&buffer, 10).is_none());
3323 }
3324
3325 #[test]
3326 fn raw_to_socket_addr_rejects_truncated_ipv6() {
3327 let mut buffer = [0u8; 28];
3329 buffer[0..2].copy_from_slice(&10u16.to_ne_bytes());
3330 assert!(raw_to_socket_addr(&buffer, 20).is_none());
3331 }
3332
3333 #[test]
3336 fn socket_addr_bytes_round_trip_ipv4() {
3337 let address = SocketAddr::Ipv4(Ipv4SocketAddr {
3338 address: [127, 0, 0, 1],
3339 port: 443,
3340 });
3341 let mut buffer = [0u8; 128];
3342 let length = socket_addr_to_bytes(&address, &mut buffer).expect("encode IPv4");
3343 assert_eq!(length, 16);
3344 assert_eq!(socket_addr_from_bytes(&buffer, length), Some(address));
3345 }
3346
3347 #[test]
3348 fn socket_addr_bytes_round_trip_ipv6() {
3349 let address = SocketAddr::Ipv6(Ipv6SocketAddr {
3350 address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
3351 port: 443,
3352 flowinfo: 0,
3353 scope_id: 0,
3354 });
3355 let mut buffer = [0u8; 128];
3356 let length = socket_addr_to_bytes(&address, &mut buffer).expect("encode IPv6");
3357 assert_eq!(length, 28);
3358 assert_eq!(socket_addr_from_bytes(&buffer, length), Some(address));
3359 }
3360
3361 #[test]
3362 fn socket_addr_to_bytes_reports_none_when_out_too_small() {
3363 let address = SocketAddr::Ipv4(Ipv4SocketAddr {
3364 address: [10, 0, 0, 1],
3365 port: 22,
3366 });
3367 let mut tiny = [0u8; 8];
3369 assert_eq!(socket_addr_to_bytes(&address, &mut tiny), None);
3370 }
3371
3372 #[test]
3373 fn socket_option_int_public_wrappers_round_trip() {
3374 let sock = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3376 set_socket_option_int(sock.as_fd(), 1, 2, 1).expect("set SO_REUSEADDR");
3378 let kind = get_socket_option_int(sock.as_fd(), 1, 3).expect("get SO_TYPE");
3379 assert_eq!(kind, 1); }
3381
3382 #[test]
3383 fn socket_option_int_reports_error_on_bad_option() {
3384 let sock = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3386 assert!(get_socket_option_int(sock.as_fd(), 1, 99_999).is_err());
3387 }
3388
3389 #[test]
3390 fn socket_addr_from_bytes_rejects_unknown_family() {
3391 assert!(socket_addr_from_bytes(&[99, 0, 0, 0, 0, 0, 0, 0], 8).is_none());
3393 }
3394
3395 #[test]
3398 fn scm_rights_fd_passing_roundtrip() {
3399 let (fd_a, fd_b) =
3402 socketpair(SocketDomain::Unix, SocketType::Stream, 0).expect("socketpair");
3403 let passed =
3404 crate::mem::memfd_create(c"air-scm-rights", air_sys_types::mem::MemfdFlags::empty())
3405 .expect("memfd");
3406 let data = *b"fd!";
3407 let iov = [IoSlice::new(&data)];
3408 let fds = [passed.as_fd()];
3409 let req = SendMessageRequest {
3410 iov: &iov,
3411 address: None,
3412 fds: &fds,
3413 flags: MessageFlags::empty(),
3414 };
3415 sendmsg(fd_a.as_fd(), &req).expect("sendmsg with fd");
3416
3417 let mut buffer = [0u8; 8];
3418 let mut iov_r = [IoSliceMut::new(&mut buffer)];
3419 let mut req_r = ReceiveMessageRequest {
3420 iov: &mut iov_r,
3421 flags: MessageFlags::empty(),
3422 };
3423 let res = recvmsg(fd_b.as_fd(), &mut req_r).expect("recvmsg with fd");
3424 assert_eq!(&buffer[..res.bytes_received], b"fd!");
3425 assert_eq!(res.fds.len(), 1, "un FD doit être reçu via SCM_RIGHTS");
3426 }
3427
3428 #[test]
3429 fn socket_eprotonosupport_on_mismatched_protocol() {
3430 let eprotonosupport = Errno::try_from_raw(93).unwrap();
3433 let err = socket(SocketDomain::Ipv4, SocketType::Stream, 17).unwrap_err();
3434 assert_eq!(err, eprotonosupport);
3435 }
3436
3437 #[test]
3438 fn listen_clamps_oversized_backlog() {
3439 let fd = socket(SocketDomain::Ipv4, SocketType::Stream, 0).expect("socket");
3442 let _ = listen(fd.as_fd(), 0x8000_0000);
3443 }
3444
3445 #[test]
3448 fn parse_scm_rights_ignores_non_scm_rights_cmsg() {
3449 let mut buffer = vec![0u8; 16];
3452 buffer[0..8].copy_from_slice(&16u64.to_ne_bytes()); buffer[8..12].copy_from_slice(&0i32.to_ne_bytes()); buffer[12..16].copy_from_slice(&0i32.to_ne_bytes()); let fds = parse_scm_rights(&buffer, 16);
3456 assert!(fds.is_empty());
3457 }
3458
3459 #[test]
3460 fn parse_scm_rights_ignores_too_short_payload() {
3461 let mut buffer = vec![0u8; 16];
3464 buffer[0..8].copy_from_slice(&8u64.to_ne_bytes()); buffer[8..12].copy_from_slice(&1i32.to_ne_bytes()); buffer[12..16].copy_from_slice(&1i32.to_ne_bytes()); let fds = parse_scm_rights(&buffer, 16);
3468 assert!(fds.is_empty());
3469 }
3470
3471 #[test]
3472 fn parse_scm_rights_stops_when_cmsg_len_exceeds_buffer() {
3473 let buffer = vec![0u8; 8]; let fds = parse_scm_rights(&buffer, 1000);
3477 assert!(fds.is_empty());
3478 }
3479
3480 #[test]
3481 fn parse_scm_rights_ignores_wrong_cmsg_type() {
3482 let mut buffer = vec![0u8; 16];
3484 buffer[0..8].copy_from_slice(&16u64.to_ne_bytes());
3485 buffer[8..12].copy_from_slice(&1i32.to_ne_bytes()); buffer[12..16].copy_from_slice(&2i32.to_ne_bytes()); assert!(parse_scm_rights(&buffer, 16).is_empty());
3488 }
3489
3490 #[test]
3491 fn parse_scm_rights_breaks_on_zero_len_entry() {
3492 let mut buffer = vec![0u8; 16];
3495 buffer[0..8].copy_from_slice(&0u64.to_ne_bytes()); buffer[8..12].copy_from_slice(&1i32.to_ne_bytes());
3497 buffer[12..16].copy_from_slice(&1i32.to_ne_bytes());
3498 assert!(parse_scm_rights(&buffer, 16).is_empty());
3499 }
3500
3501 #[test]
3502 fn get_so_rcvbuf_ebadf_on_invalid_fd() {
3503 assert_eq!(get_so_rcvbuf(invalid_fd()).unwrap_err(), Errno::EBADF);
3505 }
3506
3507 #[test]
3508 fn sendmsg_with_explicit_addr_on_udp() {
3509 let fd = socket(SocketDomain::Ipv4, SocketType::Datagram, 0).expect("socket UDP");
3512 let dest = SocketAddr::Ipv4(Ipv4SocketAddr {
3513 address: [127, 0, 0, 1],
3514 port: 9,
3515 });
3516 let data = *b"udp";
3517 let iov = [IoSlice::new(&data)];
3518 let req = SendMessageRequest {
3519 iov: &iov,
3520 address: Some(&dest),
3521 fds: &[],
3522 flags: MessageFlags::empty(),
3523 };
3524 let _ = sendmsg(fd.as_fd(), &req);
3525 }
3526}