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    // Report a proper socket filetype: callers (e.g. libuv's uv_guess_handle)
87    // fstat socketpair fds to classify them, and the default filestat's
88    // Unknown filetype makes them unusable as streams.
89    let stat = Filestat {
90        st_filetype: Filetype::SocketStream,
91        ..Filestat::default()
92    };
93    let inode1 = state.fs.create_inode_with_stat(
94        inodes,
95        Kind::DuplexPipe { pipe: end1 },
96        false,
97        "socketpair".into(),
98        stat,
99    );
100    let inode2 = state.fs.create_inode_with_stat(
101        inodes,
102        Kind::DuplexPipe { pipe: end2 },
103        false,
104        "socketpair".into(),
105        stat,
106    );
107
108    let rights = Rights::all_socket();
109    let fd1 = if let Some(fd) = with_fd1 {
110        state
111            .fs
112            .with_fd(
113                rights,
114                rights,
115                Fdflags::empty(),
116                Fdflagsext::empty(),
117                0,
118                inode1,
119                fd,
120            )
121            .map(|_| fd)?
122    } else {
123        state.fs.create_fd(
124            rights,
125            rights,
126            Fdflags::empty(),
127            Fdflagsext::empty(),
128            0,
129            inode1,
130        )?
131    };
132    let fd2 = if let Some(fd) = with_fd2 {
133        state
134            .fs
135            .with_fd(
136                rights,
137                rights,
138                Fdflags::empty(),
139                Fdflagsext::empty(),
140                0,
141                inode2,
142                fd,
143            )
144            .map(|_| fd)?
145    } else {
146        state.fs.create_fd(
147            rights,
148            rights,
149            Fdflags::empty(),
150            Fdflagsext::empty(),
151            0,
152            inode2,
153        )?
154    };
155    Span::current().record("end1", fd1);
156    Span::current().record("end2", fd2);
157
158    Ok((fd1, fd2))
159}