1use crate::p2::bindings::sockets::network::{ErrorCode, IpAddressFamily, IpSocketAddress, Network};
2use crate::p2::bindings::sockets::udp;
3use crate::p2::udp::{AsyncOperation, IncomingDatagramStream, OutgoingDatagramStream};
4use crate::p2::{Pollable, SocketError, SocketResult, UdpSocket};
5use crate::runtime::poll_now;
6use crate::sockets::{SocketAddressFamily, WasiSocketsCtxView};
7use async_trait::async_trait;
8use std::future::poll_fn;
9use std::net::SocketAddr;
10use wasmtime::component::Resource;
11use wasmtime::format_err;
12use wasmtime_wasi_io::poll::DynPollable;
13
14const MAX_DATAGRAMS: usize = 16;
15
16impl udp::Host for WasiSocketsCtxView<'_> {}
17
18impl udp::HostUdpSocket for WasiSocketsCtxView<'_> {
19 async fn start_bind(
20 &mut self,
21 this: Resource<UdpSocket>,
22 network: Resource<Network>,
23 local_address: IpSocketAddress,
24 ) -> SocketResult<()> {
25 _ = self.table.get(&network)?;
29
30 let local_address = SocketAddr::from(local_address);
31 let socket = self.table.get_mut(&this)?;
32 if socket.in_progress_operation.is_some() {
33 return Err(ErrorCode::ConcurrencyConflict.into());
34 }
35
36 socket
37 .get_mut()
38 .ok_or(ErrorCode::InvalidState)?
39 .bind(local_address)
40 .await?;
41
42 socket.in_progress_operation = Some(AsyncOperation::Bind);
43 Ok(())
44 }
45
46 fn finish_bind(&mut self, this: Resource<UdpSocket>) -> SocketResult<()> {
47 let socket = self.table.get_mut(&this)?;
48 if socket.in_progress_operation != Some(AsyncOperation::Bind) {
49 return Err(ErrorCode::NotInProgress.into());
50 };
51 socket.in_progress_operation = None;
52 Ok(())
53 }
54
55 async fn stream(
56 &mut self,
57 this: Resource<UdpSocket>,
58 remote_address: Option<IpSocketAddress>,
59 ) -> SocketResult<(
60 Resource<udp::IncomingDatagramStream>,
61 Resource<udp::OutgoingDatagramStream>,
62 )> {
63 let has_active_streams = self
64 .table
65 .iter_children(&this)?
66 .any(|c| c.is::<IncomingDatagramStream>() || c.is::<OutgoingDatagramStream>());
67
68 if has_active_streams {
69 return Err(SocketError::trap(format_err!(
70 "UDP streams not dropped yet"
71 )));
72 }
73
74 let socket = self.table.get_mut(&this)?;
75 let inner = socket
76 .get_mut()
77 .ok_or(SocketError::trap(wasmtime::error::format_err!(
78 "`connect` needs exclusive access"
79 )))?;
80
81 if !inner.is_bound() {
82 return Err(ErrorCode::InvalidState.into());
84 }
85
86 if let Some(connect_addr) = remote_address {
87 inner.connect(connect_addr.into()).await?;
88 } else if inner.is_connected() {
89 inner.disconnect()?;
90 }
91 let incoming_stream = IncomingDatagramStream::new(socket.inner.clone());
92 let outgoing_stream = OutgoingDatagramStream::new(socket.inner.clone());
93 Ok((
94 self.table.push_child(incoming_stream, &this)?,
95 self.table.push_child(outgoing_stream, &this)?,
96 ))
97 }
98
99 fn local_address(&mut self, this: Resource<UdpSocket>) -> SocketResult<IpSocketAddress> {
100 let mut socket = self.table.get(&this)?.lock();
101 Ok(socket.local_address()?.into())
102 }
103
104 fn remote_address(&mut self, this: Resource<UdpSocket>) -> SocketResult<IpSocketAddress> {
105 let mut socket = self.table.get(&this)?.lock();
106 Ok(socket.remote_address()?.into())
107 }
108
109 fn address_family(
110 &mut self,
111 this: Resource<UdpSocket>,
112 ) -> Result<IpAddressFamily, wasmtime::Error> {
113 let socket = self.table.get(&this)?.lock();
114 Ok(socket.address_family().into())
115 }
116
117 fn unicast_hop_limit(&mut self, this: Resource<UdpSocket>) -> SocketResult<u8> {
118 let socket = self.table.get(&this)?.lock();
119 Ok(socket.unicast_hop_limit()?)
120 }
121
122 fn set_unicast_hop_limit(&mut self, this: Resource<UdpSocket>, value: u8) -> SocketResult<()> {
123 let socket = self.table.get(&this)?.lock();
124 socket.set_unicast_hop_limit(value)?;
125 Ok(())
126 }
127
128 fn receive_buffer_size(&mut self, this: Resource<UdpSocket>) -> SocketResult<u64> {
129 let socket = self.table.get(&this)?.lock();
130 Ok(socket.receive_buffer_size()?)
131 }
132
133 fn set_receive_buffer_size(
134 &mut self,
135 this: Resource<UdpSocket>,
136 value: u64,
137 ) -> SocketResult<()> {
138 let socket = self.table.get(&this)?.lock();
139 socket.set_receive_buffer_size(value)?;
140 Ok(())
141 }
142
143 fn send_buffer_size(&mut self, this: Resource<UdpSocket>) -> SocketResult<u64> {
144 let socket = self.table.get(&this)?.lock();
145 Ok(socket.send_buffer_size()?)
146 }
147
148 fn set_send_buffer_size(&mut self, this: Resource<UdpSocket>, value: u64) -> SocketResult<()> {
149 let socket = self.table.get(&this)?.lock();
150 socket.set_send_buffer_size(value)?;
151 Ok(())
152 }
153
154 fn subscribe(&mut self, this: Resource<UdpSocket>) -> wasmtime::Result<Resource<DynPollable>> {
155 wasmtime_wasi_io::poll::subscribe(self.table, this)
156 }
157
158 fn drop(&mut self, this: Resource<UdpSocket>) -> Result<(), wasmtime::Error> {
159 let dropped = self.table.delete(this)?;
162 drop(dropped);
163
164 Ok(())
165 }
166}
167
168#[async_trait]
169impl Pollable for UdpSocket {
170 async fn ready(&mut self) {
171 }
173}
174
175impl udp::HostIncomingDatagramStream for WasiSocketsCtxView<'_> {
176 fn receive(
177 &mut self,
178 this: Resource<udp::IncomingDatagramStream>,
179 max_results: u64,
180 ) -> SocketResult<Vec<udp::IncomingDatagram>> {
181 let stream = self.table.get_mut(&this)?;
182 let max_results: usize = max_results
183 .try_into()
184 .unwrap_or(usize::MAX)
185 .min(MAX_DATAGRAMS);
186 if max_results == 0 {
187 return Ok(vec![]);
188 }
189
190 let mut datagrams = vec![];
191 let mut sum = 0;
192
193 while datagrams.len() < max_results && sum < crate::MAX_READ_SIZE_ALLOC {
194 match stream.try_recv() {
195 Err(ErrorCode::WouldBlock) => break,
196 Ok((data, remote_addr)) => {
197 sum += 1 + data.len();
198 datagrams.push(udp::IncomingDatagram {
199 data,
200 remote_address: remote_addr.into(),
201 });
202 }
203 Err(_) if datagrams.len() > 0 => break,
204 Err(e) => {
205 return Err(e.into());
206 }
207 }
208 }
209
210 Ok(datagrams)
211 }
212
213 fn subscribe(
214 &mut self,
215 this: Resource<udp::IncomingDatagramStream>,
216 ) -> wasmtime::Result<Resource<DynPollable>> {
217 wasmtime_wasi_io::poll::subscribe(self.table, this)
218 }
219
220 async fn drop(
221 &mut self,
222 this: Resource<udp::IncomingDatagramStream>,
223 ) -> Result<(), wasmtime::Error> {
224 let dropped = self.table.delete(this)?;
225 dropped.finish().await;
226 Ok(())
227 }
228}
229
230#[async_trait]
231impl Pollable for IncomingDatagramStream {
232 async fn ready(&mut self) {
233 poll_fn(|cx| self.poll_recv_ready(cx)).await
234 }
235}
236
237impl udp::HostOutgoingDatagramStream for WasiSocketsCtxView<'_> {
238 fn check_send(&mut self, this: Resource<udp::OutgoingDatagramStream>) -> SocketResult<u64> {
239 let stream = self.table.get_mut(&this)?;
240
241 let count = if let Some(()) = poll_now(|cx| stream.poll_send_ready(cx)) {
242 MAX_DATAGRAMS
247 } else {
248 0
249 };
250
251 stream.check_send_permit_count = count;
252
253 Ok(count.try_into().unwrap())
254 }
255
256 fn send(
257 &mut self,
258 this: Resource<udp::OutgoingDatagramStream>,
259 datagrams: Vec<udp::OutgoingDatagram>,
260 ) -> SocketResult<u64> {
261 let stream = self.table.get_mut(&this)?;
262
263 if datagrams.is_empty() {
264 return Ok(0);
265 }
266
267 if datagrams.len() > stream.check_send_permit_count {
268 return Err(SocketError::trap(wasmtime::format_err!(
269 "unpermitted: argument exceeds permitted size"
270 )));
271 }
272
273 stream.check_send_permit_count = 0;
276
277 let mut count = 0;
278
279 for datagram in datagrams {
280 match stream.try_send(datagram.data, datagram.remote_address.map(SocketAddr::from)) {
281 Err(ErrorCode::WouldBlock) => break,
282 Ok(()) => count += 1,
283 Err(_) if count > 0 => {
284 break;
286 }
287 Err(e) => {
288 return Err(e.into());
289 }
290 }
291 }
292
293 Ok(count)
294 }
295
296 fn subscribe(
297 &mut self,
298 this: Resource<udp::OutgoingDatagramStream>,
299 ) -> wasmtime::Result<Resource<DynPollable>> {
300 wasmtime_wasi_io::poll::subscribe(self.table, this)
301 }
302
303 async fn drop(
304 &mut self,
305 this: Resource<udp::OutgoingDatagramStream>,
306 ) -> Result<(), wasmtime::Error> {
307 let mut stream = self.table.delete(this)?;
308 std::future::poll_fn(|cx| stream.poll_send_ready(cx)).await;
313 drop(stream);
314 Ok(())
315 }
316}
317
318#[async_trait]
319impl Pollable for OutgoingDatagramStream {
320 async fn ready(&mut self) {
321 poll_fn(|cx| self.poll_send_ready(cx)).await
322 }
323}
324
325impl From<SocketAddressFamily> for IpAddressFamily {
326 fn from(family: SocketAddressFamily) -> IpAddressFamily {
327 match family {
328 SocketAddressFamily::Ipv4 => IpAddressFamily::Ipv4,
329 SocketAddressFamily::Ipv6 => IpAddressFamily::Ipv6,
330 }
331 }
332}
333
334pub mod sync {
335 use wasmtime::component::Resource;
336
337 use crate::p2::{
338 SocketError, UdpSocket,
339 bindings::{
340 sockets::{
341 network::Network,
342 udp::{
343 self as async_udp,
344 HostIncomingDatagramStream as AsyncHostIncomingDatagramStream,
345 HostOutgoingDatagramStream as AsyncHostOutgoingDatagramStream,
346 HostUdpSocket as AsyncHostUdpSocket, IncomingDatagramStream,
347 OutgoingDatagramStream,
348 },
349 },
350 sync::sockets::udp::{
351 self, HostIncomingDatagramStream, HostOutgoingDatagramStream, HostUdpSocket,
352 IncomingDatagram, IpAddressFamily, IpSocketAddress, OutgoingDatagram, Pollable,
353 },
354 },
355 };
356 use crate::runtime::in_tokio;
357 use crate::sockets::WasiSocketsCtxView;
358
359 impl udp::Host for WasiSocketsCtxView<'_> {}
360
361 impl HostUdpSocket for WasiSocketsCtxView<'_> {
362 fn start_bind(
363 &mut self,
364 self_: Resource<UdpSocket>,
365 network: Resource<Network>,
366 local_address: IpSocketAddress,
367 ) -> Result<(), SocketError> {
368 in_tokio(async {
369 AsyncHostUdpSocket::start_bind(self, self_, network, local_address).await
370 })
371 }
372
373 fn finish_bind(&mut self, self_: Resource<UdpSocket>) -> Result<(), SocketError> {
374 AsyncHostUdpSocket::finish_bind(self, self_)
375 }
376
377 fn stream(
378 &mut self,
379 self_: Resource<UdpSocket>,
380 remote_address: Option<IpSocketAddress>,
381 ) -> Result<
382 (
383 Resource<IncomingDatagramStream>,
384 Resource<OutgoingDatagramStream>,
385 ),
386 SocketError,
387 > {
388 in_tokio(async { AsyncHostUdpSocket::stream(self, self_, remote_address).await })
389 }
390
391 fn local_address(
392 &mut self,
393 self_: Resource<UdpSocket>,
394 ) -> Result<IpSocketAddress, SocketError> {
395 AsyncHostUdpSocket::local_address(self, self_)
396 }
397
398 fn remote_address(
399 &mut self,
400 self_: Resource<UdpSocket>,
401 ) -> Result<IpSocketAddress, SocketError> {
402 AsyncHostUdpSocket::remote_address(self, self_)
403 }
404
405 fn address_family(
406 &mut self,
407 self_: Resource<UdpSocket>,
408 ) -> wasmtime::Result<IpAddressFamily> {
409 AsyncHostUdpSocket::address_family(self, self_)
410 }
411
412 fn unicast_hop_limit(&mut self, self_: Resource<UdpSocket>) -> Result<u8, SocketError> {
413 AsyncHostUdpSocket::unicast_hop_limit(self, self_)
414 }
415
416 fn set_unicast_hop_limit(
417 &mut self,
418 self_: Resource<UdpSocket>,
419 value: u8,
420 ) -> Result<(), SocketError> {
421 AsyncHostUdpSocket::set_unicast_hop_limit(self, self_, value)
422 }
423
424 fn receive_buffer_size(&mut self, self_: Resource<UdpSocket>) -> Result<u64, SocketError> {
425 AsyncHostUdpSocket::receive_buffer_size(self, self_)
426 }
427
428 fn set_receive_buffer_size(
429 &mut self,
430 self_: Resource<UdpSocket>,
431 value: u64,
432 ) -> Result<(), SocketError> {
433 AsyncHostUdpSocket::set_receive_buffer_size(self, self_, value)
434 }
435
436 fn send_buffer_size(&mut self, self_: Resource<UdpSocket>) -> Result<u64, SocketError> {
437 AsyncHostUdpSocket::send_buffer_size(self, self_)
438 }
439
440 fn set_send_buffer_size(
441 &mut self,
442 self_: Resource<UdpSocket>,
443 value: u64,
444 ) -> Result<(), SocketError> {
445 AsyncHostUdpSocket::set_send_buffer_size(self, self_, value)
446 }
447
448 fn subscribe(
449 &mut self,
450 self_: Resource<UdpSocket>,
451 ) -> wasmtime::Result<Resource<Pollable>> {
452 AsyncHostUdpSocket::subscribe(self, self_)
453 }
454
455 fn drop(&mut self, rep: Resource<UdpSocket>) -> wasmtime::Result<()> {
456 AsyncHostUdpSocket::drop(self, rep)
457 }
458 }
459
460 impl HostIncomingDatagramStream for WasiSocketsCtxView<'_> {
461 fn receive(
462 &mut self,
463 self_: Resource<IncomingDatagramStream>,
464 max_results: u64,
465 ) -> Result<Vec<IncomingDatagram>, SocketError> {
466 Ok(
467 AsyncHostIncomingDatagramStream::receive(self, self_, max_results)?
468 .into_iter()
469 .map(Into::into)
470 .collect(),
471 )
472 }
473
474 fn subscribe(
475 &mut self,
476 self_: Resource<IncomingDatagramStream>,
477 ) -> wasmtime::Result<Resource<Pollable>> {
478 AsyncHostIncomingDatagramStream::subscribe(self, self_)
479 }
480
481 fn drop(&mut self, rep: Resource<IncomingDatagramStream>) -> wasmtime::Result<()> {
482 in_tokio(async { AsyncHostIncomingDatagramStream::drop(self, rep).await })
483 }
484 }
485
486 impl From<async_udp::IncomingDatagram> for IncomingDatagram {
487 fn from(other: async_udp::IncomingDatagram) -> Self {
488 let async_udp::IncomingDatagram {
489 data,
490 remote_address,
491 } = other;
492 Self {
493 data,
494 remote_address,
495 }
496 }
497 }
498
499 impl HostOutgoingDatagramStream for WasiSocketsCtxView<'_> {
500 fn check_send(
501 &mut self,
502 self_: Resource<OutgoingDatagramStream>,
503 ) -> Result<u64, SocketError> {
504 AsyncHostOutgoingDatagramStream::check_send(self, self_)
505 }
506
507 fn send(
508 &mut self,
509 self_: Resource<OutgoingDatagramStream>,
510 datagrams: Vec<OutgoingDatagram>,
511 ) -> Result<u64, SocketError> {
512 let datagrams = datagrams.into_iter().map(Into::into).collect();
513 AsyncHostOutgoingDatagramStream::send(self, self_, datagrams)
514 }
515
516 fn subscribe(
517 &mut self,
518 self_: Resource<OutgoingDatagramStream>,
519 ) -> wasmtime::Result<Resource<Pollable>> {
520 AsyncHostOutgoingDatagramStream::subscribe(self, self_)
521 }
522
523 fn drop(&mut self, rep: Resource<OutgoingDatagramStream>) -> wasmtime::Result<()> {
524 in_tokio(async { AsyncHostOutgoingDatagramStream::drop(self, rep).await })
525 }
526 }
527
528 impl From<OutgoingDatagram> for async_udp::OutgoingDatagram {
529 fn from(other: OutgoingDatagram) -> Self {
530 let OutgoingDatagram {
531 data,
532 remote_address,
533 } = other;
534 Self {
535 data,
536 remote_address,
537 }
538 }
539 }
540}