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