wasmtime_wasi/
ip_name_lookup.rs1use crate::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream};
2use crate::bindings::sockets::network::{ErrorCode, IpAddress, Network};
3use crate::host::network::util;
4use crate::runtime::{spawn_blocking, AbortOnDropJoinHandle};
5use crate::{IoView, SocketError, WasiImpl, WasiView};
6use anyhow::Result;
7use std::mem;
8use std::net::{Ipv6Addr, ToSocketAddrs};
9use std::pin::Pin;
10use std::str::FromStr;
11use std::vec;
12use wasmtime::component::Resource;
13use wasmtime_wasi_io::poll::{subscribe, DynPollable, Pollable};
14
15use super::network::{from_ipv4_addr, from_ipv6_addr};
16
17pub enum ResolveAddressStream {
18 Waiting(AbortOnDropJoinHandle<Result<Vec<IpAddress>, SocketError>>),
19 Done(Result<vec::IntoIter<IpAddress>, SocketError>),
20}
21
22impl<T> Host for WasiImpl<T>
23where
24 T: WasiView,
25{
26 fn resolve_addresses(
27 &mut self,
28 network: Resource<Network>,
29 name: String,
30 ) -> Result<Resource<ResolveAddressStream>, SocketError> {
31 let network = self.table().get(&network)?;
32
33 let host = parse(&name)?;
34
35 if !network.allow_ip_name_lookup {
36 return Err(ErrorCode::PermanentResolverFailure.into());
37 }
38
39 let task = spawn_blocking(move || blocking_resolve(&host));
40 let resource = self.table().push(ResolveAddressStream::Waiting(task))?;
41 Ok(resource)
42 }
43}
44
45impl<T> HostResolveAddressStream for WasiImpl<T>
46where
47 T: WasiView,
48{
49 fn resolve_next_address(
50 &mut self,
51 resource: Resource<ResolveAddressStream>,
52 ) -> Result<Option<IpAddress>, SocketError> {
53 let stream: &mut ResolveAddressStream = self.table().get_mut(&resource)?;
54 loop {
55 match stream {
56 ResolveAddressStream::Waiting(future) => {
57 match crate::runtime::poll_noop(Pin::new(future)) {
58 Some(result) => {
59 *stream = ResolveAddressStream::Done(result.map(|v| v.into_iter()));
60 }
61 None => return Err(ErrorCode::WouldBlock.into()),
62 }
63 }
64 ResolveAddressStream::Done(slot @ Err(_)) => {
65 mem::replace(slot, Ok(Vec::new().into_iter()))?;
66 unreachable!();
67 }
68 ResolveAddressStream::Done(Ok(iter)) => return Ok(iter.next()),
69 }
70 }
71 }
72
73 fn subscribe(
74 &mut self,
75 resource: Resource<ResolveAddressStream>,
76 ) -> Result<Resource<DynPollable>> {
77 subscribe(self.table(), resource)
78 }
79
80 fn drop(&mut self, resource: Resource<ResolveAddressStream>) -> Result<()> {
81 self.table().delete(resource)?;
82 Ok(())
83 }
84}
85
86#[async_trait::async_trait]
87impl Pollable for ResolveAddressStream {
88 async fn ready(&mut self) {
89 if let ResolveAddressStream::Waiting(future) = self {
90 *self = ResolveAddressStream::Done(future.await.map(|v| v.into_iter()));
91 }
92 }
93}
94
95fn parse(name: &str) -> Result<url::Host, SocketError> {
96 match url::Host::parse(&name) {
100 Ok(host) => Ok(host),
101
102 Err(_) => {
104 if let Ok(addr) = Ipv6Addr::from_str(name) {
105 Ok(url::Host::Ipv6(addr))
106 } else {
107 Err(ErrorCode::InvalidArgument.into())
108 }
109 }
110 }
111}
112
113fn blocking_resolve(host: &url::Host) -> Result<Vec<IpAddress>, SocketError> {
114 match host {
115 url::Host::Ipv4(v4addr) => Ok(vec![IpAddress::Ipv4(from_ipv4_addr(*v4addr))]),
116 url::Host::Ipv6(v6addr) => Ok(vec![IpAddress::Ipv6(from_ipv6_addr(*v6addr))]),
117 url::Host::Domain(domain) => {
118 let addresses = (domain.as_str(), 0)
122 .to_socket_addrs()
123 .map_err(|_| ErrorCode::NameUnresolvable)? .map(|addr| util::to_canonical(&addr.ip()).into())
125 .collect();
126
127 Ok(addresses)
128 }
129 }
130}