Skip to main content

miri/shims/unix/
socket.rs

1use std::io;
2use std::net::{Shutdown, SocketAddr};
3use std::time::Duration;
4
5use rustc_abi::Size;
6use rustc_target::spec::Os;
7
8use crate::shims::FileDescriptionRef;
9use crate::shims::files::FdNum;
10use crate::shims::unix::UnixFileDescription;
11use crate::shims::unix::socket_address::EvalContextExt as _;
12use crate::shims::unix::tcp_socket::TcpSocket;
13use crate::*;
14
15/// Represents unix-specific socket file descriptions.
16///
17/// Not to be confused with Unix domain sockets.
18pub trait UnixSocketFileDescription: UnixFileDescription {
19    /// Bind the socket to `address`.
20    fn bind<'tcx>(
21        self: FileDescriptionRef<Self>,
22        _communicate_allowed: bool,
23        _address: SocketAddr,
24        _ecx: &mut MiriInterpCx<'tcx>,
25    ) -> InterpResult<'tcx, Result<(), IoError>> {
26        throw_unsup_format!("cannot bind {}", self.name());
27    }
28
29    /// Start listening on the socket.
30    /// `backlog` specifies how many pending incoming connections can exist at the same
31    /// time before new requests are rejected.
32    fn listen<'tcx>(
33        self: FileDescriptionRef<Self>,
34        _communicate_allowed: bool,
35        _backlog: i32,
36        _ecx: &mut MiriInterpCx<'tcx>,
37    ) -> InterpResult<'tcx, Result<(), IoError>> {
38        throw_unsup_format!("cannot listen on {}", self.name());
39    }
40
41    /// Accept an incoming connection on the socket.
42    /// `is_client_sock_non_block` specifies whether the newly accepted client connection
43    /// should be non-blocking.
44    /// After a successful accept, `finish` should be called with a tuple containing the
45    /// file descriptor of the peer socket and it's address.
46    fn accept<'tcx>(
47        self: FileDescriptionRef<Self>,
48        _communicate_allowed: bool,
49        _is_client_sock_non_block: bool,
50        _ecx: &mut MiriInterpCx<'tcx>,
51        _finish: DynMachineCallback<'tcx, Result<(FdNum, SocketAddr), IoError>>,
52    ) -> InterpResult<'tcx> {
53        throw_unsup_format!("cannot accept {}", self.name());
54    }
55
56    /// Connect the socket to `address`.
57    fn connect<'tcx>(
58        self: FileDescriptionRef<Self>,
59        _communicate_allowed: bool,
60        _address: SocketAddr,
61        _ecx: &mut MiriInterpCx<'tcx>,
62        _finish: DynMachineCallback<'tcx, Result<(), IoError>>,
63    ) -> InterpResult<'tcx> {
64        throw_unsup_format!("cannot connect {}", self.name());
65    }
66
67    /// Receive data on the socket into the given buffer `ptr`.
68    /// `len` indicates how many bytes we should try to receive.
69    /// `is_peek` specifies whether the receive removes the bytes from the receive buffer
70    /// ([`false`]) or leaves them in the receive buffer ([`true`]).
71    /// `is_non_block` specifies whether the receive operation is non-blocking.
72    /// After a successful receive, `finish` should be called with the amount of bytes received.
73    fn recv<'tcx>(
74        self: FileDescriptionRef<Self>,
75        _communicate_allowed: bool,
76        _ptr: Pointer,
77        _len: usize,
78        _is_peek: bool,
79        _is_non_block: bool,
80        _ecx: &mut MiriInterpCx<'tcx>,
81        _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
82    ) -> InterpResult<'tcx> {
83        throw_unsup_format!("cannot receive from {}", self.name());
84    }
85
86    /// Send data from the given buffer `ptr` into the socket.
87    /// `len` indicates how many bytes we should try to send.
88    /// `is_non_block` specifies whether the send operation is non-blocking.
89    /// After a successful send, `finish` should be called with the amount of bytes sent.
90    fn send<'tcx>(
91        self: FileDescriptionRef<Self>,
92        _communicate_allowed: bool,
93        _ptr: Pointer,
94        _len: usize,
95        _is_non_block: bool,
96        _ecx: &mut MiriInterpCx<'tcx>,
97        _finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
98    ) -> InterpResult<'tcx> {
99        throw_unsup_format!("cannot send to {}", self.name());
100    }
101
102    /// Set the socket option `option` on `level`.
103    /// `value_ptr` points to the new value of the socket option, and `value_len` contains
104    /// the amount of bytes the value uses at `value_ptr`.
105    fn setsockopt<'tcx>(
106        self: FileDescriptionRef<Self>,
107        _level: i32,
108        _option: i32,
109        _value_ptr: Pointer,
110        _value_len: u64,
111        _ecx: &mut MiriInterpCx<'tcx>,
112    ) -> InterpResult<'tcx, Result<(), IoError>> {
113        throw_unsup_format!("cannot set socket option on {}", self.name());
114    }
115
116    /// Get the value of a socket option `option` on `level`.
117    fn getsockopt<'tcx>(
118        self: FileDescriptionRef<Self>,
119        _level: i32,
120        _option: i32,
121        _ecx: &mut MiriInterpCx<'tcx>,
122    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
123        throw_unsup_format!("cannot get socket option for {}", self.name());
124    }
125
126    /// Get the local address of the socket.
127    fn getsockname<'tcx>(
128        self: FileDescriptionRef<Self>,
129        _communicate_allowed: bool,
130        _ecx: &mut MiriInterpCx<'tcx>,
131    ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
132        throw_unsup_format!("cannot get socket name for {}", self.name());
133    }
134
135    /// Get the remote address of the socket.
136    fn getpeername<'tcx>(
137        self: FileDescriptionRef<Self>,
138        _communicate_allowed: bool,
139        _ecx: &mut MiriInterpCx<'tcx>,
140        _finish: DynMachineCallback<'tcx, Result<SocketAddr, IoError>>,
141    ) -> InterpResult<'tcx> {
142        throw_unsup_format!("cannot get peer name for {}", self.name());
143    }
144
145    /// Shut down the read and/or the write end of the socket.
146    fn shutdown<'tcx>(
147        self: FileDescriptionRef<Self>,
148        _communicate_allowed: bool,
149        _how: Shutdown,
150        _ecx: &mut MiriInterpCx<'tcx>,
151    ) -> InterpResult<'tcx, Result<(), IoError>> {
152        throw_unsup_format!("cannot shut down {}", self.name());
153    }
154
155    // Socket options from SOL_SOCKET which are supported by all socket kinds.
156
157    /// Set the read timeout for the socket.
158    /// If `timeout` is [`None`], then read operations should block indefinitely.
159    /// Otherwise, read operations should block until `timeout` is expired.
160    fn set_read_timeout<'tcx>(
161        self: FileDescriptionRef<Self>,
162        _timeout: Option<Duration>,
163    ) -> InterpResult<'tcx> {
164        throw_unsup_format!("setsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
165    }
166
167    /// Get the read timeout for the socket.
168    fn get_read_timeout<'tcx>(
169        self: FileDescriptionRef<Self>,
170    ) -> InterpResult<'tcx, Option<Duration>> {
171        throw_unsup_format!("getsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
172    }
173
174    /// Set the write timeout for the socket.
175    /// If `timeout` is [`None`], then write operations should block indefinitely.
176    /// Otherwise, write operations should block until `timeout` is expired.
177    fn set_write_timeout<'tcx>(
178        self: FileDescriptionRef<Self>,
179        _timeout: Option<Duration>,
180    ) -> InterpResult<'tcx> {
181        throw_unsup_format!("setsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
182    }
183
184    /// Get the write timeout for the socket.
185    fn get_write_timeout<'tcx>(
186        self: FileDescriptionRef<Self>,
187    ) -> InterpResult<'tcx, Option<Duration>> {
188        throw_unsup_format!("getsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
189    }
190
191    /// Get the stored I/O error of the socket, if any exists.
192    /// This method should consume the stored error.
193    fn take_error<'tcx>(
194        self: FileDescriptionRef<Self>,
195        _ecx: &mut MiriInterpCx<'tcx>,
196    ) -> InterpResult<'tcx, Option<io::Error>> {
197        throw_unsup_format!("getsockopt: option SO_ERROR is unsupported for {}", self.name())
198    }
199
200    /// If the shimmed socket contains an underlying host socket, the `cb` callback
201    /// function is invoked with a [`socket2::SockRef`] to the underlying host socket
202    /// and [`Ok`] with the return value of the callback is returned.
203    /// Otherwise, when the shimmed socket is not backed by a host socket, [`Err`] is
204    /// returned.
205    fn with_socket_ref<T>(&self, _cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> Result<T, ()>
206    where
207        Self: Sized,
208    {
209        Err(())
210    }
211}
212
213impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
214pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
215    /// For more information on the arguments see the socket manpage:
216    /// <https://linux.die.net/man/2/socket>
217    fn socket(
218        &mut self,
219        domain: &OpTy<'tcx>,
220        type_: &OpTy<'tcx>,
221        protocol: &OpTy<'tcx>,
222    ) -> InterpResult<'tcx, Scalar> {
223        let this = self.eval_context_mut();
224
225        let domain = this.read_scalar(domain)?.to_i32()?;
226        let mut flags = this.read_scalar(type_)?.to_i32()?;
227        let protocol = this.read_scalar(protocol)?.to_i32()?;
228
229        // Reject if isolation is enabled
230        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
231            this.reject_in_isolation("`socket`", reject_with)?;
232            return this.set_errno_and_return_neg1_i32(LibcError("EACCES"));
233        }
234
235        let mut is_non_block = false;
236
237        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
238        // if there is anything left at the end, that's an unsupported flag.
239        if matches!(
240            this.tcx.sess.target.os,
241            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
242        ) {
243            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
244            // Solaris, and Illumos targets.
245            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
246            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
247            if flags & sock_nonblock == sock_nonblock {
248                is_non_block = true;
249                flags &= !sock_nonblock;
250            }
251            if flags & sock_cloexec == sock_cloexec {
252                // We don't support `exec` so we can ignore this.
253                flags &= !sock_cloexec;
254            }
255        }
256
257        let family = if domain == this.eval_libc_i32("AF_INET") {
258            socket2::Domain::IPV4
259        } else if domain == this.eval_libc_i32("AF_INET6") {
260            socket2::Domain::IPV6
261        } else {
262            throw_unsup_format!(
263                "socket: domain {:#x} is unsupported, only AF_INET and \
264            AF_INET6 are allowed.",
265                domain
266            );
267        };
268
269        if flags != this.eval_libc_i32("SOCK_STREAM") {
270            throw_unsup_format!(
271                "socket: type {:#x} is unsupported, only SOCK_STREAM, \
272            SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
273                flags
274            );
275        }
276        if protocol != 0 && protocol != this.eval_libc_i32("IPPROTO_TCP") {
277            throw_unsup_format!(
278                "socket: socket protocol {protocol} is unsupported, \
279            only IPPROTO_TCP and 0 are allowed"
280            );
281        }
282
283        let socket = match TcpSocket::new(family, is_non_block) {
284            Ok(socket) => socket,
285            Err(e) => return this.set_errno_and_return_neg1_i32(e),
286        };
287        let fds = &mut this.machine.fds;
288        let fd = fds.new_ref(socket);
289
290        interp_ok(Scalar::from_i32(fds.insert(fd)))
291    }
292
293    fn bind(
294        &mut self,
295        socket: &OpTy<'tcx>,
296        address: &OpTy<'tcx>,
297        address_len: &OpTy<'tcx>,
298    ) -> InterpResult<'tcx, Scalar> {
299        let this = self.eval_context_mut();
300
301        let socket = this.read_scalar(socket)?.to_i32()?;
302        let address = match this.read_socket_address(address, address_len, "bind")? {
303            Ok(addr) => addr,
304            Err(e) => return this.set_errno_and_return_neg1_i32(e),
305        };
306
307        // Get the file handle
308        let Some(fd) = this.machine.fds.get(socket) else {
309            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
310        };
311
312        let Some(socket) = fd.as_unix().as_socket() else {
313            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
314        };
315
316        match socket.bind(this.machine.communicate(), address, this)? {
317            Ok(_) => interp_ok(Scalar::from_i32(0)),
318            Err(e) => this.set_errno_and_return_neg1_i32(e),
319        }
320    }
321
322    fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
323        let this = self.eval_context_mut();
324
325        let socket = this.read_scalar(socket)?.to_i32()?;
326        let backlog = this.read_scalar(backlog)?.to_i32()?;
327
328        // Get the file handle
329        let Some(fd) = this.machine.fds.get(socket) else {
330            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
331        };
332
333        let Some(socket) = fd.as_unix().as_socket() else {
334            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
335        };
336
337        match socket.listen(this.machine.communicate(), backlog, this)? {
338            Ok(_) => interp_ok(Scalar::from_i32(0)),
339            Err(e) => this.set_errno_and_return_neg1_i32(e),
340        }
341    }
342
343    /// For more information on the arguments see the accept manpage:
344    /// <https://linux.die.net/man/2/accept4>
345    fn accept4(
346        &mut self,
347        socket: &OpTy<'tcx>,
348        address: &OpTy<'tcx>,
349        address_len: &OpTy<'tcx>,
350        flags: Option<&OpTy<'tcx>>,
351        // Location where the output scalar is written to.
352        dest: &MPlaceTy<'tcx>,
353    ) -> InterpResult<'tcx> {
354        let this = self.eval_context_mut();
355
356        let socket = this.read_scalar(socket)?.to_i32()?;
357        let address_ptr = this.read_pointer(address)?;
358        let address_len_ptr = this.read_pointer(address_len)?;
359        let mut flags =
360            if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
361
362        // Get the file handle
363        let Some(fd) = this.machine.fds.get(socket) else {
364            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
365        };
366
367        let Some(socket) = fd.as_unix().as_socket() else {
368            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
369        };
370
371        let mut is_client_sock_nonblock = false;
372
373        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
374        // if there is anything left at the end, that's an unsupported flag.
375        if matches!(
376            this.tcx.sess.target.os,
377            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
378        ) {
379            // SOCK_NONBLOCK and SOCK_CLOEXEC only exist on Linux, Android, FreeBSD,
380            // Solaris, and Illumos targets.
381            let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
382            let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
383            if flags & sock_nonblock == sock_nonblock {
384                is_client_sock_nonblock = true;
385                flags &= !sock_nonblock;
386            }
387            if flags & sock_cloexec == sock_cloexec {
388                // We don't support `exec` so we can ignore this.
389                flags &= !sock_cloexec;
390            }
391        }
392
393        if flags != 0 {
394            throw_unsup_format!(
395                "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
396                and SOCK_NONBLOCK are allowed",
397            );
398        }
399
400        let dest = dest.clone();
401        socket.accept(
402            this.machine.communicate(),
403            is_client_sock_nonblock,
404            this,
405            callback!(
406                @capture<'tcx> {
407                    address_ptr: Pointer,
408                    address_len_ptr: Pointer,
409                    dest: MPlaceTy<'tcx>
410                } |this, result: Result<(FdNum, SocketAddr), IoError>| {
411                    let (client_sockfd, address) = match result {
412                        Ok(data) => data,
413                        Err(e) => return this.set_errno_and_return_neg1(e, &dest),
414                    };
415
416                    if address_ptr != Pointer::null() {
417                        // We only attempt a write if the address pointer is not a null pointer.
418                        // If the address pointer is a null pointer the user isn't interested in the
419                        // address and we don't need to write anything.
420                        this.write_socket_address(&address, address_ptr, address_len_ptr, "accept4")?;
421                    }
422
423                    // We need to create the scalar using the destination size since
424                    // `syscall(SYS_accept4, ...)` returns a long which doesn't match
425                    // the int returned from the `accept`/`accept4` syscalls.
426                    // See <https://man7.org/linux/man-pages/man2/syscall.2.html>.
427                    this.write_scalar(Scalar::from_int(client_sockfd, dest.layout.size), &dest)
428                }
429            ),
430        )
431    }
432
433    fn connect(
434        &mut self,
435        socket: &OpTy<'tcx>,
436        address: &OpTy<'tcx>,
437        address_len: &OpTy<'tcx>,
438        // Location where the output scalar is written to.
439        dest: &MPlaceTy<'tcx>,
440    ) -> InterpResult<'tcx> {
441        let this = self.eval_context_mut();
442
443        let socket = this.read_scalar(socket)?.to_i32()?;
444        let address = match this.read_socket_address(address, address_len, "connect")? {
445            Ok(address) => address,
446            Err(e) => return this.set_errno_and_return_neg1(e, dest),
447        };
448
449        // Get the file handle
450        let Some(fd) = this.machine.fds.get(socket) else {
451            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
452        };
453
454        let Some(socket) = fd.as_unix().as_socket() else {
455            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
456        };
457
458        let dest = dest.clone();
459
460        socket.connect(
461            this.machine.communicate(),
462            address,
463            this,
464            callback!(
465                @capture<'tcx> {
466                    dest: MPlaceTy<'tcx>
467                 } |this, result: Result<(), IoError>| {
468                     match result {
469                         Ok(()) => this.write_null(&dest),
470                         Err(e) => this.set_errno_and_return_neg1(e, &dest)
471                     }
472                 }
473            ),
474        )
475    }
476
477    fn send(
478        &mut self,
479        socket: &OpTy<'tcx>,
480        buffer: &OpTy<'tcx>,
481        length: &OpTy<'tcx>,
482        flags: &OpTy<'tcx>,
483        // Location where the output scalar is written to.
484        dest: &MPlaceTy<'tcx>,
485    ) -> InterpResult<'tcx> {
486        let this = self.eval_context_mut();
487
488        let socket = this.read_scalar(socket)?.to_i32()?;
489        let buffer_ptr = this.read_pointer(buffer)?;
490        let size_layout = this.libc_ty_layout("size_t");
491        let length: usize =
492            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
493        let mut flags = this.read_scalar(flags)?.to_i32()?;
494
495        // Get the file handle
496        let Some(fd) = this.machine.fds.get(socket) else {
497            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
498        };
499
500        let Some(socket) = fd.as_unix().as_socket() else {
501            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
502        };
503
504        let mut is_non_block = false;
505
506        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
507        // if there is anything left at the end, that's an unsupported flag.
508        if matches!(
509            this.tcx.sess.target.os,
510            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
511        ) {
512            // MSG_NOSIGNAL and MSG_DONTWAIT only exist on Linux, Android, FreeBSD,
513            // Solaris, and Illumos targets.
514            let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
515            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
516            if flags & msg_nosignal == msg_nosignal {
517                // This is only needed to ensure that no EPIPE signal is sent when
518                // trying to send into a stream which is no longer connected.
519                // Since we don't support signals, we can ignore this.
520                flags &= !msg_nosignal;
521            }
522            if flags & msg_dontwait == msg_dontwait {
523                flags &= !msg_dontwait;
524                is_non_block = true;
525            }
526        }
527
528        if flags != 0 {
529            throw_unsup_format!(
530                "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
531            );
532        }
533
534        let dest = dest.clone();
535
536        socket.send(
537            this.machine.communicate(),
538            buffer_ptr,
539            length,
540            is_non_block,
541            this,
542            callback!(
543                @capture<'tcx> {
544                    dest: MPlaceTy<'tcx>,
545                } |this, result: Result<usize, IoError>| {
546                    match result {
547                        Ok(bytes_sent) =>
548                            this.write_scalar(Scalar::from_target_usize(bytes_sent.try_into().unwrap(), this), &dest),
549                        Err(e) => this.set_errno_and_return_neg1(e, &dest)
550                    }
551                }
552            )
553        )
554    }
555
556    fn recv(
557        &mut self,
558        socket: &OpTy<'tcx>,
559        buffer: &OpTy<'tcx>,
560        length: &OpTy<'tcx>,
561        flags: &OpTy<'tcx>,
562        // Location where the output scalar is written to.
563        dest: &MPlaceTy<'tcx>,
564    ) -> InterpResult<'tcx> {
565        let this = self.eval_context_mut();
566
567        let socket = this.read_scalar(socket)?.to_i32()?;
568        let buffer_ptr = this.read_pointer(buffer)?;
569        let size_layout = this.libc_ty_layout("size_t");
570        let length: usize =
571            this.read_scalar(length)?.to_uint(size_layout.size)?.try_into().unwrap();
572        let mut flags = this.read_scalar(flags)?.to_i32()?;
573
574        // Get the file handle
575        let Some(fd) = this.machine.fds.get(socket) else {
576            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
577        };
578
579        let Some(socket) = fd.as_unix().as_socket() else {
580            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
581        };
582
583        let mut is_peek = false;
584        let mut is_non_block = false;
585
586        // Interpret the flag. Every flag we recognize is "subtracted" from `flags`, so
587        // if there is anything left at the end, that's an unsupported flag.
588
589        let msg_peek = this.eval_libc_i32("MSG_PEEK");
590        if flags & msg_peek == msg_peek {
591            is_peek = true;
592            flags &= !msg_peek;
593        }
594
595        if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
596            // MSG_CMSG_CLOEXEC only exists on Linux, Android, FreeBSD,
597            // and Illumos targets.
598            let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
599            if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
600                // We don't support `exec` so we can ignore this.
601                flags &= !msg_cmsg_cloexec;
602            }
603        }
604
605        if matches!(
606            this.tcx.sess.target.os,
607            Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
608        ) {
609            // MSG_DONTWAIT only exists on Linux, Android, FreeBSD,
610            // Solaris, and Illumos targets.
611            let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
612            if flags & msg_dontwait == msg_dontwait {
613                flags &= !msg_dontwait;
614                is_non_block = true;
615            }
616        }
617
618        if flags != 0 {
619            throw_unsup_format!(
620                "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
621                and MSG_CMSG_CLOEXEC are allowed",
622            );
623        }
624
625        let dest = dest.clone();
626
627        socket.recv(
628            this.machine.communicate(),
629            buffer_ptr,
630            length,
631            is_peek,
632            is_non_block,
633            this,
634            callback!(
635                @capture<'tcx> {
636                    dest: MPlaceTy<'tcx>,
637                } |this, result: Result<usize, IoError>| {
638                    match result {
639                        Ok(bytes_sent) =>
640                            this.write_scalar(Scalar::from_target_usize(bytes_sent.try_into().unwrap(), this), &dest),
641                        Err(e) => this.set_errno_and_return_neg1(e, &dest)
642                    }
643                }
644            ),
645        )
646    }
647
648    fn setsockopt(
649        &mut self,
650        socket: &OpTy<'tcx>,
651        level: &OpTy<'tcx>,
652        option_name: &OpTy<'tcx>,
653        option_value: &OpTy<'tcx>,
654        option_len: &OpTy<'tcx>,
655    ) -> InterpResult<'tcx, Scalar> {
656        let this = self.eval_context_mut();
657
658        let socket = this.read_scalar(socket)?.to_i32()?;
659        let level = this.read_scalar(level)?.to_i32()?;
660        let option_name = this.read_scalar(option_name)?.to_i32()?;
661        let option_value_ptr = this.read_pointer(option_value)?;
662        let socklen_layout = this.libc_ty_layout("socklen_t");
663        let option_len: u64 =
664            this.read_scalar(option_len)?.to_int(socklen_layout.size)?.try_into().unwrap();
665
666        // Get the file handle
667        let Some(fd) = this.machine.fds.get(socket) else {
668            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
669        };
670
671        let Some(socket) = fd.as_unix().as_socket() else {
672            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
673        };
674
675        let result = socket.setsockopt(level, option_name, option_value_ptr, option_len, this)?;
676        match result {
677            Ok(_) => interp_ok(Scalar::from_i32(0)),
678            Err(e) => this.set_errno_and_return_neg1_i32(e),
679        }
680    }
681
682    fn getsockopt(
683        &mut self,
684        socket: &OpTy<'tcx>,
685        level: &OpTy<'tcx>,
686        option_name: &OpTy<'tcx>,
687        option_value: &OpTy<'tcx>,
688        option_len: &OpTy<'tcx>,
689    ) -> InterpResult<'tcx, Scalar> {
690        let this = self.eval_context_mut();
691
692        let socket = this.read_scalar(socket)?.to_i32()?;
693        let level = this.read_scalar(level)?.to_i32()?;
694        let option_name = this.read_scalar(option_name)?.to_i32()?;
695        // These two pointers are used to return the value: `len_ptr` initially stores how much space
696        // is available. If the actual value fits into that space, it is written to
697        // `value_ptr` and `len_ptr` is updated to represent how many bytes
698        // were actually written. If the value does not fit, it is silently truncated.
699        // Also see <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsockopt.html>.
700        let option_value_ptr = this.read_pointer(option_value)?;
701        let option_len_ptr = this.read_pointer(option_len)?;
702
703        // Get the file handle
704        let Some(fd) = this.machine.fds.get(socket) else {
705            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
706        };
707
708        let Some(socket) = fd.as_unix().as_socket() else {
709            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
710        };
711
712        if option_value_ptr == Pointer::null() || option_len_ptr == Pointer::null() {
713            // This socket option returns a value and thus we need to return EFAULT
714            // when either the value or the length pointers are null pointers.
715            return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
716        }
717
718        let socklen_layout = this.libc_ty_layout("socklen_t");
719        let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
720        let option_len: usize = this
721            .read_scalar(&option_len_ptr_mplace)?
722            .to_int(socklen_layout.size)?
723            .try_into()
724            .unwrap();
725
726        // `socket.getsockopt` returns a temporary buffer as `option_value_ptr` might not point
727        // to a large enough buffer, in which case we have to truncate.
728        let value_mplace = match socket.getsockopt(level, option_name, this)? {
729            Ok(value_mplace) => value_mplace,
730            Err(e) => return this.set_errno_and_return_neg1_i32(e),
731        };
732
733        // Truncated size of the output value.
734        let output_value_len = value_mplace.layout.size.min(Size::from_bytes(option_len));
735        // Copy the truncated value into the buffer pointed to by `option_value_ptr`.
736        this.mem_copy(
737            value_mplace.ptr(),
738            option_value_ptr,
739            // Truncate the value to fit the provided buffer.
740            output_value_len,
741            // The buffers are guaranteed to not overlap since the `value_mplace`
742            // was just newly allocated on the stack.
743            true,
744        )?;
745        // Deallocate the value buffer as it was only needed to store the value and
746        // copy it into the buffer pointed to by `option_value_ptr`.
747        this.deallocate_ptr(value_mplace.ptr(), None, MemoryKind::Stack)?;
748
749        // On output, the length pointer contains the amount of bytes written -- not the size
750        // of the value before truncation.
751        this.write_scalar(
752            Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
753            &option_len_ptr_mplace,
754        )?;
755
756        interp_ok(Scalar::from_i32(0))
757    }
758
759    fn getsockname(
760        &mut self,
761        socket: &OpTy<'tcx>,
762        address: &OpTy<'tcx>,
763        address_len: &OpTy<'tcx>,
764    ) -> InterpResult<'tcx, Scalar> {
765        let this = self.eval_context_mut();
766
767        let socket = this.read_scalar(socket)?.to_i32()?;
768        let address_ptr = this.read_pointer(address)?;
769        let address_len_ptr = this.read_pointer(address_len)?;
770
771        // Get the file handle
772        let Some(fd) = this.machine.fds.get(socket) else {
773            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
774        };
775
776        let Some(socket) = fd.as_unix().as_socket() else {
777            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
778        };
779
780        let address = match socket.getsockname(this.machine.communicate(), this)? {
781            Ok(address) => address,
782            Err(e) => return this.set_errno_and_return_neg1_i32(e),
783        };
784
785        this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname")
786            .map(|_| Scalar::from_i32(0))
787    }
788
789    fn getpeername(
790        &mut self,
791        socket: &OpTy<'tcx>,
792        address: &OpTy<'tcx>,
793        address_len: &OpTy<'tcx>,
794        // Location where the output scalar is written to.
795        dest: &MPlaceTy<'tcx>,
796    ) -> InterpResult<'tcx> {
797        let this = self.eval_context_mut();
798
799        let socket = this.read_scalar(socket)?.to_i32()?;
800        let address_ptr = this.read_pointer(address)?;
801        let address_len_ptr = this.read_pointer(address_len)?;
802
803        // Get the file handle
804        let Some(fd) = this.machine.fds.get(socket) else {
805            return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
806        };
807
808        let Some(socket) = fd.as_unix().as_socket() else {
809            return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
810        };
811
812        let dest = dest.clone();
813
814        socket.getpeername(
815            this.machine.communicate(),
816            this,
817            callback!(
818                @capture<'tcx> {
819                    address_ptr: Pointer,
820                    address_len_ptr: Pointer,
821                    dest: MPlaceTy<'tcx>,
822                } |this, result: Result<SocketAddr, IoError>| {
823                    let address = match result {
824                        Ok(address) => address,
825                        Err(e) => return this.set_errno_and_return_neg1(e, &dest)
826                    };
827
828                    this.write_socket_address(
829                        &address,
830                        address_ptr,
831                        address_len_ptr,
832                        "getpeername",
833                    )?;
834                   this.write_scalar(Scalar::from_i32(0), &dest)
835                }
836            ),
837        )
838    }
839
840    fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
841        let this = self.eval_context_mut();
842
843        let socket = this.read_scalar(socket)?.to_i32()?;
844        let how = this.read_scalar(how)?.to_i32()?;
845
846        // Get the file handle
847        let Some(fd) = this.machine.fds.get(socket) else {
848            return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
849        };
850
851        let Some(socket) = fd.as_unix().as_socket() else {
852            return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
853        };
854
855        let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
856        let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
857        let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
858
859        let how = match () {
860            _ if is_read_shutdown => Shutdown::Read,
861            _ if is_write_shutdown => Shutdown::Write,
862            _ if is_read_write_shutdown => Shutdown::Both,
863            // An invalid value was passed to `how`.
864            _ => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
865        };
866
867        match socket.shutdown(this.machine.communicate(), how, this)? {
868            Ok(_) => interp_ok(Scalar::from_i32(0)),
869            Err(e) => this.set_errno_and_return_neg1_i32(e),
870        }
871    }
872
873    /// Set the `option` socket option on level SOL_SOCKET for `socket`.
874    /// `value_ptr` points to the new value of the socket option, and `value_len`
875    /// contains the amount of bytes the value uses at `value_ptr`.
876    fn set_generic_socket_option<T: UnixSocketFileDescription>(
877        &mut self,
878        option: i32,
879        value_ptr: Pointer,
880        value_len: u64,
881        socket: FileDescriptionRef<T>,
882    ) -> InterpResult<'tcx, Result<(), IoError>> {
883        let ecx = self.eval_context_mut();
884
885        let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
886        let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
887        let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
888        let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
889            // On macOS the SO_LINGER socket option sets the linger duration in kernel ticks
890            // while the SO_LINGER_SEC socket option sets the linger duration in seconds.
891            ecx.eval_libc_i32("SO_LINGER_SEC")
892        } else {
893            // For all other targets SO_LINGER sets the linger duration in seconds.
894            ecx.eval_libc_i32("SO_LINGER")
895        };
896        let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
897
898        if matches!(ecx.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
899            // SO_NOSIGPIPE only exists on MacOS, FreeBSD, and NetBSD.
900            let opt_so_nosigpipe = ecx.eval_libc_i32("SO_NOSIGPIPE");
901
902            if option == opt_so_nosigpipe {
903                if value_len != 4 {
904                    // Option value should be C-int which is usually 4 bytes.
905                    return interp_ok(Err(LibcError("EINVAL")));
906                }
907                let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
908                let _val = ecx.read_scalar(&option_value)?.to_i32()?;
909                // We entirely ignore this value since we do not support signals anyway.
910
911                return interp_ok(Ok(()));
912            }
913        }
914
915        if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
916            let timeval_layout = ecx.libc_ty_layout("timeval");
917            let option_value = ecx.ptr_to_mplace(value_ptr, timeval_layout);
918
919            let timeout = match ecx.read_timeval(&option_value)? {
920                None => return interp_ok(Err(LibcError("EINVAL"))),
921                Some(Duration::ZERO) => None,
922                Some(duration) => Some(duration),
923            };
924
925            if option == opt_so_rcvtimeo {
926                socket.set_read_timeout(timeout)?;
927            } else {
928                socket.set_write_timeout(timeout)?;
929            }
930
931            return interp_ok(Ok(()));
932        }
933
934        if option == opt_so_reuseaddr {
935            if value_len != 4 {
936                // Option value should be C-int which is usually 4 bytes.
937                return interp_ok(Err(LibcError("EINVAL")));
938            }
939            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
940            let reuse = ecx.read_scalar(&option_value)?.to_i32()? != 0;
941
942            let result = socket.with_socket_ref(|s| s.set_reuse_address(reuse)).map_err(|()| {
943                err_unsup_format!(
944                    "setsockopt: option SO_REUSEADDR is unsupported for {}",
945                    socket.name()
946                )
947            })?;
948            return interp_ok(result.map_err(IoError::HostError));
949        } else if option == opt_so_linger {
950            let linger_layout = ecx.libc_ty_layout("linger");
951            if value_len != linger_layout.size.bytes() {
952                // Size should be equal to the size of a linger struct.
953                return interp_ok(Err(LibcError("EINVAL")));
954            }
955            let option_value = ecx.ptr_to_mplace(value_ptr, linger_layout);
956
957            let onoff_field = ecx.project_field_named(&option_value, "l_onoff")?;
958            let onoff = ecx.read_scalar(&onoff_field)?.to_i32()? != 0;
959
960            let linger_field = ecx.project_field_named(&option_value, "l_linger")?;
961            let linger = ecx.read_scalar(&linger_field)?.to_i32()?;
962
963            let linger = onoff.then_some(Duration::from_secs(u64::try_from(linger).unwrap()));
964
965            let result = socket.with_socket_ref(|s| s.set_linger(linger)).map_err(|()| {
966                err_unsup_format!(
967                    "setsockopt: option SO_LINGER is unsupported for {}",
968                    socket.name()
969                )
970            })?;
971            return interp_ok(result.map_err(IoError::HostError));
972        } else if option == opt_so_keepalive {
973            if value_len != 4 {
974                // Option value should be C-int which is usually 4 bytes.
975                return interp_ok(Err(LibcError("EINVAL")));
976            }
977            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
978            let keepalive = ecx.read_scalar(&option_value)?.to_i32()? != 0;
979
980            let result = socket.with_socket_ref(|s| s.set_keepalive(keepalive)).map_err(|()| {
981                err_unsup_format!(
982                    "setsockopt: option SO_LINGER is unsupported for {}",
983                    socket.name()
984                )
985            })?;
986            return interp_ok(result.map_err(IoError::HostError));
987        }
988
989        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level SOL_SOCKET");
990    }
991
992    /// Set the `option` socket option on level IPPROTO_IP for `socket`.
993    /// `value_ptr` points to the new value of the socket option, and `value_len`
994    /// contains the amount of bytes the value uses at `value_ptr`.
995    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
996    /// the shimmed socket.
997    fn set_ip_socket_option(
998        &self,
999        option: i32,
1000        value_ptr: Pointer,
1001        value_len: u64,
1002        socket: socket2::SockRef<'_>,
1003    ) -> InterpResult<'tcx, Result<(), IoError>> {
1004        let ecx = self.eval_context_ref();
1005
1006        let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1007
1008        if option == opt_ip_ttl {
1009            if value_len != 4 {
1010                // Option value should be C-uint which is usually 4 bytes.
1011                return interp_ok(Err(LibcError("EINVAL")));
1012            }
1013            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32);
1014            let ttl = ecx.read_scalar(&option_value)?.to_u32()?;
1015
1016            return interp_ok(socket.set_ttl_v4(ttl).map_err(IoError::HostError));
1017        }
1018
1019        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1020    }
1021
1022    /// Set the `option` socket option on level IPPROTO_TCP for `socket`.
1023    /// `value_ptr` points to the new value of the socket option, and `value_len`
1024    /// contains the amount of bytes the value uses at `value_ptr`.
1025    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1026    /// the shimmed socket.
1027    fn set_tcp_socket_option(
1028        &self,
1029        option: i32,
1030        value_ptr: Pointer,
1031        value_len: u64,
1032        socket: socket2::SockRef<'_>,
1033    ) -> InterpResult<'tcx, Result<(), IoError>> {
1034        let ecx = self.eval_context_ref();
1035
1036        let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1037
1038        if option == opt_tcp_nodelay {
1039            if value_len != 4 {
1040                // Option value should be C-int which is usually 4 bytes.
1041                return interp_ok(Err(LibcError("EINVAL")));
1042            }
1043            let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1044            let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1045
1046            return interp_ok(socket.set_tcp_nodelay(nodelay).map_err(IoError::HostError));
1047        }
1048
1049        throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1050    }
1051
1052    /// Get the `option` socket option on level SOL_SOCKET for `socket`.
1053    fn get_generic_socket_option<T: UnixSocketFileDescription>(
1054        &mut self,
1055        option: i32,
1056        socket: FileDescriptionRef<T>,
1057    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1058        let ecx = self.eval_context_mut();
1059
1060        let opt_so_error = ecx.eval_libc_i32("SO_ERROR");
1061        let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
1062        let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
1063        let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
1064        let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
1065            // On macOS the SO_LINGER socket option sets the linger duration in kernel ticks
1066            // while the SO_LINGER_SEC socket option sets the linger duration in seconds.
1067            ecx.eval_libc_i32("SO_LINGER_SEC")
1068        } else {
1069            // For all other targets SO_LINGER sets the linger duration in seconds.
1070            ecx.eval_libc_i32("SO_LINGER")
1071        };
1072        let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
1073
1074        if option == opt_so_error {
1075            let return_value = match socket.take_error(ecx)? {
1076                Some(err) => ecx.io_error_to_errnum(err)?.to_i32()?,
1077                // If there is no error, we return 0 as the option value.
1078                None => 0,
1079            };
1080
1081            // Allocate new buffer on the stack with the `i32` layout.
1082            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1083            ecx.write_int(return_value, &value_buffer)?;
1084            return interp_ok(Ok(value_buffer));
1085        } else if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
1086            let timeout = if option == opt_so_rcvtimeo {
1087                socket.get_read_timeout()?
1088            } else {
1089                socket.get_write_timeout()?
1090            }
1091            .unwrap_or_default();
1092
1093            let secs = timeout.as_secs();
1094            let usecs = timeout.subsec_micros();
1095
1096            let timeval_layout = ecx.libc_ty_layout("timeval");
1097            // Allocate new buffer on the stack with the `timeval` layout.
1098            let timeval_buffer = ecx.allocate(timeval_layout, MemoryKind::Stack)?;
1099
1100            let sec_field = ecx.project_field_named(&timeval_buffer, "tv_sec")?;
1101            ecx.write_int(secs, &sec_field)?;
1102
1103            let usec_field = ecx.project_field_named(&timeval_buffer, "tv_usec")?;
1104            ecx.write_int(usecs, &usec_field)?;
1105
1106            return interp_ok(Ok(timeval_buffer));
1107        }
1108
1109        if option == opt_so_reuseaddr {
1110            let reuse = match socket.with_socket_ref(|s| s.reuse_address()) {
1111                Ok(Ok(reuse)) => reuse,
1112                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1113                Err(()) =>
1114                    throw_unsup_format!(
1115                        "getsockopt: option SO_REUSEADDR is unsupported for {}",
1116                        socket.name()
1117                    ),
1118            };
1119
1120            // Allocate new buffer on the stack with the `i32` layout.
1121            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1122            ecx.write_int(i32::from(reuse), &value_buffer)?;
1123
1124            return interp_ok(Ok(value_buffer));
1125        } else if option == opt_so_linger {
1126            let linger = match socket.with_socket_ref(|s| s.linger()) {
1127                Ok(Ok(linger)) => linger,
1128                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1129                Err(()) =>
1130                    throw_unsup_format!(
1131                        "getsockopt: option SO_LINGER is unsupported for {}",
1132                        socket.name()
1133                    ),
1134            };
1135
1136            let linger_layout = ecx.libc_ty_layout("linger");
1137            // Allocate new buffer on the stack with the `linger` layout.
1138            let linger_buffer = ecx.allocate(linger_layout, MemoryKind::Stack)?;
1139
1140            let onoff_field = ecx.project_field_named(&linger_buffer, "l_onoff")?;
1141            let onoff = i32::from(linger.is_some());
1142            ecx.write_int(onoff, &onoff_field)?;
1143
1144            let linger_field = ecx.project_field_named(&linger_buffer, "l_linger")?;
1145            let secs = i32::try_from(linger.unwrap_or_default().as_secs()).unwrap();
1146            ecx.write_int(secs, &linger_field)?;
1147
1148            return interp_ok(Ok(linger_buffer));
1149        } else if option == opt_so_keepalive {
1150            let keepalive = match socket.with_socket_ref(|s| s.keepalive()) {
1151                Ok(Ok(keepalive)) => keepalive,
1152                Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1153                Err(()) =>
1154                    throw_unsup_format!(
1155                        "getsockopt: option SO_KEEPALIVE is unsupported for {}",
1156                        socket.name()
1157                    ),
1158            };
1159
1160            // Allocate new buffer on the stack with the `i32` layout.
1161            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1162            ecx.write_int(i32::from(keepalive), &value_buffer)?;
1163
1164            return interp_ok(Ok(value_buffer));
1165        }
1166
1167        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level SOL_SOCKET",);
1168    }
1169
1170    /// Get the `option` socket option on level IPPROTO_IP for `socket`.
1171    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1172    /// the shimmed socket.
1173    fn get_ip_socket_option(
1174        &mut self,
1175        option: i32,
1176        socket: socket2::SockRef<'_>,
1177    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1178        let ecx = self.eval_context_mut();
1179
1180        let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1181
1182        if option == opt_ip_ttl {
1183            let ttl = match socket.ttl_v4() {
1184                Ok(ttl) => ttl,
1185                Err(e) => return interp_ok(Err(IoError::HostError(e))),
1186            };
1187
1188            // Allocate new buffer on the stack with the `u32` layout.
1189            let value_buffer = ecx.allocate(ecx.machine.layouts.u32, MemoryKind::Stack)?;
1190            ecx.write_int(ttl, &value_buffer)?;
1191            return interp_ok(Ok(value_buffer));
1192        }
1193
1194        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1195    }
1196
1197    /// Get the `option` socket option on level IPPROTO_TCP for `socket`.
1198    /// `socket` is a [`socket2::SockRef`] to the underlying host socket of
1199    /// the shimmed socket.
1200    fn get_tcp_socket_option(
1201        &mut self,
1202        option: i32,
1203        socket: socket2::SockRef<'_>,
1204    ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1205        let ecx = self.eval_context_mut();
1206
1207        let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1208
1209        if option == opt_tcp_nodelay {
1210            let nodelay = match socket.tcp_nodelay() {
1211                Ok(nodelay) => nodelay,
1212                Err(e) => return interp_ok(Err(IoError::HostError(e))),
1213            };
1214
1215            // Allocate new buffer on the stack with the `i32` layout.
1216            let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1217            ecx.write_int(i32::from(nodelay), &value_buffer)?;
1218            return interp_ok(Ok(value_buffer));
1219        }
1220
1221        throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1222    }
1223}