1use 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
64struct QueryStats {
75 inner: Mutex<VecDeque<QueryWindow>>,
76}
77
78type 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 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
135impl 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 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#[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 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
303fn enum_variant_string(variant: &impl std::fmt::Debug) -> String {
305 let debug = format!("{:?}", variant);
306 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 resolver_opts.num_concurrent_reqs = 10;
320 resolver_opts.server_ordering_strategy = ServerOrderingStrategy::UserProvidedOrder;
323
324 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 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 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 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 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 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(..)
551 .map(|addr| async move {
552 let source_addr = match routes.resolve(&addr).await.map_err(|e: fidl::Error| {
553 warn!("fuchsia.net.routes/State.resolve FIDL error {:?}", e);
554 fname::LookupError::InternalError
555 })? {
556 Ok(fnet_routes::Resolved::Direct(fnet_routes::Destination {
557 source_address,
558 ..
559 }))
560 | Ok(fnet_routes::Resolved::Gateway(fnet_routes::Destination {
561 source_address,
562 ..
563 })) => source_address,
564 Err(e) => {
567 debug!(
568 "fuchsia.net.routes/State.resolve({}) failed {}",
569 net_ext::IpAddress::from(addr),
570 zx::Status::from_raw(e)
571 );
572 None
573 }
574 };
575 Ok((addr, DasCmpInfo::from_addrs(&addr, source_addr.as_ref())))
576 }),
577 )
578 .await?;
579 let () = addrs_info.sort_by(|(_laddr, left), (_raddr, right)| left.cmp(right));
580 let () = addrs.extend(addrs_info.into_iter().map(|(addr, _)| addr));
582 Ok(addrs)
583}
584
585#[derive(Debug)]
586struct Policy {
587 prefix: net_types::ip::Subnet<net_types::ip::Ipv6Addr>,
588 precedence: usize,
589 label: usize,
590}
591
592macro_rules! decl_policy {
593 ($ip:tt/$prefix:expr => $precedence:expr, $label:expr) => {
594 Policy {
595 prefix: unsafe {
599 net_types::ip::Subnet::new_unchecked(
600 net_types::ip::Ipv6Addr::from_bytes(fidl_ip_v6!($ip).addr),
601 $prefix,
602 )
603 },
604 precedence: $precedence,
605 label: $label,
606 }
607 };
608}
609
610const POLICY_TABLE: [Policy; 6] = [
631 decl_policy!("::1"/128 => 50, 0),
632 decl_policy!("::ffff:0:0"/96 => 35, 4),
633 decl_policy!("2001::"/32 => 5, 5),
634 decl_policy!("2002::"/16 => 30, 2),
635 decl_policy!("fc00::"/7 => 3, 13),
636 decl_policy!("::"/0 => 40, 1),
637];
638
639fn policy_lookup(addr: &net_types::ip::Ipv6Addr) -> &'static Policy {
640 POLICY_TABLE
641 .iter()
642 .find(|policy| policy.prefix.contains(addr))
643 .expect("policy table MUST contain the all addresses subnet")
644}
645
646#[derive(Debug)]
660struct DasCmpInfo {
661 usable: bool,
662 matching_scope: bool,
663 matching_label: bool,
664 precedence: usize,
665 scope: net_types::ip::Ipv6Scope,
666 common_prefix_len: u8,
667}
668
669impl DasCmpInfo {
670 fn convert_addr(fidl: &fnet::IpAddress) -> net_types::ip::Ipv6Addr {
673 match fidl {
674 fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => {
675 net_types::ip::Ipv6Addr::from(net_types::ip::Ipv4Addr::new(*addr))
676 }
677 fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => {
678 net_types::ip::Ipv6Addr::from_bytes(*addr)
679 }
680 }
681 }
682
683 fn from_addrs(dst_addr: &fnet::IpAddress, src_addr: Option<&fnet::IpAddress>) -> Self {
684 use net_types::ScopeableAddress;
685
686 let dst_addr = Self::convert_addr(dst_addr);
687 let Policy { prefix: _, precedence, label: dst_label } = policy_lookup(&dst_addr);
688 let (usable, matching_scope, matching_label, common_prefix_len) = match src_addr {
689 Some(src_addr) => {
690 let src_addr = Self::convert_addr(src_addr);
691 let Policy { prefix: _, precedence: _, label: src_label } =
692 policy_lookup(&src_addr);
693 (
694 true,
695 dst_addr.scope() == src_addr.scope(),
696 dst_label == src_label,
697 dst_addr.common_prefix_len(&src_addr),
698 )
699 }
700 None => (false, false, false, 0),
701 };
702 DasCmpInfo {
703 usable,
704 matching_scope,
705 matching_label,
706 precedence: *precedence,
707 scope: dst_addr.scope(),
708 common_prefix_len,
709 }
710 }
711}
712
713impl std::cmp::Ord for DasCmpInfo {
714 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
716 use std::cmp::Ordering;
717 let DasCmpInfo {
718 usable: self_usable,
719 matching_scope: self_matching_scope,
720 matching_label: self_matching_label,
721 precedence: self_precedence,
722 scope: self_scope,
723 common_prefix_len: self_common_prefix_len,
724 } = self;
725 let DasCmpInfo {
726 usable: other_usable,
727 matching_scope: other_matching_scope,
728 matching_label: other_matching_label,
729 precedence: other_precedence,
730 scope: other_scope,
731 common_prefix_len: other_common_prefix_len,
732 } = other;
733
734 fn prefer_true(left: bool, right: bool) -> Ordering {
735 match (left, right) {
736 (true, false) => Ordering::Less,
737 (false, true) => Ordering::Greater,
738 (false, false) | (true, true) => Ordering::Equal,
739 }
740 }
741
742 prefer_true(*self_usable, *other_usable)
744 .then(
745 prefer_true(*self_matching_scope, *other_matching_scope),
747 )
748 .then(
749 prefer_true(*self_matching_label, *other_matching_label),
751 )
752 .then(
753 self_precedence.cmp(other_precedence).reverse(),
755 )
756 .then(
757 self_scope.multicast_scope_id().cmp(&other_scope.multicast_scope_id()),
759 )
760 .then(
761 self_common_prefix_len.cmp(other_common_prefix_len).reverse(),
763 )
764 }
766}
767
768impl std::cmp::PartialOrd for DasCmpInfo {
769 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
770 Some(self.cmp(other))
771 }
772}
773
774impl std::cmp::PartialEq for DasCmpInfo {
775 fn eq(&self, other: &Self) -> bool {
776 self.cmp(other) == std::cmp::Ordering::Equal
777 }
778}
779
780impl std::cmp::Eq for DasCmpInfo {}
781
782async fn handle_lookup_hostname<T: ResolverLookup>(
783 resolver: &SharedResolver<T>,
784 addr: fnet::IpAddress,
785) -> Result<String, fname::LookupError> {
786 let net_ext::IpAddress(addr) = addr.into();
787 let resolver = resolver.read();
788
789 match resolver.reverse_lookup(addr).await {
790 Ok(response) => {
791 response.iter().next().ok_or(fname::LookupError::NotFound).map(ToString::to_string)
792 }
793 Err(error) => Err(handle_err("LookupHostname", error)),
794 }
795}
796
797struct IpLookupRequest {
798 hostname: String,
799 options: fname::LookupIpOptions,
800 responder: fname::LookupLookupIpResponder,
801}
802
803async fn run_lookup<T: ResolverLookup>(
804 resolver: &SharedResolver<T>,
805 stream: LookupRequestStream,
806 sender: mpsc::Sender<IpLookupRequest>,
807) -> Result<(), fidl::Error> {
808 stream
809 .try_for_each_concurrent(None, |request| async {
810 match request {
811 LookupRequest::LookupIp { hostname, options, responder } => {
812 let () = sender
813 .clone()
814 .send(IpLookupRequest { hostname, options, responder })
815 .await
816 .expect("receiver should not be closed");
817 Ok(())
818 }
819 LookupRequest::LookupHostname { addr, responder } => responder
820 .send(handle_lookup_hostname(&resolver, addr).await.as_deref().map_err(|e| *e)),
821 }
822 })
823 .await
824}
825
826const MAX_PARALLEL_REQUESTS: usize = 256;
827
828fn create_ip_lookup_fut<T: ResolverLookup>(
829 resolver: &SharedResolver<T>,
830 stats: Arc<QueryStats>,
831 routes: fnet_routes::StateProxy,
832 recv: mpsc::Receiver<IpLookupRequest>,
833) -> impl futures::Future<Output = ()> + '_ {
834 recv.for_each_concurrent(
835 MAX_PARALLEL_REQUESTS,
836 move |IpLookupRequest { hostname, options, responder }| {
837 let stats = stats.clone();
838 let routes = routes.clone();
839 async move {
840 let fname::LookupIpOptions {
841 ipv4_lookup,
842 ipv6_lookup,
843 sort_addresses,
844 canonical_name_lookup,
845 ..
846 } = options;
847 let ipv4_lookup = ipv4_lookup.unwrap_or(false);
848 let ipv6_lookup = ipv6_lookup.unwrap_or(false);
849 let sort_addresses = sort_addresses.unwrap_or(false);
850 let canonical_name_lookup = canonical_name_lookup.unwrap_or(false);
851 let lookup_result = (|| async {
852 let hostname = hostname.as_str();
853 match IpAddr::from_str(hostname) {
871 Ok(addr) => {
872 let _: IpAddr = addr;
873 return Err(fname::LookupError::InvalidArgs);
874 }
875 Err(std::net::AddrParseError { .. }) => {}
876 };
877 let resolver = resolver.read();
878 let start_time = fasync::MonotonicInstant::now();
879 let (ret1, ret2, ret3) = futures::future::join3(
880 futures::future::OptionFuture::from(
881 ipv4_lookup.then(|| {
882 resolver
883 .lookup(hostname, RecordType::A)
884 .map_err(|e| (LookupIpErrorSource::Ipv4, e))
885 }),
886 ),
887 futures::future::OptionFuture::from(
888 ipv6_lookup.then(|| {
889 resolver
890 .lookup(hostname, RecordType::AAAA)
891 .map_err(|e| (LookupIpErrorSource::Ipv6, e))
892 }),
893 ),
894 futures::future::OptionFuture::from(
895 canonical_name_lookup
896 .then(|| {
897 resolver
898 .lookup(hostname, RecordType::CNAME)
899 .map_err(|e| (LookupIpErrorSource::CanonicalName, e))
900 }),
901 ),
902 )
903 .await;
904 let result = [ret1, ret2, ret3];
905 if result.iter().all(Option::is_none) {
906 return Err(fname::LookupError::InvalidArgs);
907 }
908 let (addrs, cnames, error) =
909 result.into_iter().filter_map(std::convert::identity).fold(
910 (Vec::new(), Vec::new(), LookupIpErrorsFromSource::default()),
911 |(mut addrs, mut cnames, mut error), result| {
912 let () = match result {
913 Err((src, err)) => {
914 error.accumulate(src, err);
915 },
916 Ok(lookup) => lookup.iter().for_each(|rdata| match rdata {
917 RData::A(addr) if ipv4_lookup => addrs
918 .push(net_ext::IpAddress(IpAddr::V4(*addr)).into()),
919 RData::AAAA(addr) if ipv6_lookup => addrs
920 .push(net_ext::IpAddress(IpAddr::V6(*addr)).into()),
921 RData::CNAME(name) => {
922 if canonical_name_lookup {
925 cnames.push(name.to_utf8())
926 }
927 }
928 rdata => {
929 error!(
930 "Lookup(_, {:?}) yielded unexpected record type: {}",
931 options, rdata.to_record_type(),
932 )
933 }
934 }),
935 };
936 (addrs, cnames, error)
937 });
938 let count = match NonZeroUsize::try_from(addrs.len() + cnames.len()) {
939 Ok(count) => Ok(count),
940 Err(std::num::TryFromIntError { .. }) => match error.any_error() {
941 None => {
942 error!("resolver response unexpectedly contained no records \
955 and no error. See https://fxbug.dev/42062388.");
956 return Err(fname::LookupError::NotFound);
957 },
958 Some(any_err) => {
959 Err(any_err)
960 }
961 }
962 };
963 let () = stats
964 .finish_query(
965 start_time,
966 count.as_ref().copied().map_err(|e| e.kind()),
967 )
968 .await;
969 match count {
970 Ok(_) => {},
971 Err(_any_err) => {
972 return Err(error.handle());
974 }
975 }
976 let addrs = if sort_addresses {
977 sort_preferred_addresses(addrs, &routes).await?
978 } else {
979 addrs
980 };
981 let addrs = if addrs.len() > fname::MAX_ADDRESSES.into() {
982 warn!(
983 "Lookup(_, {:?}): {} addresses, truncating to {}",
984 options, addrs.len(), fname::MAX_ADDRESSES
985 );
986 let mut addrs = addrs;
987 addrs.truncate(fname::MAX_ADDRESSES.into());
988 addrs
989 } else {
990 addrs
991 };
992 if cnames.len() > 1 {
999 let cnames =
1000 cnames.iter().fold(HashMap::<&str, usize>::new(), |mut acc, cname| {
1001 *acc.entry(cname).or_default() += 1;
1002 acc
1003 });
1004 warn!(
1005 "Lookup(_, {:?}): multiple CNAMEs: {:?}",
1006 options, cnames
1007 )
1008 }
1009 let cname = {
1010 let mut cnames = cnames;
1011 cnames.pop()
1012 };
1013 Ok(fname::LookupResult {
1014 addresses: Some(addrs),
1015 canonical_name: cname,
1016 ..Default::default()
1017 })
1018 })()
1019 .await;
1020 responder.send(lookup_result.as_ref().map_err(|e| *e)).unwrap_or_else(|e|
1021 warn!(
1022 "failed to send IP lookup result due to FIDL error: {}",
1023 e
1024 )
1025 )
1026 }
1027 },
1028 )
1029}
1030
1031async fn run_lookup_admin<T: ResolverLookup>(
1033 resolver: &SharedResolver<T>,
1034 state: &dns::config::ServerConfigState,
1035 stream: LookupAdminRequestStream,
1036) -> Result<(), fidl::Error> {
1037 stream
1038 .try_for_each(|req| async {
1039 match req {
1040 LookupAdminRequest::SetDnsServers { servers, responder } => {
1041 let response = match state.update_servers(servers) {
1042 UpdateServersResult::Updated(servers) => {
1043 let () = update_resolver(resolver, servers);
1044 Ok(())
1045 }
1046 UpdateServersResult::NoChange => Ok(()),
1047 UpdateServersResult::InvalidsServers => {
1048 Err(zx::Status::INVALID_ARGS.into_raw())
1049 }
1050 };
1051 let () = responder.send(response)?;
1052 }
1053 LookupAdminRequest::GetDnsServers { responder } => {
1054 let () = responder.send(&state.servers())?;
1055 }
1056 }
1057 Ok(())
1058 })
1059 .await
1060}
1061
1062fn add_config_state_inspect(
1065 parent: &fuchsia_inspect::Node,
1066 config_state: Arc<dns::config::ServerConfigState>,
1067) -> fuchsia_inspect::LazyNode {
1068 parent.create_lazy_child("servers", move || {
1069 let config_state = config_state.clone();
1070 async move {
1071 let srv = fuchsia_inspect::Inspector::default();
1072 let server_list = config_state.servers();
1073 for (i, server) in server_list.into_iter().enumerate() {
1074 let child = srv.root().create_child(format!("{}", i));
1075 let net_ext::SocketAddress(addr) = server.into();
1076 let () = child.record_string("address", format!("{}", addr));
1077 let () = srv.root().record(child);
1078 }
1079 Ok(srv)
1080 }
1081 .boxed()
1082 })
1083}
1084
1085fn add_query_stats_inspect(
1087 parent: &fuchsia_inspect::Node,
1088 stats: Arc<QueryStats>,
1089) -> fuchsia_inspect::LazyNode {
1090 parent.create_lazy_child("query_stats", move || {
1091 let stats = stats.clone();
1092 async move {
1093 let past_queries = &*stats.inner.lock().await;
1094 let node = fuchsia_inspect::Inspector::default();
1095 for (
1096 i,
1097 QueryWindow {
1098 start,
1099 success_count,
1100 failure_count,
1101 success_elapsed_time,
1102 failure_elapsed_time,
1103 failure_stats,
1104 address_counts_histogram,
1105 },
1106 ) in past_queries.iter().enumerate()
1107 {
1108 let child = node.root().create_child(format!("window {}", i + 1));
1109
1110 match u64::try_from(start.into_nanos()) {
1111 Ok(nanos) => {
1112 let () = child.record_uint("start_time_nanos", nanos);
1113 },
1114 Err(e) => warn!(
1115 "error computing `start_time_nanos`: {:?}.into_nanos() from i64 -> u64 failed: {}",
1116 start, e
1117 ),
1118 }
1119 let () = child.record_uint("successful_queries", *success_count);
1120 let () = child.record_uint("failed_queries", *failure_count);
1121 let record_average = |name: &str, total: zx::MonotonicDuration, count: u64| {
1122 if count == 0 {
1124 return;
1125 }
1126 match u64::try_from(total.into_micros()) {
1127 Ok(micros) => child.record_uint(name, micros / count),
1128 Err(e) => warn!(
1129 "error computing `{}`: {:?}.into_micros() from i64 -> u64 failed: {}",
1130 name, success_elapsed_time, e
1131 ),
1132 }
1133 };
1134 let () = record_average(
1135 "average_success_duration_micros",
1136 *success_elapsed_time,
1137 *success_count,
1138 );
1139 let () = record_average(
1140 "average_failure_duration_micros",
1141 *failure_elapsed_time,
1142 *failure_count,
1143 );
1144 let FailureStats {
1145 message,
1146 no_connections,
1147 no_records_found: NoRecordsFoundStats {
1148 response_code_counts,
1149 },
1150 io,
1151 proto,
1152 timeout,
1153 unhandled_resolve_error_kind: UnhandledResolveErrorKindStats {
1154 resolve_error_kind_counts,
1155 },
1156 } = failure_stats;
1157 let errors = child.create_child("errors");
1158 let () = errors.record_uint("Message", *message);
1159 let () = errors.record_uint("NoConnections", *no_connections);
1160 let () = errors.record_uint("Io", *io);
1161 let () = errors.record_uint("Proto", *proto);
1162 let () = errors.record_uint("Timeout", *timeout);
1163
1164 let no_records_found_response_codes =
1165 errors.create_child("NoRecordsFoundResponseCodeCounts");
1166 for (HashableResponseCode { response_code }, count) in response_code_counts {
1167 let () = no_records_found_response_codes.record_uint(
1168 format!("{:?}", response_code),
1169 *count,
1170 );
1171 }
1172 let () = errors.record(no_records_found_response_codes);
1173
1174 let unhandled_resolve_error_kinds =
1175 errors.create_child("UnhandledResolveErrorKindCounts");
1176 for (error_kind, count) in resolve_error_kind_counts {
1177 let () = unhandled_resolve_error_kinds.record_uint(error_kind, *count);
1178 }
1179 let () = errors.record(unhandled_resolve_error_kinds);
1180
1181 let () = child.record(errors);
1182
1183 let address_counts_node = child.create_child("address_counts");
1184 for (count, occurrences) in address_counts_histogram {
1185 address_counts_node.record_uint(count.to_string(), *occurrences);
1186 }
1187 child.record(address_counts_node);
1188
1189 let () = node.root().record(child);
1190 }
1191 Ok(node)
1192 }
1193 .boxed()
1194 })
1195}
1196
1197#[fuchsia::main(logging_tags = ["dns"])]
1200pub async fn main() -> Result<(), Error> {
1201 info!("starting");
1202
1203 let mut resolver_opts = ResolverOpts::default();
1204 resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1206 let resolver = SharedResolver::new(
1207 Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1208 .expect("failed to create resolver"),
1209 );
1210
1211 let config_state = Arc::new(dns::config::ServerConfigState::new());
1212 let stats = Arc::new(QueryStats::new());
1213
1214 let mut fs = ServiceFs::new_local();
1215
1216 let inspector = fuchsia_inspect::component::inspector();
1217 let _state_inspect_node = add_config_state_inspect(inspector.root(), config_state.clone());
1218 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
1219 let _inspect_server_task =
1220 inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default())
1221 .context("publish Inspect task")?;
1222
1223 let routes = fuchsia_component::client::connect_to_protocol::<fnet_routes::StateMarker>()
1224 .context("failed to connect to fuchsia.net.routes/State")?;
1225
1226 let _: &mut ServiceFsDir<'_, _> = fs
1227 .dir("svc")
1228 .add_fidl_service(IncomingRequest::Lookup)
1229 .add_fidl_service(IncomingRequest::LookupAdmin);
1230 let _: &mut ServiceFs<_> =
1231 fs.take_and_serve_directory_handle().context("failed to serve directory")?;
1232
1233 let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1236 let serve_fut = fs.for_each_concurrent(None, |incoming_service| async {
1237 match incoming_service {
1238 IncomingRequest::Lookup(stream) => run_lookup(&resolver, stream, sender.clone())
1239 .await
1240 .unwrap_or_else(|e| warn!("run_lookup finished with error: {}", e)),
1241 IncomingRequest::LookupAdmin(stream) => {
1242 run_lookup_admin(&resolver, &config_state, stream)
1243 .await
1244 .unwrap_or_else(|e| error!("run_lookup_admin finished with error: {}", e))
1245 }
1246 }
1247 });
1248 let ip_lookup_fut = create_ip_lookup_fut(&resolver, stats.clone(), routes, recv);
1249
1250 match fuchsia_scheduler::set_role_for_this_thread("fuchsia.networking.dns.resolver.main") {
1254 Ok(_) => info!("Applied scheduling role"),
1255 Err(err) => warn!("Failed to apply scheduling role: {}", err),
1256 };
1257
1258 let ((), ()) = futures::future::join(serve_fut, ip_lookup_fut).await;
1259 Ok(())
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
1265 use std::pin::pin;
1266 use std::str::FromStr;
1267
1268 use assert_matches::assert_matches;
1269 use diagnostics_assertions::{assert_data_tree, tree_assertion, NonZeroUintProperty};
1270 use dns::test_util::*;
1271 use dns::DEFAULT_PORT;
1272 use futures::future::TryFutureExt as _;
1273 use itertools::Itertools as _;
1274 use net_declare::{fidl_ip, std_ip, std_ip_v4, std_ip_v6};
1275 use net_types::ip::Ip as _;
1276 use test_case::test_case;
1277 use trust_dns_proto::op::Query;
1278 use trust_dns_proto::rr::{Name, Record};
1279 use trust_dns_resolver::lookup::{Lookup, ReverseLookup};
1280
1281 use super::*;
1282
1283 const IPV4_LOOPBACK: fnet::IpAddress = fidl_ip!("127.0.0.1");
1284 const IPV6_LOOPBACK: fnet::IpAddress = fidl_ip!("::1");
1285 const LOCAL_HOST: &str = "localhost.";
1286
1287 const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1289 const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1291
1292 const REMOTE_IPV4_HOST: &str = "www.foo.com";
1294 const REMOTE_IPV6_HOST: &str = "www.bar.com";
1296 const REMOTE_IPV4_HOST_ALIAS: &str = "www.alsofoo.com";
1297 const REMOTE_IPV6_HOST_ALIAS: &str = "www.alsobar.com";
1298 const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1300 const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1302 const NO_RECORDS_AND_NO_ERROR_HOST: &str = "www.no-records-and-no-error.com";
1304
1305 async fn setup_namelookup_service() -> (fname::LookupProxy, impl futures::Future<Output = ()>) {
1306 let (name_lookup_proxy, stream) =
1307 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1308
1309 let mut resolver_opts = ResolverOpts::default();
1310 resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1311
1312 let resolver = SharedResolver::new(
1313 Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1314 .expect("failed to create resolver"),
1315 );
1316 let stats = Arc::new(QueryStats::new());
1317 let (routes_proxy, routes_stream) =
1318 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1319 let routes_fut =
1320 routes_stream.try_for_each(|req| -> futures::future::Ready<Result<(), fidl::Error>> {
1321 panic!("Should not call routes/State. Received request {:?}", req)
1322 });
1323 let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1324
1325 (name_lookup_proxy, async move {
1326 futures::future::try_join3(
1327 run_lookup(&resolver, stream, sender),
1328 routes_fut,
1329 create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).map(Ok),
1330 )
1331 .map(|r| match r {
1332 Ok(((), (), ())) => (),
1333 Err(e) => panic!("namelookup service error {:?}", e),
1334 })
1335 .await
1336 })
1337 }
1338
1339 #[fasync::run_singlethreaded(test)]
1340 async fn test_lookupip_localhost() {
1341 let (proxy, fut) = setup_namelookup_service().await;
1342 let ((), ()) = futures::future::join(fut, async move {
1343 assert_eq!(
1345 proxy
1346 .lookup_ip(
1347 LOCAL_HOST,
1348 &fname::LookupIpOptions {
1349 ipv4_lookup: Some(true),
1350 ipv6_lookup: Some(true),
1351 ..Default::default()
1352 }
1353 )
1354 .await
1355 .expect("lookup_ip"),
1356 Ok(fname::LookupResult {
1357 addresses: Some(vec![IPV4_LOOPBACK, IPV6_LOOPBACK]),
1358 ..Default::default()
1359 }),
1360 );
1361
1362 assert_eq!(
1364 proxy
1365 .lookup_ip(
1366 LOCAL_HOST,
1367 &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
1368 )
1369 .await
1370 .expect("lookup_ip"),
1371 Ok(fname::LookupResult {
1372 addresses: Some(vec![IPV4_LOOPBACK]),
1373 ..Default::default()
1374 }),
1375 );
1376
1377 assert_eq!(
1379 proxy
1380 .lookup_ip(
1381 LOCAL_HOST,
1382 &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
1383 )
1384 .await
1385 .expect("lookup_ip"),
1386 Ok(fname::LookupResult {
1387 addresses: Some(vec![IPV6_LOOPBACK]),
1388 ..Default::default()
1389 }),
1390 );
1391 })
1392 .await;
1393 }
1394
1395 #[fasync::run_singlethreaded(test)]
1396 async fn test_lookuphostname_localhost() {
1397 let (proxy, fut) = setup_namelookup_service().await;
1398 let ((), ()) = futures::future::join(fut, async move {
1399 let hostname = IPV4_LOOPBACK;
1400 assert_eq!(
1401 proxy.lookup_hostname(&hostname).await.expect("lookup_hostname").as_deref(),
1402 Ok(LOCAL_HOST)
1403 );
1404 })
1405 .await;
1406 }
1407
1408 struct MockResolver {
1409 config: ResolverConfig,
1410 repeat: u16,
1411 }
1412
1413 #[async_trait]
1414 impl ResolverLookup for MockResolver {
1415 fn new(config: ResolverConfig, _options: ResolverOpts) -> Self {
1416 Self { config, repeat: 1 }
1417 }
1418
1419 async fn lookup<N: IntoName + Send>(
1420 &self,
1421 name: N,
1422 record_type: RecordType,
1423 ) -> Result<lookup::Lookup, ResolveError> {
1424 let Self { config: _, repeat } = self;
1425
1426 let name = name.into_name()?;
1427 let host_name = name.to_utf8();
1428
1429 if host_name == NO_RECORDS_AND_NO_ERROR_HOST {
1430 return Ok(Lookup::new_with_max_ttl(Query::default(), Arc::new([])));
1431 }
1432 let rdatas = match record_type {
1433 RecordType::A => [REMOTE_IPV4_HOST, REMOTE_IPV4_IPV6_HOST]
1434 .contains(&host_name.as_str())
1435 .then_some(RData::A(IPV4_HOST)),
1436 RecordType::AAAA => [REMOTE_IPV6_HOST, REMOTE_IPV4_IPV6_HOST]
1437 .contains(&host_name.as_str())
1438 .then_some(RData::AAAA(IPV6_HOST)),
1439 RecordType::CNAME => match host_name.as_str() {
1440 REMOTE_IPV4_HOST_ALIAS => Some(REMOTE_IPV4_HOST),
1441 REMOTE_IPV6_HOST_ALIAS => Some(REMOTE_IPV6_HOST),
1442 _ => None,
1443 }
1444 .map(Name::from_str)
1445 .transpose()
1446 .unwrap()
1447 .map(RData::CNAME),
1448 record_type => {
1449 panic!("unexpected record type {:?}", record_type)
1450 }
1451 }
1452 .into_iter();
1453
1454 let len = rdatas.len() * usize::from(*repeat);
1455 let records: Vec<Record> = rdatas
1456 .map(|rdata| {
1457 Record::from_rdata(
1458 Name::new(),
1459 60,
1462 rdata,
1463 )
1464 })
1465 .cycle()
1466 .take(len)
1467 .collect();
1468
1469 if records.is_empty() {
1470 let mut response = trust_dns_proto::op::Message::new();
1471 let _: &mut trust_dns_proto::op::Message =
1472 response.set_response_code(ResponseCode::NoError);
1473 let error = ResolveError::from_response(response.into(), false)
1474 .expect_err("response with no records should be a NoRecordsFound error");
1475 return Err(error);
1476 }
1477
1478 Ok(Lookup::new_with_max_ttl(Query::default(), records.into()))
1479 }
1480
1481 async fn reverse_lookup(
1482 &self,
1483 addr: IpAddr,
1484 ) -> Result<lookup::ReverseLookup, ResolveError> {
1485 let lookup = if addr == IPV4_HOST {
1486 Lookup::from_rdata(
1487 Query::default(),
1488 RData::PTR(Name::from_str(REMOTE_IPV4_HOST).unwrap()),
1489 )
1490 } else if addr == IPV6_HOST {
1491 Lookup::new_with_max_ttl(
1492 Query::default(),
1493 Arc::new([
1494 Record::from_rdata(
1495 Name::new(),
1496 60, RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1499 ),
1500 Record::from_rdata(
1501 Name::new(),
1502 60, RData::PTR(Name::from_str(REMOTE_IPV6_HOST_EXTRA).unwrap()),
1505 ),
1506 ]),
1507 )
1508 } else {
1509 Lookup::new_with_max_ttl(Query::default(), Arc::new([]))
1510 };
1511 Ok(ReverseLookup::from(lookup))
1512 }
1513 }
1514
1515 struct TestEnvironment {
1516 shared_resolver: SharedResolver<MockResolver>,
1517 config_state: Arc<dns::config::ServerConfigState>,
1518 stats: Arc<QueryStats>,
1519 }
1520
1521 impl Default for TestEnvironment {
1522 fn default() -> Self {
1523 Self::new(1)
1524 }
1525 }
1526
1527 impl TestEnvironment {
1528 fn new(repeat: u16) -> Self {
1529 Self {
1530 shared_resolver: SharedResolver::new(MockResolver {
1531 config: ResolverConfig::from_parts(
1532 None,
1533 vec![],
1534 NameServerConfigGroup::with_capacity(0),
1537 ),
1538 repeat,
1539 }),
1540 config_state: Arc::new(dns::config::ServerConfigState::new()),
1541 stats: Arc::new(QueryStats::new()),
1542 }
1543 }
1544
1545 async fn run_lookup<F, Fut>(&self, f: F)
1546 where
1547 Fut: futures::Future<Output = ()>,
1548 F: FnOnce(fname::LookupProxy) -> Fut,
1549 {
1550 self.run_lookup_with_routes_handler(f, |req| {
1551 panic!("Should not call routes/State. Received request {:?}", req)
1552 })
1553 .await
1554 }
1555
1556 async fn run_lookup_with_routes_handler<F, Fut, R>(&self, f: F, handle_routes: R)
1557 where
1558 Fut: futures::Future<Output = ()>,
1559 F: FnOnce(fname::LookupProxy) -> Fut,
1560 R: Fn(fnet_routes::StateRequest),
1561 {
1562 let (name_lookup_proxy, name_lookup_stream) =
1563 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1564
1565 let (routes_proxy, routes_stream) =
1566 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1567
1568 let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1569 let Self { shared_resolver, config_state: _, stats } = self;
1570 let ((), (), (), ()) = futures::future::try_join4(
1571 run_lookup(shared_resolver, name_lookup_stream, sender),
1572 f(name_lookup_proxy).map(Ok),
1573 routes_stream.try_for_each(|req| futures::future::ok(handle_routes(req))),
1574 create_ip_lookup_fut(shared_resolver, stats.clone(), routes_proxy, recv).map(Ok),
1575 )
1576 .await
1577 .expect("Error running lookup future");
1578 }
1579
1580 async fn run_admin<F, Fut>(&self, f: F)
1581 where
1582 Fut: futures::Future<Output = ()>,
1583 F: FnOnce(fname::LookupAdminProxy) -> Fut,
1584 {
1585 let (lookup_admin_proxy, lookup_admin_stream) =
1586 fidl::endpoints::create_proxy_and_stream::<fname::LookupAdminMarker>();
1587 let Self { shared_resolver, config_state, stats: _ } = self;
1588 let ((), ()) = futures::future::try_join(
1589 run_lookup_admin(shared_resolver, config_state, lookup_admin_stream)
1590 .map_err(anyhow::Error::from),
1591 f(lookup_admin_proxy).map(Ok),
1592 )
1593 .await
1594 .expect("Error running admin future");
1595 }
1596 }
1597
1598 fn map_ip<T: Into<IpAddr>>(addr: T) -> fnet::IpAddress {
1599 net_ext::IpAddress(addr.into()).into()
1600 }
1601
1602 #[fasync::run_singlethreaded(test)]
1603 async fn test_no_records_and_no_error() {
1604 TestEnvironment::default()
1605 .run_lookup(|proxy| async move {
1606 let proxy = &proxy;
1607 futures::stream::iter([(true, true), (true, false), (false, true)])
1608 .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1609 assert_eq!(
1614 proxy
1615 .lookup_ip(
1616 NO_RECORDS_AND_NO_ERROR_HOST,
1617 &fname::LookupIpOptions {
1618 ipv4_lookup: Some(ipv4_lookup),
1619 ipv6_lookup: Some(ipv6_lookup),
1620 ..Default::default()
1621 }
1622 )
1623 .await
1624 .expect("lookup_ip"),
1625 Err(fname::LookupError::NotFound),
1626 );
1627 })
1628 .await
1629 })
1630 .await;
1631 }
1632
1633 #[fasync::run_singlethreaded(test)]
1634 async fn test_lookupip_remotehost_overflow() {
1635 const REPEAT: u16 = fname::MAX_ADDRESSES / 2 + 1;
1637 let expected = std::iter::empty()
1638 .chain(std::iter::repeat(map_ip(IPV4_HOST)).take(REPEAT.into()))
1639 .chain(std::iter::repeat(map_ip(IPV6_HOST)).take(REPEAT.into()))
1640 .take(fname::MAX_ADDRESSES.into())
1641 .collect::<Vec<_>>();
1642 assert_eq!(expected.len(), usize::from(fname::MAX_ADDRESSES));
1643 TestEnvironment::new(REPEAT)
1644 .run_lookup(|proxy| async move {
1645 assert_eq!(
1646 proxy
1647 .lookup_ip(
1648 REMOTE_IPV4_IPV6_HOST,
1649 &fname::LookupIpOptions {
1650 ipv4_lookup: Some(true),
1651 ipv6_lookup: Some(true),
1652 ..Default::default()
1653 }
1654 )
1655 .await
1656 .expect("lookup_ip"),
1657 Ok(fname::LookupResult { addresses: Some(expected), ..Default::default() })
1658 );
1659 })
1660 .await;
1661 }
1662
1663 #[fasync::run_singlethreaded(test)]
1664 async fn test_lookupip_remotehost_ipv4() {
1665 TestEnvironment::default()
1666 .run_lookup(|proxy| async move {
1667 assert_eq!(
1669 proxy
1670 .lookup_ip(
1671 REMOTE_IPV4_HOST,
1672 &fname::LookupIpOptions {
1673 ipv4_lookup: Some(true),
1674 ipv6_lookup: Some(true),
1675 ..Default::default()
1676 }
1677 )
1678 .await
1679 .expect("lookup_ip"),
1680 Ok(fname::LookupResult {
1681 addresses: Some(vec![map_ip(IPV4_HOST)]),
1682 ..Default::default()
1683 }),
1684 );
1685
1686 assert_eq!(
1688 proxy
1689 .lookup_ip(
1690 REMOTE_IPV4_HOST,
1691 &fname::LookupIpOptions {
1692 ipv4_lookup: Some(true),
1693 ..Default::default()
1694 }
1695 )
1696 .await
1697 .expect("lookup_ip"),
1698 Ok(fname::LookupResult {
1699 addresses: Some(vec![map_ip(IPV4_HOST)]),
1700 ..Default::default()
1701 }),
1702 );
1703
1704 assert_eq!(
1706 proxy
1707 .lookup_ip(
1708 REMOTE_IPV4_HOST,
1709 &fname::LookupIpOptions {
1710 ipv6_lookup: Some(true),
1711 ..Default::default()
1712 }
1713 )
1714 .await
1715 .expect("lookup_ip"),
1716 Err(fname::LookupError::NotFound),
1717 );
1718 })
1719 .await;
1720 }
1721
1722 #[fasync::run_singlethreaded(test)]
1723 async fn test_lookupip_remotehost_ipv6() {
1724 TestEnvironment::default()
1725 .run_lookup(|proxy| async move {
1726 assert_eq!(
1728 proxy
1729 .lookup_ip(
1730 REMOTE_IPV6_HOST,
1731 &fname::LookupIpOptions {
1732 ipv4_lookup: Some(true),
1733 ipv6_lookup: Some(true),
1734 ..Default::default()
1735 }
1736 )
1737 .await
1738 .expect("lookup_ip"),
1739 Ok(fname::LookupResult {
1740 addresses: Some(vec![map_ip(IPV6_HOST)]),
1741 ..Default::default()
1742 }),
1743 );
1744
1745 assert_eq!(
1747 proxy
1748 .lookup_ip(
1749 REMOTE_IPV6_HOST,
1750 &fname::LookupIpOptions {
1751 ipv4_lookup: Some(true),
1752 ..Default::default()
1753 }
1754 )
1755 .await
1756 .expect("lookup_ip"),
1757 Err(fname::LookupError::NotFound),
1758 );
1759
1760 assert_eq!(
1762 proxy
1763 .lookup_ip(
1764 REMOTE_IPV6_HOST,
1765 &fname::LookupIpOptions {
1766 ipv6_lookup: Some(true),
1767 ..Default::default()
1768 }
1769 )
1770 .await
1771 .expect("lookup_ip"),
1772 Ok(fname::LookupResult {
1773 addresses: Some(vec![map_ip(IPV6_HOST)]),
1774 ..Default::default()
1775 }),
1776 );
1777 })
1778 .await;
1779 }
1780
1781 #[test_case(REMOTE_IPV4_HOST_ALIAS, REMOTE_IPV4_HOST; "ipv4")]
1782 #[test_case(REMOTE_IPV6_HOST_ALIAS, REMOTE_IPV6_HOST; "ipv6")]
1783 #[fasync::run_singlethreaded(test)]
1784 async fn test_lookupip_remotehost_canonical_name(hostname: &str, expected: &str) {
1785 TestEnvironment::default()
1786 .run_lookup(|proxy| async move {
1787 assert_matches!(
1788 proxy
1789 .lookup_ip(
1790 hostname,
1791 &fname::LookupIpOptions {
1792 canonical_name_lookup: Some(true),
1793 ..Default::default()
1794 }
1795 )
1796 .await,
1797 Ok(Ok(fname::LookupResult {
1798 canonical_name: Some(cname),
1799 ..
1800 })) => assert_eq!(cname, expected)
1801 );
1802 })
1803 .await;
1804 }
1805
1806 #[fasync::run_singlethreaded(test)]
1807 async fn test_lookupip_ip_literal() {
1808 TestEnvironment::default()
1809 .run_lookup(|proxy| async move {
1810 let proxy = &proxy;
1811
1812 let range = || [true, false].into_iter();
1813
1814 futures::stream::iter(range().cartesian_product(range()))
1815 .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1816 assert_eq!(
1817 proxy
1818 .lookup_ip(
1819 "240.0.0.2",
1820 &fname::LookupIpOptions {
1821 ipv4_lookup: Some(ipv4_lookup),
1822 ipv6_lookup: Some(ipv6_lookup),
1823 ..Default::default()
1824 }
1825 )
1826 .await
1827 .expect("lookup_ip"),
1828 Err(fname::LookupError::InvalidArgs),
1829 "ipv4_lookup={},ipv6_lookup={}",
1830 ipv4_lookup,
1831 ipv6_lookup,
1832 );
1833
1834 assert_eq!(
1835 proxy
1836 .lookup_ip(
1837 "abcd::2",
1838 &fname::LookupIpOptions {
1839 ipv4_lookup: Some(ipv4_lookup),
1840 ipv6_lookup: Some(ipv6_lookup),
1841 ..Default::default()
1842 }
1843 )
1844 .await
1845 .expect("lookup_ip"),
1846 Err(fname::LookupError::InvalidArgs),
1847 "ipv4_lookup={},ipv6_lookup={}",
1848 ipv4_lookup,
1849 ipv6_lookup,
1850 );
1851 })
1852 .await
1853 })
1854 .await
1855 }
1856
1857 #[fasync::run_singlethreaded(test)]
1858 async fn test_lookup_hostname() {
1859 TestEnvironment::default()
1860 .run_lookup(|proxy| async move {
1861 assert_eq!(
1862 proxy
1863 .lookup_hostname(&map_ip(IPV4_HOST))
1864 .await
1865 .expect("lookup_hostname")
1866 .as_deref(),
1867 Ok(REMOTE_IPV4_HOST)
1868 );
1869 })
1870 .await;
1871 }
1872
1873 #[fasync::run_singlethreaded(test)]
1876 async fn test_lookup_hostname_multi() {
1877 TestEnvironment::default()
1878 .run_lookup(|proxy| async move {
1879 assert_eq!(
1880 proxy
1881 .lookup_hostname(&map_ip(IPV6_HOST))
1882 .await
1883 .expect("lookup_hostname")
1884 .as_deref(),
1885 Ok(REMOTE_IPV6_HOST)
1886 );
1887 })
1888 .await;
1889 }
1890
1891 #[fasync::run_singlethreaded(test)]
1892 async fn test_set_server_names() {
1893 let env = TestEnvironment::default();
1894
1895 let to_server_configs = |socket_addr: SocketAddr| -> [NameServerConfig; 2] {
1896 [
1897 NameServerConfig {
1898 socket_addr,
1899 protocol: Protocol::Udp,
1900 tls_dns_name: None,
1901 trust_nx_responses: false,
1902 bind_addr: None,
1903 },
1904 NameServerConfig {
1905 socket_addr,
1906 protocol: Protocol::Tcp,
1907 tls_dns_name: None,
1908 trust_nx_responses: false,
1909 bind_addr: None,
1910 },
1911 ]
1912 };
1913
1914 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1916
1917 env.run_admin(|proxy| async move {
1919 let () = proxy
1920 .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
1921 .await
1922 .expect("Failed to call SetDnsServers")
1923 .expect("SetDnsServers error");
1924 })
1925 .await;
1926 assert_eq!(
1927 env.shared_resolver.read().config.name_servers().to_vec(),
1928 vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
1929 .into_iter()
1930 .map(|s| {
1931 let net_ext::SocketAddress(s) = s.into();
1932 s
1933 })
1934 .flat_map(|x| to_server_configs(x).to_vec().into_iter())
1935 .collect::<Vec<_>>()
1936 );
1937
1938 env.run_admin(|proxy| async move {
1940 let () = proxy
1941 .set_dns_servers(&[])
1942 .await
1943 .expect("Failed to call SetDnsServers")
1944 .expect("SetDnsServers error");
1945 })
1946 .await;
1947 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
1948 }
1949
1950 #[fasync::run_singlethreaded(test)]
1951 async fn test_set_server_names_error() {
1952 let env = TestEnvironment::default();
1953 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1955
1956 env.run_admin(|proxy| async move {
1957 let status = proxy
1961 .set_dns_servers(&[fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
1962 address: fnet::Ipv4Address { addr: [224, 0, 0, 1] },
1963 port: DEFAULT_PORT,
1964 })])
1965 .await
1966 .expect("Failed to call SetDnsServers")
1967 .expect_err("SetDnsServers should fail for multicast address");
1968 assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
1969
1970 let status = proxy
1972 .set_dns_servers(&[fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
1973 address: fnet::Ipv6Address { addr: [0; 16] },
1974 port: DEFAULT_PORT,
1975 zone_index: 0,
1976 })])
1977 .await
1978 .expect("Failed to call SetDnsServers")
1979 .expect_err("SetDnsServers should fail for unspecified address");
1980 assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
1981 })
1982 .await;
1983
1984 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1986 }
1987
1988 #[fasync::run_singlethreaded(test)]
1989 async fn test_get_servers() {
1990 let env = TestEnvironment::default();
1991 env.run_admin(|proxy| async move {
1992 let expect = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
1993 let () = proxy
1994 .set_dns_servers(expect)
1995 .await
1996 .expect("FIDL error")
1997 .expect("set_servers failed");
1998 assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
1999 })
2000 .await;
2001 }
2002
2003 #[fasync::run_singlethreaded(test)]
2004 async fn test_config_inspect() {
2005 let env = TestEnvironment::default();
2006 let inspector = fuchsia_inspect::Inspector::default();
2007 let _config_state_node =
2008 add_config_state_inspect(inspector.root(), env.config_state.clone());
2009 assert_data_tree!(inspector, root:{
2010 servers: {}
2011 });
2012 env.run_admin(|proxy| async move {
2013 let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2014 let () = proxy
2015 .set_dns_servers(servers)
2016 .await
2017 .expect("FIDL error")
2018 .expect("set_servers failed");
2019 })
2020 .await;
2021 assert_data_tree!(inspector, root:{
2022 servers: {
2023 "0": {
2024 address: "[2001:4860:4860::4444%2]:53",
2025 },
2026 "1": {
2027 address: "8.8.4.4:53",
2028 },
2029 "2": {
2030 address: "[2002:4860:4860::4444%3]:53",
2031 },
2032 "3": {
2033 address: "8.8.8.8:53",
2034 },
2035 }
2036 });
2037 }
2038
2039 #[test]
2040 fn test_unhandled_resolve_error_kind_stats() {
2041 use ResolveErrorKind::{Msg, Timeout};
2042 let mut unhandled_resolve_error_kind_stats = UnhandledResolveErrorKindStats::default();
2043 assert_eq!(
2044 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2045 "Msg"
2046 );
2047 assert_eq!(
2048 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2049 "Msg"
2050 );
2051 assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2052 assert_eq!(
2053 unhandled_resolve_error_kind_stats,
2054 UnhandledResolveErrorKindStats {
2055 resolve_error_kind_counts: [(String::from("Msg"), 2), (String::from("Timeout"), 1)]
2056 .into()
2057 }
2058 )
2059 }
2060
2061 #[fasync::run_singlethreaded(test)]
2062 async fn test_query_stats_updated() {
2063 let env = TestEnvironment::default();
2064 let inspector = fuchsia_inspect::Inspector::default();
2065 let _query_stats_inspect_node =
2066 add_query_stats_inspect(inspector.root(), env.stats.clone());
2067 assert_data_tree!(inspector, root:{
2068 query_stats: {}
2069 });
2070
2071 let () = env
2072 .run_lookup(|proxy| async move {
2073 assert_eq!(
2075 proxy
2076 .lookup_ip(
2077 REMOTE_IPV4_HOST,
2078 &fname::LookupIpOptions {
2079 ipv4_lookup: Some(true),
2080 ..Default::default()
2081 }
2082 )
2083 .await
2084 .expect("lookup_ip"),
2085 Ok(fname::LookupResult {
2086 addresses: Some(vec![map_ip(IPV4_HOST)]),
2087 ..Default::default()
2088 }),
2089 );
2090 })
2091 .await;
2092 let () = env
2093 .run_lookup(|proxy| async move {
2094 assert_eq!(
2096 proxy
2097 .lookup_ip(
2098 REMOTE_IPV4_HOST,
2099 &fname::LookupIpOptions {
2100 ipv6_lookup: Some(true),
2101 ..Default::default()
2102 }
2103 )
2104 .await
2105 .expect("lookup_ip"),
2106 Err(fname::LookupError::NotFound),
2107 );
2108 })
2109 .await;
2110 assert_data_tree!(inspector, root:{
2111 query_stats: {
2112 "window 1": {
2113 start_time_nanos: NonZeroUintProperty,
2114 successful_queries: 1u64,
2115 failed_queries: 1u64,
2116 average_success_duration_micros: NonZeroUintProperty,
2117 average_failure_duration_micros: NonZeroUintProperty,
2118 errors: {
2119 Message: 0u64,
2120 NoConnections: 0u64,
2121 NoRecordsFoundResponseCodeCounts: {
2122 NoError: 1u64,
2123 },
2124 Io: 0u64,
2125 Proto: 0u64,
2126 Timeout: 0u64,
2127 UnhandledResolveErrorKindCounts: {},
2128 },
2129 address_counts: {
2130 "1": 1u64,
2131 },
2132 },
2133 }
2134 });
2135 }
2136
2137 fn run_fake_lookup(
2138 exec: &mut fasync::TestExecutor,
2139 stats: Arc<QueryStats>,
2140 result: QueryResult<'_>,
2141 delay: zx::MonotonicDuration,
2142 ) {
2143 let start_time = fasync::MonotonicInstant::now();
2144 let () = exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2145 let update_stats = stats.finish_query(start_time, result);
2146 let mut update_stats = pin!(update_stats);
2147 assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2148 }
2149
2150 const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2152
2153 #[test]
2154 fn test_query_stats_inspect_average() {
2155 let mut exec = fasync::TestExecutor::new_with_fake_time();
2156 const START_NANOS: i64 = 1_234_567;
2157 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2158
2159 let stats = Arc::new(QueryStats::new());
2160 let inspector = fuchsia_inspect::Inspector::default();
2161 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2162 const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2163 const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2164 zx::MonotonicDuration::from_seconds(30);
2165 for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2166 let () = run_fake_lookup(
2167 &mut exec,
2168 stats.clone(),
2169 Ok(NON_ZERO_USIZE_ONE),
2170 zx::MonotonicDuration::from_nanos(0),
2171 );
2172 let () = run_fake_lookup(
2173 &mut exec,
2174 stats.clone(),
2175 Ok(NON_ZERO_USIZE_ONE),
2176 SUCCESSFUL_QUERY_DURATION,
2177 );
2178 let () = exec.set_fake_time(fasync::MonotonicInstant::after(
2179 STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2180 ));
2181 }
2182 let mut expected = tree_assertion!(query_stats: {});
2183 for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2184 let name = &format!("window {}", i + 1);
2185 let child = tree_assertion!(var name: {
2186 start_time_nanos: u64::try_from(
2187 START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2188 ).unwrap(),
2189 successful_queries: 2u64,
2190 failed_queries: 0u64,
2191 average_success_duration_micros: u64::try_from(
2192 SUCCESSFUL_QUERY_DURATION.into_micros()
2193 ).unwrap() / 2,
2194 errors: {
2195 Message: 0u64,
2196 NoConnections: 0u64,
2197 NoRecordsFoundResponseCodeCounts: {},
2198 Io: 0u64,
2199 Proto: 0u64,
2200 Timeout: 0u64,
2201 UnhandledResolveErrorKindCounts: {},
2202 },
2203 address_counts: {
2204 "1": 2u64,
2205 },
2206 });
2207 expected.add_child_assertion(child);
2208 }
2209 assert_data_tree!(inspector, root: {
2210 expected,
2211 });
2212 }
2213
2214 #[test]
2215 fn test_query_stats_inspect_error_counters() {
2216 let mut exec = fasync::TestExecutor::new_with_fake_time();
2217 const START_NANOS: i64 = 1_234_567;
2218 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2219
2220 let stats = Arc::new(QueryStats::new());
2221 let inspector = fuchsia_inspect::Inspector::default();
2222 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2223 const FAILED_QUERY_COUNT: u64 = 10;
2224 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2225 zx::MonotonicDuration::from_millis(500);
2226 for _ in 0..FAILED_QUERY_COUNT {
2227 let () = run_fake_lookup(
2228 &mut exec,
2229 stats.clone(),
2230 Err(&ResolveErrorKind::Timeout),
2231 FAILED_QUERY_DURATION,
2232 );
2233 }
2234 assert_data_tree!(inspector, root:{
2235 query_stats: {
2236 "window 1": {
2237 start_time_nanos: u64::try_from(
2238 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2239 ).unwrap(),
2240 successful_queries: 0u64,
2241 failed_queries: FAILED_QUERY_COUNT,
2242 average_failure_duration_micros: u64::try_from(
2243 FAILED_QUERY_DURATION.into_micros()
2244 ).unwrap(),
2245 errors: {
2246 Message: 0u64,
2247 NoConnections: 0u64,
2248 NoRecordsFoundResponseCodeCounts: {},
2249 Io: 0u64,
2250 Proto: 0u64,
2251 Timeout: FAILED_QUERY_COUNT,
2252 UnhandledResolveErrorKindCounts: {},
2253 },
2254 address_counts: {},
2255 },
2256 }
2257 });
2258 }
2259
2260 #[test]
2261 fn test_query_stats_inspect_no_records_found() {
2262 let mut exec = fasync::TestExecutor::new_with_fake_time();
2263 const START_NANOS: i64 = 1_234_567;
2264 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2265
2266 let stats = Arc::new(QueryStats::new());
2267 let inspector = fuchsia_inspect::Inspector::default();
2268 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2269 const FAILED_QUERY_COUNT: u64 = 10;
2270 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2271 zx::MonotonicDuration::from_millis(500);
2272
2273 let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2274 run_fake_lookup(
2275 &mut exec,
2276 stats.clone(),
2277 Err(&ResolveErrorKind::NoRecordsFound {
2278 query: Box::new(Query::default()),
2279 soa: None,
2280 negative_ttl: None,
2281 response_code,
2282 trusted: false,
2283 }),
2284 FAILED_QUERY_DURATION,
2285 )
2286 };
2287
2288 for _ in 0..FAILED_QUERY_COUNT {
2289 let () = run_fake_no_records_lookup(ResponseCode::NXDomain);
2290 let () = run_fake_no_records_lookup(ResponseCode::Refused);
2291 let () = run_fake_no_records_lookup(4096.into());
2292 let () = run_fake_no_records_lookup(4097.into());
2293 }
2294
2295 assert_data_tree!(inspector, root:{
2296 query_stats: {
2297 "window 1": {
2298 start_time_nanos: u64::try_from(
2299 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2300 ).unwrap(),
2301 successful_queries: 0u64,
2302 failed_queries: FAILED_QUERY_COUNT * 4,
2303 average_failure_duration_micros: u64::try_from(
2304 FAILED_QUERY_DURATION.into_micros()
2305 ).unwrap(),
2306 errors: {
2307 Message: 0u64,
2308 NoConnections: 0u64,
2309 NoRecordsFoundResponseCodeCounts: {
2310 NXDomain: FAILED_QUERY_COUNT,
2311 Refused: FAILED_QUERY_COUNT,
2312 "Unknown(4096)": FAILED_QUERY_COUNT,
2313 "Unknown(4097)": FAILED_QUERY_COUNT,
2314 },
2315 Io: 0u64,
2316 Proto: 0u64,
2317 Timeout: 0u64,
2318 UnhandledResolveErrorKindCounts: {},
2319 },
2320 address_counts: {},
2321 },
2322 }
2323 });
2324 }
2325
2326 #[test]
2327 fn test_query_stats_resolved_address_counts() {
2328 let mut exec = fasync::TestExecutor::new_with_fake_time();
2329 const START_NANOS: i64 = 1_234_567;
2330 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2331
2332 let stats = Arc::new(QueryStats::new());
2333 let inspector = fuchsia_inspect::Inspector::default();
2334 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2335
2336 let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2342 const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2343 for (count, occurrences) in address_counts.iter() {
2344 for _ in 0..*occurrences {
2345 run_fake_lookup(
2346 &mut exec,
2347 stats.clone(),
2348 Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2349 QUERY_DURATION,
2350 );
2351 }
2352 }
2353
2354 let mut expected_address_counts = tree_assertion!(address_counts: {});
2355 for (count, occurrences) in address_counts.iter() {
2356 expected_address_counts
2357 .add_property_assertion(&count.to_string(), Box::new(*occurrences));
2358 }
2359 assert_data_tree!(inspector, root: {
2360 query_stats: {
2361 "window 1": {
2362 start_time_nanos: u64::try_from(
2363 START_NANOS + QUERY_DURATION.into_nanos()
2364 ).unwrap(),
2365 successful_queries: address_counts.values().sum::<u64>(),
2366 failed_queries: 0u64,
2367 average_success_duration_micros: u64::try_from(
2368 QUERY_DURATION.into_micros()
2369 ).unwrap(),
2370 errors: {
2371 Message: 0u64,
2372 NoConnections: 0u64,
2373 NoRecordsFoundResponseCodeCounts: {},
2374 Io: 0u64,
2375 Proto: 0u64,
2376 Timeout: 0u64,
2377 UnhandledResolveErrorKindCounts: {},
2378 },
2379 expected_address_counts,
2380 },
2381 },
2382 });
2383 }
2384
2385 #[test]
2386 fn test_query_stats_inspect_oldest_stats_erased() {
2387 let mut exec = fasync::TestExecutor::new_with_fake_time();
2388 const START_NANOS: i64 = 1_234_567;
2389 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2390
2391 let stats = Arc::new(QueryStats::new());
2392 let inspector = fuchsia_inspect::Inspector::default();
2393 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2394 const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2395 for _ in 0..STAT_WINDOW_COUNT {
2396 let () =
2397 run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2398 let () =
2399 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2400 }
2401 for _ in 0..STAT_WINDOW_COUNT {
2402 let () = run_fake_lookup(
2403 &mut exec,
2404 stats.clone(),
2405 Ok(NON_ZERO_USIZE_ONE),
2406 DELAY,
2407 );
2408 let () =
2409 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2410 }
2411 let mut expected = tree_assertion!(query_stats: {});
2414 let start_offset = START_NANOS
2415 + DELAY.into_nanos()
2416 + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2417 for i in 0..STAT_WINDOW_COUNT {
2418 let name = &format!("window {}", i + 1);
2419 let child = tree_assertion!(var name: {
2420 start_time_nanos: u64::try_from(
2421 start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2422 ).unwrap(),
2423 successful_queries: 1u64,
2424 failed_queries: 0u64,
2425 average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2426 errors: {
2427 Message: 0u64,
2428 NoConnections: 0u64,
2429 NoRecordsFoundResponseCodeCounts: {},
2430 Io: 0u64,
2431 Proto: 0u64,
2432 Timeout: 0u64,
2433 UnhandledResolveErrorKindCounts: {},
2434 },
2435 address_counts: {
2436 "1": 1u64,
2437 },
2438 });
2439 expected.add_child_assertion(child);
2440 }
2441 assert_data_tree!(inspector, root: {
2442 expected,
2443 });
2444 }
2445
2446 struct BlockingResolver {}
2447
2448 #[async_trait]
2449 impl ResolverLookup for BlockingResolver {
2450 fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2451 BlockingResolver {}
2452 }
2453
2454 async fn lookup<N: IntoName + Send>(
2455 &self,
2456 _name: N,
2457 _record_type: RecordType,
2458 ) -> Result<lookup::Lookup, ResolveError> {
2459 futures::future::pending().await
2460 }
2461
2462 async fn reverse_lookup(
2463 &self,
2464 _addr: IpAddr,
2465 ) -> Result<lookup::ReverseLookup, ResolveError> {
2466 panic!("BlockingResolver does not handle reverse lookup")
2467 }
2468 }
2469
2470 #[fasync::run_singlethreaded(test)]
2471 async fn test_parallel_query_limit() {
2472 let requests = {
2476 let (name_lookup_proxy, name_lookup_stream) =
2477 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2478 const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2479 for _ in 0..NUM_REQUESTS {
2480 let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2484 name_lookup_proxy.lookup_ip(
2485 LOCAL_HOST,
2486 &fname::LookupIpOptions {
2487 ipv4_lookup: Some(true),
2488 ipv6_lookup: Some(true),
2489 ..Default::default()
2490 },
2491 );
2492 }
2493 drop(name_lookup_proxy);
2495 let requests = name_lookup_stream
2496 .map(|request| match request.expect("channel error") {
2497 LookupRequest::LookupIp { hostname, options, responder } => {
2498 IpLookupRequest { hostname, options, responder }
2499 }
2500 req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2501 })
2502 .collect::<Vec<_>>()
2503 .await;
2504 assert_eq!(requests.len(), NUM_REQUESTS);
2505 requests
2506 };
2507
2508 let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2509
2510 const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2516 const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2517 let mut send_fut = pin!(async {
2518 for (i, req) in requests.into_iter().enumerate() {
2519 match i {
2520 BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2521 LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2522 _ => assert_matches!(sender.send(req).await, Ok(())),
2523 }
2524 }
2525 }
2526 .fuse());
2527 let mut recv_fut = pin!({
2528 let resolver = SharedResolver::new(BlockingResolver::new(
2529 ResolverConfig::default(),
2530 ResolverOpts::default(),
2531 ));
2532 let stats = Arc::new(QueryStats::new());
2533 let (routes_proxy, _routes_stream) =
2534 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2535 async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2536 .fuse()
2537 });
2538 futures::select! {
2539 () = send_fut => {},
2540 () = recv_fut => panic!("recv_fut should never complete"),
2541 };
2542 }
2543
2544 #[test]
2545 fn test_failure_stats() {
2546 use anyhow::anyhow;
2547 use trust_dns_proto::error::ProtoError;
2548 use trust_dns_proto::op::Query;
2549
2550 let mut stats = FailureStats::default();
2551 for (error_kind, expected) in &[
2552 (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2553 (
2554 ResolveErrorKind::Msg("foo".to_string()),
2555 FailureStats { message: 2, ..Default::default() },
2556 ),
2557 (
2558 ResolveErrorKind::NoRecordsFound {
2559 query: Box::new(Query::default()),
2560 soa: None,
2561 negative_ttl: None,
2562 response_code: ResponseCode::Refused,
2563 trusted: false,
2564 },
2565 FailureStats {
2566 message: 2,
2567 no_records_found: NoRecordsFoundStats {
2568 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2569 },
2570 ..Default::default()
2571 },
2572 ),
2573 (
2574 ResolveErrorKind::Io(std::io::Error::new(
2575 std::io::ErrorKind::NotFound,
2576 anyhow!("foo"),
2577 )),
2578 FailureStats {
2579 message: 2,
2580 no_records_found: NoRecordsFoundStats {
2581 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2582 },
2583 io: 1,
2584 ..Default::default()
2585 },
2586 ),
2587 (
2588 ResolveErrorKind::Proto(ProtoError::from("foo")),
2589 FailureStats {
2590 message: 2,
2591 no_records_found: NoRecordsFoundStats {
2592 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2593 },
2594 io: 1,
2595 proto: 1,
2596 ..Default::default()
2597 },
2598 ),
2599 (
2600 ResolveErrorKind::NoConnections,
2601 FailureStats {
2602 message: 2,
2603 no_connections: 1,
2604 no_records_found: NoRecordsFoundStats {
2605 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2606 },
2607 io: 1,
2608 proto: 1,
2609 ..Default::default()
2610 },
2611 ),
2612 (
2613 ResolveErrorKind::Timeout,
2614 FailureStats {
2615 message: 2,
2616 no_connections: 1,
2617 no_records_found: NoRecordsFoundStats {
2618 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2619 },
2620 io: 1,
2621 proto: 1,
2622 timeout: 1,
2623 unhandled_resolve_error_kind: Default::default(),
2624 },
2625 ),
2626 (
2627 ResolveErrorKind::NoRecordsFound {
2628 query: Box::new(Query::default()),
2629 soa: None,
2630 negative_ttl: None,
2631 response_code: ResponseCode::NXDomain,
2632 trusted: false,
2633 },
2634 FailureStats {
2635 message: 2,
2636 no_connections: 1,
2637 no_records_found: NoRecordsFoundStats {
2638 response_code_counts: [
2639 (ResponseCode::NXDomain.into(), 1),
2640 (ResponseCode::Refused.into(), 1),
2641 ]
2642 .into(),
2643 },
2644 io: 1,
2645 proto: 1,
2646 timeout: 1,
2647 unhandled_resolve_error_kind: Default::default(),
2648 },
2649 ),
2650 (
2651 ResolveErrorKind::NoRecordsFound {
2652 query: Box::new(Query::default()),
2653 soa: None,
2654 negative_ttl: None,
2655 response_code: ResponseCode::NXDomain,
2656 trusted: false,
2657 },
2658 FailureStats {
2659 message: 2,
2660 no_connections: 1,
2661 no_records_found: NoRecordsFoundStats {
2662 response_code_counts: [
2663 (ResponseCode::NXDomain.into(), 2),
2664 (ResponseCode::Refused.into(), 1),
2665 ]
2666 .into(),
2667 },
2668 io: 1,
2669 proto: 1,
2670 timeout: 1,
2671 unhandled_resolve_error_kind: Default::default(),
2672 },
2673 ),
2674 ][..]
2675 {
2676 let () = stats.increment(error_kind);
2677 assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2678 }
2679 }
2680
2681 fn test_das_helper(
2682 l_addr: fnet::IpAddress,
2683 l_src: Option<fnet::IpAddress>,
2684 r_addr: fnet::IpAddress,
2685 r_src: Option<fnet::IpAddress>,
2686 want: std::cmp::Ordering,
2687 ) {
2688 let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2689 let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2690 assert_eq!(
2691 left.cmp(&right),
2692 want,
2693 "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2694 want,
2695 l_addr,
2696 l_src,
2697 left,
2698 r_addr,
2699 r_src,
2700 right
2701 );
2702 }
2703
2704 macro_rules! add_das_test {
2705 ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2706 #[test]
2707 fn $name() {
2708 test_das_helper(
2709 $pref_dst,
2710 $pref_src,
2711 $other_dst,
2712 $other_src,
2713 std::cmp::Ordering::Less,
2714 )
2715 }
2716 };
2717 }
2718
2719 add_das_test!(
2720 prefer_reachable,
2721 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2722 other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2723 );
2724
2725 add_das_test!(
2728 prefer_matching_scope,
2729 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2730 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2731 );
2732
2733 add_das_test!(
2734 prefer_matching_label,
2735 preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2736 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2737 );
2738
2739 add_das_test!(
2740 prefer_higher_precedence_1,
2741 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2742 other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2743 );
2744
2745 add_das_test!(
2746 prefer_higher_precedence_2,
2747 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2748 other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2749 );
2750
2751 add_das_test!(
2752 prefer_smaller_scope,
2753 preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2754 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2755 );
2756
2757 add_das_test!(
2758 prefer_longest_matching_prefix,
2759 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2760 other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2761 );
2762
2763 #[test]
2764 fn test_das_equals() {
2765 for (dst, src) in [
2766 (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2767 (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2768 ]
2769 .iter()
2770 {
2771 let () = test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2772 let () = test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2773 }
2774 }
2775
2776 #[test]
2777 fn test_valid_policy_table() {
2778 assert_eq!(
2780 POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2781 net_types::ip::Subnet::new(net_types::ip::Ipv6::UNSPECIFIED_ADDRESS, 0)
2782 .expect("invalid subnet")
2783 );
2784 let () = POLICY_TABLE.windows(2).for_each(|w| {
2786 let Policy { prefix: cur, precedence: _, label: _ } = w[0];
2787 let Policy { prefix: nxt, precedence: _, label: _ } = w[1];
2788 assert!(
2789 cur.prefix() >= nxt.prefix(),
2790 "bad ordering of prefixes, {} must come after {}",
2791 cur,
2792 nxt
2793 )
2794 });
2795 for policy in POLICY_TABLE.iter() {
2798 assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2799 }
2800 }
2801
2802 #[fasync::run_singlethreaded(test)]
2803 async fn test_sort_preferred_addresses() {
2804 const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2805 (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2806 (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2807 (fidl_ip!("192.168.50.22"), None),
2808 (fidl_ip!("2001::2"), None),
2809 (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2810 ];
2811 const SORTED: [IpAddr; 5] = [
2814 std_ip!("::1"),
2815 std_ip!("2001:db8:1::1"),
2816 std_ip!("127.0.0.1"),
2817 std_ip!("192.168.50.22"),
2818 std_ip!("2001::2"),
2819 ];
2820 let (routes_proxy, routes_stream) =
2821 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2822 let routes_fut =
2823 routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2824 let (destination, responder) = assert_matches!(
2825 req,
2826 fnet_routes::StateRequest::Resolve { destination, responder }
2827 => (destination, responder)
2828 );
2829 let result = TEST_IPS
2830 .iter()
2831 .enumerate()
2832 .find_map(|(i, (dst, src))| {
2833 if *dst == destination && src.is_some() {
2834 let inner = fnet_routes::Destination {
2835 address: Some(*dst),
2836 source_address: *src,
2837 ..Default::default()
2838 };
2839 if i % 2 == 0 {
2842 Some(fnet_routes::Resolved::Direct(inner))
2843 } else {
2844 Some(fnet_routes::Resolved::Gateway(inner))
2845 }
2846 } else {
2847 None
2848 }
2849 })
2850 .ok_or(zx::Status::ADDRESS_UNREACHABLE.into_raw());
2851 futures::future::ready(
2852 responder
2853 .send(result.as_ref().map_err(|e| *e))
2854 .context("failed to send Resolve response"),
2855 )
2856 });
2857
2858 let ((), ()) = futures::future::try_join(routes_fut, async move {
2859 let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
2860 let addrs = sort_preferred_addresses(addrs, &routes_proxy)
2861 .await
2862 .expect("failed to sort addresses");
2863 let addrs = addrs
2864 .into_iter()
2865 .map(|a| {
2866 let net_ext::IpAddress(a) = a.into();
2867 a
2868 })
2869 .collect::<Vec<_>>();
2870 assert_eq!(&addrs[..], &SORTED[..]);
2871 Ok(())
2872 })
2873 .await
2874 .expect("error running futures");
2875 }
2876
2877 #[fasync::run_singlethreaded(test)]
2878 async fn test_lookupip() {
2879 let routes_handler = |req| {
2881 let (destination, responder) = assert_matches!(
2882 req,
2883 fnet_routes::StateRequest::Resolve { destination, responder }
2884 => (destination, responder)
2885 );
2886 let resolved;
2887 let response = if destination == map_ip(IPV6_HOST) {
2888 resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
2889 address: Some(destination),
2890 source_address: Some(destination),
2891 ..Default::default()
2892 });
2893 Ok(&resolved)
2894 } else {
2895 Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
2896 };
2897 let () = responder.send(response).expect("failed to send Resolve FIDL response");
2898 };
2899 TestEnvironment::default()
2900 .run_lookup_with_routes_handler(
2901 |proxy| async move {
2902 assert_eq!(
2904 proxy
2905 .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
2906 .await
2907 .expect("lookup_ip"),
2908 Err(fname::LookupError::InvalidArgs)
2909 );
2910 assert_eq!(
2912 proxy
2913 .lookup_ip(
2914 REMOTE_IPV4_HOST,
2915 &fname::LookupIpOptions {
2916 ipv4_lookup: Some(false),
2917 ipv6_lookup: Some(false),
2918 ..Default::default()
2919 }
2920 )
2921 .await
2922 .expect("lookup_ip"),
2923 Err(fname::LookupError::InvalidArgs)
2924 );
2925 assert_eq!(
2927 proxy
2928 .lookup_ip(
2929 REMOTE_IPV4_HOST,
2930 &fname::LookupIpOptions {
2931 ipv4_lookup: Some(false),
2932 ipv6_lookup: Some(true),
2933 ..Default::default()
2934 }
2935 )
2936 .await
2937 .expect("lookup_ip"),
2938 Err(fname::LookupError::NotFound)
2939 );
2940 assert_eq!(
2942 proxy
2943 .lookup_ip(
2944 REMOTE_IPV4_HOST,
2945 &fname::LookupIpOptions {
2946 ipv4_lookup: Some(true),
2947 ipv6_lookup: Some(true),
2948 ..Default::default()
2949 }
2950 )
2951 .await
2952 .expect("lookup_ip"),
2953 Ok(fname::LookupResult {
2954 addresses: Some(vec![map_ip(IPV4_HOST)]),
2955 ..Default::default()
2956 })
2957 );
2958 assert_eq!(
2960 proxy
2961 .lookup_ip(
2962 REMOTE_IPV4_IPV6_HOST,
2963 &fname::LookupIpOptions {
2964 ipv4_lookup: Some(true),
2965 ipv6_lookup: Some(true),
2966 ..Default::default()
2967 }
2968 )
2969 .await
2970 .expect("lookup_ip"),
2971 Ok(fname::LookupResult {
2972 addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
2973 ..Default::default()
2974 })
2975 );
2976 assert_eq!(
2978 proxy
2979 .lookup_ip(
2980 REMOTE_IPV4_IPV6_HOST,
2981 &fname::LookupIpOptions {
2982 ipv4_lookup: Some(true),
2983 ipv6_lookup: Some(true),
2984 sort_addresses: Some(true),
2985 ..Default::default()
2986 }
2987 )
2988 .await
2989 .expect("lookup_ip"),
2990 Ok(fname::LookupResult {
2991 addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
2992 ..Default::default()
2993 })
2994 );
2995 },
2996 routes_handler,
2997 )
2998 .await
2999 }
3000}