1use futures_util::{
2 StreamExt,
3 TryStreamExt,
4};
5use std::{
6 fmt,
7 io,
8 net::{
9 IpAddr,
10 Ipv4Addr,
11 Ipv6Addr,
12 SocketAddr,
13 SocketAddrV4,
14 SocketAddrV6,
15 },
16 pin::Pin,
17 task::{
18 Context,
19 Poll,
20 },
21};
22
23use crate::{
24 dns_consts::{
25 Class,
26 Type,
27 },
28 ffi,
29 interface::Interface,
30 service::{
31 QueryRecordData,
32 QueryRecordFlags,
33 QueryRecordResult,
34 query_record_extended,
35 },
36};
37
38fn decode_a(a: QueryRecordResult, port: u16) -> Option<ResolveHostResult> {
39 if a.rr_class == Class::IN && a.rr_type == Type::A && a.rdata.len() == 4 {
40 let mut octets = [0u8; 4];
41 octets.clone_from_slice(&a.rdata);
42 let ip = IpAddr::V4(Ipv4Addr::from(octets));
43 let addr = ScopedSocketAddr::new(ip, port, a.interface.scope_id());
44 Some(ResolveHostResult {
45 flags: ResolvedHostFlags::from_bits_truncate(a.flags.bits()),
46 address: addr,
47 })
48 } else {
49 println!("Invalid A response: {:?}", a);
50 None
51 }
52}
53
54fn decode_aaaa(a: QueryRecordResult, port: u16) -> Option<ResolveHostResult> {
55 if a.rr_class == Class::IN && a.rr_type == Type::AAAA && a.rdata.len() == 16 {
56 let mut octets = [0u8; 16];
57 octets.clone_from_slice(&a.rdata);
58 let ip = IpAddr::V6(Ipv6Addr::from(octets));
59 let addr = ScopedSocketAddr::new(ip, port, a.interface.scope_id());
60 Some(ResolveHostResult {
61 flags: ResolvedHostFlags::from_bits_truncate(a.flags.bits()),
62 address: addr,
63 })
64 } else {
65 println!("Invalid AAAA response: {:?}", a);
66 None
67 }
68}
69
70bitflags::bitflags! {
71 #[derive(Default)]
75 pub struct ResolvedHostFlags: ffi::DNSServiceFlags {
76 const ADD = ffi::FLAGS_ADD;
81 }
82}
83
84#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
96pub struct ResolveHostData {
97 pub flags: QueryRecordFlags,
99 pub interface: Interface,
101 #[doc(hidden)]
102 pub _non_exhaustive: crate::non_exhaustive_struct::NonExhaustiveMarker,
103}
104
105#[must_use = "streams do nothing unless polled"]
107pub struct ResolveHost {
108 inner: Pin<
109 Box<dyn futures_core::Stream<Item = io::Result<ResolveHostResult>> + 'static + Send + Sync>,
110 >,
111}
112
113impl futures_core::Stream for ResolveHost {
114 type Item = io::Result<ResolveHostResult>;
115
116 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
117 let this = self.get_mut();
118 this.inner.poll_next_unpin(cx)
119 }
120}
121
122#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
126pub struct ResolveHostResult {
127 pub flags: ResolvedHostFlags,
129 pub address: ScopedSocketAddr,
131}
132
133#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
138pub enum ScopedSocketAddr {
139 V4 {
141 address: Ipv4Addr,
143 port: u16,
145 scope_id: u32,
147 },
148 V6 {
150 address: Ipv6Addr,
152 port: u16,
154 scope_id: u32,
156 },
157}
158
159impl ScopedSocketAddr {
160 pub fn new(address: IpAddr, port: u16, scope_id: u32) -> Self {
162 match address {
163 IpAddr::V4(address) => Self::V4 {
164 address,
165 port,
166 scope_id,
167 },
168 IpAddr::V6(address) => Self::V6 {
169 address,
170 port,
171 scope_id,
172 },
173 }
174 }
175}
176
177impl From<ScopedSocketAddr> for SocketAddr {
178 fn from(scoped_addr: ScopedSocketAddr) -> Self {
179 match scoped_addr {
180 ScopedSocketAddr::V4 { address, port, .. } => {
181 Self::V4(SocketAddrV4::new(address, port))
183 },
184 ScopedSocketAddr::V6 {
185 address,
186 port,
187 scope_id,
188 } => Self::V6(SocketAddrV6::new(address, port, 0, scope_id)),
189 }
190 }
191}
192
193impl From<ScopedSocketAddr> for SocketAddrV6 {
194 fn from(scoped_addr: ScopedSocketAddr) -> Self {
195 match scoped_addr {
196 ScopedSocketAddr::V4 {
197 address,
198 port,
199 scope_id,
200 } => Self::new(address.to_ipv6_mapped(), port, 0, scope_id),
201 ScopedSocketAddr::V6 {
202 address,
203 port,
204 scope_id,
205 } => Self::new(address, port, 0, scope_id),
206 }
207 }
208}
209
210impl fmt::Display for ScopedSocketAddr {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 match self {
213 Self::V4 {
214 address,
215 port,
216 scope_id: 0,
217 } => write!(f, "{}:{}", address, port),
218 Self::V4 {
219 address,
220 port,
221 scope_id,
222 } => write!(f, "[{}%{}]:{}", address, scope_id, port),
223 Self::V6 {
224 address,
225 port,
226 scope_id: 0,
227 } => write!(f, "[{}]:{}", address, port),
228 Self::V6 {
229 address,
230 port,
231 scope_id,
232 } => write!(f, "[{}%{}]:{}", address, scope_id, port),
233 }
234 }
235}
236
237impl fmt::Debug for ScopedSocketAddr {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 fmt::Display::fmt(self, f)
240 }
241}
242
243#[doc(alias = "DNSServiceQueryRecord")]
249pub fn resolve_host_extended(host: &str, port: u16, data: ResolveHostData) -> ResolveHost {
250 let qrdata = QueryRecordData {
251 flags: data.flags,
252 interface: data.interface,
253 rr_class: Class::IN,
254 ..Default::default()
255 };
256
257 let inner_v6 = query_record_extended(host, Type::AAAA, qrdata)
258 .try_filter_map(move |addr| async move { Ok(decode_aaaa(addr, port)) });
259 let inner_v4 = query_record_extended(host, Type::A, qrdata)
260 .try_filter_map(move |addr| async move { Ok(decode_a(addr, port)) });
261 let inner = Box::pin(futures_util::stream::select(inner_v6, inner_v4));
262
263 ResolveHost { inner }
264}