dns_resolver/
main.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error};
6use async_trait::async_trait;
7use dns::async_resolver::{Resolver, Spawner};
8use dns::config::{ServerList, UpdateServersResult};
9use fidl_fuchsia_net_name::{
10    self as fname, LookupAdminRequest, LookupAdminRequestStream, LookupRequest, LookupRequestStream,
11};
12use fuchsia_component::server::{ServiceFs, ServiceFsDir};
13use fuchsia_sync::RwLock;
14use futures::channel::mpsc;
15use futures::lock::Mutex;
16use futures::{FutureExt as _, SinkExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _};
17use log::{debug, error, info, warn};
18use net_declare::fidl_ip_v6;
19use net_types::ip::IpAddress;
20use std::collections::{BTreeMap, HashMap, VecDeque};
21use std::convert::TryFrom as _;
22use std::hash::{Hash, Hasher};
23use std::net::IpAddr;
24use std::num::NonZeroUsize;
25use std::rc::Rc;
26use std::str::FromStr as _;
27use std::sync::Arc;
28use trust_dns_proto::op::ResponseCode;
29use trust_dns_proto::rr::domain::IntoName;
30use trust_dns_proto::rr::{RData, RecordType};
31use trust_dns_resolver::config::{
32    LookupIpStrategy, NameServerConfig, NameServerConfigGroup, Protocol, ResolverConfig,
33    ResolverOpts, ServerOrderingStrategy,
34};
35use trust_dns_resolver::error::{ResolveError, ResolveErrorKind};
36use trust_dns_resolver::lookup;
37use unicode_xid::UnicodeXID as _;
38use {
39    fidl_fuchsia_net as fnet, fidl_fuchsia_net_ext as net_ext,
40    fidl_fuchsia_net_routes as fnet_routes, fuchsia_async as fasync,
41};
42
43struct SharedResolver<T>(RwLock<Rc<T>>);
44
45impl<T> SharedResolver<T> {
46    fn new(resolver: T) -> Self {
47        SharedResolver(RwLock::new(Rc::new(resolver)))
48    }
49
50    fn read(&self) -> Rc<T> {
51        let Self(inner) = self;
52        inner.read().clone()
53    }
54
55    fn write(&self, other: Rc<T>) {
56        let Self(inner) = self;
57        *inner.write() = other;
58    }
59}
60
61const STAT_WINDOW_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(60);
62const STAT_WINDOW_COUNT: usize = 30;
63
64/// Stats about queries during the last `STAT_WINDOW_COUNT` windows of
65/// `STAT_WINDOW_DURATION` time.
66///
67/// For example, if `STAT_WINDOW_DURATION` == 1 minute, and
68/// `STAT_WINDOW_COUNT` == 30, `past_queries` contains information about, at
69/// most, 30 one-minute windows of completed queries.
70///
71/// NB: there is no guarantee that these windows are directly consecutive; only
72/// that each window begins at least `STAT_WINDOW_DURATION` after the previous
73/// window's start time.
74struct QueryStats {
75    inner: Mutex<VecDeque<QueryWindow>>,
76}
77
78/// Relevant info to be recorded about a completed query. The `Ok` variant
79/// contains the number of addresses in the response, and the `Err` variant
80/// contains the kind of error that was encountered.
81type QueryResult<'a> = Result<NonZeroUsize, &'a ResolveErrorKind>;
82
83impl QueryStats {
84    fn new() -> Self {
85        Self { inner: Mutex::new(VecDeque::new()) }
86    }
87
88    async fn finish_query(&self, start_time: fasync::MonotonicInstant, result: QueryResult<'_>) {
89        let end_time = fasync::MonotonicInstant::now();
90        let finish = move |window: &mut QueryWindow| {
91            let elapsed_time = end_time - start_time;
92            match result {
93                Ok(num_addrs) => window.succeed(elapsed_time, num_addrs),
94                Err(e) => window.fail(elapsed_time, e),
95            }
96        };
97
98        let Self { inner } = self;
99        let past_queries = &mut *inner.lock().await;
100
101        let current_window = past_queries.back_mut().and_then(|window| {
102            let QueryWindow { start, .. } = window;
103            (end_time - *start < STAT_WINDOW_DURATION).then_some(window)
104        });
105
106        match current_window {
107            Some(window) => finish(window),
108            None => {
109                if past_queries.len() == STAT_WINDOW_COUNT {
110                    // Remove the oldest window of query stats.
111                    let _: QueryWindow = past_queries
112                        .pop_front()
113                        .expect("there should be at least one element in `past_queries`");
114                }
115                let mut window = QueryWindow::new(end_time);
116                finish(&mut window);
117                past_queries.push_back(window);
118            }
119        }
120    }
121}
122
123#[derive(Debug)]
124struct HashableResponseCode {
125    response_code: ResponseCode,
126}
127
128impl Hash for HashableResponseCode {
129    fn hash<H: Hasher>(&self, state: &mut H) {
130        let HashableResponseCode { response_code } = self;
131        u16::from(*response_code).hash(state)
132    }
133}
134
135// Hand-implemented because of clippy's derive_hash_xor_eq lint.
136impl PartialEq for HashableResponseCode {
137    fn eq(&self, other: &Self) -> bool {
138        let HashableResponseCode { response_code } = self;
139        let HashableResponseCode { response_code: other } = other;
140        response_code.eq(other)
141    }
142}
143
144impl Eq for HashableResponseCode {}
145
146impl From<ResponseCode> for HashableResponseCode {
147    fn from(response_code: ResponseCode) -> Self {
148        HashableResponseCode { response_code }
149    }
150}
151
152#[derive(Default, Debug, PartialEq)]
153struct NoRecordsFoundStats {
154    response_code_counts: HashMap<HashableResponseCode, u64>,
155}
156
157impl NoRecordsFoundStats {
158    fn increment(&mut self, response_code: &ResponseCode) {
159        let NoRecordsFoundStats { response_code_counts } = self;
160        let count = response_code_counts.entry((*response_code).into()).or_insert(0);
161        *count += 1
162    }
163}
164
165#[derive(Default, Debug, PartialEq)]
166struct UnhandledResolveErrorKindStats {
167    resolve_error_kind_counts: HashMap<String, u64>,
168}
169
170impl UnhandledResolveErrorKindStats {
171    /// Increments the counter for the given error kind. Returns a string
172    /// containing the name of that kind so the caller doesn't have to perform
173    /// the processing if they want to log it.
174    fn increment(&mut self, resolve_error_kind: &ResolveErrorKind) -> String {
175        let Self { resolve_error_kind_counts } = self;
176        let truncated_debug = enum_variant_string(resolve_error_kind);
177        let count = resolve_error_kind_counts.entry(truncated_debug.clone()).or_insert(0);
178        *count += 1;
179
180        truncated_debug
181    }
182}
183
184/// Stats about queries that failed due to an internal trust-dns error.
185/// These counters map to variants of
186/// [`trust_dns_resolver::error::ResolveErrorKind`].
187#[derive(Default, Debug, PartialEq)]
188struct FailureStats {
189    message: u64,
190    no_connections: u64,
191    no_records_found: NoRecordsFoundStats,
192    io: u64,
193    proto: u64,
194    timeout: u64,
195    unhandled_resolve_error_kind: UnhandledResolveErrorKindStats,
196}
197
198impl FailureStats {
199    fn increment(&mut self, kind: &ResolveErrorKind) {
200        let FailureStats {
201            message,
202            no_connections,
203            no_records_found,
204            io,
205            proto,
206            timeout,
207            unhandled_resolve_error_kind,
208        } = self;
209
210        match kind {
211            ResolveErrorKind::Message(error) => {
212                let _: &str = error;
213                *message += 1
214            }
215            ResolveErrorKind::Msg(error) => {
216                let _: &String = error;
217                *message += 1
218            }
219            ResolveErrorKind::NoConnections => *no_connections += 1,
220            ResolveErrorKind::NoRecordsFound {
221                query: _,
222                soa: _,
223                negative_ttl: _,
224                response_code,
225                trusted: _,
226            } => no_records_found.increment(response_code),
227            ResolveErrorKind::Io(error) => {
228                let _: &std::io::Error = error;
229                *io += 1
230            }
231            ResolveErrorKind::Proto(error) => {
232                let _: &trust_dns_proto::error::ProtoError = error;
233                *proto += 1
234            }
235            ResolveErrorKind::Timeout => *timeout += 1,
236            // ResolveErrorKind is marked #[non_exhaustive] in trust-dns:
237            // https://github.com/bluejekyll/trust-dns/blob/v0.21.0-alpha.1/crates/resolver/src/error.rs#L29
238            // So we have to include a wildcard match.
239            // TODO(https://github.com/rust-lang/rust/issues/89554): remove once
240            // we're able to apply the non_exhaustive_omitted_patterns lint
241            kind => {
242                let variant = unhandled_resolve_error_kind.increment(kind);
243                error!("unhandled variant: {variant}");
244            }
245        }
246    }
247}
248
249struct QueryWindow {
250    start: fasync::MonotonicInstant,
251    success_count: u64,
252    failure_count: u64,
253    success_elapsed_time: zx::MonotonicDuration,
254    failure_elapsed_time: zx::MonotonicDuration,
255    failure_stats: FailureStats,
256    address_counts_histogram: BTreeMap<NonZeroUsize, u64>,
257}
258
259impl QueryWindow {
260    fn new(start: fasync::MonotonicInstant) -> Self {
261        Self {
262            start,
263            success_count: 0,
264            failure_count: 0,
265            success_elapsed_time: zx::MonotonicDuration::from_nanos(0),
266            failure_elapsed_time: zx::MonotonicDuration::from_nanos(0),
267            failure_stats: FailureStats::default(),
268            address_counts_histogram: Default::default(),
269        }
270    }
271
272    fn succeed(&mut self, elapsed_time: zx::MonotonicDuration, num_addrs: NonZeroUsize) {
273        let QueryWindow {
274            success_count,
275            success_elapsed_time,
276            address_counts_histogram: address_counts,
277            start: _,
278            failure_count: _,
279            failure_elapsed_time: _,
280            failure_stats: _,
281        } = self;
282        *success_count += 1;
283        *success_elapsed_time += elapsed_time;
284        *address_counts.entry(num_addrs).or_default() += 1;
285    }
286
287    fn fail(&mut self, elapsed_time: zx::MonotonicDuration, error: &ResolveErrorKind) {
288        let QueryWindow {
289            failure_count,
290            failure_elapsed_time,
291            failure_stats,
292            start: _,
293            success_count: _,
294            success_elapsed_time: _,
295            address_counts_histogram: _,
296        } = self;
297        *failure_count += 1;
298        *failure_elapsed_time += elapsed_time;
299        failure_stats.increment(error)
300    }
301}
302
303/// Returns the name of the enum variant that was set.
304fn enum_variant_string(variant: &impl std::fmt::Debug) -> String {
305    let debug = format!("{:?}", variant);
306    // We just want to keep the part of the debug string that indicates
307    // which enum variant this is.
308    // See https://doc.rust-lang.org/reference/identifiers.html
309    match debug.find(|c: char| !c.is_xid_continue() && !c.is_xid_start()) {
310        Some(i) => debug[..i].to_string(),
311        None => debug,
312    }
313}
314
315fn update_resolver<T: ResolverLookup>(resolver: &SharedResolver<T>, servers: ServerList) {
316    let mut resolver_opts = ResolverOpts::default();
317    // TODO(https://fxbug.dev/42053483): Set ip_strategy once a unified lookup API
318    // exists that respects this setting.
319    resolver_opts.num_concurrent_reqs = 10;
320    // TODO(https://github.com/bluejekyll/trust-dns/issues/1702): Use the
321    // default server ordering strategy once the algorithm is improved.
322    resolver_opts.server_ordering_strategy = ServerOrderingStrategy::UserProvidedOrder;
323
324    // We're going to add each server twice, once with protocol UDP and
325    // then with protocol TCP.
326    let mut name_servers = NameServerConfigGroup::with_capacity(servers.len() * 2);
327
328    name_servers.extend(servers.into_iter().flat_map(|server| {
329        let net_ext::SocketAddress(socket_addr) = server.into();
330        // Every server config gets UDP and TCP versions with
331        // preference for UDP.
332        std::iter::once(NameServerConfig {
333            socket_addr,
334            protocol: Protocol::Udp,
335            tls_dns_name: None,
336            trust_nx_responses: false,
337            bind_addr: None,
338        })
339        .chain(std::iter::once(NameServerConfig {
340            socket_addr,
341            protocol: Protocol::Tcp,
342            tls_dns_name: None,
343            trust_nx_responses: false,
344            bind_addr: None,
345        }))
346    }));
347
348    let new_resolver =
349        T::new(ResolverConfig::from_parts(None, Vec::new(), name_servers), resolver_opts);
350    let () = resolver.write(Rc::new(new_resolver));
351}
352
353enum IncomingRequest {
354    Lookup(LookupRequestStream),
355    LookupAdmin(LookupAdminRequestStream),
356}
357
358#[async_trait]
359trait ResolverLookup {
360    fn new(config: ResolverConfig, options: ResolverOpts) -> Self;
361
362    async fn lookup<N: IntoName + Send>(
363        &self,
364        name: N,
365        record_type: RecordType,
366    ) -> Result<lookup::Lookup, ResolveError>;
367
368    async fn reverse_lookup(&self, addr: IpAddr) -> Result<lookup::ReverseLookup, ResolveError>;
369}
370
371#[async_trait]
372impl ResolverLookup for Resolver {
373    fn new(config: ResolverConfig, options: ResolverOpts) -> Self {
374        Resolver::new(config, options, Spawner).expect("failed to create resolver")
375    }
376
377    async fn lookup<N: IntoName + Send>(
378        &self,
379        name: N,
380        record_type: RecordType,
381    ) -> Result<lookup::Lookup, ResolveError> {
382        self.lookup(name, record_type).await
383    }
384
385    async fn reverse_lookup(&self, addr: IpAddr) -> Result<lookup::ReverseLookup, ResolveError> {
386        self.reverse_lookup(addr).await
387    }
388}
389
390#[derive(Debug)]
391enum LookupIpErrorSource {
392    Ipv4,
393    Ipv6,
394    CanonicalName,
395}
396
397#[derive(Default)]
398struct LookupIpErrorsFromSource {
399    ipv4: Option<ResolveError>,
400    ipv6: Option<ResolveError>,
401    canonical_name: Option<ResolveError>,
402}
403
404impl LookupIpErrorsFromSource {
405    fn any_error(&self) -> Option<&ResolveError> {
406        let Self { ipv4, ipv6, canonical_name } = self;
407        ipv4.as_ref().or(ipv6.as_ref()).or(canonical_name.as_ref())
408    }
409
410    fn accumulate(&mut self, src: LookupIpErrorSource, error: ResolveError) {
411        let Self { ipv4, ipv6, canonical_name } = self;
412        let target = match src {
413            LookupIpErrorSource::Ipv4 => ipv4,
414            LookupIpErrorSource::Ipv6 => ipv6,
415            LookupIpErrorSource::CanonicalName => canonical_name,
416        };
417        debug_assert!(target.is_none(), "multiple errors observed for {src:?}");
418        *target = Some(error)
419    }
420
421    fn handle(self) -> fname::LookupError {
422        let Self { ipv4, ipv6, canonical_name } = self;
423        let mut ret = None;
424        for (src, err) in [
425            ("LookupIp(IPv4)", ipv4),
426            ("LookupIp(IPv6)", ipv6),
427            ("LookupIp(CanonicalName)", canonical_name),
428        ]
429        .into_iter()
430        .filter_map(|(src, err)| err.map(|e| (src, e)))
431        {
432            // We want to log all errors, but only convert one of them to the
433            // return. The fixed order IPv4, IPv6, CanonicalName is chosen to
434            // maximize the likelihood of the reported error being useful.
435            let err = handle_err(src, err);
436            if ret.is_none() {
437                ret = Some(err)
438            }
439        }
440        ret.unwrap_or(fname::LookupError::InternalError)
441    }
442}
443
444fn handle_err(source: &str, err: ResolveError) -> fname::LookupError {
445    use trust_dns_proto::error::ProtoErrorKind;
446
447    let (lookup_err, ioerr): (_, Option<(std::io::ErrorKind, _)>) = match err.kind() {
448        // The following mapping is based on the analysis of `ResolveError` enumerations.
449        // For cases that are not obvious such as `ResolveErrorKind::Msg` and
450        // `ResolveErrorKind::Message`, I (chunyingw) did code searches to have more insights.
451        // `ResolveErrorKind::Msg`: An error with arbitrary message, it could be ex. "lock was
452        // poisoned, this is non-recoverable" and ""DNS Error".
453        // `ResolveErrorKind::Message`: An error with arbitrary message, it is mostly returned when
454        // there is no name in the input vector to look up with "can not lookup for no names".
455        // This is a best-effort mapping.
456        ResolveErrorKind::NoRecordsFound {
457            query: _,
458            soa: _,
459            negative_ttl: _,
460            response_code: _,
461            trusted: _,
462        } => (fname::LookupError::NotFound, None),
463        ResolveErrorKind::Proto(err) => match err.kind() {
464            ProtoErrorKind::DomainNameTooLong(_) | ProtoErrorKind::EdnsNameNotRoot(_) => {
465                (fname::LookupError::InvalidArgs, None)
466            }
467            ProtoErrorKind::Busy | ProtoErrorKind::Canceled(_) | ProtoErrorKind::Timeout => {
468                (fname::LookupError::Transient, None)
469            }
470            ProtoErrorKind::Io(inner) => {
471                (fname::LookupError::Transient, Some((inner.kind(), inner.raw_os_error())))
472            }
473            ProtoErrorKind::BadQueryCount(_)
474            | ProtoErrorKind::CharacterDataTooLong { max: _, len: _ }
475            | ProtoErrorKind::LabelOverlapsWithOther { label: _, other: _ }
476            | ProtoErrorKind::DnsKeyProtocolNot3(_)
477            | ProtoErrorKind::FormError { header: _, error: _ }
478            | ProtoErrorKind::HmacInvalid()
479            | ProtoErrorKind::IncorrectRDataLengthRead { read: _, len: _ }
480            | ProtoErrorKind::LabelBytesTooLong(_)
481            | ProtoErrorKind::PointerNotPriorToLabel { idx: _, ptr: _ }
482            | ProtoErrorKind::MaxBufferSizeExceeded(_)
483            | ProtoErrorKind::Message(_)
484            | ProtoErrorKind::Msg(_)
485            | ProtoErrorKind::NoError
486            | ProtoErrorKind::NotAllRecordsWritten { count: _ }
487            | ProtoErrorKind::RrsigsNotPresent { name: _, record_type: _ }
488            | ProtoErrorKind::UnknownAlgorithmTypeValue(_)
489            | ProtoErrorKind::UnknownDnsClassStr(_)
490            | ProtoErrorKind::UnknownDnsClassValue(_)
491            | ProtoErrorKind::UnknownRecordTypeStr(_)
492            | ProtoErrorKind::UnknownRecordTypeValue(_)
493            | ProtoErrorKind::UnrecognizedLabelCode(_)
494            | ProtoErrorKind::UnrecognizedNsec3Flags(_)
495            | ProtoErrorKind::UnrecognizedCsyncFlags(_)
496            | ProtoErrorKind::Poisoned
497            | ProtoErrorKind::Ring(_)
498            | ProtoErrorKind::SSL(_)
499            | ProtoErrorKind::Timer
500            | ProtoErrorKind::UrlParsing(_)
501            | ProtoErrorKind::Utf8(_)
502            | ProtoErrorKind::FromUtf8(_)
503            | ProtoErrorKind::ParseInt(_) => (fname::LookupError::InternalError, None),
504            // ProtoErrorKind is marked #[non_exhaustive] in trust-dns:
505            // https://github.com/bluejekyll/trust-dns/blob/v0.21.0-alpha.1/crates/proto/src/error.rs#L66
506            // So we have to include a wildcard match.
507            kind => {
508                error!("unhandled variant {:?}", enum_variant_string(kind));
509                (fname::LookupError::InternalError, None)
510            }
511        },
512        ResolveErrorKind::Io(inner) => {
513            (fname::LookupError::Transient, Some((inner.kind(), inner.raw_os_error())))
514        }
515        ResolveErrorKind::Timeout => (fname::LookupError::Transient, None),
516        ResolveErrorKind::Msg(_)
517        | ResolveErrorKind::Message(_)
518        | ResolveErrorKind::NoConnections => (fname::LookupError::InternalError, None),
519        // ResolveErrorKind is marked #[non_exhaustive] in trust-dns:
520        // https://github.com/bluejekyll/trust-dns/blob/v0.21.0-alpha.1/crates/resolver/src/error.rs#L29
521        // So we have to include a wildcard match.
522        kind => {
523            error!("unhandled variant {:?}", enum_variant_string(kind));
524            (fname::LookupError::InternalError, None)
525        }
526    };
527
528    if let Some((ioerr, raw_os_error)) = ioerr {
529        match raw_os_error {
530            Some(libc::EHOSTUNREACH | libc::ENETUNREACH) => {
531                debug!("{} error: {:?}; (IO error {:?})", source, lookup_err, ioerr)
532            }
533            _ => warn!("{} error: {:?}; (IO error {:?})", source, lookup_err, ioerr),
534        }
535    } else {
536        warn!("{} error: {:?}", source, lookup_err);
537    }
538
539    lookup_err
540}
541
542async fn sort_preferred_addresses(
543    mut addrs: Vec<fnet::IpAddress>,
544    routes: &fnet_routes::StateProxy,
545) -> Result<Vec<fnet::IpAddress>, fname::LookupError> {
546    let mut addrs_info = futures::future::try_join_all(
547        addrs
548            // Drain addresses from addrs, but keep it alive so we don't need to
549            // reallocate.
550            .drain(..)
551            .map(|addr| async move {
552                let source_addr = match routes.resolve(&addr).await? {
553                    Ok(fnet_routes::Resolved::Direct(fnet_routes::Destination {
554                        source_address,
555                        ..
556                    }))
557                    | Ok(fnet_routes::Resolved::Gateway(fnet_routes::Destination {
558                        source_address,
559                        ..
560                    })) => source_address,
561                    // If resolving routes returns an error treat it as an
562                    // unreachable address.
563                    Err(e) => {
564                        debug!(
565                            "fuchsia.net.routes/State.resolve({}) failed {}",
566                            net_ext::IpAddress::from(addr),
567                            zx::Status::from_raw(e)
568                        );
569                        None
570                    }
571                };
572                Ok((addr, DasCmpInfo::from_addrs(&addr, source_addr.as_ref())))
573            }),
574    )
575    .await
576    .map_err(|e: fidl::Error| {
577        warn!("fuchsia.net.routes/State.resolve FIDL error {:?}", e);
578        fname::LookupError::InternalError
579    })?;
580
581    let () = addrs_info.sort_by(|(_laddr, left), (_raddr, right)| left.cmp(right));
582    // Reinsert the addresses in order from addr_info.
583    let () = addrs.extend(addrs_info.into_iter().map(|(addr, _)| addr));
584    Ok(addrs)
585}
586
587#[derive(Debug)]
588struct Policy {
589    prefix: net_types::ip::Subnet<net_types::ip::Ipv6Addr>,
590    precedence: usize,
591    label: usize,
592}
593
594macro_rules! decl_policy {
595    ($ip:tt/$prefix:expr => $precedence:expr, $label:expr) => {
596        Policy {
597            // Unsafe allows us to declare constant subnets.
598            // We make sure no invalid subnets are created in
599            // test_valid_policy_table.
600            prefix: unsafe {
601                net_types::ip::Subnet::new_unchecked(
602                    net_types::ip::Ipv6Addr::from_bytes(fidl_ip_v6!($ip).addr),
603                    $prefix,
604                )
605            },
606            precedence: $precedence,
607            label: $label,
608        }
609    };
610}
611
612/// Policy table is defined in RFC 6724, section 2.1
613///
614/// A more human-readable version:
615///
616///  Prefix        Precedence Label
617///  ::1/128               50     0
618///  ::/0                  40     1
619///  ::ffff:0:0/96         35     4
620///  2002::/16             30     2
621///  2001::/32              5     5
622///  fc00::/7               3    13
623///  ::/96                  1     3
624///  fec0::/10              1    11
625///  3ffe::/16              1    12
626///
627/// We willingly left out ::/96, fec0::/10, 3ffe::/16 since those prefix
628/// assignments are deprecated.
629///
630/// The table is sorted by prefix length so longest-prefix match can be easily
631/// achieved.
632const POLICY_TABLE: [Policy; 6] = [
633    decl_policy!("::1"/128 => 50, 0),
634    decl_policy!("::ffff:0:0"/96 => 35, 4),
635    decl_policy!("2001::"/32 => 5, 5),
636    decl_policy!("2002::"/16 => 30, 2),
637    decl_policy!("fc00::"/7 => 3, 13),
638    decl_policy!("::"/0 => 40, 1),
639];
640
641fn policy_lookup(addr: &net_types::ip::Ipv6Addr) -> &'static Policy {
642    POLICY_TABLE
643        .iter()
644        .find(|policy| policy.prefix.contains(addr))
645        .expect("policy table MUST contain the all addresses subnet")
646}
647
648/// Destination Address selection information.
649///
650/// `DasCmpInfo` provides an implementation of a subset of Destination Address
651/// Selection according to the sorting rules defined in [RFC 6724 Section 6].
652///
653/// TODO(https://fxbug.dev/42143905): Implement missing rules 3, 4, and 7.
654/// Rules 3, 4, and 7 are omitted for compatibility with the equivalent
655/// implementation in Fuchsia's libc.
656///
657/// `DasCmpInfo` provides an [`std::cmp::Ord`] implementation that will return
658/// preferred addresses as "lesser" values.
659///
660/// [RFC 6724 Section 6]: https://tools.ietf.org/html/rfc6724#section-6
661#[derive(Debug)]
662struct DasCmpInfo {
663    usable: bool,
664    matching_scope: bool,
665    matching_label: bool,
666    precedence: usize,
667    scope: net_types::ip::Ipv6Scope,
668    common_prefix_len: u8,
669}
670
671impl DasCmpInfo {
672    /// Helper function to convert a FIDL IP address into
673    /// [`net_types::ip::Ipv6Addr`], using a mapped IPv4 when that's the case.
674    fn convert_addr(fidl: &fnet::IpAddress) -> net_types::ip::Ipv6Addr {
675        match fidl {
676            fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => {
677                net_types::ip::Ipv6Addr::from(net_types::ip::Ipv4Addr::new(*addr))
678            }
679            fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => {
680                net_types::ip::Ipv6Addr::from_bytes(*addr)
681            }
682        }
683    }
684
685    fn from_addrs(dst_addr: &fnet::IpAddress, src_addr: Option<&fnet::IpAddress>) -> Self {
686        use net_types::ScopeableAddress;
687
688        let dst_addr = Self::convert_addr(dst_addr);
689        let Policy { prefix: _, precedence, label: dst_label } = policy_lookup(&dst_addr);
690        let (usable, matching_scope, matching_label, common_prefix_len) = match src_addr {
691            Some(src_addr) => {
692                let src_addr = Self::convert_addr(src_addr);
693                let Policy { prefix: _, precedence: _, label: src_label } =
694                    policy_lookup(&src_addr);
695                (
696                    true,
697                    dst_addr.scope() == src_addr.scope(),
698                    dst_label == src_label,
699                    dst_addr.common_prefix_len(&src_addr),
700                )
701            }
702            None => (false, false, false, 0),
703        };
704        DasCmpInfo {
705            usable,
706            matching_scope,
707            matching_label,
708            precedence: *precedence,
709            scope: dst_addr.scope(),
710            common_prefix_len,
711        }
712    }
713}
714
715impl std::cmp::Ord for DasCmpInfo {
716    // TODO(https://fxbug.dev/42143905): Implement missing rules 3, 4, and 7.
717    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
718        use std::cmp::Ordering;
719        let DasCmpInfo {
720            usable: self_usable,
721            matching_scope: self_matching_scope,
722            matching_label: self_matching_label,
723            precedence: self_precedence,
724            scope: self_scope,
725            common_prefix_len: self_common_prefix_len,
726        } = self;
727        let DasCmpInfo {
728            usable: other_usable,
729            matching_scope: other_matching_scope,
730            matching_label: other_matching_label,
731            precedence: other_precedence,
732            scope: other_scope,
733            common_prefix_len: other_common_prefix_len,
734        } = other;
735
736        fn prefer_true(left: bool, right: bool) -> Ordering {
737            match (left, right) {
738                (true, false) => Ordering::Less,
739                (false, true) => Ordering::Greater,
740                (false, false) | (true, true) => Ordering::Equal,
741            }
742        }
743
744        // Rule 1: Avoid unusable destinations.
745        prefer_true(*self_usable, *other_usable)
746            .then(
747                // Rule 2: Prefer matching scope.
748                prefer_true(*self_matching_scope, *other_matching_scope),
749            )
750            .then(
751                // Rule 5: Prefer matching label.
752                prefer_true(*self_matching_label, *other_matching_label),
753            )
754            .then(
755                // Rule 6: Prefer higher precedence.
756                self_precedence.cmp(other_precedence).reverse(),
757            )
758            .then(
759                // Rule 8: Prefer smaller scope.
760                self_scope.multicast_scope_id().cmp(&other_scope.multicast_scope_id()),
761            )
762            .then(
763                // Rule 9: Use longest matching prefix.
764                self_common_prefix_len.cmp(other_common_prefix_len).reverse(),
765            )
766        // Rule 10: Otherwise, leave the order unchanged.
767    }
768}
769
770impl std::cmp::PartialOrd for DasCmpInfo {
771    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
772        Some(self.cmp(other))
773    }
774}
775
776impl std::cmp::PartialEq for DasCmpInfo {
777    fn eq(&self, other: &Self) -> bool {
778        self.cmp(other) == std::cmp::Ordering::Equal
779    }
780}
781
782impl std::cmp::Eq for DasCmpInfo {}
783
784async fn handle_lookup_hostname<T: ResolverLookup>(
785    resolver: &SharedResolver<T>,
786    addr: fnet::IpAddress,
787) -> Result<String, fname::LookupError> {
788    let net_ext::IpAddress(addr) = addr.into();
789    let resolver = resolver.read();
790
791    match resolver.reverse_lookup(addr).await {
792        Ok(response) => {
793            response.iter().next().ok_or(fname::LookupError::NotFound).map(ToString::to_string)
794        }
795        Err(error) => Err(handle_err("LookupHostname", error)),
796    }
797}
798
799struct IpLookupRequest {
800    hostname: String,
801    options: fname::LookupIpOptions,
802    responder: fname::LookupLookupIpResponder,
803}
804
805async fn run_lookup<T: ResolverLookup>(
806    resolver: &SharedResolver<T>,
807    stream: LookupRequestStream,
808    sender: mpsc::Sender<IpLookupRequest>,
809) -> Result<(), fidl::Error> {
810    stream
811        .try_for_each_concurrent(None, |request| async {
812            match request {
813                LookupRequest::LookupIp { hostname, options, responder } => {
814                    let () = sender
815                        .clone()
816                        .send(IpLookupRequest { hostname, options, responder })
817                        .await
818                        .expect("receiver should not be closed");
819                    Ok(())
820                }
821                LookupRequest::LookupHostname { addr, responder } => responder
822                    .send(handle_lookup_hostname(&resolver, addr).await.as_deref().map_err(|e| *e)),
823            }
824        })
825        .await
826}
827
828const MAX_PARALLEL_REQUESTS: usize = 256;
829
830fn create_ip_lookup_fut<T: ResolverLookup>(
831    resolver: &SharedResolver<T>,
832    stats: Arc<QueryStats>,
833    routes: fnet_routes::StateProxy,
834    recv: mpsc::Receiver<IpLookupRequest>,
835) -> impl futures::Future<Output = ()> + '_ {
836    recv.for_each_concurrent(
837        MAX_PARALLEL_REQUESTS,
838        move |IpLookupRequest { hostname, options, responder }| {
839            let stats = stats.clone();
840            let routes = routes.clone();
841            async move {
842                let fname::LookupIpOptions {
843                    ipv4_lookup,
844                    ipv6_lookup,
845                    sort_addresses,
846                    canonical_name_lookup,
847                    ..
848                } = options;
849                let ipv4_lookup = ipv4_lookup.unwrap_or(false);
850                let ipv6_lookup = ipv6_lookup.unwrap_or(false);
851                let sort_addresses = sort_addresses.unwrap_or(false);
852                let canonical_name_lookup = canonical_name_lookup.unwrap_or(false);
853                let lookup_result = (|| async {
854                    let hostname = hostname.as_str();
855                    // The [`IntoName`] implementation for &str does not
856                    // properly reject IPv4 addresses in accordance with RFC
857                    // 1123 section 2.1:
858                    //
859                    //   If a dotted-decimal number can be entered without such
860                    //   identifying delimiters, then a full syntactic check must be
861                    //   made, because a segment of a host domain name is now allowed
862                    //   to begin with a digit and could legally be entirely numeric
863                    //   (see Section 6.1.2.4).  However, a valid host name can never
864                    //   have the dotted-decimal form #.#.#.#, since at least the
865                    //   highest-level component label will be alphabetic.
866                    //
867                    // Thus we explicitly reject such input here.
868                    //
869                    // TODO(https://github.com/bluejekyll/trust-dns/issues/1725):
870                    // Remove this when the implementation is sufficiently
871                    // strict.
872                    match IpAddr::from_str(hostname) {
873                        Ok(addr) => {
874                            let _: IpAddr = addr;
875                            return Err(fname::LookupError::InvalidArgs);
876                        }
877                        Err(std::net::AddrParseError { .. }) => {}
878                    };
879                    let resolver = resolver.read();
880                    let start_time = fasync::MonotonicInstant::now();
881                    let (ret1, ret2, ret3) = futures::future::join3(
882                        futures::future::OptionFuture::from(
883                            ipv4_lookup.then(|| {
884                                resolver
885                                    .lookup(hostname, RecordType::A)
886                                    .map_err(|e| (LookupIpErrorSource::Ipv4, e))
887                            }),
888                        ),
889                        futures::future::OptionFuture::from(
890                            ipv6_lookup.then(|| {
891                                resolver
892                                    .lookup(hostname, RecordType::AAAA)
893                                    .map_err(|e| (LookupIpErrorSource::Ipv6, e))
894                            }),
895                        ),
896                        futures::future::OptionFuture::from(
897                            canonical_name_lookup
898                                .then(|| {
899                                    resolver
900                                        .lookup(hostname, RecordType::CNAME)
901                                        .map_err(|e| (LookupIpErrorSource::CanonicalName, e))
902                                }),
903                        ),
904                    )
905                    .await;
906                    let result = [ret1, ret2, ret3];
907                    if result.iter().all(Option::is_none) {
908                        return Err(fname::LookupError::InvalidArgs);
909                    }
910                    let (addrs, cnames, error) =
911                        result.into_iter().filter_map(std::convert::identity).fold(
912                            (Vec::new(), Vec::new(), LookupIpErrorsFromSource::default()),
913                            |(mut addrs, mut cnames, mut error), result| {
914                                let () = match result {
915                                    Err((src, err)) => {
916                                        error.accumulate(src, err);
917                                    },
918                                    Ok(lookup) => lookup.iter().for_each(|rdata| match rdata {
919                                        RData::A(addr) if ipv4_lookup => addrs
920                                            .push(net_ext::IpAddress(IpAddr::V4(*addr)).into()),
921                                        RData::AAAA(addr) if ipv6_lookup => addrs
922                                            .push(net_ext::IpAddress(IpAddr::V6(*addr)).into()),
923                                        RData::CNAME(name) => {
924                                            // CNAME records are known to be present with other
925                                            // query types; avoid logging in that case.
926                                            if canonical_name_lookup {
927                                                cnames.push(name.to_utf8())
928                                            }
929                                        }
930                                        rdata => {
931                                            error!(
932                                                "Lookup(_, {:?}) yielded unexpected record type: {}",
933                                                options, rdata.to_record_type(),
934                                            )
935                                        }
936                                    }),
937                                };
938                            (addrs, cnames, error)
939                        });
940                    let count = match NonZeroUsize::try_from(addrs.len() + cnames.len()) {
941                        Ok(count) => Ok(count),
942                        Err(std::num::TryFromIntError { .. }) => match error.any_error() {
943                            None => {
944                                // TODO(https://fxbug.dev/42062388): Remove this
945                                // once Trust-DNS enforces that all responses
946                                // with no records return a `NoRecordsFound`
947                                // error.
948                                //
949                                // Note that returning here means that query
950                                // stats for inspect will not get logged. This
951                                // is ok since this case should be rare and is
952                                // considered to be temporary. Moreover, the
953                                // failed query counters are based on the
954                                // `ResolverError::kind`, which isn't applicable
955                                // here.
956                                error!("resolver response unexpectedly contained no records \
957                                        and no error. See https://fxbug.dev/42062388.");
958                                return Err(fname::LookupError::NotFound);
959                            },
960                            Some(any_err) => {
961                                Err(any_err)
962                            }
963                        }
964                    };
965                    let () = stats
966                        .finish_query(
967                            start_time,
968                            count.as_ref().copied().map_err(|e| e.kind()),
969                        )
970                        .await;
971                    match count {
972                        Ok(_) => {},
973                        Err(_any_err) => {
974                            // Handle all the errors instead of just the one used for stats.
975                            return Err(error.handle());
976                        }
977                    }
978                    let addrs = if sort_addresses {
979                        sort_preferred_addresses(addrs, &routes).await?
980                    } else {
981                        addrs
982                    };
983                    let addrs = if addrs.len() > fname::MAX_ADDRESSES.into() {
984                        warn!(
985                            "Lookup(_, {:?}): {} addresses, truncating to {}",
986                            options, addrs.len(), fname::MAX_ADDRESSES
987                        );
988                        let mut addrs = addrs;
989                        addrs.truncate(fname::MAX_ADDRESSES.into());
990                        addrs
991                    } else {
992                        addrs
993                    };
994                    // Per RFC 1034 section 3.6.2:
995                    //
996                    //   If a CNAME RR is present at a node, no other data should be present; this
997                    //   ensures that the data for a canonical name and its aliases cannot be
998                    //   different.  This rule also insures that a cached CNAME can be used without
999                    //   checking with an authoritative server for other RR types.
1000                    if cnames.len() > 1 {
1001                        let cnames =
1002                            cnames.iter().fold(HashMap::<&str, usize>::new(), |mut acc, cname| {
1003                                *acc.entry(cname).or_default() += 1;
1004                                acc
1005                            });
1006                        warn!(
1007                            "Lookup(_, {:?}): multiple CNAMEs: {:?}",
1008                            options, cnames
1009                        )
1010                    }
1011                    let cname = {
1012                        let mut cnames = cnames;
1013                        cnames.pop()
1014                    };
1015                    Ok(fname::LookupResult {
1016                        addresses: Some(addrs),
1017                        canonical_name: cname,
1018                        ..Default::default()
1019                    })
1020                })()
1021                .await;
1022                responder.send(lookup_result.as_ref().map_err(|e| *e)).unwrap_or_else(|e|
1023                    warn!(
1024                        "failed to send IP lookup result due to FIDL error: {}",
1025                        e
1026                    )
1027                )
1028            }
1029        },
1030    )
1031}
1032
1033/// Serves `stream` and forwards received configurations to `sink`.
1034async fn run_lookup_admin<T: ResolverLookup>(
1035    resolver: &SharedResolver<T>,
1036    state: &dns::config::ServerConfigState,
1037    stream: LookupAdminRequestStream,
1038) -> Result<(), fidl::Error> {
1039    stream
1040        .try_for_each(|req| async {
1041            match req {
1042                LookupAdminRequest::SetDnsServers { servers, responder } => {
1043                    let response = match state.update_servers(servers) {
1044                        UpdateServersResult::Updated(servers) => {
1045                            let () = update_resolver(resolver, servers);
1046                            Ok(())
1047                        }
1048                        UpdateServersResult::NoChange => Ok(()),
1049                        UpdateServersResult::InvalidsServers => {
1050                            Err(zx::Status::INVALID_ARGS.into_raw())
1051                        }
1052                    };
1053                    let () = responder.send(response)?;
1054                }
1055                LookupAdminRequest::GetDnsServers { responder } => {
1056                    let () = responder.send(&state.servers())?;
1057                }
1058            }
1059            Ok(())
1060        })
1061        .await
1062}
1063
1064/// Adds a [`dns::policy::ServerConfigState`] inspection child node to
1065/// `parent`.
1066fn add_config_state_inspect(
1067    parent: &fuchsia_inspect::Node,
1068    config_state: Arc<dns::config::ServerConfigState>,
1069) -> fuchsia_inspect::LazyNode {
1070    parent.create_lazy_child("servers", move || {
1071        let config_state = config_state.clone();
1072        async move {
1073            let srv = fuchsia_inspect::Inspector::default();
1074            let server_list = config_state.servers();
1075            for (i, server) in server_list.into_iter().enumerate() {
1076                let child = srv.root().create_child(format!("{}", i));
1077                let net_ext::SocketAddress(addr) = server.into();
1078                let () = child.record_string("address", format!("{}", addr));
1079                let () = srv.root().record(child);
1080            }
1081            Ok(srv)
1082        }
1083        .boxed()
1084    })
1085}
1086
1087/// Adds a [`QueryStats`] inspection child node to `parent`.
1088fn add_query_stats_inspect(
1089    parent: &fuchsia_inspect::Node,
1090    stats: Arc<QueryStats>,
1091) -> fuchsia_inspect::LazyNode {
1092    parent.create_lazy_child("query_stats", move || {
1093        let stats = stats.clone();
1094        async move {
1095            let past_queries = &*stats.inner.lock().await;
1096            let node = fuchsia_inspect::Inspector::default();
1097            for (
1098                i,
1099                QueryWindow {
1100                    start,
1101                    success_count,
1102                    failure_count,
1103                    success_elapsed_time,
1104                    failure_elapsed_time,
1105                    failure_stats,
1106                    address_counts_histogram,
1107                },
1108            ) in past_queries.iter().enumerate()
1109            {
1110                let child = node.root().create_child(format!("window {}", i + 1));
1111
1112                match u64::try_from(start.into_nanos()) {
1113                    Ok(nanos) => {
1114                        let () = child.record_uint("start_time_nanos", nanos);
1115                    },
1116                    Err(e) => warn!(
1117                        "error computing `start_time_nanos`: {:?}.into_nanos() from i64 -> u64 failed: {}",
1118                        start, e
1119                    ),
1120                }
1121                let () = child.record_uint("successful_queries", *success_count);
1122                let () = child.record_uint("failed_queries", *failure_count);
1123                let record_average = |name: &str, total: zx::MonotonicDuration, count: u64| {
1124                    // Don't record an average if there are no stats.
1125                    if count == 0 {
1126                        return;
1127                    }
1128                    match u64::try_from(total.into_micros()) {
1129                        Ok(micros) => child.record_uint(name, micros / count),
1130                        Err(e) => warn!(
1131                            "error computing `{}`: {:?}.into_micros() from i64 -> u64 failed: {}",
1132                            name, success_elapsed_time, e
1133                        ),
1134                    }
1135                };
1136                let () = record_average(
1137                    "average_success_duration_micros",
1138                    *success_elapsed_time,
1139                    *success_count,
1140                );
1141                let () = record_average(
1142                    "average_failure_duration_micros",
1143                    *failure_elapsed_time,
1144                    *failure_count,
1145                );
1146                let FailureStats {
1147                    message,
1148                    no_connections,
1149                    no_records_found: NoRecordsFoundStats {
1150                        response_code_counts,
1151                    },
1152                    io,
1153                    proto,
1154                    timeout,
1155                    unhandled_resolve_error_kind: UnhandledResolveErrorKindStats {
1156                        resolve_error_kind_counts,
1157                    },
1158                } = failure_stats;
1159                let errors = child.create_child("errors");
1160                let () = errors.record_uint("Message", *message);
1161                let () = errors.record_uint("NoConnections", *no_connections);
1162                let () = errors.record_uint("Io", *io);
1163                let () = errors.record_uint("Proto", *proto);
1164                let () = errors.record_uint("Timeout", *timeout);
1165
1166                let no_records_found_response_codes =
1167                    errors.create_child("NoRecordsFoundResponseCodeCounts");
1168                for (HashableResponseCode { response_code }, count) in response_code_counts {
1169                    let () = no_records_found_response_codes.record_uint(
1170                        format!("{:?}", response_code),
1171                        *count,
1172                    );
1173                }
1174                let () = errors.record(no_records_found_response_codes);
1175
1176                let unhandled_resolve_error_kinds =
1177                    errors.create_child("UnhandledResolveErrorKindCounts");
1178                for (error_kind, count) in resolve_error_kind_counts {
1179                    let () = unhandled_resolve_error_kinds.record_uint(error_kind, *count);
1180                }
1181                let () = errors.record(unhandled_resolve_error_kinds);
1182
1183                let () = child.record(errors);
1184
1185                let address_counts_node = child.create_child("address_counts");
1186                for (count, occurrences) in address_counts_histogram {
1187                    address_counts_node.record_uint(count.to_string(), *occurrences);
1188                }
1189                child.record(address_counts_node);
1190
1191                let () = node.root().record(child);
1192            }
1193            Ok(node)
1194        }
1195        .boxed()
1196    })
1197}
1198
1199// NB: We manually set tags so logs from trust-dns crates also get the same
1200// tags as opposed to only the crate path.
1201#[fuchsia::main(logging_tags = ["dns"])]
1202pub async fn main() -> Result<(), Error> {
1203    info!("starting");
1204
1205    let mut resolver_opts = ResolverOpts::default();
1206    // Resolver will query for A and AAAA in parallel for lookup_ip.
1207    resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1208    let resolver = SharedResolver::new(
1209        Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1210            .expect("failed to create resolver"),
1211    );
1212
1213    let config_state = Arc::new(dns::config::ServerConfigState::new());
1214    let stats = Arc::new(QueryStats::new());
1215
1216    let mut fs = ServiceFs::new_local();
1217
1218    let inspector = fuchsia_inspect::component::inspector();
1219    let _state_inspect_node = add_config_state_inspect(inspector.root(), config_state.clone());
1220    let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
1221    let _inspect_server_task =
1222        inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default())
1223            .context("publish Inspect task")?;
1224
1225    let routes = fuchsia_component::client::connect_to_protocol::<fnet_routes::StateMarker>()
1226        .context("failed to connect to fuchsia.net.routes/State")?;
1227
1228    let _: &mut ServiceFsDir<'_, _> = fs
1229        .dir("svc")
1230        .add_fidl_service(IncomingRequest::Lookup)
1231        .add_fidl_service(IncomingRequest::LookupAdmin);
1232    let _: &mut ServiceFs<_> =
1233        fs.take_and_serve_directory_handle().context("failed to serve directory")?;
1234
1235    // Create a channel with buffer size `MAX_PARALLEL_REQUESTS`, which allows
1236    // request processing to always be fully saturated.
1237    let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1238    let serve_fut = fs.for_each_concurrent(None, |incoming_service| async {
1239        match incoming_service {
1240            IncomingRequest::Lookup(stream) => run_lookup(&resolver, stream, sender.clone())
1241                .await
1242                .unwrap_or_else(|e| warn!("run_lookup finished with error: {}", e)),
1243            IncomingRequest::LookupAdmin(stream) => {
1244                run_lookup_admin(&resolver, &config_state, stream)
1245                    .await
1246                    .unwrap_or_else(|e| error!("run_lookup_admin finished with error: {}", e))
1247            }
1248        }
1249    });
1250    let ip_lookup_fut = create_ip_lookup_fut(&resolver, stats.clone(), routes, recv);
1251
1252    // Failing to apply a scheduling role is not fatal. Issue a warning in case
1253    // DNS latency is important to a product and running at default priority is
1254    // insufficient.
1255    match fuchsia_scheduler::set_role_for_this_thread("fuchsia.networking.dns.resolver.main") {
1256        Ok(_) => info!("Applied scheduling role"),
1257        Err(err) => warn!("Failed to apply scheduling role: {}", err),
1258    };
1259
1260    let ((), ()) = futures::future::join(serve_fut, ip_lookup_fut).await;
1261    Ok(())
1262}
1263
1264#[cfg(test)]
1265mod tests {
1266    use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
1267    use std::pin::pin;
1268    use std::str::FromStr;
1269
1270    use assert_matches::assert_matches;
1271    use diagnostics_assertions::{NonZeroUintProperty, assert_data_tree, tree_assertion};
1272    use dns::DEFAULT_PORT;
1273    use dns::test_util::*;
1274    use futures::future::TryFutureExt as _;
1275    use itertools::Itertools as _;
1276    use net_declare::{fidl_ip, std_ip, std_ip_v4, std_ip_v6};
1277    use net_types::ip::Ip as _;
1278    use test_case::test_case;
1279    use trust_dns_proto::op::Query;
1280    use trust_dns_proto::rr::{Name, Record};
1281    use trust_dns_resolver::lookup::{Lookup, ReverseLookup};
1282
1283    use super::*;
1284
1285    const IPV4_LOOPBACK: fnet::IpAddress = fidl_ip!("127.0.0.1");
1286    const IPV6_LOOPBACK: fnet::IpAddress = fidl_ip!("::1");
1287    const LOCAL_HOST: &str = "localhost.";
1288
1289    // IPv4 address returned by mock lookup.
1290    const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1291    // IPv6 address returned by mock lookup.
1292    const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1293
1294    // host which has IPv4 address only.
1295    const REMOTE_IPV4_HOST: &str = "www.foo.com";
1296    // host which has IPv6 address only.
1297    const REMOTE_IPV6_HOST: &str = "www.bar.com";
1298    const REMOTE_IPV4_HOST_ALIAS: &str = "www.alsofoo.com";
1299    const REMOTE_IPV6_HOST_ALIAS: &str = "www.alsobar.com";
1300    // host used in reverse_lookup when multiple hostnames are returned.
1301    const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1302    // host which has IPv4 and IPv6 address if reset name servers.
1303    const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1304    // host which has no records and does not result in an error.
1305    const NO_RECORDS_AND_NO_ERROR_HOST: &str = "www.no-records-and-no-error.com";
1306
1307    async fn setup_namelookup_service() -> (fname::LookupProxy, impl futures::Future<Output = ()>) {
1308        let (name_lookup_proxy, stream) =
1309            fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1310
1311        let mut resolver_opts = ResolverOpts::default();
1312        resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1313
1314        let resolver = SharedResolver::new(
1315            Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1316                .expect("failed to create resolver"),
1317        );
1318        let stats = Arc::new(QueryStats::new());
1319        let (routes_proxy, routes_stream) =
1320            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1321        let routes_fut =
1322            routes_stream.try_for_each(|req| -> futures::future::Ready<Result<(), fidl::Error>> {
1323                panic!("Should not call routes/State. Received request {:?}", req)
1324            });
1325        let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1326
1327        (name_lookup_proxy, async move {
1328            futures::future::try_join3(
1329                run_lookup(&resolver, stream, sender),
1330                routes_fut,
1331                create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).map(Ok),
1332            )
1333            .map(|r| match r {
1334                Ok(((), (), ())) => (),
1335                Err(e) => panic!("namelookup service error {:?}", e),
1336            })
1337            .await
1338        })
1339    }
1340
1341    #[fasync::run_singlethreaded(test)]
1342    async fn test_lookupip_localhost() {
1343        let (proxy, fut) = setup_namelookup_service().await;
1344        let ((), ()) = futures::future::join(fut, async move {
1345            // IP Lookup IPv4 and IPv6 for localhost.
1346            assert_eq!(
1347                proxy
1348                    .lookup_ip(
1349                        LOCAL_HOST,
1350                        &fname::LookupIpOptions {
1351                            ipv4_lookup: Some(true),
1352                            ipv6_lookup: Some(true),
1353                            ..Default::default()
1354                        }
1355                    )
1356                    .await
1357                    .expect("lookup_ip"),
1358                Ok(fname::LookupResult {
1359                    addresses: Some(vec![IPV4_LOOPBACK, IPV6_LOOPBACK]),
1360                    ..Default::default()
1361                }),
1362            );
1363
1364            // IP Lookup IPv4 only for localhost.
1365            assert_eq!(
1366                proxy
1367                    .lookup_ip(
1368                        LOCAL_HOST,
1369                        &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
1370                    )
1371                    .await
1372                    .expect("lookup_ip"),
1373                Ok(fname::LookupResult {
1374                    addresses: Some(vec![IPV4_LOOPBACK]),
1375                    ..Default::default()
1376                }),
1377            );
1378
1379            // IP Lookup IPv6 only for localhost.
1380            assert_eq!(
1381                proxy
1382                    .lookup_ip(
1383                        LOCAL_HOST,
1384                        &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
1385                    )
1386                    .await
1387                    .expect("lookup_ip"),
1388                Ok(fname::LookupResult {
1389                    addresses: Some(vec![IPV6_LOOPBACK]),
1390                    ..Default::default()
1391                }),
1392            );
1393        })
1394        .await;
1395    }
1396
1397    #[fasync::run_singlethreaded(test)]
1398    async fn test_lookuphostname_localhost() {
1399        let (proxy, fut) = setup_namelookup_service().await;
1400        let ((), ()) = futures::future::join(fut, async move {
1401            let hostname = IPV4_LOOPBACK;
1402            assert_eq!(
1403                proxy.lookup_hostname(&hostname).await.expect("lookup_hostname").as_deref(),
1404                Ok(LOCAL_HOST)
1405            );
1406        })
1407        .await;
1408    }
1409
1410    struct MockResolver {
1411        config: ResolverConfig,
1412        repeat: u16,
1413    }
1414
1415    #[async_trait]
1416    impl ResolverLookup for MockResolver {
1417        fn new(config: ResolverConfig, _options: ResolverOpts) -> Self {
1418            Self { config, repeat: 1 }
1419        }
1420
1421        async fn lookup<N: IntoName + Send>(
1422            &self,
1423            name: N,
1424            record_type: RecordType,
1425        ) -> Result<lookup::Lookup, ResolveError> {
1426            let Self { config: _, repeat } = self;
1427
1428            let name = name.into_name()?;
1429            let host_name = name.to_utf8();
1430
1431            if host_name == NO_RECORDS_AND_NO_ERROR_HOST {
1432                return Ok(Lookup::new_with_max_ttl(Query::default(), Arc::new([])));
1433            }
1434            let rdatas = match record_type {
1435                RecordType::A => [REMOTE_IPV4_HOST, REMOTE_IPV4_IPV6_HOST]
1436                    .contains(&host_name.as_str())
1437                    .then_some(RData::A(IPV4_HOST)),
1438                RecordType::AAAA => [REMOTE_IPV6_HOST, REMOTE_IPV4_IPV6_HOST]
1439                    .contains(&host_name.as_str())
1440                    .then_some(RData::AAAA(IPV6_HOST)),
1441                RecordType::CNAME => match host_name.as_str() {
1442                    REMOTE_IPV4_HOST_ALIAS => Some(REMOTE_IPV4_HOST),
1443                    REMOTE_IPV6_HOST_ALIAS => Some(REMOTE_IPV6_HOST),
1444                    _ => None,
1445                }
1446                .map(Name::from_str)
1447                .transpose()
1448                .unwrap()
1449                .map(RData::CNAME),
1450                record_type => {
1451                    panic!("unexpected record type {:?}", record_type)
1452                }
1453            }
1454            .into_iter();
1455
1456            let len = rdatas.len() * usize::from(*repeat);
1457            let records: Vec<Record> = rdatas
1458                .map(|rdata| {
1459                    Record::from_rdata(
1460                        Name::new(),
1461                        // The following ttl value is taken arbitrarily and does not matter in the
1462                        // test.
1463                        60,
1464                        rdata,
1465                    )
1466                })
1467                .cycle()
1468                .take(len)
1469                .collect();
1470
1471            if records.is_empty() {
1472                let mut response = trust_dns_proto::op::Message::new();
1473                let _: &mut trust_dns_proto::op::Message =
1474                    response.set_response_code(ResponseCode::NoError);
1475                let error = ResolveError::from_response(response.into(), false)
1476                    .expect_err("response with no records should be a NoRecordsFound error");
1477                return Err(error);
1478            }
1479
1480            Ok(Lookup::new_with_max_ttl(Query::default(), records.into()))
1481        }
1482
1483        async fn reverse_lookup(
1484            &self,
1485            addr: IpAddr,
1486        ) -> Result<lookup::ReverseLookup, ResolveError> {
1487            let lookup = if addr == IPV4_HOST {
1488                Lookup::from_rdata(
1489                    Query::default(),
1490                    RData::PTR(Name::from_str(REMOTE_IPV4_HOST).unwrap()),
1491                )
1492            } else if addr == IPV6_HOST {
1493                Lookup::new_with_max_ttl(
1494                    Query::default(),
1495                    Arc::new([
1496                        Record::from_rdata(
1497                            Name::new(),
1498                            60, // The value is taken arbitrarily and does not matter
1499                            // in the test.
1500                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1501                        ),
1502                        Record::from_rdata(
1503                            Name::new(),
1504                            60, // The value is taken arbitrarily and does not matter
1505                            // in the test.
1506                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST_EXTRA).unwrap()),
1507                        ),
1508                    ]),
1509                )
1510            } else {
1511                Lookup::new_with_max_ttl(Query::default(), Arc::new([]))
1512            };
1513            Ok(ReverseLookup::from(lookup))
1514        }
1515    }
1516
1517    struct TestEnvironment {
1518        shared_resolver: SharedResolver<MockResolver>,
1519        config_state: Arc<dns::config::ServerConfigState>,
1520        stats: Arc<QueryStats>,
1521    }
1522
1523    impl Default for TestEnvironment {
1524        fn default() -> Self {
1525            Self::new(1)
1526        }
1527    }
1528
1529    impl TestEnvironment {
1530        fn new(repeat: u16) -> Self {
1531            Self {
1532                shared_resolver: SharedResolver::new(MockResolver {
1533                    config: ResolverConfig::from_parts(
1534                        None,
1535                        vec![],
1536                        // Set name_servers as empty, so it's guaranteed to be different from IPV4_NAMESERVER
1537                        // and IPV6_NAMESERVER.
1538                        NameServerConfigGroup::with_capacity(0),
1539                    ),
1540                    repeat,
1541                }),
1542                config_state: Arc::new(dns::config::ServerConfigState::new()),
1543                stats: Arc::new(QueryStats::new()),
1544            }
1545        }
1546
1547        async fn run_lookup<F, Fut>(&self, f: F)
1548        where
1549            Fut: futures::Future<Output = ()>,
1550            F: FnOnce(fname::LookupProxy) -> Fut,
1551        {
1552            self.run_lookup_with_routes_handler(f, |req| {
1553                panic!("Should not call routes/State. Received request {:?}", req)
1554            })
1555            .await
1556        }
1557
1558        async fn run_lookup_with_routes_handler<F, Fut, R>(&self, f: F, handle_routes: R)
1559        where
1560            Fut: futures::Future<Output = ()>,
1561            F: FnOnce(fname::LookupProxy) -> Fut,
1562            R: Fn(fnet_routes::StateRequest),
1563        {
1564            let (name_lookup_proxy, name_lookup_stream) =
1565                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1566
1567            let (routes_proxy, routes_stream) =
1568                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1569
1570            let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1571            let Self { shared_resolver, config_state: _, stats } = self;
1572            let ((), (), (), ()) = futures::future::try_join4(
1573                run_lookup(shared_resolver, name_lookup_stream, sender),
1574                f(name_lookup_proxy).map(Ok),
1575                routes_stream.try_for_each(|req| futures::future::ok(handle_routes(req))),
1576                create_ip_lookup_fut(shared_resolver, stats.clone(), routes_proxy, recv).map(Ok),
1577            )
1578            .await
1579            .expect("Error running lookup future");
1580        }
1581
1582        async fn run_admin<F, Fut>(&self, f: F)
1583        where
1584            Fut: futures::Future<Output = ()>,
1585            F: FnOnce(fname::LookupAdminProxy) -> Fut,
1586        {
1587            let (lookup_admin_proxy, lookup_admin_stream) =
1588                fidl::endpoints::create_proxy_and_stream::<fname::LookupAdminMarker>();
1589            let Self { shared_resolver, config_state, stats: _ } = self;
1590            let ((), ()) = futures::future::try_join(
1591                run_lookup_admin(shared_resolver, config_state, lookup_admin_stream)
1592                    .map_err(anyhow::Error::from),
1593                f(lookup_admin_proxy).map(Ok),
1594            )
1595            .await
1596            .expect("Error running admin future");
1597        }
1598    }
1599
1600    fn map_ip<T: Into<IpAddr>>(addr: T) -> fnet::IpAddress {
1601        net_ext::IpAddress(addr.into()).into()
1602    }
1603
1604    #[fasync::run_singlethreaded(test)]
1605    async fn test_no_records_and_no_error() {
1606        TestEnvironment::default()
1607            .run_lookup(|proxy| async move {
1608                let proxy = &proxy;
1609                futures::stream::iter([(true, true), (true, false), (false, true)])
1610                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1611                        // Verify that the resolver does not panic when the
1612                        // response contains no records and no error. This
1613                        // scenario should theoretically not occur, but
1614                        // currently does. See https://fxbug.dev/42062388.
1615                        assert_eq!(
1616                            proxy
1617                                .lookup_ip(
1618                                    NO_RECORDS_AND_NO_ERROR_HOST,
1619                                    &fname::LookupIpOptions {
1620                                        ipv4_lookup: Some(ipv4_lookup),
1621                                        ipv6_lookup: Some(ipv6_lookup),
1622                                        ..Default::default()
1623                                    }
1624                                )
1625                                .await
1626                                .expect("lookup_ip"),
1627                            Err(fname::LookupError::NotFound),
1628                        );
1629                    })
1630                    .await
1631            })
1632            .await;
1633    }
1634
1635    #[fasync::run_singlethreaded(test)]
1636    async fn test_lookupip_remotehost_overflow() {
1637        // We're returning two addresses, so we need each one to repeat only half as many times.
1638        const REPEAT: u16 = fname::MAX_ADDRESSES / 2 + 1;
1639        let expected = std::iter::empty()
1640            .chain(std::iter::repeat(map_ip(IPV4_HOST)).take(REPEAT.into()))
1641            .chain(std::iter::repeat(map_ip(IPV6_HOST)).take(REPEAT.into()))
1642            .take(fname::MAX_ADDRESSES.into())
1643            .collect::<Vec<_>>();
1644        assert_eq!(expected.len(), usize::from(fname::MAX_ADDRESSES));
1645        TestEnvironment::new(REPEAT)
1646            .run_lookup(|proxy| async move {
1647                assert_eq!(
1648                    proxy
1649                        .lookup_ip(
1650                            REMOTE_IPV4_IPV6_HOST,
1651                            &fname::LookupIpOptions {
1652                                ipv4_lookup: Some(true),
1653                                ipv6_lookup: Some(true),
1654                                ..Default::default()
1655                            }
1656                        )
1657                        .await
1658                        .expect("lookup_ip"),
1659                    Ok(fname::LookupResult { addresses: Some(expected), ..Default::default() })
1660                );
1661            })
1662            .await;
1663    }
1664
1665    #[fasync::run_singlethreaded(test)]
1666    async fn test_lookupip_remotehost_ipv4() {
1667        TestEnvironment::default()
1668            .run_lookup(|proxy| async move {
1669                // IP Lookup IPv4 and IPv6 for REMOTE_IPV4_HOST.
1670                assert_eq!(
1671                    proxy
1672                        .lookup_ip(
1673                            REMOTE_IPV4_HOST,
1674                            &fname::LookupIpOptions {
1675                                ipv4_lookup: Some(true),
1676                                ipv6_lookup: Some(true),
1677                                ..Default::default()
1678                            }
1679                        )
1680                        .await
1681                        .expect("lookup_ip"),
1682                    Ok(fname::LookupResult {
1683                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1684                        ..Default::default()
1685                    }),
1686                );
1687
1688                // IP Lookup IPv4 for REMOTE_IPV4_HOST.
1689                assert_eq!(
1690                    proxy
1691                        .lookup_ip(
1692                            REMOTE_IPV4_HOST,
1693                            &fname::LookupIpOptions {
1694                                ipv4_lookup: Some(true),
1695                                ..Default::default()
1696                            }
1697                        )
1698                        .await
1699                        .expect("lookup_ip"),
1700                    Ok(fname::LookupResult {
1701                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1702                        ..Default::default()
1703                    }),
1704                );
1705
1706                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1707                assert_eq!(
1708                    proxy
1709                        .lookup_ip(
1710                            REMOTE_IPV4_HOST,
1711                            &fname::LookupIpOptions {
1712                                ipv6_lookup: Some(true),
1713                                ..Default::default()
1714                            }
1715                        )
1716                        .await
1717                        .expect("lookup_ip"),
1718                    Err(fname::LookupError::NotFound),
1719                );
1720            })
1721            .await;
1722    }
1723
1724    #[fasync::run_singlethreaded(test)]
1725    async fn test_lookupip_remotehost_ipv6() {
1726        TestEnvironment::default()
1727            .run_lookup(|proxy| async move {
1728                // IP Lookup IPv4 and IPv6 for REMOTE_IPV6_HOST.
1729                assert_eq!(
1730                    proxy
1731                        .lookup_ip(
1732                            REMOTE_IPV6_HOST,
1733                            &fname::LookupIpOptions {
1734                                ipv4_lookup: Some(true),
1735                                ipv6_lookup: Some(true),
1736                                ..Default::default()
1737                            }
1738                        )
1739                        .await
1740                        .expect("lookup_ip"),
1741                    Ok(fname::LookupResult {
1742                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1743                        ..Default::default()
1744                    }),
1745                );
1746
1747                // IP Lookup IPv4 for REMOTE_IPV6_HOST.
1748                assert_eq!(
1749                    proxy
1750                        .lookup_ip(
1751                            REMOTE_IPV6_HOST,
1752                            &fname::LookupIpOptions {
1753                                ipv4_lookup: Some(true),
1754                                ..Default::default()
1755                            }
1756                        )
1757                        .await
1758                        .expect("lookup_ip"),
1759                    Err(fname::LookupError::NotFound),
1760                );
1761
1762                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1763                assert_eq!(
1764                    proxy
1765                        .lookup_ip(
1766                            REMOTE_IPV6_HOST,
1767                            &fname::LookupIpOptions {
1768                                ipv6_lookup: Some(true),
1769                                ..Default::default()
1770                            }
1771                        )
1772                        .await
1773                        .expect("lookup_ip"),
1774                    Ok(fname::LookupResult {
1775                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1776                        ..Default::default()
1777                    }),
1778                );
1779            })
1780            .await;
1781    }
1782
1783    #[test_case(REMOTE_IPV4_HOST_ALIAS, REMOTE_IPV4_HOST; "ipv4")]
1784    #[test_case(REMOTE_IPV6_HOST_ALIAS, REMOTE_IPV6_HOST; "ipv6")]
1785    #[fasync::run_singlethreaded(test)]
1786    async fn test_lookupip_remotehost_canonical_name(hostname: &str, expected: &str) {
1787        TestEnvironment::default()
1788            .run_lookup(|proxy| async move {
1789                assert_matches!(
1790                    proxy
1791                        .lookup_ip(
1792                            hostname,
1793                            &fname::LookupIpOptions {
1794                                canonical_name_lookup: Some(true),
1795                                ..Default::default()
1796                            }
1797                        )
1798                        .await,
1799                    Ok(Ok(fname::LookupResult {
1800                        canonical_name: Some(cname),
1801                        ..
1802                    })) => assert_eq!(cname, expected)
1803                );
1804            })
1805            .await;
1806    }
1807
1808    #[fasync::run_singlethreaded(test)]
1809    async fn test_lookupip_ip_literal() {
1810        TestEnvironment::default()
1811            .run_lookup(|proxy| async move {
1812                let proxy = &proxy;
1813
1814                let range = || [true, false].into_iter();
1815
1816                futures::stream::iter(range().cartesian_product(range()))
1817                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1818                        assert_eq!(
1819                            proxy
1820                                .lookup_ip(
1821                                    "240.0.0.2",
1822                                    &fname::LookupIpOptions {
1823                                        ipv4_lookup: Some(ipv4_lookup),
1824                                        ipv6_lookup: Some(ipv6_lookup),
1825                                        ..Default::default()
1826                                    }
1827                                )
1828                                .await
1829                                .expect("lookup_ip"),
1830                            Err(fname::LookupError::InvalidArgs),
1831                            "ipv4_lookup={},ipv6_lookup={}",
1832                            ipv4_lookup,
1833                            ipv6_lookup,
1834                        );
1835
1836                        assert_eq!(
1837                            proxy
1838                                .lookup_ip(
1839                                    "abcd::2",
1840                                    &fname::LookupIpOptions {
1841                                        ipv4_lookup: Some(ipv4_lookup),
1842                                        ipv6_lookup: Some(ipv6_lookup),
1843                                        ..Default::default()
1844                                    }
1845                                )
1846                                .await
1847                                .expect("lookup_ip"),
1848                            Err(fname::LookupError::InvalidArgs),
1849                            "ipv4_lookup={},ipv6_lookup={}",
1850                            ipv4_lookup,
1851                            ipv6_lookup,
1852                        );
1853                    })
1854                    .await
1855            })
1856            .await
1857    }
1858
1859    #[fasync::run_singlethreaded(test)]
1860    async fn test_lookup_hostname() {
1861        TestEnvironment::default()
1862            .run_lookup(|proxy| async move {
1863                assert_eq!(
1864                    proxy
1865                        .lookup_hostname(&map_ip(IPV4_HOST))
1866                        .await
1867                        .expect("lookup_hostname")
1868                        .as_deref(),
1869                    Ok(REMOTE_IPV4_HOST)
1870                );
1871            })
1872            .await;
1873    }
1874
1875    // Multiple hostnames returned from trust-dns* APIs, and only the first one will be returned
1876    // by the FIDL.
1877    #[fasync::run_singlethreaded(test)]
1878    async fn test_lookup_hostname_multi() {
1879        TestEnvironment::default()
1880            .run_lookup(|proxy| async move {
1881                assert_eq!(
1882                    proxy
1883                        .lookup_hostname(&map_ip(IPV6_HOST))
1884                        .await
1885                        .expect("lookup_hostname")
1886                        .as_deref(),
1887                    Ok(REMOTE_IPV6_HOST)
1888                );
1889            })
1890            .await;
1891    }
1892
1893    #[fasync::run_singlethreaded(test)]
1894    async fn test_set_server_names() {
1895        let env = TestEnvironment::default();
1896
1897        let to_server_configs = |socket_addr: SocketAddr| -> [NameServerConfig; 2] {
1898            [
1899                NameServerConfig {
1900                    socket_addr,
1901                    protocol: Protocol::Udp,
1902                    tls_dns_name: None,
1903                    trust_nx_responses: false,
1904                    bind_addr: None,
1905                },
1906                NameServerConfig {
1907                    socket_addr,
1908                    protocol: Protocol::Tcp,
1909                    tls_dns_name: None,
1910                    trust_nx_responses: false,
1911                    bind_addr: None,
1912                },
1913            ]
1914        };
1915
1916        // Assert that mock config has no servers originally.
1917        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1918
1919        // Set servers.
1920        env.run_admin(|proxy| async move {
1921            let () = proxy
1922                .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
1923                .await
1924                .expect("Failed to call SetDnsServers")
1925                .expect("SetDnsServers error");
1926        })
1927        .await;
1928        assert_eq!(
1929            env.shared_resolver.read().config.name_servers().to_vec(),
1930            vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
1931                .into_iter()
1932                .map(|s| {
1933                    let net_ext::SocketAddress(s) = s.into();
1934                    s
1935                })
1936                .flat_map(|x| to_server_configs(x).to_vec().into_iter())
1937                .collect::<Vec<_>>()
1938        );
1939
1940        // Clear servers.
1941        env.run_admin(|proxy| async move {
1942            let () = proxy
1943                .set_dns_servers(&[])
1944                .await
1945                .expect("Failed to call SetDnsServers")
1946                .expect("SetDnsServers error");
1947        })
1948        .await;
1949        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
1950    }
1951
1952    #[fasync::run_singlethreaded(test)]
1953    async fn test_set_server_names_error() {
1954        let env = TestEnvironment::default();
1955        // Assert that mock config has no servers originally.
1956        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1957
1958        env.run_admin(|proxy| async move {
1959            // Attempt to set bad addresses.
1960
1961            // Multicast not allowed.
1962            let status = proxy
1963                .set_dns_servers(&[fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
1964                    address: fnet::Ipv4Address { addr: [224, 0, 0, 1] },
1965                    port: DEFAULT_PORT,
1966                })])
1967                .await
1968                .expect("Failed to call SetDnsServers")
1969                .expect_err("SetDnsServers should fail for multicast address");
1970            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
1971
1972            // Unspecified not allowed.
1973            let status = proxy
1974                .set_dns_servers(&[fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
1975                    address: fnet::Ipv6Address { addr: [0; 16] },
1976                    port: DEFAULT_PORT,
1977                    zone_index: 0,
1978                })])
1979                .await
1980                .expect("Failed to call SetDnsServers")
1981                .expect_err("SetDnsServers should fail for unspecified address");
1982            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
1983        })
1984        .await;
1985
1986        // Assert that config didn't change.
1987        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1988    }
1989
1990    #[fasync::run_singlethreaded(test)]
1991    async fn test_get_servers() {
1992        let env = TestEnvironment::default();
1993        env.run_admin(|proxy| async move {
1994            let expect = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
1995            let () = proxy
1996                .set_dns_servers(expect)
1997                .await
1998                .expect("FIDL error")
1999                .expect("set_servers failed");
2000            assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
2001        })
2002        .await;
2003    }
2004
2005    #[fasync::run_singlethreaded(test)]
2006    async fn test_config_inspect() {
2007        let env = TestEnvironment::default();
2008        let inspector = fuchsia_inspect::Inspector::default();
2009        let _config_state_node =
2010            add_config_state_inspect(inspector.root(), env.config_state.clone());
2011        assert_data_tree!(inspector, root:{
2012            servers: {}
2013        });
2014        env.run_admin(|proxy| async move {
2015            let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2016            let () = proxy
2017                .set_dns_servers(servers)
2018                .await
2019                .expect("FIDL error")
2020                .expect("set_servers failed");
2021        })
2022        .await;
2023        assert_data_tree!(inspector, root:{
2024            servers: {
2025                "0": {
2026                    address: "[2001:4860:4860::4444%2]:53",
2027                },
2028                "1": {
2029                    address: "8.8.4.4:53",
2030                },
2031                "2": {
2032                    address: "[2002:4860:4860::4444%3]:53",
2033                },
2034                "3": {
2035                    address: "8.8.8.8:53",
2036                },
2037            }
2038        });
2039    }
2040
2041    #[test]
2042    fn test_unhandled_resolve_error_kind_stats() {
2043        use ResolveErrorKind::{Msg, Timeout};
2044        let mut unhandled_resolve_error_kind_stats = UnhandledResolveErrorKindStats::default();
2045        assert_eq!(
2046            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2047            "Msg"
2048        );
2049        assert_eq!(
2050            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2051            "Msg"
2052        );
2053        assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2054        assert_eq!(
2055            unhandled_resolve_error_kind_stats,
2056            UnhandledResolveErrorKindStats {
2057                resolve_error_kind_counts: [(String::from("Msg"), 2), (String::from("Timeout"), 1)]
2058                    .into()
2059            }
2060        )
2061    }
2062
2063    #[fasync::run_singlethreaded(test)]
2064    async fn test_query_stats_updated() {
2065        let env = TestEnvironment::default();
2066        let inspector = fuchsia_inspect::Inspector::default();
2067        let _query_stats_inspect_node =
2068            add_query_stats_inspect(inspector.root(), env.stats.clone());
2069        assert_data_tree!(inspector, root:{
2070            query_stats: {}
2071        });
2072
2073        let () = env
2074            .run_lookup(|proxy| async move {
2075                // IP Lookup IPv4 for REMOTE_IPV4_HOST.
2076                assert_eq!(
2077                    proxy
2078                        .lookup_ip(
2079                            REMOTE_IPV4_HOST,
2080                            &fname::LookupIpOptions {
2081                                ipv4_lookup: Some(true),
2082                                ..Default::default()
2083                            }
2084                        )
2085                        .await
2086                        .expect("lookup_ip"),
2087                    Ok(fname::LookupResult {
2088                        addresses: Some(vec![map_ip(IPV4_HOST)]),
2089                        ..Default::default()
2090                    }),
2091                );
2092            })
2093            .await;
2094        let () = env
2095            .run_lookup(|proxy| async move {
2096                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
2097                assert_eq!(
2098                    proxy
2099                        .lookup_ip(
2100                            REMOTE_IPV4_HOST,
2101                            &fname::LookupIpOptions {
2102                                ipv6_lookup: Some(true),
2103                                ..Default::default()
2104                            }
2105                        )
2106                        .await
2107                        .expect("lookup_ip"),
2108                    Err(fname::LookupError::NotFound),
2109                );
2110            })
2111            .await;
2112        assert_data_tree!(inspector, root:{
2113            query_stats: {
2114                "window 1": {
2115                    start_time_nanos: NonZeroUintProperty,
2116                    successful_queries: 1u64,
2117                    failed_queries: 1u64,
2118                    average_success_duration_micros: NonZeroUintProperty,
2119                    average_failure_duration_micros: NonZeroUintProperty,
2120                    errors: {
2121                        Message: 0u64,
2122                        NoConnections: 0u64,
2123                        NoRecordsFoundResponseCodeCounts: {
2124                            NoError: 1u64,
2125                        },
2126                        Io: 0u64,
2127                        Proto: 0u64,
2128                        Timeout: 0u64,
2129                        UnhandledResolveErrorKindCounts: {},
2130                    },
2131                    address_counts: {
2132                        "1": 1u64,
2133                    },
2134                },
2135            }
2136        });
2137    }
2138
2139    fn run_fake_lookup(
2140        exec: &mut fasync::TestExecutor,
2141        stats: Arc<QueryStats>,
2142        result: QueryResult<'_>,
2143        delay: zx::MonotonicDuration,
2144    ) {
2145        let start_time = fasync::MonotonicInstant::now();
2146        let () = exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2147        let update_stats = stats.finish_query(start_time, result);
2148        let mut update_stats = pin!(update_stats);
2149        assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2150    }
2151
2152    // Safety: This is safe because the initial value is not zero.
2153    const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2154
2155    #[test]
2156    fn test_query_stats_inspect_average() {
2157        let mut exec = fasync::TestExecutor::new_with_fake_time();
2158        const START_NANOS: i64 = 1_234_567;
2159        let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2160
2161        let stats = Arc::new(QueryStats::new());
2162        let inspector = fuchsia_inspect::Inspector::default();
2163        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2164        const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2165        const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2166            zx::MonotonicDuration::from_seconds(30);
2167        for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2168            let () = run_fake_lookup(
2169                &mut exec,
2170                stats.clone(),
2171                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2172                zx::MonotonicDuration::from_nanos(0),
2173            );
2174            let () = run_fake_lookup(
2175                &mut exec,
2176                stats.clone(),
2177                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2178                SUCCESSFUL_QUERY_DURATION,
2179            );
2180            let () = exec.set_fake_time(fasync::MonotonicInstant::after(
2181                STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2182            ));
2183        }
2184        let mut expected = tree_assertion!(query_stats: {});
2185        for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2186            let name = &format!("window {}", i + 1);
2187            let child = tree_assertion!(var name: {
2188                start_time_nanos: u64::try_from(
2189                    START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2190                ).unwrap(),
2191                successful_queries: 2u64,
2192                failed_queries: 0u64,
2193                average_success_duration_micros: u64::try_from(
2194                    SUCCESSFUL_QUERY_DURATION.into_micros()
2195                ).unwrap() / 2,
2196                errors: {
2197                    Message: 0u64,
2198                    NoConnections: 0u64,
2199                    NoRecordsFoundResponseCodeCounts: {},
2200                    Io: 0u64,
2201                    Proto: 0u64,
2202                    Timeout: 0u64,
2203                    UnhandledResolveErrorKindCounts: {},
2204                },
2205                address_counts: {
2206                    "1": 2u64,
2207                },
2208            });
2209            expected.add_child_assertion(child);
2210        }
2211        assert_data_tree!(@executor exec, inspector, root: {
2212            expected,
2213        });
2214    }
2215
2216    #[test]
2217    fn test_query_stats_inspect_error_counters() {
2218        let mut exec = fasync::TestExecutor::new_with_fake_time();
2219        const START_NANOS: i64 = 1_234_567;
2220        let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2221
2222        let stats = Arc::new(QueryStats::new());
2223        let inspector = fuchsia_inspect::Inspector::default();
2224        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2225        const FAILED_QUERY_COUNT: u64 = 10;
2226        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2227            zx::MonotonicDuration::from_millis(500);
2228        for _ in 0..FAILED_QUERY_COUNT {
2229            let () = run_fake_lookup(
2230                &mut exec,
2231                stats.clone(),
2232                Err(&ResolveErrorKind::Timeout),
2233                FAILED_QUERY_DURATION,
2234            );
2235        }
2236        assert_data_tree!(@executor exec, inspector, root:{
2237            query_stats: {
2238                "window 1": {
2239                    start_time_nanos: u64::try_from(
2240                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2241                    ).unwrap(),
2242                    successful_queries: 0u64,
2243                    failed_queries: FAILED_QUERY_COUNT,
2244                    average_failure_duration_micros: u64::try_from(
2245                        FAILED_QUERY_DURATION.into_micros()
2246                    ).unwrap(),
2247                    errors: {
2248                        Message: 0u64,
2249                        NoConnections: 0u64,
2250                        NoRecordsFoundResponseCodeCounts: {},
2251                        Io: 0u64,
2252                        Proto: 0u64,
2253                        Timeout: FAILED_QUERY_COUNT,
2254                        UnhandledResolveErrorKindCounts: {},
2255                    },
2256                    address_counts: {},
2257                },
2258            }
2259        });
2260    }
2261
2262    #[test]
2263    fn test_query_stats_inspect_no_records_found() {
2264        let mut exec = fasync::TestExecutor::new_with_fake_time();
2265        const START_NANOS: i64 = 1_234_567;
2266        let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2267
2268        let stats = Arc::new(QueryStats::new());
2269        let inspector = fuchsia_inspect::Inspector::default();
2270        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2271        const FAILED_QUERY_COUNT: u64 = 10;
2272        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2273            zx::MonotonicDuration::from_millis(500);
2274
2275        let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2276            run_fake_lookup(
2277                &mut exec,
2278                stats.clone(),
2279                Err(&ResolveErrorKind::NoRecordsFound {
2280                    query: Box::new(Query::default()),
2281                    soa: None,
2282                    negative_ttl: None,
2283                    response_code,
2284                    trusted: false,
2285                }),
2286                FAILED_QUERY_DURATION,
2287            )
2288        };
2289
2290        for _ in 0..FAILED_QUERY_COUNT {
2291            let () = run_fake_no_records_lookup(ResponseCode::NXDomain);
2292            let () = run_fake_no_records_lookup(ResponseCode::Refused);
2293            let () = run_fake_no_records_lookup(4096.into());
2294            let () = run_fake_no_records_lookup(4097.into());
2295        }
2296
2297        assert_data_tree!(@executor exec, inspector, root:{
2298            query_stats: {
2299                "window 1": {
2300                    start_time_nanos: u64::try_from(
2301                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2302                    ).unwrap(),
2303                    successful_queries: 0u64,
2304                    failed_queries: FAILED_QUERY_COUNT * 4,
2305                    average_failure_duration_micros: u64::try_from(
2306                        FAILED_QUERY_DURATION.into_micros()
2307                    ).unwrap(),
2308                    errors: {
2309                        Message: 0u64,
2310                        NoConnections: 0u64,
2311                        NoRecordsFoundResponseCodeCounts: {
2312                          NXDomain: FAILED_QUERY_COUNT,
2313                          Refused: FAILED_QUERY_COUNT,
2314                          "Unknown(4096)": FAILED_QUERY_COUNT,
2315                          "Unknown(4097)": FAILED_QUERY_COUNT,
2316                        },
2317                        Io: 0u64,
2318                        Proto: 0u64,
2319                        Timeout: 0u64,
2320                        UnhandledResolveErrorKindCounts: {},
2321                    },
2322                    address_counts: {},
2323                },
2324            }
2325        });
2326    }
2327
2328    #[test]
2329    fn test_query_stats_resolved_address_counts() {
2330        let mut exec = fasync::TestExecutor::new_with_fake_time();
2331        const START_NANOS: i64 = 1_234_567;
2332        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2333
2334        let stats = Arc::new(QueryStats::new());
2335        let inspector = fuchsia_inspect::Inspector::default();
2336        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2337
2338        // Create some test data to run fake lookups. Simulate a histogram with:
2339        //  - 99 occurrences of a response with 1 address,
2340        //  - 98 occurrences of a response with 2 addresses,
2341        //  - ...
2342        //  - 1 occurrence of a response with 99 addresses.
2343        let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2344        const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2345        for (count, occurrences) in address_counts.iter() {
2346            for _ in 0..*occurrences {
2347                run_fake_lookup(
2348                    &mut exec,
2349                    stats.clone(),
2350                    Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2351                    QUERY_DURATION,
2352                );
2353            }
2354        }
2355
2356        let mut expected_address_counts = tree_assertion!(address_counts: {});
2357        for (count, occurrences) in address_counts.iter() {
2358            expected_address_counts
2359                .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2360        }
2361        assert_data_tree!(@executor exec, inspector, root: {
2362            query_stats: {
2363                "window 1": {
2364                    start_time_nanos: u64::try_from(
2365                        START_NANOS + QUERY_DURATION.into_nanos()
2366                    ).unwrap(),
2367                    successful_queries: address_counts.values().sum::<u64>(),
2368                    failed_queries: 0u64,
2369                    average_success_duration_micros: u64::try_from(
2370                        QUERY_DURATION.into_micros()
2371                    ).unwrap(),
2372                    errors: {
2373                        Message: 0u64,
2374                        NoConnections: 0u64,
2375                        NoRecordsFoundResponseCodeCounts: {},
2376                        Io: 0u64,
2377                        Proto: 0u64,
2378                        Timeout: 0u64,
2379                        UnhandledResolveErrorKindCounts: {},
2380                    },
2381                    expected_address_counts,
2382                },
2383            },
2384        });
2385    }
2386
2387    #[test]
2388    fn test_query_stats_inspect_oldest_stats_erased() {
2389        let mut exec = fasync::TestExecutor::new_with_fake_time();
2390        const START_NANOS: i64 = 1_234_567;
2391        let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2392
2393        let stats = Arc::new(QueryStats::new());
2394        let inspector = fuchsia_inspect::Inspector::default();
2395        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2396        const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2397        for _ in 0..STAT_WINDOW_COUNT {
2398            let () =
2399                run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2400            let () =
2401                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2402        }
2403        for _ in 0..STAT_WINDOW_COUNT {
2404            let () = run_fake_lookup(
2405                &mut exec,
2406                stats.clone(),
2407                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2408                DELAY,
2409            );
2410            let () =
2411                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2412        }
2413        // All the failed queries should be erased from the stats as they are
2414        // now out of date.
2415        let mut expected = tree_assertion!(query_stats: {});
2416        let start_offset = START_NANOS
2417            + DELAY.into_nanos()
2418            + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2419        for i in 0..STAT_WINDOW_COUNT {
2420            let name = &format!("window {}", i + 1);
2421            let child = tree_assertion!(var name: {
2422                start_time_nanos: u64::try_from(
2423                    start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2424                ).unwrap(),
2425                successful_queries: 1u64,
2426                failed_queries: 0u64,
2427                average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2428                errors: {
2429                    Message: 0u64,
2430                    NoConnections: 0u64,
2431                    NoRecordsFoundResponseCodeCounts: {},
2432                    Io: 0u64,
2433                    Proto: 0u64,
2434                    Timeout: 0u64,
2435                    UnhandledResolveErrorKindCounts: {},
2436                },
2437                address_counts: {
2438                    "1": 1u64,
2439                },
2440            });
2441            expected.add_child_assertion(child);
2442        }
2443        assert_data_tree!(@executor exec, inspector, root: {
2444            expected,
2445        });
2446    }
2447
2448    struct BlockingResolver {}
2449
2450    #[async_trait]
2451    impl ResolverLookup for BlockingResolver {
2452        fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2453            BlockingResolver {}
2454        }
2455
2456        async fn lookup<N: IntoName + Send>(
2457            &self,
2458            _name: N,
2459            _record_type: RecordType,
2460        ) -> Result<lookup::Lookup, ResolveError> {
2461            futures::future::pending().await
2462        }
2463
2464        async fn reverse_lookup(
2465            &self,
2466            _addr: IpAddr,
2467        ) -> Result<lookup::ReverseLookup, ResolveError> {
2468            panic!("BlockingResolver does not handle reverse lookup")
2469        }
2470    }
2471
2472    #[fasync::run_singlethreaded(test)]
2473    async fn test_parallel_query_limit() {
2474        // Collect requests by setting up a FIDL proxy and stream for the Lookup
2475        // protocol, because there isn't a good way to directly construct fake
2476        // requests to be used for testing.
2477        let requests = {
2478            let (name_lookup_proxy, name_lookup_stream) =
2479                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2480            const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2481            for _ in 0..NUM_REQUESTS {
2482                // Don't await on this future because we are using these
2483                // requests to collect FIDL responders in order to send test
2484                // requests later, and will not respond to these requests.
2485                let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2486                    name_lookup_proxy.lookup_ip(
2487                        LOCAL_HOST,
2488                        &fname::LookupIpOptions {
2489                            ipv4_lookup: Some(true),
2490                            ipv6_lookup: Some(true),
2491                            ..Default::default()
2492                        },
2493                    );
2494            }
2495            // Terminate the stream so its items can be collected below.
2496            drop(name_lookup_proxy);
2497            let requests = name_lookup_stream
2498                .map(|request| match request.expect("channel error") {
2499                    LookupRequest::LookupIp { hostname, options, responder } => {
2500                        IpLookupRequest { hostname, options, responder }
2501                    }
2502                    req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2503                })
2504                .collect::<Vec<_>>()
2505                .await;
2506            assert_eq!(requests.len(), NUM_REQUESTS);
2507            requests
2508        };
2509
2510        let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2511
2512        // The channel's capacity is equal to buffer + num-senders. Thus the
2513        // channel has a capacity of `MAX_PARALLEL_REQUESTS` + 1, and the
2514        // `for_each_concurrent` future has a limit of `MAX_PARALLEL_REQUESTS`,
2515        // so the sender should be able to queue `MAX_PARALLEL_REQUESTS` * 2 + 1
2516        // requests before `send` fails.
2517        const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2518        const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2519        let mut send_fut = pin!(
2520            async {
2521                for (i, req) in requests.into_iter().enumerate() {
2522                    match i {
2523                        BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2524                        LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2525                        _ => assert_matches!(sender.send(req).await, Ok(())),
2526                    }
2527                }
2528            }
2529            .fuse()
2530        );
2531        let mut recv_fut = pin!({
2532            let resolver = SharedResolver::new(BlockingResolver::new(
2533                ResolverConfig::default(),
2534                ResolverOpts::default(),
2535            ));
2536            let stats = Arc::new(QueryStats::new());
2537            let (routes_proxy, _routes_stream) =
2538                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2539            async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2540                .fuse()
2541        });
2542        futures::select! {
2543            () = send_fut => {},
2544            () = recv_fut => panic!("recv_fut should never complete"),
2545        };
2546    }
2547
2548    #[test]
2549    fn test_failure_stats() {
2550        use anyhow::anyhow;
2551        use trust_dns_proto::error::ProtoError;
2552        use trust_dns_proto::op::Query;
2553
2554        let mut stats = FailureStats::default();
2555        for (error_kind, expected) in &[
2556            (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2557            (
2558                ResolveErrorKind::Msg("foo".to_string()),
2559                FailureStats { message: 2, ..Default::default() },
2560            ),
2561            (
2562                ResolveErrorKind::NoRecordsFound {
2563                    query: Box::new(Query::default()),
2564                    soa: None,
2565                    negative_ttl: None,
2566                    response_code: ResponseCode::Refused,
2567                    trusted: false,
2568                },
2569                FailureStats {
2570                    message: 2,
2571                    no_records_found: NoRecordsFoundStats {
2572                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2573                    },
2574                    ..Default::default()
2575                },
2576            ),
2577            (
2578                ResolveErrorKind::Io(std::io::Error::new(
2579                    std::io::ErrorKind::NotFound,
2580                    anyhow!("foo"),
2581                )),
2582                FailureStats {
2583                    message: 2,
2584                    no_records_found: NoRecordsFoundStats {
2585                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2586                    },
2587                    io: 1,
2588                    ..Default::default()
2589                },
2590            ),
2591            (
2592                ResolveErrorKind::Proto(ProtoError::from("foo")),
2593                FailureStats {
2594                    message: 2,
2595                    no_records_found: NoRecordsFoundStats {
2596                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2597                    },
2598                    io: 1,
2599                    proto: 1,
2600                    ..Default::default()
2601                },
2602            ),
2603            (
2604                ResolveErrorKind::NoConnections,
2605                FailureStats {
2606                    message: 2,
2607                    no_connections: 1,
2608                    no_records_found: NoRecordsFoundStats {
2609                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2610                    },
2611                    io: 1,
2612                    proto: 1,
2613                    ..Default::default()
2614                },
2615            ),
2616            (
2617                ResolveErrorKind::Timeout,
2618                FailureStats {
2619                    message: 2,
2620                    no_connections: 1,
2621                    no_records_found: NoRecordsFoundStats {
2622                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2623                    },
2624                    io: 1,
2625                    proto: 1,
2626                    timeout: 1,
2627                    unhandled_resolve_error_kind: Default::default(),
2628                },
2629            ),
2630            (
2631                ResolveErrorKind::NoRecordsFound {
2632                    query: Box::new(Query::default()),
2633                    soa: None,
2634                    negative_ttl: None,
2635                    response_code: ResponseCode::NXDomain,
2636                    trusted: false,
2637                },
2638                FailureStats {
2639                    message: 2,
2640                    no_connections: 1,
2641                    no_records_found: NoRecordsFoundStats {
2642                        response_code_counts: [
2643                            (ResponseCode::NXDomain.into(), 1),
2644                            (ResponseCode::Refused.into(), 1),
2645                        ]
2646                        .into(),
2647                    },
2648                    io: 1,
2649                    proto: 1,
2650                    timeout: 1,
2651                    unhandled_resolve_error_kind: Default::default(),
2652                },
2653            ),
2654            (
2655                ResolveErrorKind::NoRecordsFound {
2656                    query: Box::new(Query::default()),
2657                    soa: None,
2658                    negative_ttl: None,
2659                    response_code: ResponseCode::NXDomain,
2660                    trusted: false,
2661                },
2662                FailureStats {
2663                    message: 2,
2664                    no_connections: 1,
2665                    no_records_found: NoRecordsFoundStats {
2666                        response_code_counts: [
2667                            (ResponseCode::NXDomain.into(), 2),
2668                            (ResponseCode::Refused.into(), 1),
2669                        ]
2670                        .into(),
2671                    },
2672                    io: 1,
2673                    proto: 1,
2674                    timeout: 1,
2675                    unhandled_resolve_error_kind: Default::default(),
2676                },
2677            ),
2678        ][..]
2679        {
2680            let () = stats.increment(error_kind);
2681            assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2682        }
2683    }
2684
2685    fn test_das_helper(
2686        l_addr: fnet::IpAddress,
2687        l_src: Option<fnet::IpAddress>,
2688        r_addr: fnet::IpAddress,
2689        r_src: Option<fnet::IpAddress>,
2690        want: std::cmp::Ordering,
2691    ) {
2692        let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2693        let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2694        assert_eq!(
2695            left.cmp(&right),
2696            want,
2697            "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2698            want,
2699            l_addr,
2700            l_src,
2701            left,
2702            r_addr,
2703            r_src,
2704            right
2705        );
2706    }
2707
2708    macro_rules! add_das_test {
2709        ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2710            #[test]
2711            fn $name() {
2712                test_das_helper(
2713                    $pref_dst,
2714                    $pref_src,
2715                    $other_dst,
2716                    $other_src,
2717                    std::cmp::Ordering::Less,
2718                )
2719            }
2720        };
2721    }
2722
2723    add_das_test!(
2724        prefer_reachable,
2725        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2726        other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2727    );
2728
2729    // These test cases are taken from RFC 6724, section 10.2.
2730
2731    add_das_test!(
2732        prefer_matching_scope,
2733        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2734        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2735    );
2736
2737    add_das_test!(
2738        prefer_matching_label,
2739        preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2740        other:  fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2741    );
2742
2743    add_das_test!(
2744        prefer_higher_precedence_1,
2745        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2746        other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2747    );
2748
2749    add_das_test!(
2750        prefer_higher_precedence_2,
2751        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2752        other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2753    );
2754
2755    add_das_test!(
2756        prefer_smaller_scope,
2757        preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2758        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2759    );
2760
2761    add_das_test!(
2762        prefer_longest_matching_prefix,
2763        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2764        other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2765    );
2766
2767    #[test]
2768    fn test_das_equals() {
2769        for (dst, src) in [
2770            (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2771            (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2772        ]
2773        .iter()
2774        {
2775            let () = test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2776            let () = test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2777        }
2778    }
2779
2780    #[test]
2781    fn test_valid_policy_table() {
2782        // Last element in policy table MUST be ::/0.
2783        assert_eq!(
2784            POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2785            net_types::ip::Subnet::new(net_types::ip::Ipv6::UNSPECIFIED_ADDRESS, 0)
2786                .expect("invalid subnet")
2787        );
2788        // Policy table must be sorted by prefix length.
2789        let () = POLICY_TABLE.windows(2).for_each(|w| {
2790            let Policy { prefix: cur, precedence: _, label: _ } = w[0];
2791            let Policy { prefix: nxt, precedence: _, label: _ } = w[1];
2792            assert!(
2793                cur.prefix() >= nxt.prefix(),
2794                "bad ordering of prefixes, {} must come after {}",
2795                cur,
2796                nxt
2797            )
2798        });
2799        // Assert that POLICY_TABLE declaration does not use any invalid
2800        // subnets.
2801        for policy in POLICY_TABLE.iter() {
2802            assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2803        }
2804    }
2805
2806    #[fasync::run_singlethreaded(test)]
2807    async fn test_sort_preferred_addresses() {
2808        const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2809            (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2810            (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2811            (fidl_ip!("192.168.50.22"), None),
2812            (fidl_ip!("2001::2"), None),
2813            (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2814        ];
2815        // Declared using std types so we get cleaner output when we assert
2816        // expectations.
2817        const SORTED: [IpAddr; 5] = [
2818            std_ip!("::1"),
2819            std_ip!("2001:db8:1::1"),
2820            std_ip!("127.0.0.1"),
2821            std_ip!("192.168.50.22"),
2822            std_ip!("2001::2"),
2823        ];
2824        let (routes_proxy, routes_stream) =
2825            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2826        let routes_fut =
2827            routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2828                let (destination, responder) = assert_matches!(
2829                    req,
2830                    fnet_routes::StateRequest::Resolve { destination, responder }
2831                        => (destination, responder)
2832                );
2833                let result = TEST_IPS
2834                    .iter()
2835                    .enumerate()
2836                    .find_map(|(i, (dst, src))| {
2837                        if *dst == destination && src.is_some() {
2838                            let inner = fnet_routes::Destination {
2839                                address: Some(*dst),
2840                                source_address: *src,
2841                                ..Default::default()
2842                            };
2843                            // Send both Direct and Gateway resolved routes to show we
2844                            // don't care about that part.
2845                            if i % 2 == 0 {
2846                                Some(fnet_routes::Resolved::Direct(inner))
2847                            } else {
2848                                Some(fnet_routes::Resolved::Gateway(inner))
2849                            }
2850                        } else {
2851                            None
2852                        }
2853                    })
2854                    .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
2855                futures::future::ready(
2856                    responder
2857                        .send(result.as_ref().map_err(|e| *e))
2858                        .context("failed to send Resolve response"),
2859                )
2860            });
2861
2862        let ((), ()) = futures::future::try_join(routes_fut, async move {
2863            let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
2864            let addrs = sort_preferred_addresses(addrs, &routes_proxy)
2865                .await
2866                .expect("failed to sort addresses");
2867            let addrs = addrs
2868                .into_iter()
2869                .map(|a| {
2870                    let net_ext::IpAddress(a) = a.into();
2871                    a
2872                })
2873                .collect::<Vec<_>>();
2874            assert_eq!(&addrs[..], &SORTED[..]);
2875            Ok(())
2876        })
2877        .await
2878        .expect("error running futures");
2879    }
2880
2881    #[fasync::run_singlethreaded(test)]
2882    async fn test_lookupip() {
2883        // Routes handler will say that only IPV6_HOST is reachable.
2884        let routes_handler = |req| {
2885            let (destination, responder) = assert_matches!(
2886                req,
2887                fnet_routes::StateRequest::Resolve { destination, responder }
2888                    => (destination, responder)
2889            );
2890            let resolved;
2891            let response = if destination == map_ip(IPV6_HOST) {
2892                resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
2893                    address: Some(destination),
2894                    source_address: Some(destination),
2895                    ..Default::default()
2896                });
2897                Ok(&resolved)
2898            } else {
2899                Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
2900            };
2901            let () = responder.send(response).expect("failed to send Resolve FIDL response");
2902        };
2903        TestEnvironment::default()
2904            .run_lookup_with_routes_handler(
2905                |proxy| async move {
2906                    // All arguments unset.
2907                    assert_eq!(
2908                        proxy
2909                            .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
2910                            .await
2911                            .expect("lookup_ip"),
2912                        Err(fname::LookupError::InvalidArgs)
2913                    );
2914                    // No IP addresses to look.
2915                    assert_eq!(
2916                        proxy
2917                            .lookup_ip(
2918                                REMOTE_IPV4_HOST,
2919                                &fname::LookupIpOptions {
2920                                    ipv4_lookup: Some(false),
2921                                    ipv6_lookup: Some(false),
2922                                    ..Default::default()
2923                                }
2924                            )
2925                            .await
2926                            .expect("lookup_ip"),
2927                        Err(fname::LookupError::InvalidArgs)
2928                    );
2929                    // No results for an IPv4 only host.
2930                    assert_eq!(
2931                        proxy
2932                            .lookup_ip(
2933                                REMOTE_IPV4_HOST,
2934                                &fname::LookupIpOptions {
2935                                    ipv4_lookup: Some(false),
2936                                    ipv6_lookup: Some(true),
2937                                    ..Default::default()
2938                                }
2939                            )
2940                            .await
2941                            .expect("lookup_ip"),
2942                        Err(fname::LookupError::NotFound)
2943                    );
2944                    // Successfully resolve IPv4.
2945                    assert_eq!(
2946                        proxy
2947                            .lookup_ip(
2948                                REMOTE_IPV4_HOST,
2949                                &fname::LookupIpOptions {
2950                                    ipv4_lookup: Some(true),
2951                                    ipv6_lookup: Some(true),
2952                                    ..Default::default()
2953                                }
2954                            )
2955                            .await
2956                            .expect("lookup_ip"),
2957                        Ok(fname::LookupResult {
2958                            addresses: Some(vec![map_ip(IPV4_HOST)]),
2959                            ..Default::default()
2960                        })
2961                    );
2962                    // Successfully resolve IPv4 + IPv6 (no sorting).
2963                    assert_eq!(
2964                        proxy
2965                            .lookup_ip(
2966                                REMOTE_IPV4_IPV6_HOST,
2967                                &fname::LookupIpOptions {
2968                                    ipv4_lookup: Some(true),
2969                                    ipv6_lookup: Some(true),
2970                                    ..Default::default()
2971                                }
2972                            )
2973                            .await
2974                            .expect("lookup_ip"),
2975                        Ok(fname::LookupResult {
2976                            addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
2977                            ..Default::default()
2978                        })
2979                    );
2980                    // Successfully resolve IPv4 + IPv6 (with sorting).
2981                    assert_eq!(
2982                        proxy
2983                            .lookup_ip(
2984                                REMOTE_IPV4_IPV6_HOST,
2985                                &fname::LookupIpOptions {
2986                                    ipv4_lookup: Some(true),
2987                                    ipv6_lookup: Some(true),
2988                                    sort_addresses: Some(true),
2989                                    ..Default::default()
2990                                }
2991                            )
2992                            .await
2993                            .expect("lookup_ip"),
2994                        Ok(fname::LookupResult {
2995                            addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
2996                            ..Default::default()
2997                        })
2998                    );
2999                },
3000                routes_handler,
3001            )
3002            .await
3003    }
3004}