Skip to main content

wasmer_wasix/syscalls/wasix/
sock_connect.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `sock_connect()`
5/// Initiate a connection on a socket to the specified address
6///
7/// Polling the socket handle will wait for data to arrive or for
8/// the socket status to change which can be queried via 'sock_status'
9///
10/// Note: This is similar to `connect` in POSIX
11///
12/// ## Parameters
13///
14/// * `fd` - Socket descriptor
15/// * `addr` - Address of the socket to connect to
16#[instrument(level = "trace", skip_all, fields(%sock, addr = field::Empty), ret)]
17pub fn sock_connect<M: MemorySize>(
18    mut ctx: FunctionEnvMut<'_, WasiEnv>,
19    sock: WasiFd,
20    addr: WasmPtr<__wasi_addr_port_t, M>,
21) -> Result<Errno, WasiError> {
22    WasiEnv::do_pending_operations(&mut ctx)?;
23
24    let env = ctx.data();
25    let memory = unsafe { env.memory_view(&ctx) };
26    let addr = wasi_try_ok!(crate::net::read_ip_port(&memory, addr));
27    let peer_addr = SocketAddr::new(addr.0, addr.1);
28    Span::current().record("addr", format!("{peer_addr:?}"));
29
30    wasi_try_ok!(sock_connect_internal(&mut ctx, sock, peer_addr)?);
31
32    #[cfg(feature = "journal")]
33    if ctx.data().enable_journal {
34        let local_addr = wasi_try_ok!(__sock_actor(
35            &mut ctx,
36            sock,
37            Rights::empty(),
38            |socket, _| socket.addr_local()
39        ));
40        JournalEffector::save_sock_connect(&mut ctx, sock, local_addr, peer_addr).map_err(
41            |err| {
42                tracing::error!("failed to save sock_connected event - {}", err);
43                WasiError::Exit(ExitCode::from(Errno::Fault))
44            },
45        )?;
46    }
47
48    Ok(Errno::Success)
49}
50
51fn nonblocking_connect_result(status: crate::net::socket::WasiSocketStatus) -> Result<(), Errno> {
52    match status {
53        // This is called immediately after initiating a nonblocking connect.
54        // A failure observed here is asynchronous and must remain available
55        // via SO_ERROR, so report EINPROGRESS rather than consume it.
56        crate::net::socket::WasiSocketStatus::Opening
57        | crate::net::socket::WasiSocketStatus::Closed
58        | crate::net::socket::WasiSocketStatus::Failed => Err(Errno::Inprogress),
59        crate::net::socket::WasiSocketStatus::Opened => Ok(()),
60    }
61}
62
63pub(crate) fn sock_connect_internal(
64    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
65    sock: WasiFd,
66    addr: SocketAddr,
67) -> Result<Result<(), Errno>, WasiError> {
68    let env = ctx.data();
69    let net = env.net().clone();
70    let tasks = ctx.data().tasks().clone();
71    let nonblocking = match env.state.fs.get_fd(sock) {
72        Ok(fd_entry) => fd_entry.inner.flags.contains(Fdflags::NONBLOCK),
73        Err(err) => return Ok(Err(err)),
74    };
75    wasi_try_ok_ok!(__sock_upgrade(
76        ctx,
77        sock,
78        Rights::SOCK_CONNECT,
79        move |mut socket, flags| async move {
80            // Auto-bind UDP
81            let bound_socket = socket.auto_bind_udp(tasks.deref(), net.deref()).await?;
82            socket = bound_socket.clone().unwrap_or(socket);
83            let connected_socket = socket
84                .connect(
85                    tasks.deref(),
86                    net.deref(),
87                    addr,
88                    None,
89                    flags.contains(Fdflags::NONBLOCK),
90                )
91                .await?;
92            Ok(connected_socket.or(bound_socket))
93        }
94    ));
95
96    if nonblocking {
97        let status = match __sock_actor(ctx, sock, Rights::empty(), |socket, _| socket.status()) {
98            Ok(status) => status,
99            Err(err) => return Ok(Err(err)),
100        };
101        return Ok(nonblocking_connect_result(status));
102    }
103
104    Ok(Ok(()))
105}
106
107#[cfg(test)]
108mod tests {
109    use super::nonblocking_connect_result;
110    use crate::net::socket::WasiSocketStatus;
111    use wasmer_wasix_types::wasi::Errno;
112
113    #[test]
114    fn nonblocking_connect_result_maps_socket_states() {
115        assert_eq!(
116            nonblocking_connect_result(WasiSocketStatus::Opening),
117            Err(Errno::Inprogress)
118        );
119        assert_eq!(nonblocking_connect_result(WasiSocketStatus::Opened), Ok(()));
120        assert_eq!(
121            nonblocking_connect_result(WasiSocketStatus::Failed),
122            Err(Errno::Inprogress)
123        );
124        assert_eq!(
125            nonblocking_connect_result(WasiSocketStatus::Closed),
126            Err(Errno::Inprogress)
127        );
128    }
129}