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 query_record_extended,
32 QueryRecordData,
33 QueryRecordFlags,
34 QueryRecordResult,
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(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
117 self.inner.poll_next_unpin(cx)
118 }
119}
120
121#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
125pub struct ResolveHostResult {
126 pub flags: ResolvedHostFlags,
128 pub address: ScopedSocketAddr,
130}
131
132#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137pub enum ScopedSocketAddr {
138 V4 {
140 address: Ipv4Addr,
142 port: u16,
144 scope_id: u32,
146 },
147 V6 {
149 address: Ipv6Addr,
151 port: u16,
153 scope_id: u32,
155 },
156}
157
158impl ScopedSocketAddr {
159 pub fn new(address: IpAddr, port: u16, scope_id: u32) -> Self {
161 match address {
162 IpAddr::V4(address) => Self::V4 {
163 address,
164 port,
165 scope_id,
166 },
167 IpAddr::V6(address) => Self::V6 {
168 address,
169 port,
170 scope_id,
171 },
172 }
173 }
174}
175
176impl From<ScopedSocketAddr> for SocketAddr {
177 fn from(scoped_addr: ScopedSocketAddr) -> Self {
178 match scoped_addr {
179 ScopedSocketAddr::V4 { address, port, .. } => {
180 Self::V4(SocketAddrV4::new(address, port))
182 },
183 ScopedSocketAddr::V6 {
184 address,
185 port,
186 scope_id,
187 } => Self::V6(SocketAddrV6::new(address, port, 0, scope_id)),
188 }
189 }
190}
191
192impl From<ScopedSocketAddr> for SocketAddrV6 {
193 fn from(scoped_addr: ScopedSocketAddr) -> Self {
194 match scoped_addr {
195 ScopedSocketAddr::V4 {
196 address,
197 port,
198 scope_id,
199 } => Self::new(address.to_ipv6_mapped(), port, 0, scope_id),
200 ScopedSocketAddr::V6 {
201 address,
202 port,
203 scope_id,
204 } => Self::new(address, port, 0, scope_id),
205 }
206 }
207}
208
209impl fmt::Display for ScopedSocketAddr {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::V4 {
213 address,
214 port,
215 scope_id: 0,
216 } => write!(f, "{}:{}", address, port),
217 Self::V4 {
218 address,
219 port,
220 scope_id,
221 } => write!(f, "[{}%{}]:{}", address, scope_id, port),
222 Self::V6 {
223 address,
224 port,
225 scope_id: 0,
226 } => write!(f, "[{}]:{}", address, port),
227 Self::V6 {
228 address,
229 port,
230 scope_id,
231 } => write!(f, "[{}%{}]:{}", address, scope_id, port),
232 }
233 }
234}
235
236impl fmt::Debug for ScopedSocketAddr {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 fmt::Display::fmt(self, f)
239 }
240}
241
242#[doc(alias = "DNSServiceQueryRecord")]
248pub fn resolve_host_extended(host: &str, port: u16, data: ResolveHostData) -> ResolveHost {
249 let qrdata = QueryRecordData {
250 flags: data.flags,
251 interface: data.interface,
252 rr_class: Class::IN,
253 ..Default::default()
254 };
255
256 let inner_v6 = query_record_extended(host, Type::AAAA, qrdata)
257 .try_filter_map(move |addr| async move { Ok(decode_aaaa(addr, port)) });
258 let inner_v4 = query_record_extended(host, Type::A, qrdata)
259 .try_filter_map(move |addr| async move { Ok(decode_a(addr, port)) });
260 let inner = Box::pin(futures_util::stream::select(inner_v6, inner_v4));
261
262 ResolveHost { inner }
263}