shadow_rs/host/syscall/handler/
socket.rs

1use linux_api::errno::Errno;
2use linux_api::fcntl::DescriptorFlags;
3use linux_api::socket::Shutdown;
4use log::*;
5use nix::sys::socket::SockFlag;
6use shadow_shim_helper_rs::syscall_types::ForeignPtr;
7
8use crate::host::descriptor::descriptor_table::DescriptorHandle;
9use crate::host::descriptor::socket::inet::InetSocket;
10use crate::host::descriptor::socket::inet::legacy_tcp::LegacyTcpSocket;
11use crate::host::descriptor::socket::inet::tcp::TcpSocket;
12use crate::host::descriptor::socket::inet::udp::UdpSocket;
13use crate::host::descriptor::socket::netlink::{NetlinkFamily, NetlinkSocket, NetlinkSocketType};
14use crate::host::descriptor::socket::unix::{UnixSocket, UnixSocketType};
15use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs, Socket};
16use crate::host::descriptor::{CompatFile, Descriptor, File, FileState, FileStatus, OpenFile};
17use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
18use crate::host::syscall::io::{self, IoVec};
19use crate::host::syscall::type_formatting::{SyscallBufferArg, SyscallSockAddrArg};
20use crate::host::syscall::types::ForeignArrayPtr;
21use crate::host::syscall::types::SyscallError;
22use crate::utility::callback_queue::CallbackQueue;
23use crate::utility::sockaddr::SockaddrStorage;
24
25impl SyscallHandler {
26    log_syscall!(
27        socket,
28        /* rv */ std::ffi::c_int,
29        /* domain */ linux_api::socket::AddressFamily,
30        /* type */ std::ffi::c_int,
31        /* protocol */ std::ffi::c_int,
32    );
33    pub fn socket(
34        ctx: &mut SyscallContext,
35        domain: std::ffi::c_int,
36        socket_type: std::ffi::c_int,
37        protocol: std::ffi::c_int,
38    ) -> Result<DescriptorHandle, Errno> {
39        // remove any flags from the socket type
40        let flags = socket_type & (libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC);
41        let socket_type = socket_type & !flags;
42
43        let mut file_flags = FileStatus::empty();
44        let mut descriptor_flags = DescriptorFlags::empty();
45
46        if flags & libc::SOCK_NONBLOCK != 0 {
47            file_flags.insert(FileStatus::NONBLOCK);
48        }
49
50        if flags & libc::SOCK_CLOEXEC != 0 {
51            descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC);
52        }
53
54        let socket = match domain {
55            libc::AF_UNIX => {
56                let socket_type = match UnixSocketType::try_from(socket_type) {
57                    Ok(x) => x,
58                    Err(e) => {
59                        warn!("{}", e);
60                        return Err(Errno::EPROTONOSUPPORT);
61                    }
62                };
63
64                // unix sockets don't support any protocols
65                if protocol != 0 {
66                    warn!(
67                        "Unsupported socket protocol {}, we only support default protocol 0",
68                        protocol
69                    );
70                    return Err(Errno::EPROTONOSUPPORT);
71                }
72
73                Socket::Unix(UnixSocket::new(
74                    file_flags,
75                    socket_type,
76                    &ctx.objs.host.abstract_unix_namespace(),
77                ))
78            }
79            libc::AF_INET => match socket_type {
80                libc::SOCK_STREAM => {
81                    if protocol != 0 && protocol != libc::IPPROTO_TCP {
82                        log::debug!("Unsupported inet stream socket protocol {protocol}");
83                        return Err(Errno::EPROTONOSUPPORT);
84                    }
85
86                    if ctx.objs.host.params.use_new_tcp {
87                        Socket::Inet(InetSocket::Tcp(TcpSocket::new(file_flags)))
88                    } else {
89                        Socket::Inet(InetSocket::LegacyTcp(LegacyTcpSocket::new(
90                            file_flags,
91                            ctx.objs.host,
92                        )))
93                    }
94                }
95                libc::SOCK_DGRAM => {
96                    if protocol != 0 && protocol != libc::IPPROTO_UDP {
97                        log::debug!("Unsupported inet dgram socket protocol {protocol}");
98                        return Err(Errno::EPROTONOSUPPORT);
99                    }
100                    let send_buf_size = ctx.objs.host.params.init_sock_send_buf_size;
101                    let recv_buf_size = ctx.objs.host.params.init_sock_recv_buf_size;
102                    Socket::Inet(InetSocket::Udp(UdpSocket::new(
103                        file_flags,
104                        send_buf_size.try_into().unwrap(),
105                        recv_buf_size.try_into().unwrap(),
106                    )))
107                }
108                _ => return Err(Errno::ESOCKTNOSUPPORT),
109            },
110            libc::AF_NETLINK => {
111                let socket_type = match NetlinkSocketType::try_from(socket_type) {
112                    Ok(x) => x,
113                    Err(e) => {
114                        warn!("{}", e);
115                        return Err(Errno::EPROTONOSUPPORT);
116                    }
117                };
118                let family = match NetlinkFamily::try_from(protocol) {
119                    Ok(x) => x,
120                    Err(e) => {
121                        warn!("{}", e);
122                        return Err(Errno::EPROTONOSUPPORT);
123                    }
124                };
125                Socket::Netlink(NetlinkSocket::new(file_flags, socket_type, family))
126            }
127            _ => return Err(Errno::EAFNOSUPPORT),
128        };
129
130        let mut desc = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(socket))));
131        desc.set_flags(descriptor_flags);
132
133        let fd = ctx
134            .objs
135            .thread
136            .descriptor_table_borrow_mut(ctx.objs.host)
137            .register_descriptor(desc)
138            .or(Err(Errno::ENFILE))?;
139
140        log::trace!("Created socket fd {fd}");
141
142        Ok(fd)
143    }
144
145    log_syscall!(
146        bind,
147        /* rv */ std::ffi::c_int,
148        /* sockfd */ std::ffi::c_int,
149        /* addr */ SyscallSockAddrArg</* addrlen */ 2>,
150        /* addrlen */ libc::socklen_t,
151    );
152    pub fn bind(
153        ctx: &mut SyscallContext,
154        fd: std::ffi::c_int,
155        addr_ptr: ForeignPtr<u8>,
156        addr_len: libc::socklen_t,
157    ) -> Result<(), SyscallError> {
158        let file = {
159            // get the descriptor, or return early if it doesn't exist
160            let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
161            let desc = Self::get_descriptor(&desc_table, fd)?;
162
163            let CompatFile::New(file) = desc.file() else {
164                // we don't have any C socket objects
165                return Err(Errno::ENOTSOCK.into());
166            };
167
168            file.inner_file().clone()
169        };
170
171        let File::Socket(socket) = file else {
172            return Err(Errno::ENOTSOCK.into());
173        };
174
175        let addr = io::read_sockaddr(&ctx.objs.process.memory_borrow(), addr_ptr, addr_len)?;
176
177        log::trace!("Attempting to bind fd {} to {:?}", fd, addr);
178
179        let mut rng = ctx.objs.host.random_mut();
180        let net_ns = ctx.objs.host.network_namespace_borrow();
181        Socket::bind(&socket, addr.as_ref(), &net_ns, &mut *rng)
182    }
183
184    log_syscall!(
185        sendto,
186        /* rv */ libc::ssize_t,
187        /* sockfd */ std::ffi::c_int,
188        /* buf */ SyscallBufferArg</* len */ 2>,
189        /* len */ libc::size_t,
190        /* flags */ nix::sys::socket::MsgFlags,
191        /* dest_addr */ SyscallSockAddrArg</* addrlen */ 5>,
192        /* addrlen */ libc::socklen_t,
193    );
194    pub fn sendto(
195        ctx: &mut SyscallContext,
196        fd: std::ffi::c_int,
197        buf_ptr: ForeignPtr<u8>,
198        buf_len: libc::size_t,
199        flags: std::ffi::c_int,
200        addr_ptr: ForeignPtr<u8>,
201        addr_len: libc::socklen_t,
202    ) -> Result<libc::ssize_t, SyscallError> {
203        // if we were previously blocked, get the active file from the last syscall handler
204        // invocation since it may no longer exist in the descriptor table
205        let file = ctx
206            .objs
207            .thread
208            .syscall_condition()
209            // if this was for a C descriptor, then there won't be an active file object
210            .and_then(|x| x.active_file().cloned());
211
212        let file = match file {
213            // we were previously blocked, so re-use the file from the previous syscall invocation
214            Some(x) => x,
215            // get the file from the descriptor table, or return early if it doesn't exist
216            None => {
217                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
218                let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
219                    // we don't have any C socket objects
220                    return Err(Errno::ENOTSOCK.into());
221                };
222                file.clone()
223            }
224        };
225
226        let File::Socket(socket) = file.inner_file() else {
227            return Err(Errno::ENOTSOCK.into());
228        };
229
230        let mut mem = ctx.objs.process.memory_borrow_mut();
231        let mut rng = ctx.objs.host.random_mut();
232        let net_ns = ctx.objs.host.network_namespace_borrow();
233
234        let addr = io::read_sockaddr(&mem, addr_ptr, addr_len)?;
235
236        log::trace!("Attempting to send {} bytes to {:?}", buf_len, addr);
237
238        let iov = IoVec {
239            base: buf_ptr,
240            len: buf_len,
241        };
242
243        let args = SendmsgArgs {
244            addr,
245            iovs: &[iov],
246            control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
247            flags,
248        };
249
250        // call the socket's sendmsg(), and run any resulting events
251        let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
252            Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
253        });
254
255        // if the syscall will block, keep the file open until the syscall restarts
256        if let Some(err) = result.as_mut().err() {
257            if let Some(cond) = err.blocked_condition() {
258                cond.set_active_file(file);
259            }
260        }
261
262        let bytes_sent = result?;
263        Ok(bytes_sent)
264    }
265
266    log_syscall!(
267        sendmsg,
268        /* rv */ libc::ssize_t,
269        /* sockfd */ std::ffi::c_int,
270        /* msg */ *const libc::msghdr,
271        /* flags */ nix::sys::socket::MsgFlags,
272    );
273    pub fn sendmsg(
274        ctx: &mut SyscallContext,
275        fd: std::ffi::c_int,
276        msg_ptr: ForeignPtr<libc::msghdr>,
277        flags: std::ffi::c_int,
278    ) -> Result<libc::ssize_t, SyscallError> {
279        // if we were previously blocked, get the active file from the last syscall handler
280        // invocation since it may no longer exist in the descriptor table
281        let file = ctx
282            .objs
283            .thread
284            .syscall_condition()
285            // if this was for a C descriptor, then there won't be an active file object
286            .and_then(|x| x.active_file().cloned());
287
288        let file = match file {
289            // we were previously blocked, so re-use the file from the previous syscall invocation
290            Some(x) => x,
291            // get the file from the descriptor table, or return early if it doesn't exist
292            None => {
293                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
294                match Self::get_descriptor(&desc_table, fd)?.file() {
295                    CompatFile::New(file) => file.clone(),
296                    CompatFile::Legacy(_file) => {
297                        return Err(Errno::ENOTSOCK.into());
298                    }
299                }
300            }
301        };
302
303        let File::Socket(socket) = file.inner_file() else {
304            return Err(Errno::ENOTSOCK.into());
305        };
306
307        let mut mem = ctx.objs.process.memory_borrow_mut();
308        let mut rng = ctx.objs.host.random_mut();
309        let net_ns = ctx.objs.host.network_namespace_borrow();
310
311        let msg = io::read_msghdr(&mem, msg_ptr)?;
312
313        let args = SendmsgArgs {
314            addr: io::read_sockaddr(&mem, msg.name, msg.name_len)?,
315            iovs: &msg.iovs,
316            control_ptr: ForeignArrayPtr::new(msg.control, msg.control_len),
317            // note: "the msg_flags field is ignored" for sendmsg; see send(2)
318            flags,
319        };
320
321        // call the socket's sendmsg(), and run any resulting events
322        let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
323            Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
324        });
325
326        // if the syscall will block, keep the file open until the syscall restarts
327        if let Some(err) = result.as_mut().err() {
328            if let Some(cond) = err.blocked_condition() {
329                cond.set_active_file(file);
330            }
331        }
332
333        let bytes_written = result?;
334        Ok(bytes_written)
335    }
336
337    log_syscall!(
338        recvfrom,
339        /* rv */ libc::ssize_t,
340        /* sockfd */ std::ffi::c_int,
341        /* buf */ *const std::ffi::c_void,
342        /* len */ libc::size_t,
343        /* flags */ nix::sys::socket::MsgFlags,
344        /* src_addr */ *const libc::sockaddr,
345        /* addrlen */ *const libc::socklen_t,
346    );
347    pub fn recvfrom(
348        ctx: &mut SyscallContext,
349        fd: std::ffi::c_int,
350        buf_ptr: ForeignPtr<u8>,
351        buf_len: libc::size_t,
352        flags: std::ffi::c_int,
353        addr_ptr: ForeignPtr<u8>,
354        addr_len_ptr: ForeignPtr<libc::socklen_t>,
355    ) -> Result<libc::ssize_t, SyscallError> {
356        // if we were previously blocked, get the active file from the last syscall handler
357        // invocation since it may no longer exist in the descriptor table
358        let file = ctx
359            .objs
360            .thread
361            .syscall_condition()
362            // if this was for a C descriptor, then there won't be an active file object
363            .and_then(|x| x.active_file().cloned());
364
365        let file = match file {
366            // we were previously blocked, so re-use the file from the previous syscall invocation
367            Some(x) => x,
368            // get the file from the descriptor table, or return early if it doesn't exist
369            None => {
370                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
371                let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
372                    // we don't have any C socket objects
373                    return Err(Errno::ENOTSOCK.into());
374                };
375                file.clone()
376            }
377        };
378
379        let File::Socket(socket) = file.inner_file() else {
380            return Err(Errno::ENOTSOCK.into());
381        };
382
383        let mut mem = ctx.objs.process.memory_borrow_mut();
384
385        log::trace!("Attempting to recv {} bytes", buf_len);
386
387        let iov = IoVec {
388            base: buf_ptr,
389            len: buf_len,
390        };
391
392        let args = RecvmsgArgs {
393            iovs: &[iov],
394            control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
395            flags,
396        };
397
398        // call the socket's recvmsg(), and run any resulting events
399        let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
400            Socket::recvmsg(socket, args, &mut mem, cb_queue)
401        });
402
403        // if the syscall will block, keep the file open until the syscall restarts
404        if let Some(err) = result.as_mut().err() {
405            if let Some(cond) = err.blocked_condition() {
406                cond.set_active_file(file);
407            }
408        }
409
410        let RecvmsgReturn {
411            return_val,
412            addr: from_addr,
413            ..
414        } = result?;
415
416        if !addr_ptr.is_null() {
417            io::write_sockaddr_and_len(&mut mem, from_addr.as_ref(), addr_ptr, addr_len_ptr)?;
418        }
419
420        Ok(return_val)
421    }
422
423    log_syscall!(
424        recvmsg,
425        /* rv */ libc::ssize_t,
426        /* sockfd */ std::ffi::c_int,
427        /* msg */ *const libc::msghdr,
428        /* flags */ nix::sys::socket::MsgFlags,
429    );
430    pub fn recvmsg(
431        ctx: &mut SyscallContext,
432        fd: std::ffi::c_int,
433        msg_ptr: ForeignPtr<libc::msghdr>,
434        flags: std::ffi::c_int,
435    ) -> Result<libc::ssize_t, SyscallError> {
436        // if we were previously blocked, get the active file from the last syscall handler
437        // invocation since it may no longer exist in the descriptor table
438        let file = ctx
439            .objs
440            .thread
441            .syscall_condition()
442            // if this was for a C descriptor, then there won't be an active file object
443            .and_then(|x| x.active_file().cloned());
444
445        let file = match file {
446            // we were previously blocked, so re-use the file from the previous syscall invocation
447            Some(x) => x,
448            // get the file from the descriptor table, or return early if it doesn't exist
449            None => {
450                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
451                match Self::get_descriptor(&desc_table, fd)?.file() {
452                    CompatFile::New(file) => file.clone(),
453                    CompatFile::Legacy(_file) => {
454                        return Err(Errno::ENOTSOCK.into());
455                    }
456                }
457            }
458        };
459
460        let File::Socket(socket) = file.inner_file() else {
461            return Err(Errno::ENOTSOCK.into());
462        };
463
464        let mut mem = ctx.objs.process.memory_borrow_mut();
465
466        let mut msg = io::read_msghdr(&mem, msg_ptr)?;
467
468        let args = RecvmsgArgs {
469            iovs: &msg.iovs,
470            control_ptr: ForeignArrayPtr::new(msg.control, msg.control_len),
471            flags,
472        };
473
474        // call the socket's recvmsg(), and run any resulting events
475        let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
476            Socket::recvmsg(socket, args, &mut mem, cb_queue)
477        });
478
479        // if the syscall will block, keep the file open until the syscall restarts
480        if let Some(err) = result.as_mut().err() {
481            if let Some(cond) = err.blocked_condition() {
482                cond.set_active_file(file);
483            }
484        }
485
486        let result = result?;
487
488        // write the socket address to the plugin and update the length in msg
489        if !msg.name.is_null() {
490            if let Some(from_addr) = result.addr.as_ref() {
491                msg.name_len = io::write_sockaddr(&mut mem, from_addr, msg.name, msg.name_len)?;
492            } else {
493                msg.name_len = 0;
494            }
495        }
496
497        // update the control len and flags in msg
498        msg.control_len = result.control_len;
499        msg.flags = result.msg_flags;
500
501        // write msg back to the plugin
502        io::update_msghdr(&mut mem, msg_ptr, msg)?;
503
504        Ok(result.return_val)
505    }
506
507    log_syscall!(
508        getsockname,
509        /* rv */ std::ffi::c_int,
510        /* sockfd */ std::ffi::c_int,
511        /* addr */ *const libc::sockaddr,
512        /* addrlen */ *const libc::socklen_t,
513    );
514    pub fn getsockname(
515        ctx: &mut SyscallContext,
516        fd: std::ffi::c_int,
517        addr_ptr: ForeignPtr<u8>,
518        addr_len_ptr: ForeignPtr<libc::socklen_t>,
519    ) -> Result<(), Errno> {
520        let addr_to_write: Option<SockaddrStorage> = {
521            // get the descriptor, or return early if it doesn't exist
522            let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
523            let desc = Self::get_descriptor(&desc_table, fd)?;
524
525            let CompatFile::New(file) = desc.file() else {
526                // we don't have any C socket objects
527                return Err(Errno::ENOTSOCK);
528            };
529
530            let File::Socket(socket) = file.inner_file() else {
531                return Err(Errno::ENOTSOCK);
532            };
533
534            // linux will return an EFAULT before other errors
535            if addr_ptr.is_null() || addr_len_ptr.is_null() {
536                return Err(Errno::EFAULT);
537            }
538
539            let socket = socket.borrow();
540            socket.getsockname()?
541        };
542
543        debug!("Returning socket address of {:?}", addr_to_write);
544        io::write_sockaddr_and_len(
545            &mut ctx.objs.process.memory_borrow_mut(),
546            addr_to_write.as_ref(),
547            addr_ptr,
548            addr_len_ptr,
549        )?;
550
551        Ok(())
552    }
553
554    log_syscall!(
555        getpeername,
556        /* rv */ std::ffi::c_int,
557        /* sockfd */ std::ffi::c_int,
558        /* addr */ *const libc::sockaddr,
559        /* addrlen */ *const libc::socklen_t,
560    );
561    pub fn getpeername(
562        ctx: &mut SyscallContext,
563        fd: std::ffi::c_int,
564        addr_ptr: ForeignPtr<u8>,
565        addr_len_ptr: ForeignPtr<libc::socklen_t>,
566    ) -> Result<(), Errno> {
567        let addr_to_write = {
568            // get the descriptor, or return early if it doesn't exist
569            let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
570            let desc = Self::get_descriptor(&desc_table, fd)?;
571
572            let CompatFile::New(file) = desc.file() else {
573                // we don't have any C socket objects
574                return Err(Errno::ENOTSOCK);
575            };
576
577            let File::Socket(socket) = file.inner_file() else {
578                return Err(Errno::ENOTSOCK);
579            };
580
581            // linux will return an EFAULT before other errors like ENOTCONN
582            if addr_ptr.is_null() || addr_len_ptr.is_null() {
583                return Err(Errno::EFAULT);
584            }
585
586            // this is a clippy false-positive
587            #[allow(clippy::let_and_return)]
588            let addr_to_write = socket.borrow().getpeername()?;
589            addr_to_write
590        };
591
592        debug!("Returning peer address of {:?}", addr_to_write);
593        io::write_sockaddr_and_len(
594            &mut ctx.objs.process.memory_borrow_mut(),
595            addr_to_write.as_ref(),
596            addr_ptr,
597            addr_len_ptr,
598        )?;
599
600        Ok(())
601    }
602
603    log_syscall!(
604        listen,
605        /* rv */ std::ffi::c_int,
606        /* sockfd */ std::ffi::c_int,
607        /* backlog */ std::ffi::c_int,
608    );
609    pub fn listen(
610        ctx: &mut SyscallContext,
611        fd: std::ffi::c_int,
612        backlog: std::ffi::c_int,
613    ) -> Result<(), Errno> {
614        // get the descriptor, or return early if it doesn't exist
615        let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
616        let desc = Self::get_descriptor(&desc_table, fd)?;
617
618        let CompatFile::New(file) = desc.file() else {
619            // we don't have any C socket objects
620            return Err(Errno::ENOTSOCK);
621        };
622
623        let File::Socket(socket) = file.inner_file() else {
624            return Err(Errno::ENOTSOCK);
625        };
626
627        let mut rng = ctx.objs.host.random_mut();
628        let net_ns = ctx.objs.host.network_namespace_borrow();
629
630        CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
631            Socket::listen(socket, backlog, &net_ns, &mut *rng, cb_queue)
632        })?;
633
634        Ok(())
635    }
636
637    log_syscall!(
638        accept,
639        /* rv */ std::ffi::c_int,
640        /* sockfd */ std::ffi::c_int,
641        /* addr */ *const libc::sockaddr,
642        /* addrlen */ *const libc::socklen_t,
643    );
644    pub fn accept(
645        ctx: &mut SyscallContext,
646        fd: std::ffi::c_int,
647        addr_ptr: ForeignPtr<u8>,
648        addr_len_ptr: ForeignPtr<libc::socklen_t>,
649    ) -> Result<DescriptorHandle, SyscallError> {
650        // if we were previously blocked, get the active file from the last syscall handler
651        // invocation since it may no longer exist in the descriptor table
652        let file = ctx
653            .objs
654            .thread
655            .syscall_condition()
656            // if this was for a C descriptor, then there won't be an active file object
657            .and_then(|x| x.active_file().cloned());
658
659        let file = match file {
660            // we were previously blocked, so re-use the file from the previous syscall invocation
661            Some(x) => x,
662            // get the file from the descriptor table, or return early if it doesn't exist
663            None => {
664                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
665                let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
666                    // we don't have any C socket objects
667                    return Err(Errno::ENOTSOCK.into());
668                };
669                file.clone()
670            }
671        };
672
673        let mut result = Self::accept_helper(ctx, file.inner_file(), addr_ptr, addr_len_ptr, 0);
674
675        // if the syscall will block, keep the file open until the syscall restarts
676        if let Some(err) = result.as_mut().err() {
677            if let Some(cond) = err.blocked_condition() {
678                cond.set_active_file(file);
679            }
680        }
681
682        result
683    }
684
685    log_syscall!(
686        accept4,
687        /* rv */ std::ffi::c_int,
688        /* sockfd */ std::ffi::c_int,
689        /* addr */ *const libc::sockaddr,
690        /* addrlen */ *const libc::socklen_t,
691        /* flags */ std::ffi::c_int,
692    );
693    pub fn accept4(
694        ctx: &mut SyscallContext,
695        fd: std::ffi::c_int,
696        addr_ptr: ForeignPtr<u8>,
697        addr_len_ptr: ForeignPtr<libc::socklen_t>,
698        flags: std::ffi::c_int,
699    ) -> Result<DescriptorHandle, SyscallError> {
700        // if we were previously blocked, get the active file from the last syscall handler
701        // invocation since it may no longer exist in the descriptor table
702        let file = ctx
703            .objs
704            .thread
705            .syscall_condition()
706            // if this was for a C descriptor, then there won't be an active file object
707            .and_then(|x| x.active_file().cloned());
708
709        let file = match file {
710            // we were previously blocked, so re-use the file from the previous syscall invocation
711            Some(x) => x,
712            // get the file from the descriptor table, or return early if it doesn't exist
713            None => {
714                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
715                let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
716                    // we don't have any C socket objects
717                    return Err(Errno::ENOTSOCK.into());
718                };
719                file.clone()
720            }
721        };
722
723        let mut result = Self::accept_helper(ctx, file.inner_file(), addr_ptr, addr_len_ptr, flags);
724
725        // if the syscall will block, keep the file open until the syscall restarts
726        if let Some(err) = result.as_mut().err() {
727            if let Some(cond) = err.blocked_condition() {
728                cond.set_active_file(file);
729            }
730        }
731
732        result
733    }
734
735    fn accept_helper(
736        ctx: &mut SyscallContext,
737        file: &File,
738        addr_ptr: ForeignPtr<u8>,
739        addr_len_ptr: ForeignPtr<libc::socklen_t>,
740        flags: std::ffi::c_int,
741    ) -> Result<DescriptorHandle, SyscallError> {
742        let File::Socket(socket) = file else {
743            return Err(Errno::ENOTSOCK.into());
744        };
745
746        // get the accept flags
747        let flags = match SockFlag::from_bits(flags) {
748            Some(x) => x,
749            None => {
750                // linux doesn't return an error if there are unexpected flags
751                warn!("Invalid recvfrom flags: {}", flags);
752                SockFlag::from_bits_truncate(flags)
753            }
754        };
755
756        let mut rng = ctx.objs.host.random_mut();
757        let net_ns = ctx.objs.host.network_namespace_borrow();
758
759        let result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
760            socket.borrow_mut().accept(&net_ns, &mut *rng, cb_queue)
761        });
762
763        let file_status = socket.borrow().status();
764
765        // if the syscall would block and it's a blocking descriptor
766        if result.as_ref().err() == Some(&Errno::EWOULDBLOCK.into())
767            && !file_status.contains(FileStatus::NONBLOCK)
768        {
769            return Err(SyscallError::new_blocked_on_file(
770                file.clone(),
771                FileState::READABLE,
772                socket.borrow().supports_sa_restart(),
773            ));
774        }
775
776        let new_socket = result?;
777
778        let from_addr = {
779            let File::Socket(new_socket) = new_socket.inner_file() else {
780                panic!("Accepted file should be a socket");
781            };
782            new_socket.borrow().getpeername().unwrap()
783        };
784
785        if !addr_ptr.is_null() {
786            io::write_sockaddr_and_len(
787                &mut ctx.objs.process.memory_borrow_mut(),
788                from_addr.as_ref(),
789                addr_ptr,
790                addr_len_ptr,
791            )?;
792        }
793
794        if flags.contains(SockFlag::SOCK_NONBLOCK) {
795            new_socket
796                .inner_file()
797                .borrow_mut()
798                .set_status(FileStatus::NONBLOCK);
799        }
800
801        let mut new_desc = Descriptor::new(CompatFile::New(new_socket));
802
803        if flags.contains(SockFlag::SOCK_CLOEXEC) {
804            new_desc.set_flags(DescriptorFlags::FD_CLOEXEC);
805        }
806
807        Ok(ctx
808            .objs
809            .thread
810            .descriptor_table_borrow_mut(ctx.objs.host)
811            .register_descriptor(new_desc)
812            .or(Err(Errno::ENFILE))?)
813    }
814
815    log_syscall!(
816        connect,
817        /* rv */ std::ffi::c_int,
818        /* sockfd */ std::ffi::c_int,
819        /* addr */ SyscallSockAddrArg</* addrlen */ 2>,
820        /* addrlen */ libc::socklen_t,
821    );
822    pub fn connect(
823        ctx: &mut SyscallContext,
824        fd: std::ffi::c_int,
825        addr_ptr: ForeignPtr<u8>,
826        addr_len: libc::socklen_t,
827    ) -> Result<(), SyscallError> {
828        // if we were previously blocked, get the active file from the last syscall handler
829        // invocation since it may no longer exist in the descriptor table
830        let file = ctx
831            .objs
832            .thread
833            .syscall_condition()
834            // if this was for a C descriptor, then there won't be an active file object
835            .and_then(|x| x.active_file().cloned());
836
837        let file = match file {
838            // we were previously blocked, so re-use the file from the previous syscall invocation
839            Some(x) => x,
840            // get the file from the descriptor table, or return early if it doesn't exist
841            None => {
842                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
843                let CompatFile::New(file) = Self::get_descriptor(&desc_table, fd)?.file() else {
844                    // we don't have any C socket objects
845                    return Err(Errno::ENOTSOCK.into());
846                };
847                file.clone()
848            }
849        };
850
851        let File::Socket(socket) = file.inner_file() else {
852            return Err(Errno::ENOTSOCK.into());
853        };
854
855        let addr = io::read_sockaddr(&ctx.objs.process.memory_borrow(), addr_ptr, addr_len)?
856            .ok_or(Errno::EFAULT)?;
857
858        let mut rng = ctx.objs.host.random_mut();
859        let net_ns = ctx.objs.host.network_namespace_borrow();
860
861        let mut result = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
862            Socket::connect(socket, &addr, &net_ns, &mut *rng, cb_queue)
863        });
864
865        // if the syscall will block, keep the file open until the syscall restarts
866        if let Some(err) = result.as_mut().err() {
867            if let Some(cond) = err.blocked_condition() {
868                cond.set_active_file(file);
869            }
870        }
871
872        result?;
873
874        Ok(())
875    }
876
877    log_syscall!(
878        shutdown,
879        /* rv */ std::ffi::c_int,
880        /* sockfd */ std::ffi::c_int,
881        /* how */ std::ffi::c_uint,
882    );
883    pub fn shutdown(
884        ctx: &mut SyscallContext,
885        fd: std::ffi::c_int,
886        how: std::ffi::c_uint,
887    ) -> Result<(), SyscallError> {
888        // get the descriptor, or return early if it doesn't exist
889        let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
890        let desc = Self::get_descriptor(&desc_table, fd)?;
891
892        let CompatFile::New(file) = desc.file() else {
893            // we don't have any C socket objects
894            return Err(Errno::ENOTSOCK.into());
895        };
896
897        let how = Shutdown::try_from(how).or(Err(Errno::EINVAL))?;
898
899        let File::Socket(socket) = file.inner_file() else {
900            return Err(Errno::ENOTSOCK.into());
901        };
902
903        CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
904            socket.borrow_mut().shutdown(how, cb_queue)
905        })?;
906
907        Ok(())
908    }
909
910    log_syscall!(
911        socketpair,
912        /* rv */ std::ffi::c_int,
913        /* domain */ linux_api::socket::AddressFamily,
914        /* type */ std::ffi::c_int,
915        /* protocol */ std::ffi::c_int,
916        /* sv */ [std::ffi::c_int; 2],
917    );
918    pub fn socketpair(
919        ctx: &mut SyscallContext,
920        domain: std::ffi::c_int,
921        socket_type: std::ffi::c_int,
922        protocol: std::ffi::c_int,
923        fd_ptr: ForeignPtr<[std::ffi::c_int; 2]>,
924    ) -> Result<(), SyscallError> {
925        // remove any flags from the socket type
926        let flags = socket_type & (libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC);
927        let socket_type = socket_type & !flags;
928
929        // only AF_UNIX (AF_LOCAL) is supported on Linux (and technically AF_TIPC)
930        if domain != libc::AF_UNIX {
931            warn!("Domain {domain} is not supported for socketpair()");
932            return Err(Errno::EOPNOTSUPP.into());
933        }
934
935        let socket_type = match UnixSocketType::try_from(socket_type) {
936            Ok(x) => x,
937            Err(e) => {
938                warn!("Not a unix socket type: {e}");
939                return Err(Errno::EPROTONOSUPPORT.into());
940            }
941        };
942
943        // unix sockets don't support any protocols
944        if protocol != 0 {
945            warn!("Unsupported socket protocol {protocol}, we only support default protocol 0");
946            return Err(Errno::EPROTONOSUPPORT.into());
947        }
948
949        let mut file_flags = FileStatus::empty();
950        let mut descriptor_flags = DescriptorFlags::empty();
951
952        if flags & libc::SOCK_NONBLOCK != 0 {
953            file_flags.insert(FileStatus::NONBLOCK);
954        }
955
956        if flags & libc::SOCK_CLOEXEC != 0 {
957            descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC);
958        }
959
960        let (socket_1, socket_2) = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
961            UnixSocket::pair(
962                file_flags,
963                socket_type,
964                &ctx.objs.host.abstract_unix_namespace(),
965                cb_queue,
966            )
967        });
968
969        // file descriptors for the sockets
970        let mut desc_1 = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
971            Socket::Unix(socket_1),
972        ))));
973        let mut desc_2 = Descriptor::new(CompatFile::New(OpenFile::new(File::Socket(
974            Socket::Unix(socket_2),
975        ))));
976
977        // set the file descriptor flags
978        desc_1.set_flags(descriptor_flags);
979        desc_2.set_flags(descriptor_flags);
980
981        // register the file descriptors
982        let mut dt = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
983        // unwrap here since the error handling would be messy (need to deregister) and we shouldn't
984        // ever need to worry about this in practice
985        let fd_1 = dt.register_descriptor(desc_1).unwrap();
986        let fd_2 = dt.register_descriptor(desc_2).unwrap();
987
988        // try to write them to the caller
989        let fds = [i32::from(fd_1), i32::from(fd_2)];
990        let write_res = ctx.objs.process.memory_borrow_mut().write(fd_ptr, &fds);
991
992        // clean up in case of error
993        match write_res {
994            Ok(_) => Ok(()),
995            Err(e) => {
996                CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
997                    // ignore any errors when closing
998                    dt.deregister_descriptor(fd_1)
999                        .unwrap()
1000                        .close(ctx.objs.host, cb_queue);
1001                    dt.deregister_descriptor(fd_2)
1002                        .unwrap()
1003                        .close(ctx.objs.host, cb_queue);
1004                });
1005                Err(e.into())
1006            }
1007        }
1008    }
1009
1010    log_syscall!(
1011        getsockopt,
1012        /* rv */ std::ffi::c_int,
1013        /* sockfd */ std::ffi::c_int,
1014        /* level */ std::ffi::c_int,
1015        /* optname */ std::ffi::c_int,
1016        /* optval */ *const std::ffi::c_void,
1017        /* optlen */ *const libc::socklen_t,
1018    );
1019    pub fn getsockopt(
1020        ctx: &mut SyscallContext,
1021        fd: std::ffi::c_int,
1022        level: std::ffi::c_int,
1023        optname: std::ffi::c_int,
1024        optval_ptr: ForeignPtr<()>,
1025        optlen_ptr: ForeignPtr<libc::socklen_t>,
1026    ) -> Result<(), SyscallError> {
1027        // get the descriptor, or return early if it doesn't exist
1028        let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
1029        let desc = Self::get_descriptor(&desc_table, fd)?;
1030
1031        let CompatFile::New(file) = desc.file() else {
1032            // we don't have any C socket objects
1033            return Err(Errno::ENOTSOCK.into());
1034        };
1035
1036        let File::Socket(socket) = file.inner_file() else {
1037            return Err(Errno::ENOTSOCK.into());
1038        };
1039
1040        let mut mem = ctx.objs.process.memory_borrow_mut();
1041
1042        // get the provided optlen
1043        let optlen = mem.read(optlen_ptr)?;
1044
1045        let mut optlen_new = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
1046            socket
1047                .borrow_mut()
1048                .getsockopt(level, optname, optval_ptr, optlen, &mut mem, cb_queue)
1049        })?;
1050
1051        if optlen_new > optlen {
1052            // this is probably a bug in the socket's getsockopt implementation
1053            log::warn!(
1054                "Attempting to return an optlen {} that's greater than the provided optlen {}",
1055                optlen_new,
1056                optlen
1057            );
1058            optlen_new = optlen;
1059        }
1060
1061        // write the new optlen back to the plugin
1062        mem.write(optlen_ptr, &optlen_new)?;
1063
1064        Ok(())
1065    }
1066
1067    log_syscall!(
1068        setsockopt,
1069        /* rv */ std::ffi::c_int,
1070        /* sockfd */ std::ffi::c_int,
1071        /* level */ std::ffi::c_int,
1072        /* optname */ std::ffi::c_int,
1073        /* optval */ *const std::ffi::c_void,
1074        /* optlen */ libc::socklen_t,
1075    );
1076    pub fn setsockopt(
1077        ctx: &mut SyscallContext,
1078        fd: std::ffi::c_int,
1079        level: std::ffi::c_int,
1080        optname: std::ffi::c_int,
1081        optval_ptr: ForeignPtr<()>,
1082        optlen: libc::socklen_t,
1083    ) -> Result<(), SyscallError> {
1084        // get the descriptor, or return early if it doesn't exist
1085        let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
1086        let desc = Self::get_descriptor(&desc_table, fd)?;
1087
1088        let CompatFile::New(file) = desc.file() else {
1089            // we don't have any C socket objects
1090            return Err(Errno::ENOTSOCK.into());
1091        };
1092
1093        let File::Socket(socket) = file.inner_file() else {
1094            return Err(Errno::ENOTSOCK.into());
1095        };
1096
1097        let mem = ctx.objs.process.memory_borrow();
1098
1099        socket
1100            .borrow_mut()
1101            .setsockopt(level, optname, optval_ptr, optlen, &mem)?;
1102
1103        Ok(())
1104    }
1105}