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