wasmer_wasix/syscalls/wasix/
sock_pair.rs

1use virtual_fs::Pipe;
2
3use super::*;
4use crate::{
5    net::socket::{self, SocketProperties},
6    syscalls::*,
7};
8
9// FIXME
10/// ### `sock_pair()`
11/// Create an interconnected socket pair; or at least it's supposed to.
12///
13/// Currently, this creates a pipe rather than a pair of sockets. Before this
14/// syscall was added, wasix-libc would just do pipe2 in its socketpair
15/// implementation. Since we fixed pipe2 to return a simplex pipe, that was no
16/// longer an option; hence this syscall was added, but the implementation
17/// still uses a pipe as the underlying communication mechanism. This is not
18/// the correct implementation and needs to be fixed. We hope that the pipe
19/// is sufficient for anything that doesn't do socket-specific stuff, such
20/// as sending out-of-band packets.
21///
22/// Note: This is (supposed to be) similar to `socketpair` in POSIX using PF_INET
23///
24/// Note 2: This requires hacks in `sock_send` and `sock_recv` as well.
25///
26/// ## Parameters
27///
28/// * `af` - Address family
29/// * `socktype` - Socket type, either datagram or stream
30/// * `sock_proto` - Socket protocol
31///
32/// ## Return
33///
34/// The file descriptor of the socket that has been opened.
35#[instrument(level = "trace", skip_all, fields(?af, ?ty, ?pt, sock1 = field::Empty, sock2 = field::Empty), ret)]
36pub fn sock_pair<M: MemorySize>(
37    mut ctx: FunctionEnvMut<'_, WasiEnv>,
38    af: Addressfamily,
39    ty: Socktype,
40    pt: SockProto,
41    ro_sock1: WasmPtr<WasiFd, M>,
42    ro_sock2: WasmPtr<WasiFd, M>,
43) -> Result<Errno, WasiError> {
44    WasiEnv::do_pending_operations(&mut ctx)?;
45
46    // only certain combinations are supported
47    match pt {
48        SockProto::Tcp if ty != Socktype::Stream => {
49            return Ok(Errno::Notsup);
50        }
51        SockProto::Udp if ty != Socktype::Dgram => {
52            return Ok(Errno::Notsup);
53        }
54        _ => {}
55    }
56
57    // FIXME: currently, socket properties are ignored outright, since they
58    // make no sense for the underlying pipe
59    let (fd1, fd2) = wasi_try_ok!(sock_pair_internal(&mut ctx, None, None));
60
61    #[cfg(feature = "journal")]
62    if ctx.data().enable_journal {
63        JournalEffector::save_sock_pair(&mut ctx, fd1, fd2).map_err(|err| {
64            tracing::error!("failed to save sock_pair event - {}", err);
65            WasiError::Exit(ExitCode::from(Errno::Fault))
66        })?;
67    }
68
69    let env = ctx.data();
70    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
71    wasi_try_mem_ok!(ro_sock1.write(&memory, fd1));
72    wasi_try_mem_ok!(ro_sock2.write(&memory, fd2));
73
74    Ok(Errno::Success)
75}
76
77pub(crate) fn sock_pair_internal(
78    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
79    with_fd1: Option<WasiFd>,
80    with_fd2: Option<WasiFd>,
81) -> Result<(WasiFd, WasiFd), Errno> {
82    let env = ctx.data();
83    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
84    let (end1, end2) = Pipe::channel();
85
86    let inode1 = state.fs.create_inode_with_default_stat(
87        inodes,
88        Kind::DuplexPipe { pipe: end1 },
89        false,
90        "socketpair".into(),
91    );
92    let inode2 = state.fs.create_inode_with_default_stat(
93        inodes,
94        Kind::DuplexPipe { pipe: end2 },
95        false,
96        "socketpair".into(),
97    );
98
99    let rights = Rights::all_socket();
100    let fd1 = if let Some(fd) = with_fd1 {
101        state
102            .fs
103            .with_fd(
104                rights,
105                rights,
106                Fdflags::empty(),
107                Fdflagsext::empty(),
108                0,
109                inode1,
110                fd,
111            )
112            .map(|_| fd)?
113    } else {
114        state.fs.create_fd(
115            rights,
116            rights,
117            Fdflags::empty(),
118            Fdflagsext::empty(),
119            0,
120            inode1,
121        )?
122    };
123    let fd2 = if let Some(fd) = with_fd2 {
124        state
125            .fs
126            .with_fd(
127                rights,
128                rights,
129                Fdflags::empty(),
130                Fdflagsext::empty(),
131                0,
132                inode2,
133                fd,
134            )
135            .map(|_| fd)?
136    } else {
137        state.fs.create_fd(
138            rights,
139            rights,
140            Fdflags::empty(),
141            Fdflagsext::empty(),
142            0,
143            inode2,
144        )?
145    };
146    Span::current().record("end1", fd1);
147    Span::current().record("end2", fd2);
148
149    Ok((fd1, fd2))
150}