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::files::FdNum;
9use crate::shims::unix::UnixFileDescription;
10use crate::shims::unix::socket_address::EvalContextExt as _;
11use crate::shims::unix::tcp_socket::TcpSocket;
12use crate::shims::unix::udp_socket::UdpSocket;
13use crate::shims::{FileDescription, FileDescriptionRef};
14use crate::*;
15
16pub trait UnixSocketFileDescription: UnixFileDescription {
20 fn bind<'tcx>(
22 self: FileDescriptionRef<Self>,
23 _communicate_allowed: bool,
24 _address: SocketAddr,
25 _ecx: &mut MiriInterpCx<'tcx>,
26 ) -> InterpResult<'tcx, Result<(), IoError>> {
27 throw_unsup_format!("cannot bind {}", self.name());
28 }
29
30 fn listen<'tcx>(
34 self: FileDescriptionRef<Self>,
35 _communicate_allowed: bool,
36 _backlog: i32,
37 _ecx: &mut MiriInterpCx<'tcx>,
38 ) -> InterpResult<'tcx, Result<(), IoError>> {
39 throw_unsup_format!("cannot listen on {}", self.name());
40 }
41
42 fn accept<'tcx>(
48 self: FileDescriptionRef<Self>,
49 _communicate_allowed: bool,
50 _is_client_sock_non_block: bool,
51 _ecx: &mut MiriInterpCx<'tcx>,
52 _finish: DynMachineCallback<'tcx, Result<(FdNum, SocketAddr), IoError>>,
53 ) -> InterpResult<'tcx> {
54 throw_unsup_format!("cannot accept {}", self.name());
55 }
56
57 fn connect<'tcx>(
59 self: FileDescriptionRef<Self>,
60 _communicate_allowed: bool,
61 _address: SocketAddr,
62 _ecx: &mut MiriInterpCx<'tcx>,
63 _finish: DynMachineCallback<'tcx, Result<(), IoError>>,
64 ) -> InterpResult<'tcx> {
65 throw_unsup_format!("cannot connect {}", self.name());
66 }
67
68 fn recv<'tcx>(
78 self: FileDescriptionRef<Self>,
79 _communicate_allowed: bool,
80 _ptr: Pointer,
81 _len: u64,
82 _is_peek: bool,
83 _is_non_block: bool,
84 _ecx: &mut MiriInterpCx<'tcx>,
85 _finish: DynMachineCallback<'tcx, Result<(u64, Option<SocketAddr>), IoError>>,
86 ) -> InterpResult<'tcx> {
87 throw_unsup_format!("cannot receive from {}", self.name());
88 }
89
90 fn send<'tcx>(
97 self: FileDescriptionRef<Self>,
98 _communicate_allowed: bool,
99 _ptr: Pointer,
100 _len: u64,
101 _is_non_block: bool,
102 _dest_addr: Option<SocketAddr>,
103 _ecx: &mut MiriInterpCx<'tcx>,
104 _finish: DynMachineCallback<'tcx, Result<u64, IoError>>,
105 ) -> InterpResult<'tcx> {
106 throw_unsup_format!("cannot send to {}", self.name());
107 }
108
109 fn setsockopt<'tcx>(
113 self: FileDescriptionRef<Self>,
114 _level: i32,
115 _option: i32,
116 _value_ptr: Pointer,
117 _value_len: u64,
118 _ecx: &mut MiriInterpCx<'tcx>,
119 ) -> InterpResult<'tcx, Result<(), IoError>> {
120 throw_unsup_format!("cannot set socket option on {}", self.name());
121 }
122
123 fn getsockopt<'tcx>(
125 self: FileDescriptionRef<Self>,
126 _level: i32,
127 _option: i32,
128 _ecx: &mut MiriInterpCx<'tcx>,
129 ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
130 throw_unsup_format!("cannot get socket option for {}", self.name());
131 }
132
133 fn getsockname<'tcx>(
135 self: FileDescriptionRef<Self>,
136 _communicate_allowed: bool,
137 _ecx: &mut MiriInterpCx<'tcx>,
138 ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
139 throw_unsup_format!("cannot get socket name for {}", self.name());
140 }
141
142 fn getpeername<'tcx>(
144 self: FileDescriptionRef<Self>,
145 _communicate_allowed: bool,
146 _ecx: &mut MiriInterpCx<'tcx>,
147 _finish: DynMachineCallback<'tcx, Result<SocketAddr, IoError>>,
148 ) -> InterpResult<'tcx> {
149 throw_unsup_format!("cannot get peer name for {}", self.name());
150 }
151
152 fn shutdown<'tcx>(
154 self: FileDescriptionRef<Self>,
155 _communicate_allowed: bool,
156 _how: Shutdown,
157 _ecx: &mut MiriInterpCx<'tcx>,
158 ) -> InterpResult<'tcx, Result<(), IoError>> {
159 throw_unsup_format!("cannot shut down {}", self.name());
160 }
161
162 fn set_read_timeout<'tcx>(
168 self: FileDescriptionRef<Self>,
169 _timeout: Option<Duration>,
170 ) -> InterpResult<'tcx> {
171 throw_unsup_format!("setsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
172 }
173
174 fn get_read_timeout<'tcx>(
176 self: FileDescriptionRef<Self>,
177 ) -> InterpResult<'tcx, Option<Duration>> {
178 throw_unsup_format!("getsockopt: option SO_RCVTIMEO is unsupported for {}", self.name())
179 }
180
181 fn set_write_timeout<'tcx>(
185 self: FileDescriptionRef<Self>,
186 _timeout: Option<Duration>,
187 ) -> InterpResult<'tcx> {
188 throw_unsup_format!("setsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
189 }
190
191 fn get_write_timeout<'tcx>(
193 self: FileDescriptionRef<Self>,
194 ) -> InterpResult<'tcx, Option<Duration>> {
195 throw_unsup_format!("getsockopt: option SO_SNDTIMEO is unsupported for {}", self.name())
196 }
197
198 fn take_error<'tcx>(
201 self: FileDescriptionRef<Self>,
202 _ecx: &mut MiriInterpCx<'tcx>,
203 ) -> InterpResult<'tcx, Option<io::Error>> {
204 throw_unsup_format!("getsockopt: option SO_ERROR is unsupported for {}", self.name())
205 }
206
207 fn with_socket_ref<T>(&self, _cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> Result<T, ()>
213 where
214 Self: Sized,
215 {
216 Err(())
217 }
218}
219
220impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
221pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
222 fn socket(
225 &mut self,
226 domain: &OpTy<'tcx>,
227 type_: &OpTy<'tcx>,
228 protocol: &OpTy<'tcx>,
229 ) -> InterpResult<'tcx, Scalar> {
230 let this = self.eval_context_mut();
231
232 let domain = this.read_scalar(domain)?.to_i32()?;
233 let mut flags = this.read_scalar(type_)?.to_i32()?;
234 let protocol = this.read_scalar(protocol)?.to_i32()?;
235
236 if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
238 this.reject_in_isolation("`socket`", reject_with)?;
239 return this.set_errno_and_return_neg1_i32(LibcError("EACCES"));
240 }
241
242 let mut is_non_block = false;
243
244 if matches!(
247 this.tcx.sess.target.os,
248 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
249 ) {
250 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
253 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
254 if flags & sock_nonblock == sock_nonblock {
255 is_non_block = true;
256 flags &= !sock_nonblock;
257 }
258 if flags & sock_cloexec == sock_cloexec {
259 flags &= !sock_cloexec;
261 }
262 }
263
264 let family = if domain == this.eval_libc_i32("AF_INET") {
265 socket2::Domain::IPV4
266 } else if domain == this.eval_libc_i32("AF_INET6") {
267 socket2::Domain::IPV6
268 } else {
269 throw_unsup_format!(
270 "socket: domain {:#x} is unsupported, only AF_INET and \
271 AF_INET6 are allowed.",
272 domain
273 );
274 };
275
276 let fd_result = if flags == this.eval_libc_i32("SOCK_STREAM") {
277 if ![0, this.eval_libc_i32("IPPROTO_TCP")].contains(&protocol) {
278 throw_unsup_format!(
279 "socket: socket protocol {protocol} is unsupported for \
280 streaming socket, only IPPROTO_TCP and 0 are allowed"
281 );
282 }
283
284 TcpSocket::new(family, is_non_block, this).map(|fd| fd.as_unix())
285 } else if flags == this.eval_libc_i32("SOCK_DGRAM") {
286 if ![0, this.eval_libc_i32("IPPROTO_UDP")].contains(&protocol) {
287 throw_unsup_format!(
288 "socket: socket protocol {protocol} is unsupported for \
289 datagram socket, only IPPROTO_UDP and 0 are allowed"
290 );
291 }
292
293 UdpSocket::new(family, is_non_block, this).map(|fd| fd.as_unix())
294 } else {
295 throw_unsup_format!(
296 "socket: type {:#x} is unsupported, only SOCK_STREAM, \
297 SOCK_DGRAM, SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
298 flags
299 );
300 };
301 let fd = match fd_result {
302 Ok(fd) => fd,
303 Err(e) => return this.set_errno_and_return_neg1_i32(e),
304 };
305 let Some(fd_num) = this.machine.fds.insert(fd) else {
306 return this.set_errno_and_return_neg1_i32(LibcError("EMFILE"));
307 };
308 interp_ok(Scalar::from_i32(fd_num))
309 }
310
311 fn bind(
312 &mut self,
313 socket: &OpTy<'tcx>,
314 address: &OpTy<'tcx>,
315 address_len: &OpTy<'tcx>,
316 ) -> InterpResult<'tcx, Scalar> {
317 let this = self.eval_context_mut();
318
319 let socket = this.read_scalar(socket)?.to_i32()?;
320 let address = match this.read_socket_address(address, address_len, "bind")? {
321 Ok(addr) => addr,
322 Err(e) => return this.set_errno_and_return_neg1_i32(e),
323 };
324
325 let Some(fd) = this.machine.fds.get(socket) else {
327 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
328 };
329
330 let Some(socket) = fd.as_unix().as_socket() else {
331 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
332 };
333
334 match socket.bind(this.machine.communicate(), address, this)? {
335 Ok(_) => interp_ok(Scalar::from_i32(0)),
336 Err(e) => this.set_errno_and_return_neg1_i32(e),
337 }
338 }
339
340 fn listen(&mut self, socket: &OpTy<'tcx>, backlog: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
341 let this = self.eval_context_mut();
342
343 let socket = this.read_scalar(socket)?.to_i32()?;
344 let backlog = this.read_scalar(backlog)?.to_i32()?;
345
346 let Some(fd) = this.machine.fds.get(socket) else {
348 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
349 };
350
351 let Some(socket) = fd.as_unix().as_socket() else {
352 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
353 };
354
355 match socket.listen(this.machine.communicate(), backlog, this)? {
356 Ok(_) => interp_ok(Scalar::from_i32(0)),
357 Err(e) => this.set_errno_and_return_neg1_i32(e),
358 }
359 }
360
361 fn accept4(
364 &mut self,
365 socket: &OpTy<'tcx>,
366 address: &OpTy<'tcx>,
367 address_len: &OpTy<'tcx>,
368 flags: Option<&OpTy<'tcx>>,
369 dest: &MPlaceTy<'tcx>,
371 ) -> InterpResult<'tcx> {
372 let this = self.eval_context_mut();
373
374 let socket = this.read_scalar(socket)?.to_i32()?;
375 let address_ptr = this.read_pointer(address)?;
376 let address_len_place = this.deref_pointer(address_len)?;
377 let mut flags =
378 if let Some(flags) = flags { this.read_scalar(flags)?.to_i32()? } else { 0 };
379
380 let Some(fd) = this.machine.fds.get(socket) else {
382 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
383 };
384
385 let Some(socket) = fd.as_unix().as_socket() else {
386 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
387 };
388
389 let mut is_client_sock_nonblock = false;
390
391 if matches!(
394 this.tcx.sess.target.os,
395 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
396 ) {
397 let sock_nonblock = this.eval_libc_i32("SOCK_NONBLOCK");
400 let sock_cloexec = this.eval_libc_i32("SOCK_CLOEXEC");
401 if flags & sock_nonblock == sock_nonblock {
402 is_client_sock_nonblock = true;
403 flags &= !sock_nonblock;
404 }
405 if flags & sock_cloexec == sock_cloexec {
406 flags &= !sock_cloexec;
408 }
409 }
410
411 if flags != 0 {
412 throw_unsup_format!(
413 "accept4: flag {flags:#x} is unsupported, only SOCK_CLOEXEC \
414 and SOCK_NONBLOCK are allowed",
415 );
416 }
417
418 let dest = dest.clone();
419 socket.accept(
420 this.machine.communicate(),
421 is_client_sock_nonblock,
422 this,
423 callback!(
424 @capture<'tcx> {
425 address_ptr: Pointer,
426 address_len_place: MPlaceTy<'tcx>,
427 dest: MPlaceTy<'tcx>
428 } |ecx, result: Result<(FdNum, SocketAddr), IoError>| {
429 let (client_sockfd, address) = match result {
430 Ok(data) => data,
431 Err(e) => return ecx.set_errno_and_return_neg1(e, &dest),
432 };
433
434 if !ecx.ptr_is_null(address_ptr)? {
435 ecx.write_socket_address(Some(&address), address_ptr, &address_len_place)?;
439 }
440
441 ecx.write_scalar(Scalar::from_int(client_sockfd, dest.layout.size), &dest)
446 }
447 ),
448 )
449 }
450
451 fn connect(
452 &mut self,
453 socket: &OpTy<'tcx>,
454 address: &OpTy<'tcx>,
455 address_len: &OpTy<'tcx>,
456 dest: &MPlaceTy<'tcx>,
458 ) -> InterpResult<'tcx> {
459 let this = self.eval_context_mut();
460
461 let socket = this.read_scalar(socket)?.to_i32()?;
462 let address = match this.read_socket_address(address, address_len, "connect")? {
463 Ok(address) => address,
464 Err(e) => return this.set_errno_and_return_neg1(e, dest),
465 };
466
467 let Some(fd) = this.machine.fds.get(socket) else {
469 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
470 };
471
472 let Some(socket) = fd.as_unix().as_socket() else {
473 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
474 };
475
476 let dest = dest.clone();
477
478 socket.connect(
479 this.machine.communicate(),
480 address,
481 this,
482 callback!(
483 @capture<'tcx> {
484 dest: MPlaceTy<'tcx>
485 } |ecx, result: Result<(), IoError>| {
486 ecx.return_io_result_via_errno(result.map(|()| 0), &dest)
487 }
488 ),
489 )
490 }
491
492 fn send(
493 &mut self,
494 socket: &OpTy<'tcx>,
495 buffer: &OpTy<'tcx>,
496 length: &OpTy<'tcx>,
497 flags: &OpTy<'tcx>,
498 dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>,
499 dest: &MPlaceTy<'tcx>,
501 ) -> InterpResult<'tcx> {
502 let this = self.eval_context_mut();
503
504 let socket = this.read_scalar(socket)?.to_i32()?;
505 let buffer_ptr = this.read_pointer(buffer)?;
506 let length = this.read_target_usize(length)?;
507 let mut flags = this.read_scalar(flags)?.to_i32()?;
508
509 let dest_addr = match dest_addr {
510 Some((dest_addr, dest_addr_len)) => {
511 let dest_addr_ptr = this.read_pointer(dest_addr)?;
512 let socklen_layout = this.libc_ty_layout("socklen_t");
513 let dest_addr_len_int =
514 this.read_scalar(dest_addr_len)?.to_uint(socklen_layout.size)?;
515
516 if this.ptr_is_null(dest_addr_ptr)? || dest_addr_len_int == 0 {
519 None
524 } else {
525 match this.read_socket_address(dest_addr, dest_addr_len, "sendto")? {
526 Ok(dest_addr) => Some(dest_addr),
527 Err(e) => return this.set_errno_and_return_neg1(e, dest),
528 }
529 }
530 }
531 None => None,
532 };
533
534 let Some(fd) = this.machine.fds.get(socket) else {
536 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
537 };
538
539 let Some(socket) = fd.as_unix().as_socket() else {
540 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
541 };
542
543 let mut is_non_block = false;
544
545 if matches!(
548 this.tcx.sess.target.os,
549 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
550 ) {
551 let msg_nosignal = this.eval_libc_i32("MSG_NOSIGNAL");
554 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
555 if flags & msg_nosignal == msg_nosignal {
556 flags &= !msg_nosignal;
560 }
561 if flags & msg_dontwait == msg_dontwait {
562 flags &= !msg_dontwait;
563 is_non_block = true;
564 }
565 }
566
567 if flags != 0 {
568 throw_unsup_format!(
569 "send: flag {flags:#x} is unsupported, only MSG_NOSIGNAL and MSG_DONTWAIT are allowed",
570 );
571 }
572
573 let dest = dest.clone();
574
575 socket.send(
576 this.machine.communicate(),
577 buffer_ptr,
578 length,
579 is_non_block,
580 dest_addr,
581 this,
582 callback!(
583 @capture<'tcx> {
584 dest: MPlaceTy<'tcx>,
585 } |ecx, result: Result<u64, IoError>| {
586 match result {
587 Ok(bytes_sent) =>
588 ecx.write_scalar(Scalar::from_target_isize(bytes_sent.try_into().unwrap(), ecx), &dest),
589 Err(e) => ecx.set_errno_and_return_neg1(e, &dest)
590 }
591 }
592 )
593 )
594 }
595
596 fn recv(
597 &mut self,
598 socket: &OpTy<'tcx>,
599 buffer: &OpTy<'tcx>,
600 length: &OpTy<'tcx>,
601 flags: &OpTy<'tcx>,
602 dest_addr: Option<(&OpTy<'tcx>, &OpTy<'tcx>)>,
603 dest: &MPlaceTy<'tcx>,
605 ) -> InterpResult<'tcx> {
606 let this = self.eval_context_mut();
607
608 let socket = this.read_scalar(socket)?.to_i32()?;
609 let buffer_ptr = this.read_pointer(buffer)?;
610 let length = this.read_target_usize(length)?;
611 let mut flags = this.read_scalar(flags)?.to_i32()?;
612
613 let dest_addr = match dest_addr {
614 Some((addr, addr_len)) =>
615 Some((this.read_pointer(addr)?, this.deref_pointer(addr_len)?)),
616 None => None,
617 };
618
619 let Some(fd) = this.machine.fds.get(socket) else {
621 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
622 };
623
624 let Some(socket) = fd.as_unix().as_socket() else {
625 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
626 };
627
628 let mut is_peek = false;
629 let mut is_non_block = false;
630
631 let msg_peek = this.eval_libc_i32("MSG_PEEK");
635 if flags & msg_peek == msg_peek {
636 is_peek = true;
637 flags &= !msg_peek;
638 }
639
640 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
641 let msg_cmsg_cloexec = this.eval_libc_i32("MSG_CMSG_CLOEXEC");
644 if flags & msg_cmsg_cloexec == msg_cmsg_cloexec {
645 flags &= !msg_cmsg_cloexec;
647 }
648 }
649
650 if matches!(
651 this.tcx.sess.target.os,
652 Os::Linux | Os::Android | Os::FreeBsd | Os::Solaris | Os::Illumos
653 ) {
654 let msg_dontwait = this.eval_libc_i32("MSG_DONTWAIT");
657 if flags & msg_dontwait == msg_dontwait {
658 flags &= !msg_dontwait;
659 is_non_block = true;
660 }
661 }
662
663 if flags != 0 {
664 throw_unsup_format!(
665 "recv: flag {flags:#x} is unsupported, only MSG_PEEK, MSG_DONTWAIT \
666 and MSG_CMSG_CLOEXEC are allowed",
667 );
668 }
669
670 let dest = dest.clone();
671
672 socket.recv(
673 this.machine.communicate(),
674 buffer_ptr,
675 length,
676 is_peek,
677 is_non_block,
678 this,
679 callback!(
680 @capture<'tcx> {
681 dest_addr: Option<(Pointer, MPlaceTy<'tcx>)>,
682 dest: MPlaceTy<'tcx>,
683 } |ecx, result: Result<(u64, Option<SocketAddr>), IoError>| {
684 let (bytes_received, mut origin_addr_opt) = match result {
685 Ok(data) => data,
686 Err(e) => return ecx.set_errno_and_return_neg1(e, &dest)
687 };
688
689 if let Some((addr_ptr, addr_len_mplace)) = dest_addr {
690 if ecx.ptr_is_null(addr_ptr)? {
691 origin_addr_opt = None;
694 }
695 ecx.write_socket_address(origin_addr_opt.as_ref(), addr_ptr, &addr_len_mplace)?;
700 }
701
702 ecx.write_scalar(Scalar::from_target_isize(bytes_received.try_into().unwrap(), ecx), &dest)
703
704 }
705 ),
706 )
707 }
708
709 fn setsockopt(
710 &mut self,
711 socket: &OpTy<'tcx>,
712 level: &OpTy<'tcx>,
713 option_name: &OpTy<'tcx>,
714 option_value: &OpTy<'tcx>,
715 option_len: &OpTy<'tcx>,
716 ) -> InterpResult<'tcx, Scalar> {
717 let this = self.eval_context_mut();
718
719 let socket = this.read_scalar(socket)?.to_i32()?;
720 let level = this.read_scalar(level)?.to_i32()?;
721 let option_name = this.read_scalar(option_name)?.to_i32()?;
722 let option_value_ptr = this.read_pointer(option_value)?;
723 let Some(option_len) = this.read_socklen(option_len)? else {
724 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
725 };
726
727 this.check_ptr_access(
729 option_value_ptr,
730 Size::from_bytes(option_len),
731 CheckInAllocMsg::MemoryAccess,
732 )?;
733
734 let Some(fd) = this.machine.fds.get(socket) else {
736 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
737 };
738
739 let Some(socket) = fd.as_unix().as_socket() else {
740 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
741 };
742
743 let result = socket.setsockopt(level, option_name, option_value_ptr, option_len, this)?;
744 match result {
745 Ok(_) => interp_ok(Scalar::from_i32(0)),
746 Err(e) => this.set_errno_and_return_neg1_i32(e),
747 }
748 }
749
750 fn getsockopt(
751 &mut self,
752 socket: &OpTy<'tcx>,
753 level: &OpTy<'tcx>,
754 option_name: &OpTy<'tcx>,
755 option_value: &OpTy<'tcx>,
756 option_len: &OpTy<'tcx>,
757 ) -> InterpResult<'tcx, Scalar> {
758 let this = self.eval_context_mut();
759
760 let socket = this.read_scalar(socket)?.to_i32()?;
761 let level = this.read_scalar(level)?.to_i32()?;
762 let option_name = this.read_scalar(option_name)?.to_i32()?;
763 let option_value_ptr = this.read_pointer(option_value)?;
769 let option_len_ptr = this.read_pointer(option_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 if this.ptr_is_null(option_value_ptr)? || this.ptr_is_null(option_len_ptr)? {
781 return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
784 }
785
786 let socklen_layout = this.libc_ty_layout("socklen_t");
787 let option_len_ptr_mplace = this.ptr_to_mplace(option_len_ptr, socklen_layout);
788 let Some(option_len) = this.read_socklen(&option_len_ptr_mplace)? else {
789 return this.set_errno_and_return_neg1_i32(LibcError("EINVAL"));
790 };
791
792 this.check_ptr_access(
794 option_value_ptr,
795 Size::from_bytes(option_len),
796 CheckInAllocMsg::MemoryAccess,
797 )?;
798
799 let value_mplace = match socket.getsockopt(level, option_name, this)? {
802 Ok(value_mplace) => value_mplace,
803 Err(e) => return this.set_errno_and_return_neg1_i32(e),
804 };
805
806 let output_value_len = value_mplace.layout.size.min(Size::from_bytes(option_len));
808 this.mem_copy(
810 value_mplace.ptr(),
811 option_value_ptr,
812 output_value_len,
814 true,
817 )?;
818 this.deallocate_ptr(value_mplace.ptr(), None, MemoryKind::Stack)?;
821
822 this.write_scalar(
825 Scalar::from_uint(output_value_len.bytes(), socklen_layout.size),
826 &option_len_ptr_mplace,
827 )?;
828
829 interp_ok(Scalar::from_i32(0))
830 }
831
832 fn getsockname(
833 &mut self,
834 socket: &OpTy<'tcx>,
835 address: &OpTy<'tcx>,
836 address_len: &OpTy<'tcx>,
837 ) -> InterpResult<'tcx, Scalar> {
838 let this = self.eval_context_mut();
839
840 let socket = this.read_scalar(socket)?.to_i32()?;
841 let address_ptr = this.read_pointer(address)?;
842 let address_len_place = this.deref_pointer(address_len)?;
843
844 let Some(fd) = this.machine.fds.get(socket) else {
846 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
847 };
848
849 let Some(socket) = fd.as_unix().as_socket() else {
850 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
851 };
852
853 let address = match socket.getsockname(this.machine.communicate(), this)? {
854 Ok(address) => address,
855 Err(e) => return this.set_errno_and_return_neg1_i32(e),
856 };
857
858 this.write_socket_address(Some(&address), address_ptr, &address_len_place)
859 .map(|()| Scalar::from_i32(0))
860 }
861
862 fn getpeername(
863 &mut self,
864 socket: &OpTy<'tcx>,
865 address: &OpTy<'tcx>,
866 address_len: &OpTy<'tcx>,
867 dest: &MPlaceTy<'tcx>,
869 ) -> InterpResult<'tcx> {
870 let this = self.eval_context_mut();
871
872 let socket = this.read_scalar(socket)?.to_i32()?;
873 let address_ptr = this.read_pointer(address)?;
874 let address_len_place = this.deref_pointer(address_len)?;
875
876 let Some(fd) = this.machine.fds.get(socket) else {
878 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
879 };
880
881 let Some(socket) = fd.as_unix().as_socket() else {
882 return this.set_errno_and_return_neg1(LibcError("ENOTSOCK"), dest);
883 };
884
885 let dest = dest.clone();
886
887 socket.getpeername(
888 this.machine.communicate(),
889 this,
890 callback!(
891 @capture<'tcx> {
892 address_ptr: Pointer,
893 address_len_place: MPlaceTy<'tcx>,
894 dest: MPlaceTy<'tcx>,
895 } |ecx, result: Result<SocketAddr, IoError>| {
896 let address = match result {
897 Ok(address) => address,
898 Err(e) => return ecx.set_errno_and_return_neg1(e, &dest)
899 };
900
901 ecx.write_socket_address(
902 Some(&address),
903 address_ptr,
904 &address_len_place,
905 )?;
906 ecx.write_scalar(Scalar::from_i32(0), &dest)
907 }
908 ),
909 )
910 }
911
912 fn shutdown(&mut self, socket: &OpTy<'tcx>, how: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
913 let this = self.eval_context_mut();
914
915 let socket = this.read_scalar(socket)?.to_i32()?;
916 let how = this.read_scalar(how)?.to_i32()?;
917
918 let Some(fd) = this.machine.fds.get(socket) else {
920 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
921 };
922
923 let Some(socket) = fd.as_unix().as_socket() else {
924 return this.set_errno_and_return_neg1_i32(LibcError("ENOTSOCK"));
925 };
926
927 let is_read_shutdown = how == this.eval_libc_i32("SHUT_RD");
928 let is_write_shutdown = how == this.eval_libc_i32("SHUT_WR");
929 let is_read_write_shutdown = how == this.eval_libc_i32("SHUT_RDWR");
930
931 let how = match () {
932 _ if is_read_shutdown => Shutdown::Read,
933 _ if is_write_shutdown => Shutdown::Write,
934 _ if is_read_write_shutdown => Shutdown::Both,
935 _ => return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")),
937 };
938
939 match socket.shutdown(this.machine.communicate(), how, this)? {
940 Ok(_) => interp_ok(Scalar::from_i32(0)),
941 Err(e) => this.set_errno_and_return_neg1_i32(e),
942 }
943 }
944
945 fn set_generic_socket_option<T: UnixSocketFileDescription>(
949 &mut self,
950 option: i32,
951 value_ptr: Pointer,
952 value_len: u64,
953 socket: FileDescriptionRef<T>,
954 ) -> InterpResult<'tcx, Result<(), IoError>> {
955 let ecx = self.eval_context_mut();
956
957 let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
958 let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
959 let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
960 let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
961 ecx.eval_libc_i32("SO_LINGER_SEC")
964 } else {
965 ecx.eval_libc_i32("SO_LINGER")
967 };
968 let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
969
970 if matches!(ecx.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::NetBsd) {
971 let opt_so_nosigpipe = ecx.eval_libc_i32("SO_NOSIGPIPE");
973
974 if option == opt_so_nosigpipe {
975 if value_len != 4 {
976 return interp_ok(Err(LibcError("EINVAL")));
978 }
979 let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
980 let _val = ecx.read_scalar(&option_value)?.to_i32()?;
981 return interp_ok(Ok(()));
984 }
985 }
986
987 if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
988 let timeval_layout = ecx.libc_ty_layout("timeval");
989 let option_value = ecx.ptr_to_mplace(value_ptr, timeval_layout);
990
991 let timeout = match ecx.read_timeval(&option_value)? {
992 None => return interp_ok(Err(LibcError("EINVAL"))),
993 Some(Duration::ZERO) => None,
994 Some(duration) => Some(duration),
995 };
996
997 if option == opt_so_rcvtimeo {
998 socket.set_read_timeout(timeout)?;
999 } else {
1000 socket.set_write_timeout(timeout)?;
1001 }
1002
1003 return interp_ok(Ok(()));
1004 }
1005
1006 if option == opt_so_reuseaddr {
1007 if value_len != 4 {
1008 return interp_ok(Err(LibcError("EINVAL")));
1010 }
1011 let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1012 let reuse = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1013
1014 let result = socket.with_socket_ref(|s| s.set_reuse_address(reuse)).map_err(|()| {
1015 err_unsup_format!(
1016 "setsockopt: option SO_REUSEADDR is unsupported for {}",
1017 socket.name()
1018 )
1019 })?;
1020 return interp_ok(result.map_err(IoError::HostError));
1021 } else if option == opt_so_linger {
1022 let linger_layout = ecx.libc_ty_layout("linger");
1023 if value_len != linger_layout.size.bytes() {
1024 return interp_ok(Err(LibcError("EINVAL")));
1026 }
1027 let option_value = ecx.ptr_to_mplace(value_ptr, linger_layout);
1028
1029 let onoff_field = ecx.project_field_named(&option_value, "l_onoff")?;
1030 let onoff = ecx.read_scalar(&onoff_field)?.to_i32()? != 0;
1031
1032 let linger_field = ecx.project_field_named(&option_value, "l_linger")?;
1033 let linger = ecx.read_scalar(&linger_field)?.to_i32()?;
1034
1035 let linger = onoff.then_some(Duration::from_secs(u64::try_from(linger).unwrap()));
1036
1037 let result = socket.with_socket_ref(|s| s.set_linger(linger)).map_err(|()| {
1038 err_unsup_format!(
1039 "setsockopt: option SO_LINGER is unsupported for {}",
1040 socket.name()
1041 )
1042 })?;
1043 return interp_ok(result.map_err(IoError::HostError));
1044 } else if option == opt_so_keepalive {
1045 if value_len != 4 {
1046 return interp_ok(Err(LibcError("EINVAL")));
1048 }
1049 let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1050 let keepalive = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1051
1052 let result = socket.with_socket_ref(|s| s.set_keepalive(keepalive)).map_err(|()| {
1053 err_unsup_format!(
1054 "setsockopt: option SO_LINGER is unsupported for {}",
1055 socket.name()
1056 )
1057 })?;
1058 return interp_ok(result.map_err(IoError::HostError));
1059 }
1060
1061 throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level SOL_SOCKET");
1062 }
1063
1064 fn set_ip_socket_option(
1070 &self,
1071 option: i32,
1072 value_ptr: Pointer,
1073 value_len: u64,
1074 socket: socket2::SockRef<'_>,
1075 ) -> InterpResult<'tcx, Result<(), IoError>> {
1076 let ecx = self.eval_context_ref();
1077
1078 let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1079
1080 if option == opt_ip_ttl {
1081 if value_len != 4 {
1082 return interp_ok(Err(LibcError("EINVAL")));
1084 }
1085 let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.u32);
1086 let ttl = ecx.read_scalar(&option_value)?.to_u32()?;
1087
1088 return interp_ok(socket.set_ttl_v4(ttl).map_err(IoError::HostError));
1089 }
1090
1091 throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1092 }
1093
1094 fn set_tcp_socket_option(
1100 &self,
1101 option: i32,
1102 value_ptr: Pointer,
1103 value_len: u64,
1104 socket: socket2::SockRef<'_>,
1105 ) -> InterpResult<'tcx, Result<(), IoError>> {
1106 let ecx = self.eval_context_ref();
1107
1108 let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1109
1110 if option == opt_tcp_nodelay {
1111 if value_len != 4 {
1112 return interp_ok(Err(LibcError("EINVAL")));
1114 }
1115 let option_value = ecx.ptr_to_mplace(value_ptr, ecx.machine.layouts.i32);
1116 let nodelay = ecx.read_scalar(&option_value)?.to_i32()? != 0;
1117
1118 return interp_ok(socket.set_tcp_nodelay(nodelay).map_err(IoError::HostError));
1119 }
1120
1121 throw_unsup_format!("setsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1122 }
1123
1124 fn get_generic_socket_option<T: UnixSocketFileDescription>(
1126 &mut self,
1127 option: i32,
1128 socket: FileDescriptionRef<T>,
1129 ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1130 let ecx = self.eval_context_mut();
1131
1132 let opt_so_error = ecx.eval_libc_i32("SO_ERROR");
1133 let opt_so_rcvtimeo = ecx.eval_libc_i32("SO_RCVTIMEO");
1134 let opt_so_sndtimeo = ecx.eval_libc_i32("SO_SNDTIMEO");
1135 let opt_so_reuseaddr = ecx.eval_libc_i32("SO_REUSEADDR");
1136 let opt_so_linger = if matches!(ecx.tcx.sess.target.os, Os::MacOs) {
1137 ecx.eval_libc_i32("SO_LINGER_SEC")
1140 } else {
1141 ecx.eval_libc_i32("SO_LINGER")
1143 };
1144 let opt_so_keepalive = ecx.eval_libc_i32("SO_KEEPALIVE");
1145
1146 if option == opt_so_error {
1147 let return_value = match socket.take_error(ecx)? {
1148 Some(err) => ecx.io_error_to_errnum(err)?.to_i32()?,
1149 None => 0,
1151 };
1152
1153 let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1155 ecx.write_int(return_value, &value_buffer)?;
1156 return interp_ok(Ok(value_buffer));
1157 } else if option == opt_so_rcvtimeo || option == opt_so_sndtimeo {
1158 let timeout = if option == opt_so_rcvtimeo {
1159 socket.get_read_timeout()?
1160 } else {
1161 socket.get_write_timeout()?
1162 }
1163 .unwrap_or_default();
1164
1165 let secs = timeout.as_secs();
1166 let usecs = timeout.subsec_micros();
1167
1168 let timeval_layout = ecx.libc_ty_layout("timeval");
1169 let timeval_buffer = ecx.allocate(timeval_layout, MemoryKind::Stack)?;
1171
1172 let sec_field = ecx.project_field_named(&timeval_buffer, "tv_sec")?;
1173 ecx.write_int(secs, &sec_field)?;
1174
1175 let usec_field = ecx.project_field_named(&timeval_buffer, "tv_usec")?;
1176 ecx.write_int(usecs, &usec_field)?;
1177
1178 return interp_ok(Ok(timeval_buffer));
1179 }
1180
1181 if option == opt_so_reuseaddr {
1182 let reuse = match socket.with_socket_ref(|s| s.reuse_address()) {
1183 Ok(Ok(reuse)) => reuse,
1184 Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1185 Err(()) =>
1186 throw_unsup_format!(
1187 "getsockopt: option SO_REUSEADDR is unsupported for {}",
1188 socket.name()
1189 ),
1190 };
1191
1192 let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1194 ecx.write_int(i32::from(reuse), &value_buffer)?;
1195
1196 return interp_ok(Ok(value_buffer));
1197 } else if option == opt_so_linger {
1198 let linger = match socket.with_socket_ref(|s| s.linger()) {
1199 Ok(Ok(linger)) => linger,
1200 Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1201 Err(()) =>
1202 throw_unsup_format!(
1203 "getsockopt: option SO_LINGER is unsupported for {}",
1204 socket.name()
1205 ),
1206 };
1207
1208 let linger_layout = ecx.libc_ty_layout("linger");
1209 let linger_buffer = ecx.allocate(linger_layout, MemoryKind::Stack)?;
1211
1212 let onoff_field = ecx.project_field_named(&linger_buffer, "l_onoff")?;
1213 let onoff = i32::from(linger.is_some());
1214 ecx.write_int(onoff, &onoff_field)?;
1215
1216 let linger_field = ecx.project_field_named(&linger_buffer, "l_linger")?;
1217 let secs = i32::try_from(linger.unwrap_or_default().as_secs()).unwrap();
1218 ecx.write_int(secs, &linger_field)?;
1219
1220 return interp_ok(Ok(linger_buffer));
1221 } else if option == opt_so_keepalive {
1222 let keepalive = match socket.with_socket_ref(|s| s.keepalive()) {
1223 Ok(Ok(keepalive)) => keepalive,
1224 Ok(Err(e)) => return interp_ok(Err(IoError::HostError(e))),
1225 Err(()) =>
1226 throw_unsup_format!(
1227 "getsockopt: option SO_KEEPALIVE is unsupported for {}",
1228 socket.name()
1229 ),
1230 };
1231
1232 let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1234 ecx.write_int(i32::from(keepalive), &value_buffer)?;
1235
1236 return interp_ok(Ok(value_buffer));
1237 }
1238
1239 throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level SOL_SOCKET",);
1240 }
1241
1242 fn get_ip_socket_option(
1246 &mut self,
1247 option: i32,
1248 socket: socket2::SockRef<'_>,
1249 ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1250 let ecx = self.eval_context_mut();
1251
1252 let opt_ip_ttl = ecx.eval_libc_i32("IP_TTL");
1253
1254 if option == opt_ip_ttl {
1255 let ttl = match socket.ttl_v4() {
1256 Ok(ttl) => ttl,
1257 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1258 };
1259
1260 let value_buffer = ecx.allocate(ecx.machine.layouts.u32, MemoryKind::Stack)?;
1262 ecx.write_int(ttl, &value_buffer)?;
1263 return interp_ok(Ok(value_buffer));
1264 }
1265
1266 throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_IP",);
1267 }
1268
1269 fn get_tcp_socket_option(
1273 &mut self,
1274 option: i32,
1275 socket: socket2::SockRef<'_>,
1276 ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
1277 let ecx = self.eval_context_mut();
1278
1279 let opt_tcp_nodelay = ecx.eval_libc_i32("TCP_NODELAY");
1280
1281 if option == opt_tcp_nodelay {
1282 let nodelay = match socket.tcp_nodelay() {
1283 Ok(nodelay) => nodelay,
1284 Err(e) => return interp_ok(Err(IoError::HostError(e))),
1285 };
1286
1287 let value_buffer = ecx.allocate(ecx.machine.layouts.i32, MemoryKind::Stack)?;
1289 ecx.write_int(i32::from(nodelay), &value_buffer)?;
1290 return interp_ok(Ok(value_buffer));
1291 }
1292
1293 throw_unsup_format!("getsockopt: option {option:#x} is unsupported for level IPPROTO_TCP");
1294 }
1295}