Skip to main content

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