Skip to main content

shadow_rs/host/descriptor/socket/inet/
mod.rs

1use std::net::{Ipv4Addr, SocketAddrV4};
2use std::sync::{Arc, Weak};
3
4use atomic_refcell::AtomicRefCell;
5use linux_api::errno::Errno;
6use linux_api::ioctls::IoctlRequest;
7use linux_api::socket::Shutdown;
8use shadow_shim_helper_rs::emulated_time::EmulatedTime;
9use shadow_shim_helper_rs::syscall_types::ForeignPtr;
10
11use crate::cshadow as c;
12use crate::host::descriptor::listener::{StateListenHandle, StateListenerFilter};
13use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs};
14use crate::host::descriptor::{
15    FileMode, FileSignals, FileState, FileStatus, OpenFile, SyscallResult,
16};
17use crate::host::memory_manager::MemoryManager;
18use crate::host::network::interface::FifoPacketPriority;
19use crate::host::network::namespace::{AssociationHandle, NetworkNamespace};
20use crate::host::syscall::io::IoVec;
21use crate::host::syscall::types::SyscallError;
22use crate::network::packet::{IanaProtocol, PacketRc};
23use crate::utility::HostTreePointer;
24use crate::utility::callback_queue::CallbackQueue;
25use crate::utility::sockaddr::SockaddrStorage;
26
27use self::legacy_tcp::LegacyTcpSocket;
28use self::tcp::TcpSocket;
29use self::udp::UdpSocket;
30
31pub mod legacy_tcp;
32pub mod tcp;
33pub mod udp;
34
35#[derive(Clone)]
36pub enum InetSocket {
37    LegacyTcp(Arc<AtomicRefCell<LegacyTcpSocket>>),
38    Tcp(Arc<AtomicRefCell<TcpSocket>>),
39    Udp(Arc<AtomicRefCell<UdpSocket>>),
40}
41
42impl InetSocket {
43    pub fn borrow(&self) -> InetSocketRef<'_> {
44        match self {
45            Self::LegacyTcp(f) => InetSocketRef::LegacyTcp(f.borrow()),
46            Self::Tcp(f) => InetSocketRef::Tcp(f.borrow()),
47            Self::Udp(f) => InetSocketRef::Udp(f.borrow()),
48        }
49    }
50
51    pub fn try_borrow(&self) -> Result<InetSocketRef<'_>, atomic_refcell::BorrowError> {
52        Ok(match self {
53            Self::LegacyTcp(f) => InetSocketRef::LegacyTcp(f.try_borrow()?),
54            Self::Tcp(f) => InetSocketRef::Tcp(f.try_borrow()?),
55            Self::Udp(f) => InetSocketRef::Udp(f.try_borrow()?),
56        })
57    }
58
59    pub fn borrow_mut(&self) -> InetSocketRefMut<'_> {
60        match self {
61            Self::LegacyTcp(f) => InetSocketRefMut::LegacyTcp(f.borrow_mut()),
62            Self::Tcp(f) => InetSocketRefMut::Tcp(f.borrow_mut()),
63            Self::Udp(f) => InetSocketRefMut::Udp(f.borrow_mut()),
64        }
65    }
66
67    pub fn try_borrow_mut(&self) -> Result<InetSocketRefMut<'_>, atomic_refcell::BorrowMutError> {
68        Ok(match self {
69            Self::LegacyTcp(f) => InetSocketRefMut::LegacyTcp(f.try_borrow_mut()?),
70            Self::Tcp(f) => InetSocketRefMut::Tcp(f.try_borrow_mut()?),
71            Self::Udp(f) => InetSocketRefMut::Udp(f.try_borrow_mut()?),
72        })
73    }
74
75    pub fn downgrade(&self) -> InetSocketWeak {
76        match self {
77            Self::LegacyTcp(x) => InetSocketWeak::LegacyTcp(Arc::downgrade(x)),
78            Self::Tcp(x) => InetSocketWeak::Tcp(Arc::downgrade(x)),
79            Self::Udp(x) => InetSocketWeak::Udp(Arc::downgrade(x)),
80        }
81    }
82
83    /// Useful for getting a unique integer handle for a socket, or when we need to compare a C
84    /// `LegacySocket` to a rust `InetSocket` (which may internally point to the same
85    /// `LegacySocket`).
86    pub fn canonical_handle(&self) -> usize {
87        match self {
88            // usually we'd use `Arc::as_ptr()`, but we want to use the handle for the C `TCP`
89            // object for consistency with the handle for the `LegacySocket`
90            Self::LegacyTcp(f) => f.borrow().canonical_handle(),
91            Self::Tcp(f) => Arc::as_ptr(f) as usize,
92            Self::Udp(f) => Arc::as_ptr(f) as usize,
93        }
94    }
95
96    pub fn bind(
97        &self,
98        addr: Option<&SockaddrStorage>,
99        net_ns: &NetworkNamespace,
100        rng: impl rand::Rng,
101    ) -> Result<(), SyscallError> {
102        match self {
103            Self::LegacyTcp(socket) => LegacyTcpSocket::bind(socket, addr, net_ns, rng),
104            Self::Tcp(socket) => TcpSocket::bind(socket, addr, net_ns, rng),
105            Self::Udp(socket) => UdpSocket::bind(socket, addr, net_ns, rng),
106        }
107    }
108
109    pub fn listen(
110        &self,
111        backlog: i32,
112        net_ns: &NetworkNamespace,
113        rng: impl rand::Rng,
114        cb_queue: &mut CallbackQueue,
115    ) -> Result<(), Errno> {
116        match self {
117            Self::LegacyTcp(socket) => {
118                LegacyTcpSocket::listen(socket, backlog, net_ns, rng, cb_queue)
119            }
120            Self::Tcp(socket) => TcpSocket::listen(socket, backlog, net_ns, rng, cb_queue),
121            Self::Udp(socket) => UdpSocket::listen(socket, backlog, net_ns, rng, cb_queue),
122        }
123    }
124
125    pub fn connect(
126        &self,
127        addr: &SockaddrStorage,
128        net_ns: &NetworkNamespace,
129        rng: impl rand::Rng,
130        cb_queue: &mut CallbackQueue,
131    ) -> Result<(), SyscallError> {
132        match self {
133            Self::LegacyTcp(socket) => {
134                LegacyTcpSocket::connect(socket, addr, net_ns, rng, cb_queue)
135            }
136            Self::Tcp(socket) => TcpSocket::connect(socket, addr, net_ns, rng, cb_queue),
137            Self::Udp(socket) => UdpSocket::connect(socket, addr, net_ns, rng, cb_queue),
138        }
139    }
140
141    pub fn sendmsg(
142        &self,
143        args: SendmsgArgs,
144        memory_manager: &mut MemoryManager,
145        net_ns: &NetworkNamespace,
146        rng: impl rand::Rng,
147        cb_queue: &mut CallbackQueue,
148    ) -> Result<libc::ssize_t, SyscallError> {
149        match self {
150            Self::LegacyTcp(socket) => {
151                LegacyTcpSocket::sendmsg(socket, args, memory_manager, net_ns, rng, cb_queue)
152            }
153            Self::Tcp(socket) => {
154                TcpSocket::sendmsg(socket, args, memory_manager, net_ns, rng, cb_queue)
155            }
156            Self::Udp(socket) => {
157                UdpSocket::sendmsg(socket, args, memory_manager, net_ns, rng, cb_queue)
158            }
159        }
160    }
161
162    pub fn recvmsg(
163        &self,
164        args: RecvmsgArgs,
165        memory_manager: &mut MemoryManager,
166        cb_queue: &mut CallbackQueue,
167    ) -> Result<RecvmsgReturn, SyscallError> {
168        match self {
169            Self::LegacyTcp(socket) => {
170                LegacyTcpSocket::recvmsg(socket, args, memory_manager, cb_queue)
171            }
172            Self::Tcp(socket) => TcpSocket::recvmsg(socket, args, memory_manager, cb_queue),
173            Self::Udp(socket) => UdpSocket::recvmsg(socket, args, memory_manager, cb_queue),
174        }
175    }
176}
177
178impl std::fmt::Debug for InetSocket {
179    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180        match self {
181            Self::LegacyTcp(_) => write!(f, "LegacyTcp")?,
182            Self::Tcp(_) => write!(f, "Tcp")?,
183            Self::Udp(_) => write!(f, "Udp")?,
184        }
185
186        if let Ok(file) = self.try_borrow() {
187            write!(
188                f,
189                "(state: {:?}, status: {:?})",
190                file.state(),
191                file.status()
192            )
193        } else {
194            write!(f, "(already borrowed)")
195        }
196    }
197}
198
199impl PartialEq for InetSocket {
200    /// Equal only if they are the same type and point to the same object. Two different socket
201    /// objects with the exact same state are not considered equal.
202    // Normally rust types implement `Eq` and `Hash` based on their internal state. So two different
203    // objects with the same state will be equal and produce the same hash. We don't want that
204    // behaviour in Shadow, where two different socket objects should always be considered unique.
205    // I'm not sure if we should implement `Eq` and `Hash` on `InetSocket` directly, or if we should
206    // create a wrapper type around `InetSocket` that implements our non-standard `Eq` and `Hash`
207    // behaviour. For now I'm just implementing them directly on `InetSocket`.
208    fn eq(&self, other: &Self) -> bool {
209        // compare addresses first to shortcut more-expensive check
210        if std::ptr::eq(self, other) {
211            return true;
212        }
213
214        match (self, other) {
215            (Self::LegacyTcp(self_), Self::LegacyTcp(other)) => Arc::ptr_eq(self_, other),
216            (Self::Tcp(self_), Self::Tcp(other)) => Arc::ptr_eq(self_, other),
217            (Self::Udp(self_), Self::Udp(other)) => Arc::ptr_eq(self_, other),
218            _ => false,
219        }
220    }
221}
222
223impl Eq for InetSocket {}
224
225impl std::hash::Hash for InetSocket {
226    /// Returns a hash for the socket based on its address, and not the socket's state. Two
227    /// different sockets with the same state will return different hashes, and the same socket will
228    /// return the same hash even after being mutated.
229    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
230        // To match the `Eq` behaviour of `InetSocket`, the hashes of two sockets *must* be equal if
231        // the types are the same and the Arc's pointers are equal, and the hashes *should not* be
232        // equal if the two types are not the same or the Arc's pointers are not equal. We do return
233        // the same hash if the two types are different but the pointers are equal, but this is
234        // allowed by the `Hash` trait (it's just a hash collision) and we should never run into
235        // this without a variant containing a zero-sized type, which wouldn't make sense in the
236        // context of Shadow's sockets anyways.
237        match self {
238            Self::LegacyTcp(x) => Arc::as_ptr(x).cast::<libc::c_void>(),
239            Self::Tcp(x) => Arc::as_ptr(x).cast(),
240            Self::Udp(x) => Arc::as_ptr(x).cast(),
241        }
242        .hash(state);
243    }
244}
245
246pub enum InetSocketRef<'a> {
247    LegacyTcp(atomic_refcell::AtomicRef<'a, LegacyTcpSocket>),
248    Tcp(atomic_refcell::AtomicRef<'a, TcpSocket>),
249    Udp(atomic_refcell::AtomicRef<'a, UdpSocket>),
250}
251
252pub enum InetSocketRefMut<'a> {
253    LegacyTcp(atomic_refcell::AtomicRefMut<'a, LegacyTcpSocket>),
254    Tcp(atomic_refcell::AtomicRefMut<'a, TcpSocket>),
255    Udp(atomic_refcell::AtomicRefMut<'a, UdpSocket>),
256}
257
258// file functions
259impl InetSocketRef<'_> {
260    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
261        pub fn state(&self) -> FileState
262    );
263    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
264        pub fn mode(&self) -> FileMode
265    );
266    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
267        pub fn status(&self) -> FileStatus
268    );
269    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
270        pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
271    );
272    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
273        pub fn has_open_file(&self) -> bool
274    );
275    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
276        pub fn supports_sa_restart(&self) -> bool
277    );
278}
279
280// socket-specific functions
281impl InetSocketRef<'_> {
282    pub fn getpeername(&self) -> Result<Option<SockaddrStorage>, Errno> {
283        match self {
284            Self::LegacyTcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
285            Self::Tcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
286            Self::Udp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
287        }
288    }
289
290    pub fn getsockname(&self) -> Result<Option<SockaddrStorage>, Errno> {
291        match self {
292            Self::LegacyTcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
293            Self::Tcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
294            Self::Udp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
295        }
296    }
297
298    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
299        pub fn address_family(&self) -> linux_api::socket::AddressFamily
300    );
301}
302
303// inet socket-specific functions
304impl InetSocketRef<'_> {
305    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
306        pub fn peek_next_packet_priority(&self) -> Option<FifoPacketPriority>
307    );
308    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
309        pub fn has_data_to_send(&self) -> bool
310    );
311}
312
313// file functions
314impl InetSocketRefMut<'_> {
315    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
316        pub fn state(&self) -> FileState
317    );
318    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
319        pub fn mode(&self) -> FileMode
320    );
321    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
322        pub fn status(&self) -> FileStatus
323    );
324    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
325        pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError>
326    );
327    enum_passthrough!(self, (off, whence), LegacyTcp, Tcp, Udp;
328        pub fn lseek(
329            &mut self,
330            off: linux_api::posix_types::kernel_off_t,
331            whence: linux_api::unistd::LSeekWhence,
332        ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError>
333    );
334    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
335        pub fn has_open_file(&self) -> bool
336    );
337    enum_passthrough!(self, (val), LegacyTcp, Tcp, Udp;
338        pub fn set_has_open_file(&mut self, val: bool)
339    );
340    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
341        pub fn supports_sa_restart(&self) -> bool
342    );
343    enum_passthrough!(self, (cb_queue), LegacyTcp, Tcp, Udp;
344        pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
345    );
346    enum_passthrough!(self, (status), LegacyTcp, Tcp, Udp;
347        pub fn set_status(&mut self, status: FileStatus)
348    );
349    enum_passthrough!(self, (request, arg_ptr, memory_manager), LegacyTcp, Tcp, Udp;
350        pub fn ioctl(&mut self, request: IoctlRequest, arg_ptr: ForeignPtr<()>, memory_manager: &mut MemoryManager) -> SyscallResult
351    );
352    enum_passthrough!(self, (monitoring_state, monitoring_signals, filter, notify_fn), LegacyTcp, Tcp, Udp;
353        pub fn add_listener(
354            &mut self,
355            monitoring_state: FileState,
356            monitoring_signals: FileSignals,
357            filter: StateListenerFilter,
358            notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue) + Send + Sync + 'static,
359        ) -> StateListenHandle
360    );
361    enum_passthrough!(self, (ptr), LegacyTcp, Tcp, Udp;
362        pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>)
363    );
364    enum_passthrough!(self, (ptr), LegacyTcp, Tcp, Udp;
365        pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener)
366    );
367    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp, Tcp, Udp;
368        pub fn readv(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
369                     mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
370    );
371    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp, Tcp, Udp;
372        pub fn writev(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
373                      mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
374    );
375}
376
377// socket-specific functions
378impl InetSocketRefMut<'_> {
379    pub fn getpeername(&self) -> Result<Option<SockaddrStorage>, Errno> {
380        match self {
381            Self::LegacyTcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
382            Self::Tcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
383            Self::Udp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
384        }
385    }
386
387    pub fn getsockname(&self) -> Result<Option<SockaddrStorage>, Errno> {
388        match self {
389            Self::LegacyTcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
390            Self::Tcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
391            Self::Udp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
392        }
393    }
394
395    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
396        pub fn address_family(&self) -> linux_api::socket::AddressFamily
397    );
398
399    enum_passthrough!(self, (level, optname, optval_ptr, optlen, memory_manager, cb_queue), LegacyTcp, Tcp, Udp;
400        pub fn getsockopt(&mut self, level: libc::c_int, optname: libc::c_int, optval_ptr: ForeignPtr<()>,
401                          optlen: libc::socklen_t, memory_manager: &mut MemoryManager, cb_queue: &mut CallbackQueue)
402        -> Result<libc::socklen_t, SyscallError>
403    );
404
405    enum_passthrough!(self, (level, optname, optval_ptr, optlen, memory_manager), LegacyTcp, Tcp, Udp;
406        pub fn setsockopt(&mut self, level: libc::c_int, optname: libc::c_int, optval_ptr: ForeignPtr<()>,
407                          optlen: libc::socklen_t, memory_manager: &MemoryManager)
408        -> Result<(), SyscallError>
409    );
410
411    pub fn accept(
412        &mut self,
413        net_ns: &NetworkNamespace,
414        rng: impl rand::Rng,
415        cb_queue: &mut CallbackQueue,
416    ) -> Result<OpenFile, SyscallError> {
417        match self {
418            Self::LegacyTcp(socket) => socket.accept(net_ns, rng, cb_queue),
419            Self::Tcp(socket) => socket.accept(net_ns, rng, cb_queue),
420            Self::Udp(socket) => socket.accept(net_ns, rng, cb_queue),
421        }
422    }
423
424    enum_passthrough!(self, (how, cb_queue), LegacyTcp, Tcp, Udp;
425        pub fn shutdown(&mut self, how: Shutdown, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
426    );
427}
428
429// inet socket-specific functions
430impl InetSocketRefMut<'_> {
431    enum_passthrough!(self, (packet, cb_queue, recv_time), LegacyTcp, Tcp, Udp;
432        pub fn push_in_packet(&mut self, packet: PacketRc, cb_queue: &mut CallbackQueue, recv_time: EmulatedTime)
433    );
434    enum_passthrough!(self, (cb_queue), LegacyTcp, Tcp, Udp;
435        pub fn pull_out_packet(&mut self, cb_queue: &mut CallbackQueue) -> Option<PacketRc>
436    );
437    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
438        pub fn peek_next_packet_priority(&self) -> Option<FifoPacketPriority>
439    );
440    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
441        pub fn has_data_to_send(&self) -> bool
442    );
443}
444
445impl std::fmt::Debug for InetSocketRef<'_> {
446    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
447        match self {
448            Self::LegacyTcp(_) => write!(f, "LegacyTcp")?,
449            Self::Tcp(_) => write!(f, "Tcp")?,
450            Self::Udp(_) => write!(f, "Udp")?,
451        }
452
453        write!(
454            f,
455            "(state: {:?}, status: {:?})",
456            self.state(),
457            self.status()
458        )
459    }
460}
461
462impl std::fmt::Debug for InetSocketRefMut<'_> {
463    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464        match self {
465            Self::LegacyTcp(_) => write!(f, "LegacyTcp")?,
466            Self::Tcp(_) => write!(f, "Tcp")?,
467            Self::Udp(_) => write!(f, "Udp")?,
468        }
469
470        write!(
471            f,
472            "(state: {:?}, status: {:?})",
473            self.state(),
474            self.status()
475        )
476    }
477}
478
479#[derive(Clone)]
480pub enum InetSocketWeak {
481    LegacyTcp(Weak<AtomicRefCell<LegacyTcpSocket>>),
482    Tcp(Weak<AtomicRefCell<TcpSocket>>),
483    Udp(Weak<AtomicRefCell<UdpSocket>>),
484}
485
486impl InetSocketWeak {
487    pub fn upgrade(&self) -> Option<InetSocket> {
488        match self {
489            Self::LegacyTcp(x) => x.upgrade().map(InetSocket::LegacyTcp),
490            Self::Tcp(x) => x.upgrade().map(InetSocket::Tcp),
491            Self::Udp(x) => x.upgrade().map(InetSocket::Udp),
492        }
493    }
494}
495
496/// Associate the socket with a network interface. If the local address is unspecified, the socket
497/// will be associated with every available interface. If the local address has a port of 0, a
498/// non-zero port will be chosen. The final local address will be returned. If the peer address is
499/// unspecified and has a port of 0, the socket will receive packets from every peer address. The
500/// socket will be automatically disassociated when the returned [`AssociationHandle`] is dropped.
501/// If `check_generic_peer` is true, the association will also fail if there is already a socket
502/// associated with the local address `local_addr` and peer address 0.0.0.0:0.
503fn associate_socket(
504    socket: InetSocket,
505    local_addr: SocketAddrV4,
506    peer_addr: SocketAddrV4,
507    check_generic_peer: bool,
508    net_ns: &NetworkNamespace,
509    rng: impl rand::Rng,
510) -> Result<(SocketAddrV4, AssociationHandle), Errno> {
511    log::trace!("Trying to associate socket with addresses (local={local_addr}, peer={peer_addr})");
512
513    if !local_addr.ip().is_unspecified() && net_ns.interface_borrow(*local_addr.ip()).is_none() {
514        log::debug!(
515            "No network interface exists for the provided local address {}",
516            local_addr.ip(),
517        );
518        return Err(Errno::EINVAL);
519    };
520
521    let protocol = match socket {
522        InetSocket::LegacyTcp(_) => IanaProtocol::Tcp,
523        InetSocket::Tcp(_) => IanaProtocol::Tcp,
524        InetSocket::Udp(_) => IanaProtocol::Udp,
525    };
526
527    // get a free ephemeral port if they didn't specify one
528    let local_addr = if local_addr.port() != 0 {
529        local_addr
530    } else {
531        let Some(new_port) =
532            net_ns.get_random_free_port(protocol, *local_addr.ip(), peer_addr, rng)
533        else {
534            log::debug!("Association required an ephemeral port but none are available");
535            return Err(Errno::EADDRINUSE);
536        };
537
538        log::debug!("Associating with generated ephemeral port {new_port}");
539
540        // update the address with the same ip, but new port
541        SocketAddrV4::new(*local_addr.ip(), new_port)
542    };
543
544    // make sure the port is available at this address for this protocol
545    match net_ns.is_addr_in_use(protocol, local_addr, peer_addr) {
546        Ok(true) => {
547            log::debug!(
548                "The provided addresses (local={local_addr}, peer={peer_addr}) are not available"
549            );
550            return Err(Errno::EADDRINUSE);
551        }
552        Err(_e) => return Err(Errno::EADDRNOTAVAIL),
553        Ok(false) => {}
554    }
555
556    if check_generic_peer {
557        match net_ns.is_addr_in_use(
558            protocol,
559            local_addr,
560            SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0),
561        ) {
562            Ok(true) => {
563                log::debug!(
564                    "The generic addresses (local={local_addr}, peer={}) are not available",
565                    SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)
566                );
567                return Err(Errno::EADDRINUSE);
568            }
569            Err(_e) => return Err(Errno::EADDRNOTAVAIL),
570            Ok(false) => {}
571        }
572    }
573
574    // associate the interfaces corresponding to addr with socket
575    let handle = unsafe { net_ns.associate_interface(&socket, protocol, local_addr, peer_addr) };
576
577    Ok((local_addr, handle))
578}
579
580mod export {
581    use super::*;
582
583    /// Decrement the ref count of the `InetSocket` object. The pointer must not be used after
584    /// calling this function.
585    #[unsafe(no_mangle)]
586    pub extern "C-unwind" fn inetsocket_drop(socket: *const InetSocket) {
587        assert!(!socket.is_null());
588        drop(unsafe { Box::from_raw(socket.cast_mut()) });
589    }
590
591    /// Helper for GLib functions that take a `TaskObjectFreeFunc`. See [`inetsocket_drop`].
592    #[unsafe(no_mangle)]
593    pub extern "C-unwind" fn inetsocket_dropVoid(socket: *mut libc::c_void) {
594        inetsocket_drop(socket.cast_const().cast())
595    }
596
597    /// Get a legacy C [`TCP`](c::TCP) pointer for the socket. Will panic if `socket` is not a
598    /// legacy TCP socket or if `socket` is already mutably borrowed. Will never return `NULL`.
599    #[unsafe(no_mangle)]
600    pub extern "C-unwind" fn inetsocket_asLegacyTcp(socket: *const InetSocket) -> *mut c::TCP {
601        let socket = unsafe { socket.as_ref() }.unwrap();
602
603        #[allow(irrefutable_let_patterns)]
604        let InetSocket::LegacyTcp(socket) = socket else {
605            panic!("Socket was not a legacy TCP socket: {socket:?}");
606        };
607
608        let ptr = socket.borrow().as_legacy_tcp();
609        // this should never be true
610        assert!(!ptr.is_null());
611
612        ptr
613    }
614
615    /// Decrement the ref count of the `InetSocketWeak` object. The pointer must not be used after
616    /// calling this function.
617    #[unsafe(no_mangle)]
618    pub extern "C-unwind" fn inetsocketweak_drop(socket: *mut InetSocketWeak) {
619        assert!(!socket.is_null());
620        drop(unsafe { Box::from_raw(socket) });
621    }
622
623    /// Upgrade the weak reference. May return `NULL` if the socket has no remaining strong
624    /// references and has been dropped. Returns an owned `InetSocket` that must be dropped as a
625    /// `Box` later (for example using `inetsocket_drop`).
626    #[unsafe(no_mangle)]
627    pub extern "C-unwind" fn inetsocketweak_upgrade(
628        socket: *const InetSocketWeak,
629    ) -> *mut InetSocket {
630        let socket = unsafe { socket.as_ref() }.unwrap();
631        socket
632            .upgrade()
633            .map(Box::new)
634            .map(Box::into_raw)
635            .unwrap_or(std::ptr::null_mut())
636    }
637}