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, (), LegacyTcp, Tcp, Udp;
328        pub fn has_open_file(&self) -> bool
329    );
330    enum_passthrough!(self, (val), LegacyTcp, Tcp, Udp;
331        pub fn set_has_open_file(&mut self, val: bool)
332    );
333    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
334        pub fn supports_sa_restart(&self) -> bool
335    );
336    enum_passthrough!(self, (cb_queue), LegacyTcp, Tcp, Udp;
337        pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
338    );
339    enum_passthrough!(self, (status), LegacyTcp, Tcp, Udp;
340        pub fn set_status(&mut self, status: FileStatus)
341    );
342    enum_passthrough!(self, (request, arg_ptr, memory_manager), LegacyTcp, Tcp, Udp;
343        pub fn ioctl(&mut self, request: IoctlRequest, arg_ptr: ForeignPtr<()>, memory_manager: &mut MemoryManager) -> SyscallResult
344    );
345    enum_passthrough!(self, (monitoring_state, monitoring_signals, filter, notify_fn), LegacyTcp, Tcp, Udp;
346        pub fn add_listener(
347            &mut self,
348            monitoring_state: FileState,
349            monitoring_signals: FileSignals,
350            filter: StateListenerFilter,
351            notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue) + Send + Sync + 'static,
352        ) -> StateListenHandle
353    );
354    enum_passthrough!(self, (ptr), LegacyTcp, Tcp, Udp;
355        pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>)
356    );
357    enum_passthrough!(self, (ptr), LegacyTcp, Tcp, Udp;
358        pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener)
359    );
360    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp, Tcp, Udp;
361        pub fn readv(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
362                     mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
363    );
364    enum_passthrough!(self, (iovs, offset, flags, mem, cb_queue), LegacyTcp, Tcp, Udp;
365        pub fn writev(&mut self, iovs: &[IoVec], offset: Option<libc::off_t>, flags: libc::c_int,
366                      mem: &mut MemoryManager, cb_queue: &mut CallbackQueue) -> Result<libc::ssize_t, SyscallError>
367    );
368}
369
370// socket-specific functions
371impl InetSocketRefMut<'_> {
372    pub fn getpeername(&self) -> Result<Option<SockaddrStorage>, Errno> {
373        match self {
374            Self::LegacyTcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
375            Self::Tcp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
376            Self::Udp(socket) => socket.getpeername().map(|opt| opt.map(Into::into)),
377        }
378    }
379
380    pub fn getsockname(&self) -> Result<Option<SockaddrStorage>, Errno> {
381        match self {
382            Self::LegacyTcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
383            Self::Tcp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
384            Self::Udp(socket) => socket.getsockname().map(|opt| opt.map(Into::into)),
385        }
386    }
387
388    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
389        pub fn address_family(&self) -> linux_api::socket::AddressFamily
390    );
391
392    enum_passthrough!(self, (level, optname, optval_ptr, optlen, memory_manager, cb_queue), LegacyTcp, Tcp, Udp;
393        pub fn getsockopt(&mut self, level: libc::c_int, optname: libc::c_int, optval_ptr: ForeignPtr<()>,
394                          optlen: libc::socklen_t, memory_manager: &mut MemoryManager, cb_queue: &mut CallbackQueue)
395        -> Result<libc::socklen_t, SyscallError>
396    );
397
398    enum_passthrough!(self, (level, optname, optval_ptr, optlen, memory_manager), LegacyTcp, Tcp, Udp;
399        pub fn setsockopt(&mut self, level: libc::c_int, optname: libc::c_int, optval_ptr: ForeignPtr<()>,
400                          optlen: libc::socklen_t, memory_manager: &MemoryManager)
401        -> Result<(), SyscallError>
402    );
403
404    pub fn accept(
405        &mut self,
406        net_ns: &NetworkNamespace,
407        rng: impl rand::Rng,
408        cb_queue: &mut CallbackQueue,
409    ) -> Result<OpenFile, SyscallError> {
410        match self {
411            Self::LegacyTcp(socket) => socket.accept(net_ns, rng, cb_queue),
412            Self::Tcp(socket) => socket.accept(net_ns, rng, cb_queue),
413            Self::Udp(socket) => socket.accept(net_ns, rng, cb_queue),
414        }
415    }
416
417    enum_passthrough!(self, (how, cb_queue), LegacyTcp, Tcp, Udp;
418        pub fn shutdown(&mut self, how: Shutdown, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError>
419    );
420}
421
422// inet socket-specific functions
423impl InetSocketRefMut<'_> {
424    enum_passthrough!(self, (packet, cb_queue, recv_time), LegacyTcp, Tcp, Udp;
425        pub fn push_in_packet(&mut self, packet: PacketRc, cb_queue: &mut CallbackQueue, recv_time: EmulatedTime)
426    );
427    enum_passthrough!(self, (cb_queue), LegacyTcp, Tcp, Udp;
428        pub fn pull_out_packet(&mut self, cb_queue: &mut CallbackQueue) -> Option<PacketRc>
429    );
430    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
431        pub fn peek_next_packet_priority(&self) -> Option<FifoPacketPriority>
432    );
433    enum_passthrough!(self, (), LegacyTcp, Tcp, Udp;
434        pub fn has_data_to_send(&self) -> bool
435    );
436}
437
438impl std::fmt::Debug for InetSocketRef<'_> {
439    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
440        match self {
441            Self::LegacyTcp(_) => write!(f, "LegacyTcp")?,
442            Self::Tcp(_) => write!(f, "Tcp")?,
443            Self::Udp(_) => write!(f, "Udp")?,
444        }
445
446        write!(
447            f,
448            "(state: {:?}, status: {:?})",
449            self.state(),
450            self.status()
451        )
452    }
453}
454
455impl std::fmt::Debug for InetSocketRefMut<'_> {
456    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
457        match self {
458            Self::LegacyTcp(_) => write!(f, "LegacyTcp")?,
459            Self::Tcp(_) => write!(f, "Tcp")?,
460            Self::Udp(_) => write!(f, "Udp")?,
461        }
462
463        write!(
464            f,
465            "(state: {:?}, status: {:?})",
466            self.state(),
467            self.status()
468        )
469    }
470}
471
472#[derive(Clone)]
473pub enum InetSocketWeak {
474    LegacyTcp(Weak<AtomicRefCell<LegacyTcpSocket>>),
475    Tcp(Weak<AtomicRefCell<TcpSocket>>),
476    Udp(Weak<AtomicRefCell<UdpSocket>>),
477}
478
479impl InetSocketWeak {
480    pub fn upgrade(&self) -> Option<InetSocket> {
481        match self {
482            Self::LegacyTcp(x) => x.upgrade().map(InetSocket::LegacyTcp),
483            Self::Tcp(x) => x.upgrade().map(InetSocket::Tcp),
484            Self::Udp(x) => x.upgrade().map(InetSocket::Udp),
485        }
486    }
487}
488
489/// Associate the socket with a network interface. If the local address is unspecified, the socket
490/// will be associated with every available interface. If the local address has a port of 0, a
491/// non-zero port will be chosen. The final local address will be returned. If the peer address is
492/// unspecified and has a port of 0, the socket will receive packets from every peer address. The
493/// socket will be automatically disassociated when the returned [`AssociationHandle`] is dropped.
494/// If `check_generic_peer` is true, the association will also fail if there is already a socket
495/// associated with the local address `local_addr` and peer address 0.0.0.0:0.
496fn associate_socket(
497    socket: InetSocket,
498    local_addr: SocketAddrV4,
499    peer_addr: SocketAddrV4,
500    check_generic_peer: bool,
501    net_ns: &NetworkNamespace,
502    rng: impl rand::Rng,
503) -> Result<(SocketAddrV4, AssociationHandle), Errno> {
504    log::trace!("Trying to associate socket with addresses (local={local_addr}, peer={peer_addr})");
505
506    if !local_addr.ip().is_unspecified() && net_ns.interface_borrow(*local_addr.ip()).is_none() {
507        log::debug!(
508            "No network interface exists for the provided local address {}",
509            local_addr.ip(),
510        );
511        return Err(Errno::EINVAL);
512    };
513
514    let protocol = match socket {
515        InetSocket::LegacyTcp(_) => IanaProtocol::Tcp,
516        InetSocket::Tcp(_) => IanaProtocol::Tcp,
517        InetSocket::Udp(_) => IanaProtocol::Udp,
518    };
519
520    // get a free ephemeral port if they didn't specify one
521    let local_addr = if local_addr.port() != 0 {
522        local_addr
523    } else {
524        let Some(new_port) =
525            net_ns.get_random_free_port(protocol, *local_addr.ip(), peer_addr, rng)
526        else {
527            log::debug!("Association required an ephemeral port but none are available");
528            return Err(Errno::EADDRINUSE);
529        };
530
531        log::debug!("Associating with generated ephemeral port {new_port}");
532
533        // update the address with the same ip, but new port
534        SocketAddrV4::new(*local_addr.ip(), new_port)
535    };
536
537    // make sure the port is available at this address for this protocol
538    match net_ns.is_addr_in_use(protocol, local_addr, peer_addr) {
539        Ok(true) => {
540            log::debug!(
541                "The provided addresses (local={local_addr}, peer={peer_addr}) are not available"
542            );
543            return Err(Errno::EADDRINUSE);
544        }
545        Err(_e) => return Err(Errno::EADDRNOTAVAIL),
546        Ok(false) => {}
547    }
548
549    if check_generic_peer {
550        match net_ns.is_addr_in_use(
551            protocol,
552            local_addr,
553            SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0),
554        ) {
555            Ok(true) => {
556                log::debug!(
557                    "The generic addresses (local={local_addr}, peer={}) are not available",
558                    SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)
559                );
560                return Err(Errno::EADDRINUSE);
561            }
562            Err(_e) => return Err(Errno::EADDRNOTAVAIL),
563            Ok(false) => {}
564        }
565    }
566
567    // associate the interfaces corresponding to addr with socket
568    let handle = unsafe { net_ns.associate_interface(&socket, protocol, local_addr, peer_addr) };
569
570    Ok((local_addr, handle))
571}
572
573mod export {
574    use super::*;
575
576    /// Decrement the ref count of the `InetSocket` object. The pointer must not be used after
577    /// calling this function.
578    #[unsafe(no_mangle)]
579    pub extern "C-unwind" fn inetsocket_drop(socket: *const InetSocket) {
580        assert!(!socket.is_null());
581        drop(unsafe { Box::from_raw(socket.cast_mut()) });
582    }
583
584    /// Helper for GLib functions that take a `TaskObjectFreeFunc`. See [`inetsocket_drop`].
585    #[unsafe(no_mangle)]
586    pub extern "C-unwind" fn inetsocket_dropVoid(socket: *mut libc::c_void) {
587        inetsocket_drop(socket.cast_const().cast())
588    }
589
590    /// Get a legacy C [`TCP`](c::TCP) pointer for the socket. Will panic if `socket` is not a
591    /// legacy TCP socket or if `socket` is already mutably borrowed. Will never return `NULL`.
592    #[unsafe(no_mangle)]
593    pub extern "C-unwind" fn inetsocket_asLegacyTcp(socket: *const InetSocket) -> *mut c::TCP {
594        let socket = unsafe { socket.as_ref() }.unwrap();
595
596        #[allow(irrefutable_let_patterns)]
597        let InetSocket::LegacyTcp(socket) = socket else {
598            panic!("Socket was not a legacy TCP socket: {socket:?}");
599        };
600
601        let ptr = socket.borrow().as_legacy_tcp();
602        // this should never be true
603        assert!(!ptr.is_null());
604
605        ptr
606    }
607
608    /// Decrement the ref count of the `InetSocketWeak` object. The pointer must not be used after
609    /// calling this function.
610    #[unsafe(no_mangle)]
611    pub extern "C-unwind" fn inetsocketweak_drop(socket: *mut InetSocketWeak) {
612        assert!(!socket.is_null());
613        drop(unsafe { Box::from_raw(socket) });
614    }
615
616    /// Upgrade the weak reference. May return `NULL` if the socket has no remaining strong
617    /// references and has been dropped. Returns an owned `InetSocket` that must be dropped as a
618    /// `Box` later (for example using `inetsocket_drop`).
619    #[unsafe(no_mangle)]
620    pub extern "C-unwind" fn inetsocketweak_upgrade(
621        socket: *const InetSocketWeak,
622    ) -> *mut InetSocket {
623        let socket = unsafe { socket.as_ref() }.unwrap();
624        socket
625            .upgrade()
626            .map(Box::new)
627            .map(Box::into_raw)
628            .unwrap_or(std::ptr::null_mut())
629    }
630}