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
15pub trait UnixSocketFileDescription: UnixFileDescription {
19 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 if matches!(
240 this.tcx.sess.target.os,
241 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
242 ) {
243 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 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 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 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 fn accept4(
346 &mut self,
347 socket: &OpTy<'tcx>,
348 address: &OpTy<'tcx>,
349 address_len: &OpTy<'tcx>,
350 flags: Option<&OpTy<'tcx>>,
351 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 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 if matches!(
376 this.tcx.sess.target.os,
377 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
378 ) {
379 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 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 this.write_socket_address(&address, address_ptr, address_len_ptr, "accept4")?;
421 }
422
423 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 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 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 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 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 if matches!(
509 this.tcx.sess.target.os,
510 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
511 ) {
512 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 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 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 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 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 let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
599 if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
600 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 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 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 let option_value_ptr = this.read_pointer(option_value)?;
701 let option_len_ptr = this.read_pointer(option_len)?;
702
703 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 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 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 let output_value_len = value_mplace.layout.size.min(Size::from_bytes(option_len));
735 this.mem_copy(
737 value_mplace.ptr(),
738 option_value_ptr,
739 output_value_len,
741 true,
744 )?;
745 this.deallocate_ptr(value_mplace.ptr(), None, MemoryKind::Stack)?;
748
749 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 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 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 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 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 _ => 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 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 ecx.eval_libc_i32("SO_LINGER_SEC")
892 } else {
893 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 let opt_so_nosigpipe = ecx.eval_libc_i32("SO_NOSIGPIPE");
901
902 if option == opt_so_nosigpipe {
903 if value_len != 4 {
904 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 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 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 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 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 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 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 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 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 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 ecx.eval_libc_i32("SO_LINGER_SEC")
1068 } else {
1069 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 None => 0,
1079 };
1080
1081 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 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 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 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 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 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 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 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 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}