Skip to main content

ts_netstack_smoltcp_socket/
create_socket.rs

1use alloc::vec::Vec;
2use core::net::SocketAddr;
3
4use netcore::{Command, HasChannel, raw, smoltcp::wire, tcp, udp};
5
6use crate::{RawSocket, TcpListener, TcpStream, UdpSocket};
7
8/// API for creating sockets over a [`HasChannel`].
9pub trait CreateSocket {
10    /// Create and bind a new [`UdpSocket`] to the given local endpoint.
11    fn udp_bind_blocking(&self, endpoint: SocketAddr) -> Result<UdpSocket, netcore::Error>;
12    /// Asynchronously create and bind a new [`UdpSocket`] to the given local endpoint.
13    fn udp_bind(
14        &self,
15        endpoint: SocketAddr,
16    ) -> impl Future<Output = Result<UdpSocket, netcore::Error>> + Send;
17
18    /// Create a new [`TcpListener`] on the given endpoint.
19    fn tcp_listen_blocking(
20        &self,
21        local_endpoint: SocketAddr,
22    ) -> Result<TcpListener, netcore::Error>;
23    /// Asynchronously create a new [`TcpListener`] on the given endpoint.
24    fn tcp_listen(
25        &self,
26        local_endpoint: SocketAddr,
27    ) -> impl Future<Output = Result<TcpListener, netcore::Error>> + Send;
28
29    /// Snapshot the set of local ports that currently have an explicit TCP listener.
30    ///
31    /// Read-only: answered from the listener registry without touching the packet ingress /
32    /// accept path. The fallback-TCP-handler manager uses this to avoid binding a competing
33    /// any-IP listener on a port the embedder is already serving with an explicit `tcp_listen`.
34    fn bound_tcp_ports_blocking(&self) -> Result<Vec<u16>, netcore::Error>;
35    /// Asynchronously snapshot the set of local ports that currently have an explicit TCP
36    /// listener. See [`CreateSocket::bound_tcp_ports_blocking`].
37    fn bound_tcp_ports(&self) -> impl Future<Output = Result<Vec<u16>, netcore::Error>> + Send;
38
39    /// Create a new [`TcpStream`] bound to the given `local` address and connected to
40    /// the given `remote`.
41    ///
42    /// Waits for the handshake to complete before returning.
43    fn tcp_connect_blocking(
44        &self,
45        local_endpoint: SocketAddr,
46        remote_endpoint: SocketAddr,
47    ) -> Result<TcpStream, netcore::Error>;
48    /// Asynchronously create a new [`TcpStream`] bound to the given `local` address and
49    /// connected to the given `remote`.
50    ///
51    /// Waits for the handshake to complete before returning.
52    fn tcp_connect(
53        &self,
54        local_endpoint: SocketAddr,
55        remote_endpoint: SocketAddr,
56    ) -> impl Future<Output = Result<TcpStream, netcore::Error>> + Send;
57
58    /// Create a new [`RawSocket`] on the selected ip version and protocol.
59    ///
60    /// NB: this will intercept _all_ matching traffic, even if you have other sockets open.
61    fn raw_open_blocking(
62        &self,
63        ipv4: bool,
64        ip_protocol: wire::IpProtocol,
65    ) -> Result<RawSocket, netcore::Error>;
66    /// Asynchronously create a new [`RawSocket`] on the selected ip version and protocol.
67    ///
68    /// NB: this will intercept _all_ matching traffic, even if you have other sockets open.
69    fn raw_open(
70        &self,
71        ipv4: bool,
72        ip_protocol: wire::IpProtocol,
73    ) -> impl Future<Output = Result<RawSocket, netcore::Error>> + Send;
74}
75
76impl<T> CreateSocket for T
77where
78    T: HasChannel + Sync,
79{
80    fn udp_bind_blocking(&self, endpoint: SocketAddr) -> Result<UdpSocket, netcore::Error> {
81        let resp = self.request_blocking(None, udp::Command::Bind { endpoint })?;
82
83        netcore::try_response_as!(resp, udp::Response::Bound { local, handle });
84
85        Ok(UdpSocket {
86            sender: self.command_channel(),
87            local,
88            handle,
89        })
90    }
91
92    async fn udp_bind(&self, endpoint: SocketAddr) -> Result<UdpSocket, netcore::Error> {
93        let resp = self.request(None, udp::Command::Bind { endpoint }).await?;
94
95        netcore::try_response_as!(resp, udp::Response::Bound { local, handle });
96
97        Ok(UdpSocket {
98            sender: self.command_channel(),
99            local,
100            handle,
101        })
102    }
103
104    fn tcp_listen_blocking(
105        &self,
106        local_endpoint: SocketAddr,
107    ) -> Result<TcpListener, netcore::Error> {
108        let resp = self.request_blocking(None, tcp::listen::Command::Listen { local_endpoint })?;
109
110        netcore::try_response_as!(resp, tcp::listen::Response::Listening { handle });
111
112        Ok(TcpListener {
113            sender: self.command_channel(),
114            handle,
115            endpoint: local_endpoint,
116        })
117    }
118
119    async fn tcp_listen(&self, local_endpoint: SocketAddr) -> Result<TcpListener, netcore::Error> {
120        let resp = self
121            .request(None, tcp::listen::Command::Listen { local_endpoint })
122            .await?;
123
124        netcore::try_response_as!(resp, tcp::listen::Response::Listening { handle });
125
126        Ok(TcpListener {
127            sender: self.command_channel(),
128            handle,
129            endpoint: local_endpoint,
130        })
131    }
132
133    fn bound_tcp_ports_blocking(&self) -> Result<Vec<u16>, netcore::Error> {
134        let resp = self.request_blocking(None, tcp::listen::Command::BoundPorts)?;
135
136        netcore::try_response_as!(resp, tcp::listen::Response::BoundPorts { ports });
137
138        Ok(ports)
139    }
140
141    async fn bound_tcp_ports(&self) -> Result<Vec<u16>, netcore::Error> {
142        let resp = self.request(None, tcp::listen::Command::BoundPorts).await?;
143
144        netcore::try_response_as!(resp, tcp::listen::Response::BoundPorts { ports });
145
146        Ok(ports)
147    }
148
149    fn tcp_connect_blocking(
150        &self,
151        local_endpoint: SocketAddr,
152        remote_endpoint: SocketAddr,
153    ) -> Result<TcpStream, netcore::Error> {
154        let resp = self.request_blocking(
155            None,
156            tcp::stream::Command::Connect {
157                remote_endpoint,
158                local_endpoint,
159            },
160        )?;
161
162        netcore::try_response_as!(resp, tcp::stream::Response::Connected { handle });
163
164        Ok(TcpStream::new(
165            self.command_channel(),
166            handle,
167            remote_endpoint,
168            local_endpoint,
169        ))
170    }
171
172    async fn tcp_connect(
173        &self,
174        local_endpoint: SocketAddr,
175        remote_endpoint: SocketAddr,
176    ) -> Result<TcpStream, netcore::Error> {
177        let resp = self
178            .request(
179                None,
180                tcp::stream::Command::Connect {
181                    remote_endpoint,
182                    local_endpoint,
183                },
184            )
185            .await?;
186
187        netcore::try_response_as!(resp, tcp::stream::Response::Connected { handle });
188
189        Ok(TcpStream::new(
190            self.command_channel(),
191            handle,
192            remote_endpoint,
193            local_endpoint,
194        ))
195    }
196
197    fn raw_open_blocking(
198        &self,
199        ipv4: bool,
200        ip_protocol: wire::IpProtocol,
201    ) -> Result<RawSocket, netcore::Error> {
202        let ip_version = if ipv4 {
203            wire::IpVersion::Ipv4
204        } else {
205            wire::IpVersion::Ipv6
206        };
207
208        let resp = self.request_blocking(
209            None,
210            Command::Raw(raw::Command::Open {
211                ip_version,
212                protocol: ip_protocol,
213            }),
214        )?;
215
216        netcore::try_response_as!(resp, raw::Response::Opened { handle });
217
218        Ok(RawSocket::new(
219            self.command_channel(),
220            handle,
221            ip_protocol,
222            ip_version,
223        ))
224    }
225
226    async fn raw_open(
227        &self,
228        ipv4: bool,
229        ip_protocol: wire::IpProtocol,
230    ) -> Result<RawSocket, netcore::Error> {
231        let ip_version = if ipv4 {
232            wire::IpVersion::Ipv4
233        } else {
234            wire::IpVersion::Ipv6
235        };
236
237        let resp = self
238            .request(
239                None,
240                Command::Raw(raw::Command::Open {
241                    ip_version,
242                    protocol: ip_protocol,
243                }),
244            )
245            .await?;
246
247        netcore::try_response_as!(resp, raw::Response::Opened { handle });
248
249        Ok(RawSocket::new(
250            self.command_channel(),
251            handle,
252            ip_protocol,
253            ip_version,
254        ))
255    }
256}