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 => {
49            if ty != Socktype::Stream {
50                return Ok(Errno::Notsup);
51            }
52        }
53        SockProto::Udp => {
54            if ty != Socktype::Dgram {
55                return Ok(Errno::Notsup);
56            }
57        }
58        _ => {}
59    }
60
61    // FIXME: currently, socket properties are ignored outright, since they
62    // make no sense for the underlying pipe
63    let (fd1, fd2) = wasi_try_ok!(sock_pair_internal(&mut ctx, None, None));
64
65    #[cfg(feature = "journal")]
66    if ctx.data().enable_journal {
67        JournalEffector::save_sock_pair(&mut ctx, fd1, fd2).map_err(|err| {
68            tracing::error!("failed to save sock_pair event - {}", err);
69            WasiError::Exit(ExitCode::from(Errno::Fault))
70        })?;
71    }
72
73    let env = ctx.data();
74    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
75    wasi_try_mem_ok!(ro_sock1.write(&memory, fd1));
76    wasi_try_mem_ok!(ro_sock2.write(&memory, fd2));
77
78    Ok(Errno::Success)
79}
80
81pub(crate) fn sock_pair_internal(
82    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
83    with_fd1: Option<WasiFd>,
84    with_fd2: Option<WasiFd>,
85) -> Result<(WasiFd, WasiFd), Errno> {
86    let env = ctx.data();
87    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
88    let (end1, end2) = Pipe::channel();
89
90    let inode1 = state.fs.create_inode_with_default_stat(
91        inodes,
92        Kind::DuplexPipe { pipe: end1 },
93        false,
94        "socketpair".into(),
95    );
96    let inode2 = state.fs.create_inode_with_default_stat(
97        inodes,
98        Kind::DuplexPipe { pipe: end2 },
99        false,
100        "socketpair".into(),
101    );
102
103    let rights = Rights::all_socket();
104    let fd1 = if let Some(fd) = with_fd1 {
105        state
106            .fs
107            .with_fd(
108                rights,
109                rights,
110                Fdflags::empty(),
111                Fdflagsext::empty(),
112                0,
113                inode1,
114                fd,
115            )
116            .map(|_| fd)?
117    } else {
118        state.fs.create_fd(
119            rights,
120            rights,
121            Fdflags::empty(),
122            Fdflagsext::empty(),
123            0,
124            inode1,
125        )?
126    };
127    let fd2 = if let Some(fd) = with_fd2 {
128        state
129            .fs
130            .with_fd(
131                rights,
132                rights,
133                Fdflags::empty(),
134                Fdflagsext::empty(),
135                0,
136                inode2,
137                fd,
138            )
139            .map(|_| fd)?
140    } else {
141        state.fs.create_fd(
142            rights,
143            rights,
144            Fdflags::empty(),
145            Fdflagsext::empty(),
146            0,
147            inode2,
148        )?
149    };
150    Span::current().record("end1", fd1);
151    Span::current().record("end2", fd2);
152
153    Ok((fd1, fd2))
154}