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 lseek(
634 &mut self,
635 _off: linux_api::posix_types::kernel_off_t,
636 _whence: linux_api::unistd::LSeekWhence,
637 ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
638 warn_once_then_debug!("We do not yet handle lseek calls on tcp sockets");
639 Err(Errno::EBADF.into())
640 }
641
642 pub fn listen(
643 socket: &Arc<AtomicRefCell<Self>>,
644 backlog: i32,
645 net_ns: &NetworkNamespace,
646 rng: impl rand::Rng,
647 _cb_queue: &mut CallbackQueue,
648 ) -> Result<(), Errno> {
649 let socket_ref = socket.borrow();
650
651 let is_listening_allowed =
653 unsafe { c::tcp_isListeningAllowed(socket_ref.as_legacy_tcp()) } == 1;
654 if !is_listening_allowed {
655 log::debug!("Cannot listen on previously used socket");
656 return Err(Errno::EOPNOTSUPP);
657 }
658
659 let is_valid_listener = unsafe { c::tcp_isValidListener(socket_ref.as_legacy_tcp()) } == 1;
661 if is_valid_listener {
662 log::trace!("Socket already set up as a listener; updating backlog");
663 unsafe { c::tcp_updateServerBacklog(socket_ref.as_legacy_tcp(), backlog) };
664 return Ok(());
665 }
666
667 let is_bound = unsafe { c::legacysocket_isBound(socket_ref.as_legacy_socket()) } == 1;
669 if !is_bound {
670 log::trace!("Implicitly binding listener socket");
671
672 let local_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
674
675 let peer_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
677
678 let (local_addr, handle) = super::associate_socket(
680 super::InetSocket::LegacyTcp(socket.clone()),
681 local_addr,
682 peer_addr,
683 true,
684 net_ns,
685 rng,
686 )?;
687
688 std::mem::forget(handle);
691
692 unsafe {
693 c::legacysocket_setSocketName(
694 socket_ref.as_legacy_socket(),
695 u32::from(*local_addr.ip()).to_be(),
696 local_addr.port().to_be(),
697 )
698 };
699 }
700
701 Worker::with_active_host(|host| {
703 unsafe {
704 c::tcp_enterServerMode(
705 socket_ref.as_legacy_tcp(),
706 host,
707 Worker::active_process_id().unwrap().into(),
708 backlog,
709 )
710 };
711 })
712 .unwrap();
713
714 Ok(())
715 }
716
717 pub fn connect(
718 socket: &Arc<AtomicRefCell<Self>>,
719 peer_addr: &SockaddrStorage,
720 net_ns: &NetworkNamespace,
721 rng: impl rand::Rng,
722 _cb_queue: &mut CallbackQueue,
723 ) -> Result<(), SyscallError> {
724 let mut socket_ref = socket.borrow_mut();
725
726 if let Some(tid) = socket_ref.thread_of_blocked_connect {
727 if tid != Worker::active_thread_id().unwrap() {
729 log::warn!("Two threads are attempting to connect() on a blocking socket");
734 return Err(Errno::EBADFD.into());
735 }
736 }
737
738 let is_valid_listener = unsafe { c::tcp_isValidListener(socket_ref.as_legacy_tcp()) } == 1;
740 if is_valid_listener {
741 return Err(Errno::EISCONN.into());
742 }
743
744 let Some(peer_addr) = peer_addr.as_inet() else {
745 return Err(Errno::EINVAL.into());
746 };
747
748 let mut peer_addr: std::net::SocketAddrV4 = (*peer_addr).into();
749
750 if peer_addr.ip().is_unspecified() {
752 peer_addr.set_ip(std::net::Ipv4Addr::LOCALHOST);
753 }
754
755 let host_default_ip = net_ns.default_ip;
756
757 if peer_addr.ip() != &std::net::Ipv4Addr::LOCALHOST {
764 let is_routable = Worker::is_routable(host_default_ip.into(), (*peer_addr.ip()).into());
765
766 if !is_routable {
767 log::warn!(
769 "Attempting to connect to address '{peer_addr}' for which no host exists"
770 );
771 return Err(Errno::ECONNREFUSED.into());
772 }
773 }
774
775 let is_bound = unsafe { c::legacysocket_isBound(socket_ref.as_legacy_socket()) } == 1;
777 if !is_bound {
778 log::trace!("Implicitly binding listener socket");
779
780 let local_addr = if peer_addr.ip() == &std::net::Ipv4Addr::LOCALHOST {
783 SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)
784 } else {
785 SocketAddrV4::new(host_default_ip, 0)
786 };
787
788 let (local_addr, handle) = super::associate_socket(
790 super::InetSocket::LegacyTcp(socket.clone()),
791 local_addr,
792 peer_addr,
793 true,
794 net_ns,
795 rng,
796 )?;
797
798 std::mem::forget(handle);
801
802 unsafe {
803 c::legacysocket_setSocketName(
804 socket_ref.as_legacy_socket(),
805 u32::from(*local_addr.ip()).to_be(),
806 local_addr.port().to_be(),
807 )
808 };
809 } else if let Some(bound_addr) = socket_ref.getsockname()? {
810 if !bound_addr.ip().is_unspecified() {
812 match (
815 bound_addr.ip() == Ipv4Addr::LOCALHOST,
816 peer_addr.ip() == &Ipv4Addr::LOCALHOST,
817 ) {
818 (true, true) => {}
820 (false, false) => {}
823 _ => return Err(Errno::EINVAL.into()),
824 }
825 }
826 }
827
828 unsafe {
829 c::legacysocket_setPeerName(
830 socket_ref.as_legacy_socket(),
831 u32::from(*peer_addr.ip()).to_be(),
832 peer_addr.port().to_be(),
833 )
834 };
835
836 let errcode = Worker::with_active_host(|host| unsafe {
838 c::legacysocket_connectToPeer(
839 socket_ref.as_legacy_socket(),
840 host,
841 u32::from(*peer_addr.ip()).to_be(),
842 peer_addr.port().to_be(),
843 libc::AF_INET as u16,
844 )
845 })
846 .unwrap();
847
848 assert!(errcode <= 0);
849
850 let mut errcode = if errcode < 0 {
851 Err(Errno::try_from(-errcode).unwrap())
852 } else {
853 Ok(())
854 };
855
856 if !socket_ref.status().contains(FileStatus::NONBLOCK) {
857 if errcode == Err(Errno::EINPROGRESS) {
859 let err = SyscallError::new_blocked_on_file(
863 File::Socket(Socket::Inet(InetSocket::LegacyTcp(Arc::clone(socket)))),
864 FileState::ACTIVE | FileState::WRITABLE,
865 socket_ref.supports_sa_restart(),
866 );
867
868 socket_ref.thread_of_blocked_connect = Some(Worker::active_thread_id().unwrap());
870 return Err(err);
871 }
872
873 if socket_ref.thread_of_blocked_connect.is_some() && errcode == Err(Errno::EISCONN) {
875 errcode = Ok(());
877 }
878 }
879
880 if errcode == Err(Errno::ECONNRESET) || errcode == Err(Errno::ENOTCONN) {
882 errcode = Err(Errno::EISCONN);
883 }
884 if errcode == Err(Errno::EALREADY) {
886 errcode = Err(Errno::EINPROGRESS);
887 }
888
889 socket_ref.thread_of_blocked_connect = None;
890 errcode.map_err(Into::into)
891 }
892
893 pub fn accept(
894 &mut self,
895 net_ns: &NetworkNamespace,
896 rng: impl rand::Rng,
897 _cb_queue: &mut CallbackQueue,
898 ) -> Result<OpenFile, SyscallError> {
899 let is_valid_listener = unsafe { c::tcp_isValidListener(self.as_legacy_tcp()) } == 1;
900
901 if !is_valid_listener {
903 log::debug!("Socket is not listening");
904 return Err(Errno::EINVAL.into());
905 }
906
907 let mut peer_addr: libc::sockaddr_in = shadow_pod::zeroed();
908 peer_addr.sin_family = libc::AF_INET as u16;
909 let mut accepted_fd = -1;
910
911 let errcode = Worker::with_active_host(|host| unsafe {
913 c::tcp_acceptServerPeer(
914 self.as_legacy_tcp(),
915 host,
916 &mut peer_addr.sin_addr.s_addr,
917 &mut peer_addr.sin_port,
918 &mut accepted_fd,
919 )
920 })
921 .unwrap();
922
923 assert!(errcode <= 0);
924
925 if errcode < 0 {
926 log::trace!("TCP error when accepting connection");
927 return Err(Errno::try_from(-errcode).unwrap().into());
928 }
929
930 assert!(accepted_fd >= 0);
932
933 let new_descriptor = Worker::with_active_host(|host| {
942 Worker::with_active_thread(|thread| {
943 thread
944 .descriptor_table_borrow_mut(host)
945 .deregister_descriptor(accepted_fd.try_into().unwrap())
946 .unwrap()
947 })
948 })
949 .unwrap()
950 .unwrap();
951
952 let CompatFile::New(open_file) = new_descriptor.into_file() else {
953 panic!(
954 "The TCP code should have added the TCP socket to the descriptor table as a rust socket"
955 );
956 };
957
958 {
965 let File::Socket(Socket::Inet(InetSocket::LegacyTcp(new_socket))) =
966 open_file.inner_file()
967 else {
968 panic!("Expected this to be a LegacyTcpSocket");
969 };
970
971 let child_peer_addr;
973 let child_local_addr;
974 {
975 let new_socket_ref = new_socket.borrow();
976 {
979 let mut ip: libc::in_addr_t = 0;
980 let mut port: libc::in_port_t = 0;
981
982 let okay = unsafe {
984 c::legacysocket_getPeerName(
985 new_socket_ref.as_legacy_socket(),
986 &mut ip,
987 &mut port,
988 )
989 };
990
991 assert_eq!(okay, 1);
992 assert_eq!(ip, peer_addr.sin_addr.s_addr);
993 assert_eq!(port, peer_addr.sin_port);
994 }
995 child_peer_addr = new_socket_ref
996 .getpeername()
997 .expect("error finding child peer address")
998 .expect("missing child peer address");
999 child_local_addr = new_socket_ref
1000 .getsockname()
1001 .expect("error finding local address")
1002 .expect("missing local address");
1003 let parent_local_addr = self
1004 .getsockname()
1005 .expect("error finding parent local address")
1006 .expect("missing parent local address");
1007 debug_assert_eq!(
1009 child_local_addr.port(),
1010 parent_local_addr.port(),
1011 "local address of accept'ed socket doesn't match parent listening socket"
1012 );
1013 if !parent_local_addr.ip().is_unspecified() {
1015 debug_assert_eq!(child_local_addr.ip(), parent_local_addr.ip());
1016 }
1017 debug_assert!(!child_local_addr.ip().is_unspecified());
1019 debug_assert_ne!(!child_local_addr.port(), 0);
1020 }
1021
1022 let (_addr, handle) = inet::associate_socket(
1023 InetSocket::LegacyTcp(Arc::clone(new_socket)),
1024 SocketAddrV4::from(child_local_addr),
1025 SocketAddrV4::from(child_peer_addr),
1026 false,
1030 net_ns,
1031 rng,
1032 )?;
1033 std::mem::forget(handle);
1037 }
1038
1039 Ok(open_file)
1040 }
1041
1042 pub fn shutdown(
1043 &mut self,
1044 how: Shutdown,
1045 _cb_queue: &mut CallbackQueue,
1046 ) -> Result<(), SyscallError> {
1047 let how = match how {
1048 Shutdown::SHUT_RD => libc::SHUT_RD,
1049 Shutdown::SHUT_WR => libc::SHUT_WR,
1050 Shutdown::SHUT_RDWR => libc::SHUT_RDWR,
1051 };
1052
1053 let errcode = Worker::with_active_host(|host| unsafe {
1054 c::tcp_shutdown(self.as_legacy_tcp(), host, how)
1055 })
1056 .unwrap();
1057
1058 assert!(errcode <= 0);
1059
1060 if errcode < 0 {
1061 return Err(Errno::try_from(-errcode).unwrap().into());
1062 }
1063
1064 Ok(())
1065 }
1066
1067 pub fn getsockopt(
1068 &self,
1069 level: libc::c_int,
1070 optname: libc::c_int,
1071 optval_ptr: ForeignPtr<()>,
1072 optlen: libc::socklen_t,
1073 memory_manager: &mut MemoryManager,
1074 _cb_queue: &mut CallbackQueue,
1075 ) -> Result<libc::socklen_t, SyscallError> {
1076 match (level, optname) {
1077 (libc::SOL_TCP, libc::TCP_INFO) => {
1078 let mut info = shadow_pod::zeroed();
1079 unsafe { c::tcp_getInfo(self.as_legacy_tcp(), &mut info) };
1080
1081 let optval_ptr = optval_ptr.cast::<crate::cshadow::tcp_info>();
1082 let bytes_written =
1083 write_partial(memory_manager, &info, optval_ptr, optlen as usize)?;
1084
1085 Ok(bytes_written as libc::socklen_t)
1086 }
1087 (libc::SOL_TCP, libc::TCP_NODELAY) => {
1088 let val = 1;
1091
1092 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1093 let bytes_written =
1094 write_partial(memory_manager, &val, optval_ptr, optlen as usize)?;
1095
1096 Ok(bytes_written as libc::socklen_t)
1097 }
1098 (libc::SOL_TCP, libc::TCP_CONGESTION) => {
1099 const CONG_NAME_MAX: usize = 16;
1101
1102 if optval_ptr.is_null() {
1103 return Err(Errno::EINVAL.into());
1104 }
1105
1106 let name: *const libc::c_char =
1107 unsafe { c::tcpcong_nameStr(c::tcp_cong(self.as_legacy_tcp())) };
1108 assert!(!name.is_null(), "shadow's congestion type has no name");
1109 let name = unsafe { CStr::from_ptr(name) };
1110 let name = name.to_bytes_with_nul();
1111
1112 let bytes_to_copy = *[optlen as usize, CONG_NAME_MAX, name.len()]
1113 .iter()
1114 .min()
1115 .unwrap();
1116
1117 let name = &name[..bytes_to_copy];
1118 let optval_ptr = optval_ptr.cast::<u8>();
1119 let optval_ptr = ForeignArrayPtr::new(optval_ptr, bytes_to_copy);
1120
1121 memory_manager.copy_to_ptr(optval_ptr, name)?;
1122
1123 Ok(std::cmp::min(optlen as usize, CONG_NAME_MAX) as libc::socklen_t)
1125 }
1126 (libc::SOL_SOCKET, libc::SO_SNDBUF) => {
1127 let sndbuf_size: libc::c_int =
1128 unsafe { c::legacysocket_getOutputBufferSize(self.as_legacy_socket()) }
1129 .try_into()
1130 .unwrap();
1131
1132 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1133 let bytes_written =
1134 write_partial(memory_manager, &sndbuf_size, optval_ptr, optlen as usize)?;
1135
1136 Ok(bytes_written as libc::socklen_t)
1137 }
1138 (libc::SOL_SOCKET, libc::SO_RCVBUF) => {
1139 let rcvbuf_size: libc::c_int =
1140 unsafe { c::legacysocket_getInputBufferSize(self.as_legacy_socket()) }
1141 .try_into()
1142 .unwrap();
1143
1144 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1145 let bytes_written =
1146 write_partial(memory_manager, &rcvbuf_size, optval_ptr, optlen as usize)?;
1147
1148 Ok(bytes_written as libc::socklen_t)
1149 }
1150 (libc::SOL_SOCKET, libc::SO_ERROR) => {
1151 let conn_err = unsafe { c::tcp_getConnectionError(self.as_legacy_tcp()) };
1153
1154 let error = if conn_err == -libc::ECONNRESET || conn_err == -libc::ECONNREFUSED {
1155 -conn_err
1157 } else {
1158 0
1159 };
1160
1161 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1162 let bytes_written =
1163 write_partial(memory_manager, &error, optval_ptr, optlen as usize)?;
1164
1165 Ok(bytes_written as libc::socklen_t)
1166 }
1167 (libc::SOL_SOCKET, libc::SO_DOMAIN) => {
1168 let domain = libc::AF_INET;
1169
1170 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1171 let bytes_written =
1172 write_partial(memory_manager, &domain, optval_ptr, optlen as usize)?;
1173
1174 Ok(bytes_written as libc::socklen_t)
1175 }
1176 (libc::SOL_SOCKET, libc::SO_TYPE) => {
1177 let sock_type = libc::SOCK_STREAM;
1178
1179 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1180 let bytes_written =
1181 write_partial(memory_manager, &sock_type, optval_ptr, optlen as usize)?;
1182
1183 Ok(bytes_written as libc::socklen_t)
1184 }
1185 (libc::SOL_SOCKET, libc::SO_PROTOCOL) => {
1186 let protocol = libc::IPPROTO_TCP;
1187
1188 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1189 let bytes_written =
1190 write_partial(memory_manager, &protocol, optval_ptr, optlen as usize)?;
1191
1192 Ok(bytes_written as libc::socklen_t)
1193 }
1194 (libc::SOL_SOCKET, libc::SO_ACCEPTCONN) => {
1195 let is_listener = unsafe { c::tcp_isValidListener(self.as_legacy_tcp()) };
1196
1197 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1198 let bytes_written =
1199 write_partial(memory_manager, &is_listener, optval_ptr, optlen as usize)?;
1200
1201 Ok(bytes_written as libc::socklen_t)
1202 }
1203 (libc::SOL_SOCKET, libc::SO_BROADCAST) => {
1204 let optval_ptr = optval_ptr.cast::<libc::c_int>();
1205 let bytes_written = write_partial(memory_manager, &0, optval_ptr, optlen as usize)?;
1207
1208 Ok(bytes_written as libc::socklen_t)
1209 }
1210 _ => {
1211 log_once_per_value_at_level!(
1212 (level, optname),
1213 (i32, i32),
1214 log::Level::Warn,
1215 log::Level::Debug,
1216 "getsockopt called with unsupported level {level} and opt {optname}"
1217 );
1218 Err(Errno::ENOPROTOOPT.into())
1219 }
1220 }
1221 }
1222
1223 pub fn setsockopt(
1224 &mut self,
1225 level: libc::c_int,
1226 optname: libc::c_int,
1227 optval_ptr: ForeignPtr<()>,
1228 optlen: libc::socklen_t,
1229 memory_manager: &MemoryManager,
1230 ) -> Result<(), SyscallError> {
1231 match (level, optname) {
1232 (libc::SOL_TCP, libc::TCP_NODELAY) => {
1233 type OptType = libc::c_int;
1239
1240 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1241 return Err(Errno::EINVAL.into());
1242 }
1243
1244 let optval_ptr = optval_ptr.cast::<OptType>();
1245 let enable = memory_manager.read(optval_ptr)?;
1246
1247 if enable != 0 {
1248 log::debug!("Ignoring TCP_NODELAY");
1250 } else {
1251 log::warn!(
1253 "Cannot disable TCP_NODELAY since shadow does not implement Nagle's algorithm."
1254 );
1255 return Err(Errno::ENOPROTOOPT.into());
1256 }
1257 }
1258 (libc::SOL_TCP, libc::TCP_CONGESTION) => {
1259 const CONG_NAME_MAX: usize = 16;
1261
1262 let mut name = [0u8; CONG_NAME_MAX];
1263
1264 let optlen = std::cmp::min(optlen as usize, CONG_NAME_MAX);
1265 let name = &mut name[..optlen];
1266
1267 let optval_ptr = optval_ptr.cast::<u8>();
1268 let optval_ptr = ForeignArrayPtr::new(optval_ptr, optlen);
1269 memory_manager.copy_from_ptr(name, optval_ptr)?;
1270
1271 let name = name
1274 .iter()
1275 .position(|x| *x == 0)
1276 .map(|x| &name[..x])
1277 .unwrap_or(name);
1278
1279 let reno = unsafe { CStr::from_ptr(c::TCP_CONG_RENO_NAME) }.to_bytes();
1280
1281 if name != reno {
1282 log::warn!("Shadow sockets only support '{reno:?}' for TCP_CONGESTION");
1283 return Err(Errno::ENOENT.into());
1284 }
1285
1286 }
1288 (libc::SOL_SOCKET, libc::SO_SNDBUF) => {
1289 type OptType = libc::c_int;
1290
1291 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1292 return Err(Errno::EINVAL.into());
1293 }
1294
1295 let optval_ptr = optval_ptr.cast::<OptType>();
1296 let val: u64 = memory_manager
1297 .read(optval_ptr)?
1298 .try_into()
1299 .or(Err(Errno::EINVAL))?;
1300
1301 let val = val * 2;
1303
1304 let val = std::cmp::max(val, 4096);
1308
1309 let val = std::cmp::min(val, 268435456); unsafe { c::legacysocket_setOutputBufferSize(self.as_legacy_socket(), val) };
1315 unsafe { c::tcp_disableSendBufferAutotuning(self.as_legacy_tcp()) };
1316 }
1317 (libc::SOL_SOCKET, libc::SO_RCVBUF) => {
1318 type OptType = libc::c_int;
1319
1320 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1321 return Err(Errno::EINVAL.into());
1322 }
1323
1324 let optval_ptr = optval_ptr.cast::<OptType>();
1325 let val: u64 = memory_manager
1326 .read(optval_ptr)?
1327 .try_into()
1328 .or(Err(Errno::EINVAL))?;
1329
1330 let val = val * 2;
1332
1333 let val = std::cmp::max(val, 2048);
1337
1338 let val = std::cmp::min(val, 268435456); unsafe { c::legacysocket_setInputBufferSize(self.as_legacy_socket(), val) };
1344 unsafe { c::tcp_disableReceiveBufferAutotuning(self.as_legacy_tcp()) };
1345 }
1346 (libc::SOL_SOCKET, libc::SO_REUSEADDR) => {
1347 log::trace!("setsockopt SO_REUSEADDR not yet implemented");
1349 }
1350 (libc::SOL_SOCKET, libc::SO_REUSEPORT) => {
1351 log::trace!("setsockopt SO_REUSEPORT not yet implemented");
1353 }
1354 (libc::SOL_SOCKET, libc::SO_KEEPALIVE) => {
1355 log::trace!("setsockopt SO_KEEPALIVE not yet implemented");
1358 }
1359 (libc::SOL_SOCKET, libc::SO_BROADCAST) => {
1360 type OptType = libc::c_int;
1361
1362 if usize::try_from(optlen).unwrap() < std::mem::size_of::<OptType>() {
1363 return Err(Errno::EINVAL.into());
1364 }
1365
1366 let optval_ptr = optval_ptr.cast::<OptType>();
1367 let val = memory_manager.read(optval_ptr)?;
1368
1369 if val == 0 {
1370 } else {
1372 warn_once_then_debug!(
1374 "setsockopt SO_BROADCAST not yet implemented for tcp; ignoring and returning 0"
1375 );
1376 }
1377 }
1378 _ => {
1379 log_once_per_value_at_level!(
1380 (level, optname),
1381 (i32, i32),
1382 log::Level::Warn,
1383 log::Level::Debug,
1384 "setsockopt called with unsupported level {level} and opt {optname}"
1385 );
1386 return Err(Errno::ENOPROTOOPT.into());
1387 }
1388 }
1389
1390 Ok(())
1391 }
1392
1393 pub fn add_listener(
1394 &mut self,
1395 monitoring_state: FileState,
1396 monitoring_signals: FileSignals,
1397 filter: StateListenerFilter,
1398 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
1399 + Send
1400 + Sync
1401 + 'static,
1402 ) -> StateListenHandle {
1403 let event_source = unsafe { c::legacyfile_getEventSource(self.as_legacy_file()) };
1404 let event_source = unsafe { event_source.as_ref() }.unwrap();
1405
1406 Worker::with_active_host(|host| {
1407 let mut event_source = event_source.borrow_mut(host.root());
1408 event_source.add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
1409 })
1410 .unwrap()
1411 }
1412
1413 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
1414 unsafe { c::legacyfile_addListener(self.as_legacy_file(), ptr.ptr()) };
1415 }
1416
1417 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
1418 unsafe { c::legacyfile_removeListener(self.as_legacy_file(), ptr) };
1419 }
1420
1421 pub fn state(&self) -> FileState {
1422 unsafe { c::legacyfile_getStatus(self.as_legacy_file()) }
1423 }
1424}
1425
1426impl std::ops::Drop for LegacyTcpSocket {
1427 fn drop(&mut self) {
1428 unsafe { c::legacyfile_unref(self.socket.ptr() as *mut libc::c_void) };
1429 }
1430}