1use std::ffi::CStr;
2use std::net::{Ipv4Addr, SocketAddrV4};
3use std::sync::Arc;
4
5use atomic_refcell::AtomicRefCell;
6use linux_api::errno::Errno;
7use linux_api::ioctls::IoctlRequest;
8use linux_api::socket::Shutdown;
9use nix::sys::socket::{MsgFlags, SockaddrIn};
10use shadow_shim_helper_rs::emulated_time::EmulatedTime;
11use shadow_shim_helper_rs::syscall_types::ForeignPtr;
12
13use crate::core::worker::Worker;
14use crate::cshadow as c;
15use crate::host::descriptor::listener::{StateListenHandle, StateListenerFilter};
16use crate::host::descriptor::socket::inet::{self, InetSocket};
17use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs, Socket};
18use crate::host::descriptor::{
19 CompatFile, File, FileMode, FileSignals, FileState, FileStatus, OpenFile, SyscallResult,
20};
21use crate::host::host::Host;
22use crate::host::memory_manager::MemoryManager;
23use crate::host::network::interface::FifoPacketPriority;
24use crate::host::network::namespace::NetworkNamespace;
25use crate::host::syscall::io::{IoVec, write_partial};
26use crate::host::syscall::types::{ForeignArrayPtr, SyscallError};
27use crate::host::thread::ThreadId;
28use crate::network::packet::PacketRc;
29use crate::utility::callback_queue::CallbackQueue;
30use crate::utility::sockaddr::SockaddrStorage;
31use crate::utility::{HostTreePointer, ObjectCounter};
32
33pub struct LegacyTcpSocket {
34 socket: HostTreePointer<c::TCP>,
35 has_open_file: bool,
38 thread_of_blocked_connect: Option<ThreadId>,
40 _counter: ObjectCounter,
41}
42
43impl LegacyTcpSocket {
44 pub fn new(status: FileStatus, host: &Host) -> Arc<AtomicRefCell<Self>> {
45 let recv_buf_size = host.params.init_sock_recv_buf_size.try_into().unwrap();
46 let send_buf_size = host.params.init_sock_send_buf_size.try_into().unwrap();
47
48 let tcp = unsafe { c::tcp_new(host, recv_buf_size, send_buf_size) };
49 let tcp = unsafe { Self::new_from_legacy(tcp) };
50
51 tcp.borrow_mut().set_status(status);
52
53 tcp
54 }
55
56 pub unsafe fn new_from_legacy(legacy_tcp: *mut c::TCP) -> Arc<AtomicRefCell<Self>> {
62 assert!(!legacy_tcp.is_null());
63
64 let socket = Self {
65 socket: HostTreePointer::new(legacy_tcp),
66 has_open_file: false,
67 thread_of_blocked_connect: None,
68 _counter: ObjectCounter::new("LegacyTcpSocket"),
69 };
70
71 let rv = Arc::new(AtomicRefCell::new(socket));
72
73 let inet_socket = InetSocket::LegacyTcp(rv.clone());
74 let inet_socket = Box::into_raw(Box::new(inet_socket.downgrade()));
75 unsafe { c::tcp_setRustSocket(legacy_tcp, inet_socket) };
76
77 rv
78 }
79
80 pub fn canonical_handle(&self) -> usize {
83 self.as_legacy_tcp() as usize
84 }
85
86 pub fn as_legacy_tcp(&self) -> *mut c::TCP {
88 unsafe { self.socket.ptr() }
89 }
90
91 pub fn as_legacy_socket(&self) -> *mut c::LegacySocket {
93 self.as_legacy_tcp() as *mut c::LegacySocket
94 }
95
96 pub fn as_legacy_file(&self) -> *mut c::LegacyFile {
98 self.as_legacy_tcp() as *mut c::LegacyFile
99 }
100
101 pub fn status(&self) -> FileStatus {
102 let o_flags = unsafe { c::legacyfile_getFlags(self.as_legacy_file()) };
103 let o_flags =
104 linux_api::fcntl::OFlag::from_bits(o_flags).expect("Not a valid OFlag: {o_flags:?}");
105 let (status, extra_flags) = FileStatus::from_o_flags(o_flags);
106 assert!(
107 extra_flags.is_empty(),
108 "Rust wrapper doesn't support {extra_flags:?} flags",
109 );
110 status
111 }
112
113 pub fn set_status(&mut self, status: FileStatus) {
114 let o_flags = status.as_o_flags().bits();
115 unsafe { c::legacyfile_setFlags(self.as_legacy_file(), o_flags) };
116 }
117
118 pub fn mode(&self) -> FileMode {
119 FileMode::READ | FileMode::WRITE
120 }
121
122 pub fn has_open_file(&self) -> bool {
123 self.has_open_file
124 }
125
126 pub fn supports_sa_restart(&self) -> bool {
127 true
129 }
130
131 pub fn set_has_open_file(&mut self, val: bool) {
132 self.has_open_file = val;
133 }
134
135 pub fn push_in_packet(
136 &mut self,
137 packet: PacketRc,
138 _cb_queue: &mut CallbackQueue,
139 _recv_time: EmulatedTime,
140 ) {
141 Worker::with_active_host(|host| {
142 unsafe {
144 c::legacysocket_pushInPacket(self.as_legacy_socket(), host, packet.into_raw())
145 };
146 })
147 .unwrap();
148 }
149
150 pub fn pull_out_packet(&mut self, _cb_queue: &mut CallbackQueue) -> Option<PacketRc> {
151 let packet = Worker::with_active_host(|host| unsafe {
153 c::legacysocket_pullOutPacket(self.as_legacy_socket(), host)
154 })
155 .unwrap();
156
157 if packet.is_null() {
158 return None;
159 }
160
161 Worker::with_active_host(|host| unsafe {
163 c::tcp_networkInterfaceIsAboutToSendPacket(self.as_legacy_tcp(), host, packet);
164 })
165 .unwrap();
166
167 Some(PacketRc::from_raw(packet))
169 }
170
171 fn peek_packet(&self) -> Option<PacketRc> {
172 let packet = unsafe { c::legacysocket_peekNextOutPacket(self.as_legacy_socket()) };
174
175 if packet.is_null() {
176 return None;
177 }
178
179 Some(PacketRc::from_raw(packet))
181 }
182
183 pub fn peek_next_packet_priority(&self) -> Option<FifoPacketPriority> {
184 self.peek_packet().map(|p| p.priority())
185 }
186
187 pub fn has_data_to_send(&self) -> bool {
188 self.peek_packet().is_some()
189 }
190
191 pub fn getsockname(&self) -> Result<Option<SockaddrIn>, Errno> {
192 let mut ip: libc::in_addr_t = 0;
193 let mut port: libc::in_port_t = 0;
194
195 let okay =
197 unsafe { c::legacysocket_getSocketName(self.as_legacy_socket(), &mut ip, &mut port) };
198 if okay != 1 {
199 return Ok(Some(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into()));
200 }
201
202 let ip = Ipv4Addr::from(u32::from_be(ip));
203 let port = u16::from_be(port);
204 let addr = SocketAddrV4::new(ip, port);
205
206 Ok(Some(addr.into()))
207 }
208
209 pub fn getpeername(&self) -> Result<Option<SockaddrIn>, Errno> {
210 let mut ip: libc::in_addr_t = 0;
211 let mut port: libc::in_port_t = 0;
212
213 let okay =
215 unsafe { c::legacysocket_getPeerName(self.as_legacy_socket(), &mut ip, &mut port) };
216 if okay != 1 {
217 return Err(Errno::ENOTCONN);
218 }
219
220 let ip = Ipv4Addr::from(u32::from_be(ip));
221 let port = u16::from_be(port);
222 let addr = SocketAddrV4::new(ip, port);
223
224 Ok(Some(addr.into()))
225 }
226
227 pub fn address_family(&self) -> linux_api::socket::AddressFamily {
228 linux_api::socket::AddressFamily::AF_INET
229 }
230
231 pub fn close(&mut self, _cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
232 Worker::with_active_host(|h| {
233 unsafe { c::legacyfile_close(self.as_legacy_file(), h) };
234 })
235 .unwrap();
236 Ok(())
237 }
238
239 pub fn bind(
240 socket: &Arc<AtomicRefCell<Self>>,
241 addr: Option<&SockaddrStorage>,
242 net_ns: &NetworkNamespace,
243 rng: impl rand::Rng,
244 ) -> Result<(), SyscallError> {
245 let Some(addr) = addr else {
247 return Err(Errno::EFAULT.into());
248 };
249
250 let Some(addr) = addr.as_inet() else {
252 return Err(Errno::EINVAL.into());
253 };
254
255 let addr: SocketAddrV4 = (*addr).into();
256
257 {
259 let socket = socket.borrow();
260 let socket = socket.as_legacy_socket();
261 if unsafe { c::legacysocket_isBound(socket) } == 1 {
262 return Err(Errno::EINVAL.into());
263 }
264 }
265
266 {
268 let socket = socket.borrow();
271 let socket = socket.as_legacy_socket();
272 assert_eq!(0, unsafe {
273 c::legacysocket_getPeerName(socket, std::ptr::null_mut(), std::ptr::null_mut())
274 });
275 }
276
277 let peer_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
279
280 let (addr, handle) = inet::associate_socket(
282 InetSocket::LegacyTcp(Arc::clone(socket)),
283 addr,
284 peer_addr,
285 true,
286 net_ns,
287 rng,
288 )?;
289
290 std::mem::forget(handle);
293
294 let socket = socket.borrow_mut();
296 let socket = socket.as_legacy_socket();
297 unsafe {
298 c::legacysocket_setSocketName(
299 socket,
300 u32::from(*addr.ip()).to_be(),
301 addr.port().to_be(),
302 )
303 };
304
305 Ok(())
306 }
307
308 pub fn readv(
309 &mut self,
310 _iovs: &[IoVec],
311 _offset: Option<libc::off_t>,
312 _flags: libc::c_int,
313 _mem: &mut MemoryManager,
314 _cb_queue: &mut CallbackQueue,
315 ) -> Result<libc::ssize_t, SyscallError> {
316 panic!("Called LegacyTcpSocket::readv() on a TCP socket.");
320 }
321
322 pub fn writev(
323 &mut self,
324 _iovs: &[IoVec],
325 _offset: Option<libc::off_t>,
326 _flags: libc::c_int,
327 _mem: &mut MemoryManager,
328 _cb_queue: &mut CallbackQueue,
329 ) -> Result<libc::ssize_t, SyscallError> {
330 panic!("Called LegacyTcpSocket::writev() on a TCP socket");
334 }
335
336 pub fn sendmsg(
337 socket: &Arc<AtomicRefCell<Self>>,
338 args: SendmsgArgs,
339 mem: &mut MemoryManager,
340 _net_ns: &NetworkNamespace,
341 _rng: impl rand::Rng,
342 _cb_queue: &mut CallbackQueue,
343 ) -> Result<libc::ssize_t, SyscallError> {
344 let socket_ref = socket.borrow_mut();
345 let tcp = socket_ref.as_legacy_tcp();
346
347 if socket_ref.state().contains(FileState::CLOSED) {
348 log::warn!("Sending on a closed TCP socket");
354 return Err(Errno::EBADF.into());
355 }
356
357 let Some(mut flags) = MsgFlags::from_bits(args.flags) else {
358 log::warn!("Unrecognized send flags: {:#b}", args.flags);
359 return Err(Errno::EINVAL.into());
360 };
361
362 if socket_ref.status().contains(FileStatus::NONBLOCK) {
363 flags.insert(MsgFlags::MSG_DONTWAIT);
364 }
365
366 let result = (|| {
368 let mut bytes_sent = 0;
369
370 for iov in args.iovs {
371 let errcode = unsafe { c::tcp_getConnectionError(tcp) };
372
373 log::trace!("Connection error state is currently {errcode}");
374
375 #[allow(clippy::if_same_then_else)]
376 if errcode > 0 {
377 if bytes_sent == 0 {
381 return Err(Errno::EPIPE);
382 } else {
383 break;
384 }
385 } else if errcode == 0 {
386 } else if errcode == -libc::EISCONN {
389 } else if errcode == -libc::EALREADY {
391 if bytes_sent == 0 {
394 return Err(Errno::EWOULDBLOCK);
395 } else {
396 break;
397 }
398 }
399
400 let rv = Worker::with_active_host(|host| unsafe {
403 c::tcp_sendUserData(
404 tcp,
405 host,
406 iov.base.cast::<()>(),
407 iov.len.try_into().unwrap(),
408 0,
409 0,
410 mem,
411 )
412 })
413 .unwrap();
414
415 if rv < 0 {
416 if bytes_sent == 0 {
417 return Err(Errno::try_from(-rv).unwrap());
418 } else {
419 break;
420 }
421 }
422
423 bytes_sent += rv;
424
425 if usize::try_from(rv).unwrap() < iov.len {
426 break;
428 }
429 }
430
431 Ok(bytes_sent)
432 })();
433
434 if result == Err(Errno::EWOULDBLOCK) && !flags.contains(MsgFlags::MSG_DONTWAIT) {
436 return Err(SyscallError::new_blocked_on_file(
437 File::Socket(Socket::Inet(InetSocket::LegacyTcp(socket.clone()))),
438 FileState::WRITABLE,
439 socket_ref.supports_sa_restart(),
440 ));
441 }
442
443 Ok(result?.try_into().unwrap())
444 }
445
446 pub fn recvmsg(
447 socket: &Arc<AtomicRefCell<Self>>,
448 mut args: RecvmsgArgs,
449 mem: &mut MemoryManager,
450 _cb_queue: &mut CallbackQueue,
451 ) -> Result<RecvmsgReturn, SyscallError> {
452 let socket_ref = socket.borrow_mut();
453 let tcp = socket_ref.as_legacy_tcp();
454
455 if socket_ref.state().contains(FileState::CLOSED) {
456 if unsafe { c::tcp_getConnectionError(tcp) != -libc::EISCONN } {
462 log::warn!("Receiving on a closed TCP socket");
464 return Err(Errno::EBADF.into());
465 }
466 }
467
468 let Some(mut flags) = MsgFlags::from_bits(args.flags) else {
469 log::warn!("Unrecognized recv flags: {:#b}", args.flags);
470 return Err(Errno::EINVAL.into());
471 };
472
473 if socket_ref.status().contains(FileStatus::NONBLOCK) {
474 flags.insert(MsgFlags::MSG_DONTWAIT);
475 }
476
477 let result = (|| {
479 let mut bytes_read = 0;
480
481 if args.iovs.is_empty() {
483 const EMPTY_IOV: IoVec = IoVec {
484 base: ForeignPtr::null(),
485 len: 0,
486 };
487 args.iovs = std::slice::from_ref(&EMPTY_IOV);
488 }
489
490 for iov in args.iovs {
491 let errcode = unsafe { c::tcp_getConnectionError(tcp) };
492
493 if errcode > 0 {
494 if bytes_read == 0 {
496 return Err(Errno::ENOTCONN);
497 } else {
498 break;
499 }
500 } else if errcode == -libc::EALREADY {
501 if bytes_read == 0 {
503 return Err(Errno::EWOULDBLOCK);
504 } else {
505 break;
506 }
507 }
508
509 let rv = Worker::with_active_host(|host| unsafe {
512 c::tcp_receiveUserData(
513 tcp,
514 host,
515 iov.base.cast::<()>(),
516 iov.len.try_into().unwrap(),
517 std::ptr::null_mut(),
518 std::ptr::null_mut(),
519 mem,
520 )
521 })
522 .unwrap();
523
524 if rv < 0 {
525 if bytes_read == 0 {
526 return Err(Errno::try_from(-rv).unwrap());
527 } else {
528 break;
529 }
530 }
531
532 bytes_read += rv;
533
534 if usize::try_from(rv).unwrap() < iov.len {
535 break;
537 }
538 }
539
540 Ok(RecvmsgReturn {
541 return_val: bytes_read.try_into().unwrap(),
542 addr: None,
543 msg_flags: 0,
544 control_len: 0,
545 })
546 })();
547
548 if result.as_ref().err() == Some(&Errno::EWOULDBLOCK)
550 && !flags.contains(MsgFlags::MSG_DONTWAIT)
551 {
552 return Err(SyscallError::new_blocked_on_file(
553 File::Socket(Socket::Inet(InetSocket::LegacyTcp(socket.clone()))),
554 FileState::READABLE,
555 socket_ref.supports_sa_restart(),
556 ));
557 }
558
559 Ok(result?)
560 }
561
562 pub fn ioctl(
563 &mut self,
564 request: IoctlRequest,
565 arg_ptr: ForeignPtr<()>,
566 memory_manager: &mut MemoryManager,
567 ) -> SyscallResult {
568 match request {
569 IoctlRequest::FIONREAD => {
571 let len = unsafe { c::tcp_getInputBufferLength(self.as_legacy_tcp()) }
572 .try_into()
573 .unwrap();
574
575 let arg_ptr = arg_ptr.cast::<libc::c_int>();
576 memory_manager.write(arg_ptr, &len)?;
577
578 Ok(0.into())
579 }
580 IoctlRequest::TIOCOUTQ => {
582 let len = unsafe { c::tcp_getOutputBufferLength(self.as_legacy_tcp()) }
583 .try_into()
584 .unwrap();
585
586 let arg_ptr = arg_ptr.cast::<libc::c_int>();
587 memory_manager.write(arg_ptr, &len)?;
588
589 Ok(0.into())
590 }
591 IoctlRequest::SIOCOUTQNSD => {
592 let len = unsafe { c::tcp_getNotSentBytes(self.as_legacy_tcp()) }
593 .try_into()
594 .unwrap();
595
596 let arg_ptr = arg_ptr.cast::<libc::c_int>();
597 memory_manager.write(arg_ptr, &len)?;
598
599 Ok(0.into())
600 }
601 IoctlRequest::SIOCGSTAMP => Err(Errno::ENOENT.into()),
603 IoctlRequest::FIONBIO => {
604 panic!("This should have been handled by the ioctl syscall handler");
605 }
606 IoctlRequest::TCGETS
607 | IoctlRequest::TCSETS
608 | IoctlRequest::TCSETSW
609 | IoctlRequest::TCSETSF
610 | IoctlRequest::TCGETA
611 | IoctlRequest::TCSETA
612 | IoctlRequest::TCSETAW
613 | IoctlRequest::TCSETAF
614 | IoctlRequest::TIOCGWINSZ
615 | IoctlRequest::TIOCSWINSZ => {
616 Err(Errno::ENOTTY.into())
618 }
619 request => {
620 warn_once_then_debug!(
621 "We do not yet handle ioctl request {request:?} on tcp sockets"
622 );
623 Err(Errno::EINVAL.into())
624 }
625 }
626 }
627
628 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
629 warn_once_then_debug!("We do not yet handle stat calls on tcp sockets");
630 Err(Errno::EINVAL.into())
631 }
632
633 pub fn listen(
634 socket: &Arc<AtomicRefCell<Self>>,
635 backlog: i32,
636 net_ns: &NetworkNamespace,
637 rng: impl rand::Rng,
638 _cb_queue: &mut CallbackQueue,
639 ) -> Result<(), Errno> {
640 let socket_ref = socket.borrow();
641
642 let is_listening_allowed =
644 unsafe { c::tcp_isListeningAllowed(socket_ref.as_legacy_tcp()) } == 1;
645 if !is_listening_allowed {
646 log::debug!("Cannot listen on previously used socket");
647 return Err(Errno::EOPNOTSUPP);
648 }
649
650 let is_valid_listener = unsafe { c::tcp_isValidListener(socket_ref.as_legacy_tcp()) } == 1;
652 if is_valid_listener {
653 log::trace!("Socket already set up as a listener; updating backlog");
654 unsafe { c::tcp_updateServerBacklog(socket_ref.as_legacy_tcp(), backlog) };
655 return Ok(());
656 }
657
658 let is_bound = unsafe { c::legacysocket_isBound(socket_ref.as_legacy_socket()) } == 1;
660 if !is_bound {
661 log::trace!("Implicitly binding listener socket");
662
663 let local_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
665
666 let peer_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
668
669 let (local_addr, handle) = super::associate_socket(
671 super::InetSocket::LegacyTcp(socket.clone()),
672 local_addr,
673 peer_addr,
674 true,
675 net_ns,
676 rng,
677 )?;
678
679 std::mem::forget(handle);
682
683 unsafe {
684 c::legacysocket_setSocketName(
685 socket_ref.as_legacy_socket(),
686 u32::from(*local_addr.ip()).to_be(),
687 local_addr.port().to_be(),
688 )
689 };
690 }
691
692 Worker::with_active_host(|host| {
694 unsafe {
695 c::tcp_enterServerMode(
696 socket_ref.as_legacy_tcp(),
697 host,
698 Worker::active_process_id().unwrap().into(),
699 backlog,
700 )
701 };
702 })
703 .unwrap();
704
705 Ok(())
706 }
707
708 pub fn connect(
709 socket: &Arc<AtomicRefCell<Self>>,
710 peer_addr: &SockaddrStorage,
711 net_ns: &NetworkNamespace,
712 rng: impl rand::Rng,
713 _cb_queue: &mut CallbackQueue,
714 ) -> Result<(), SyscallError> {
715 let mut socket_ref = socket.borrow_mut();
716
717 if let Some(tid) = socket_ref.thread_of_blocked_connect {
718 if tid != Worker::active_thread_id().unwrap() {
720 log::warn!("Two threads are attempting to connect() on a blocking socket");
725 return Err(Errno::EBADFD.into());
726 }
727 }
728
729 let is_valid_listener = unsafe { c::tcp_isValidListener(socket_ref.as_legacy_tcp()) } == 1;
731 if is_valid_listener {
732 return Err(Errno::EISCONN.into());
733 }
734
735 let Some(peer_addr) = peer_addr.as_inet() else {
736 return Err(Errno::EINVAL.into());
737 };
738
739 let mut peer_addr: std::net::SocketAddrV4 = (*peer_addr).into();
740
741 if peer_addr.ip().is_unspecified() {
743 peer_addr.set_ip(std::net::Ipv4Addr::LOCALHOST);
744 }
745
746 let host_default_ip = net_ns.default_ip;
747
748 if peer_addr.ip() != &std::net::Ipv4Addr::LOCALHOST {
755 let is_routable = Worker::is_routable(host_default_ip.into(), (*peer_addr.ip()).into());
756
757 if !is_routable {
758 log::warn!(
760 "Attempting to connect to address '{peer_addr}' for which no host exists"
761 );
762 return Err(Errno::ECONNREFUSED.into());
763 }
764 }
765
766 let is_bound = unsafe { c::legacysocket_isBound(socket_ref.as_legacy_socket()) } == 1;
768 if !is_bound {
769 log::trace!("Implicitly binding listener socket");
770
771 let local_addr = if peer_addr.ip() == &std::net::Ipv4Addr::LOCALHOST {
774 SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)
775 } else {
776 SocketAddrV4::new(host_default_ip, 0)
777 };
778
779 let (local_addr, handle) = super::associate_socket(
781 super::InetSocket::LegacyTcp(socket.clone()),
782 local_addr,
783 peer_addr,
784 true,
785 net_ns,
786 rng,
787 )?;
788
789 std::mem::forget(handle);
792
793 unsafe {
794 c::legacysocket_setSocketName(
795 socket_ref.as_legacy_socket(),
796 u32::from(*local_addr.ip()).to_be(),
797 local_addr.port().to_be(),
798 )
799 };
800 } else if let Some(bound_addr) = socket_ref.getsockname()? {
801 if !bound_addr.ip().is_unspecified() {
803 match (
806 bound_addr.ip() == Ipv4Addr::LOCALHOST,
807 peer_addr.ip() == &Ipv4Addr::LOCALHOST,
808 ) {
809 (true, true) => {}
811 (false, false) => {}
814 _ => return Err(Errno::EINVAL.into()),
815 }
816 }
817 }
818
819 unsafe {
820 c::legacysocket_setPeerName(
821 socket_ref.as_legacy_socket(),
822 u32::from(*peer_addr.ip()).to_be(),
823 peer_addr.port().to_be(),
824 )
825 };
826
827 let errcode = Worker::with_active_host(|host| unsafe {
829 c::legacysocket_connectToPeer(
830 socket_ref.as_legacy_socket(),
831 host,
832 u32::from(*peer_addr.ip()).to_be(),
833 peer_addr.port().to_be(),
834 libc::AF_INET as u16,
835 )
836 })
837 .unwrap();
838
839 assert!(errcode <= 0);
840
841 let mut errcode = if errcode < 0 {
842 Err(Errno::try_from(-errcode).unwrap())
843 } else {
844 Ok(())
845 };
846
847 if !socket_ref.status().contains(FileStatus::NONBLOCK) {
848 if errcode == Err(Errno::EINPROGRESS) {
850 let err = SyscallError::new_blocked_on_file(
854 File::Socket(Socket::Inet(InetSocket::LegacyTcp(Arc::clone(socket)))),
855 FileState::ACTIVE | FileState::WRITABLE,
856 socket_ref.supports_sa_restart(),
857 );
858
859 socket_ref.thread_of_blocked_connect = Some(Worker::active_thread_id().unwrap());
861 return Err(err);
862 }
863
864 if socket_ref.thread_of_blocked_connect.is_some() && errcode == Err(Errno::EISCONN) {
866 errcode = Ok(());
868 }
869 }
870
871 if errcode == Err(Errno::ECONNRESET) || errcode == Err(Errno::ENOTCONN) {
873 errcode = Err(Errno::EISCONN);
874 }
875 if errcode == Err(Errno::EALREADY) {
877 errcode = Err(Errno::EINPROGRESS);
878 }
879
880 socket_ref.thread_of_blocked_connect = None;
881 errcode.map_err(Into::into)
882 }
883
884 pub fn accept(
885 &mut self,
886 _net_ns: &NetworkNamespace,
887 _rng: impl rand::Rng,
888 _cb_queue: &mut CallbackQueue,
889 ) -> Result<OpenFile, SyscallError> {
890 let is_valid_listener = unsafe { c::tcp_isValidListener(self.as_legacy_tcp()) } == 1;
891
892 if !is_valid_listener {
894 log::debug!("Socket is not listening");
895 return Err(Errno::EINVAL.into());
896 }
897
898 let mut peer_addr: libc::sockaddr_in = shadow_pod::zeroed();
899 peer_addr.sin_family = libc::AF_INET as u16;
900 let mut accepted_fd = -1;
901
902 let errcode = Worker::with_active_host(|host| unsafe {
904 c::tcp_acceptServerPeer(
905 self.as_legacy_tcp(),
906 host,
907 &mut peer_addr.sin_addr.s_addr,
908 &mut peer_addr.sin_port,
909 &mut accepted_fd,
910 )
911 })
912 .unwrap();
913
914 assert!(errcode <= 0);
915
916 if errcode < 0 {
917 log::trace!("TCP error when accepting connection");
918 return Err(Errno::try_from(-errcode).unwrap().into());
919 }
920
921 assert!(accepted_fd >= 0);
923
924 let new_descriptor = Worker::with_active_host(|host| {
933 Worker::with_active_thread(|thread| {
934 thread
935 .descriptor_table_borrow_mut(host)
936 .deregister_descriptor(accepted_fd.try_into().unwrap())
937 .unwrap()
938 })
939 })
940 .unwrap()
941 .unwrap();
942
943 let CompatFile::New(open_file) = new_descriptor.into_file() else {
944 panic!(
945 "The TCP code should have added the TCP socket to the descriptor table as a rust socket"
946 );
947 };
948
949 {
952 let File::Socket(Socket::Inet(InetSocket::LegacyTcp(new_socket))) =
953 open_file.inner_file()
954 else {
955 panic!("Expected this to be a LegacyTcpSocket");
956 };
957
958 let new_socket = new_socket.borrow();
959
960 let mut ip: libc::in_addr_t = 0;
961 let mut port: libc::in_port_t = 0;
962
963 let okay = unsafe {
965 c::legacysocket_getPeerName(new_socket.as_legacy_socket(), &mut ip, &mut port)
966 };
967
968 assert_eq!(okay, 1);
969 assert_eq!(ip, peer_addr.sin_addr.s_addr);
970 assert_eq!(port, peer_addr.sin_port);
971 }
972
973 Ok(open_file)
974 }
975
976 pub fn shutdown(
977 &mut self,
978 how: Shutdown,
979 _cb_queue: &mut CallbackQueue,
980 ) -> Result<(), SyscallError> {
981 let how = match how {
982 Shutdown::SHUT_RD => libc::SHUT_RD,
983 Shutdown::SHUT_WR => libc::SHUT_WR,
984 Shutdown::SHUT_RDWR => libc::SHUT_RDWR,
985 };
986
987 let errcode = Worker::with_active_host(|host| unsafe {
988 c::tcp_shutdown(self.as_legacy_tcp(), host, how)
989 })
990 .unwrap();
991
992 assert!(errcode <= 0);
993
994 if errcode < 0 {
995 return Err(Errno::try_from(-errcode).unwrap().into());
996 }
997
998 Ok(())
999 }
1000
1001 pub fn getsockopt(
1002 &self,
1003 level: libc::c_int,
1004 optname: libc::c_int,
1005 optval_ptr: ForeignPtr<()>,
1006 optlen: libc::socklen_t,
1007 memory_manager: &mut MemoryManager,
1008 _cb_queue: &mut CallbackQueue,
1009 ) -> Result<libc::socklen_t, SyscallError> {
1010 match (level, optname) {
1011 (libc::SOL_TCP, libc::TCP_INFO) => {
1012 let mut info = shadow_pod::zeroed();
1013 unsafe { c::tcp_getInfo(self.as_legacy_tcp(), &mut info) };
1014
1015 let optval_ptr = optval_ptr.cast::<crate::cshadow::tcp_info>();
1016 let bytes_written =
1017 write_partial(memory_manager, &info, optval_ptr, optlen as usize)?;
1018
1019 Ok(bytes_written as libc::socklen_t)
1020 }
1021 (libc::SOL_TCP, libc::TCP_NODELAY) => {
1022 let val = 1;
1025
1026 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1027 let bytes_written =
1028 write_partial(memory_manager, &val, optval_ptr, optlen as usize)?;
1029
1030 Ok(bytes_written as libc::socklen_t)
1031 }
1032 (libc::SOL_TCP, libc::TCP_CONGESTION) => {
1033 const CONG_NAME_MAX: usize = 16;
1035
1036 if optval_ptr.is_null() {
1037 return Err(Errno::EINVAL.into());
1038 }
1039
1040 let name: *const libc::c_char =
1041 unsafe { c::tcpcong_nameStr(c::tcp_cong(self.as_legacy_tcp())) };
1042 assert!(!name.is_null(), "shadow's congestion type has no name");
1043 let name = unsafe { CStr::from_ptr(name) };
1044 let name = name.to_bytes_with_nul();
1045
1046 let bytes_to_copy = *[optlen as usize, CONG_NAME_MAX, name.len()]
1047 .iter()
1048 .min()
1049 .unwrap();
1050
1051 let name = &name[..bytes_to_copy];
1052 let optval_ptr = optval_ptr.cast::<u8>();
1053 let optval_ptr = ForeignArrayPtr::new(optval_ptr, bytes_to_copy);
1054
1055 memory_manager.copy_to_ptr(optval_ptr, name)?;
1056
1057 Ok(std::cmp::min(optlen as usize, CONG_NAME_MAX) as libc::socklen_t)
1059 }
1060 (libc::SOL_SOCKET, libc::SO_SNDBUF) => {
1061 let sndbuf_size: libc::c_int =
1062 unsafe { c::legacysocket_getOutputBufferSize(self.as_legacy_socket()) }
1063 .try_into()
1064 .unwrap();
1065
1066 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1067 let bytes_written =
1068 write_partial(memory_manager, &sndbuf_size, optval_ptr, optlen as usize)?;
1069
1070 Ok(bytes_written as libc::socklen_t)
1071 }
1072 (libc::SOL_SOCKET, libc::SO_RCVBUF) => {
1073 let rcvbuf_size: libc::c_int =
1074 unsafe { c::legacysocket_getInputBufferSize(self.as_legacy_socket()) }
1075 .try_into()
1076 .unwrap();
1077
1078 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1079 let bytes_written =
1080 write_partial(memory_manager, &rcvbuf_size, optval_ptr, optlen as usize)?;
1081
1082 Ok(bytes_written as libc::socklen_t)
1083 }
1084 (libc::SOL_SOCKET, libc::SO_ERROR) => {
1085 let conn_err = unsafe { c::tcp_getConnectionError(self.as_legacy_tcp()) };
1087
1088 let error = if conn_err == -libc::ECONNRESET || conn_err == -libc::ECONNREFUSED {
1089 -conn_err
1091 } else {
1092 0
1093 };
1094
1095 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1096 let bytes_written =
1097 write_partial(memory_manager, &error, optval_ptr, optlen as usize)?;
1098
1099 Ok(bytes_written as libc::socklen_t)
1100 }
1101 (libc::SOL_SOCKET, libc::SO_DOMAIN) => {
1102 let domain = libc::AF_INET;
1103
1104 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1105 let bytes_written =
1106 write_partial(memory_manager, &domain, optval_ptr, optlen as usize)?;
1107
1108 Ok(bytes_written as libc::socklen_t)
1109 }
1110 (libc::SOL_SOCKET, libc::SO_TYPE) => {
1111 let sock_type = libc::SOCK_STREAM;
1112
1113 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1114 let bytes_written =
1115 write_partial(memory_manager, &sock_type, optval_ptr, optlen as usize)?;
1116
1117 Ok(bytes_written as libc::socklen_t)
1118 }
1119 (libc::SOL_SOCKET, libc::SO_PROTOCOL) => {
1120 let protocol = libc::IPPROTO_TCP;
1121
1122 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1123 let bytes_written =
1124 write_partial(memory_manager, &protocol, optval_ptr, optlen as usize)?;
1125
1126 Ok(bytes_written as libc::socklen_t)
1127 }
1128 (libc::SOL_SOCKET, libc::SO_ACCEPTCONN) => {
1129 let is_listener = unsafe { c::tcp_isValidListener(self.as_legacy_tcp()) };
1130
1131 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1132 let bytes_written =
1133 write_partial(memory_manager, &is_listener, optval_ptr, optlen as usize)?;
1134
1135 Ok(bytes_written as libc::socklen_t)
1136 }
1137 (libc::SOL_SOCKET, libc::SO_BROADCAST) => {
1138 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1139 let bytes_written = write_partial(memory_manager, &0, optval_ptr, optlen as usize)?;
1141
1142 Ok(bytes_written as libc::socklen_t)
1143 }
1144 _ => {
1145 log_once_per_value_at_level!(
1146 (level, optname),
1147 (i32, i32),
1148 log::Level::Warn,
1149 log::Level::Debug,
1150 "getsockopt called with unsupported level {level} and opt {optname}"
1151 );
1152 Err(Errno::ENOPROTOOPT.into())
1153 }
1154 }
1155 }
1156
1157 pub fn setsockopt(
1158 &mut self,
1159 level: libc::c_int,
1160 optname: libc::c_int,
1161 optval_ptr: ForeignPtr<()>,
1162 optlen: libc::socklen_t,
1163 memory_manager: &MemoryManager,
1164 ) -> Result<(), SyscallError> {
1165 match (level, optname) {
1166 (libc::SOL_TCP, libc::TCP_NODELAY) => {
1167 type OptType = libc::c_int;
1173
1174 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1175 return Err(Errno::EINVAL.into());
1176 }
1177
1178 let optval_ptr = optval_ptr.cast::<OptType>();
1179 let enable = memory_manager.read(optval_ptr)?;
1180
1181 if enable != 0 {
1182 log::debug!("Ignoring TCP_NODELAY");
1184 } else {
1185 log::warn!(
1187 "Cannot disable TCP_NODELAY since shadow does not implement Nagle's algorithm."
1188 );
1189 return Err(Errno::ENOPROTOOPT.into());
1190 }
1191 }
1192 (libc::SOL_TCP, libc::TCP_CONGESTION) => {
1193 const CONG_NAME_MAX: usize = 16;
1195
1196 let mut name = [0u8; CONG_NAME_MAX];
1197
1198 let optlen = std::cmp::min(optlen as usize, CONG_NAME_MAX);
1199 let name = &mut name[..optlen];
1200
1201 let optval_ptr = optval_ptr.cast::<u8>();
1202 let optval_ptr = ForeignArrayPtr::new(optval_ptr, optlen);
1203 memory_manager.copy_from_ptr(name, optval_ptr)?;
1204
1205 let name = name
1208 .iter()
1209 .position(|x| *x == 0)
1210 .map(|x| &name[..x])
1211 .unwrap_or(name);
1212
1213 let reno = unsafe { CStr::from_ptr(c::TCP_CONG_RENO_NAME) }.to_bytes();
1214
1215 if name != reno {
1216 log::warn!("Shadow sockets only support '{reno:?}' for TCP_CONGESTION");
1217 return Err(Errno::ENOENT.into());
1218 }
1219
1220 }
1222 (libc::SOL_SOCKET, libc::SO_SNDBUF) => {
1223 type OptType = libc::c_int;
1224
1225 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1226 return Err(Errno::EINVAL.into());
1227 }
1228
1229 let optval_ptr = optval_ptr.cast::<OptType>();
1230 let val: u64 = memory_manager
1231 .read(optval_ptr)?
1232 .try_into()
1233 .or(Err(Errno::EINVAL))?;
1234
1235 let val = val * 2;
1237
1238 let val = std::cmp::max(val, 4096);
1242
1243 let val = std::cmp::min(val, 268435456); unsafe { c::legacysocket_setOutputBufferSize(self.as_legacy_socket(), val) };
1249 unsafe { c::tcp_disableSendBufferAutotuning(self.as_legacy_tcp()) };
1250 }
1251 (libc::SOL_SOCKET, libc::SO_RCVBUF) => {
1252 type OptType = libc::c_int;
1253
1254 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1255 return Err(Errno::EINVAL.into());
1256 }
1257
1258 let optval_ptr = optval_ptr.cast::<OptType>();
1259 let val: u64 = memory_manager
1260 .read(optval_ptr)?
1261 .try_into()
1262 .or(Err(Errno::EINVAL))?;
1263
1264 let val = val * 2;
1266
1267 let val = std::cmp::max(val, 2048);
1271
1272 let val = std::cmp::min(val, 268435456); unsafe { c::legacysocket_setInputBufferSize(self.as_legacy_socket(), val) };
1278 unsafe { c::tcp_disableReceiveBufferAutotuning(self.as_legacy_tcp()) };
1279 }
1280 (libc::SOL_SOCKET, libc::SO_REUSEADDR) => {
1281 log::trace!("setsockopt SO_REUSEADDR not yet implemented");
1283 }
1284 (libc::SOL_SOCKET, libc::SO_REUSEPORT) => {
1285 log::trace!("setsockopt SO_REUSEPORT not yet implemented");
1287 }
1288 (libc::SOL_SOCKET, libc::SO_KEEPALIVE) => {
1289 log::trace!("setsockopt SO_KEEPALIVE not yet implemented");
1292 }
1293 (libc::SOL_SOCKET, libc::SO_BROADCAST) => {
1294 type OptType = libc::c_int;
1295
1296 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1297 return Err(Errno::EINVAL.into());
1298 }
1299
1300 let optval_ptr = optval_ptr.cast::<OptType>();
1301 let val = memory_manager.read(optval_ptr)?;
1302
1303 if val == 0 {
1304 } else {
1306 warn_once_then_debug!(
1308 "setsockopt SO_BROADCAST not yet implemented for tcp; ignoring and returning 0"
1309 );
1310 }
1311 }
1312 _ => {
1313 log_once_per_value_at_level!(
1314 (level, optname),
1315 (i32, i32),
1316 log::Level::Warn,
1317 log::Level::Debug,
1318 "setsockopt called with unsupported level {level} and opt {optname}"
1319 );
1320 return Err(Errno::ENOPROTOOPT.into());
1321 }
1322 }
1323
1324 Ok(())
1325 }
1326
1327 pub fn add_listener(
1328 &mut self,
1329 monitoring_state: FileState,
1330 monitoring_signals: FileSignals,
1331 filter: StateListenerFilter,
1332 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
1333 + Send
1334 + Sync
1335 + 'static,
1336 ) -> StateListenHandle {
1337 let event_source = unsafe { c::legacyfile_getEventSource(self.as_legacy_file()) };
1338 let event_source = unsafe { event_source.as_ref() }.unwrap();
1339
1340 Worker::with_active_host(|host| {
1341 let mut event_source = event_source.borrow_mut(host.root());
1342 event_source.add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
1343 })
1344 .unwrap()
1345 }
1346
1347 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
1348 unsafe { c::legacyfile_addListener(self.as_legacy_file(), ptr.ptr()) };
1349 }
1350
1351 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
1352 unsafe { c::legacyfile_removeListener(self.as_legacy_file(), ptr) };
1353 }
1354
1355 pub fn state(&self) -> FileState {
1356 unsafe { c::legacyfile_getStatus(self.as_legacy_file()) }
1357 }
1358}
1359
1360impl std::ops::Drop for LegacyTcpSocket {
1361 fn drop(&mut self) {
1362 unsafe { c::legacyfile_unref(self.socket.ptr() as *mut libc::c_void) };
1363 }
1364}