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 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? {
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 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 addrs_info.sort_by(|(_laddr, left), (_raddr, right)| left.cmp(right));
582 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 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
612const 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#[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 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 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 prefer_true(*self_usable, *other_usable)
746 .then(
747 prefer_true(*self_matching_scope, *other_matching_scope),
749 )
750 .then(
751 prefer_true(*self_matching_label, *other_matching_label),
753 )
754 .then(
755 self_precedence.cmp(other_precedence).reverse(),
757 )
758 .then(
759 self_scope.multicast_scope_id().cmp(&other_scope.multicast_scope_id()),
761 )
762 .then(
763 self_common_prefix_len.cmp(other_common_prefix_len).reverse(),
765 )
766 }
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 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 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 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 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 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 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 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 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
1033async 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 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 responder.send(response)?;
1054 }
1055 LookupAdminRequest::GetDnsServers { responder } => {
1056 responder.send(&state.servers())?;
1057 }
1058 }
1059 Ok(())
1060 })
1061 .await
1062}
1063
1064fn 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 child.record_string("address", format!("{}", addr));
1079 srv.root().record(child);
1080 }
1081 Ok(srv)
1082 }
1083 .boxed()
1084 })
1085}
1086
1087fn 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 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 child.record_uint("successful_queries", *success_count);
1122 child.record_uint("failed_queries", *failure_count);
1123 let record_average = |name: &str, total: zx::MonotonicDuration, count: u64| {
1124 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 record_average(
1137 "average_success_duration_micros",
1138 *success_elapsed_time,
1139 *success_count,
1140 );
1141 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 errors.record_uint("Message", *message);
1161 errors.record_uint("NoConnections", *no_connections);
1162 errors.record_uint("Io", *io);
1163 errors.record_uint("Proto", *proto);
1164 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 no_records_found_response_codes.record_uint(
1170 format!("{:?}", response_code),
1171 *count,
1172 );
1173 }
1174 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 unhandled_resolve_error_kinds.record_uint(error_kind, *count);
1180 }
1181 errors.record(unhandled_resolve_error_kinds);
1182
1183 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 node.root().record(child);
1192 }
1193 Ok(node)
1194 }
1195 .boxed()
1196 })
1197}
1198
1199#[fuchsia::main(logging_tags = ["dns"])]
1202pub async fn main() -> Result<(), Error> {
1203 info!("starting");
1204
1205 let mut resolver_opts = ResolverOpts::default();
1206 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 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 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 const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1291 const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1293
1294 const REMOTE_IPV4_HOST: &str = "www.foo.com";
1296 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 const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1302 const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1304 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 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 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 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 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, RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1501 ),
1502 Record::from_rdata(
1503 Name::new(),
1504 60, 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 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 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 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 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 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 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 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 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 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 #[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_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1918
1919 env.run_admin(|proxy| async move {
1921 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 env.run_admin(|proxy| async move {
1942 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_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
1957
1958 env.run_admin(|proxy| async move {
1959 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 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_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 proxy.set_dns_servers(expect).await.expect("FIDL error").expect("set_servers failed");
1996 assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
1997 })
1998 .await;
1999 }
2000
2001 #[fasync::run_singlethreaded(test)]
2002 async fn test_config_inspect() {
2003 let env = TestEnvironment::default();
2004 let inspector = fuchsia_inspect::Inspector::default();
2005 let _config_state_node =
2006 add_config_state_inspect(inspector.root(), env.config_state.clone());
2007 assert_data_tree!(inspector, root:{
2008 servers: {}
2009 });
2010 env.run_admin(|proxy| async move {
2011 let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2012 proxy.set_dns_servers(servers).await.expect("FIDL error").expect("set_servers failed");
2013 })
2014 .await;
2015 assert_data_tree!(inspector, root:{
2016 servers: {
2017 "0": {
2018 address: "[2001:4860:4860::4444%2]:53",
2019 },
2020 "1": {
2021 address: "8.8.4.4:53",
2022 },
2023 "2": {
2024 address: "[2002:4860:4860::4444%3]:53",
2025 },
2026 "3": {
2027 address: "8.8.8.8:53",
2028 },
2029 }
2030 });
2031 }
2032
2033 #[test]
2034 fn test_unhandled_resolve_error_kind_stats() {
2035 use ResolveErrorKind::{Msg, Timeout};
2036 let mut unhandled_resolve_error_kind_stats = UnhandledResolveErrorKindStats::default();
2037 assert_eq!(
2038 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2039 "Msg"
2040 );
2041 assert_eq!(
2042 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2043 "Msg"
2044 );
2045 assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2046 assert_eq!(
2047 unhandled_resolve_error_kind_stats,
2048 UnhandledResolveErrorKindStats {
2049 resolve_error_kind_counts: [(String::from("Msg"), 2), (String::from("Timeout"), 1)]
2050 .into()
2051 }
2052 )
2053 }
2054
2055 #[fasync::run_singlethreaded(test)]
2056 async fn test_query_stats_updated() {
2057 let env = TestEnvironment::default();
2058 let inspector = fuchsia_inspect::Inspector::default();
2059 let _query_stats_inspect_node =
2060 add_query_stats_inspect(inspector.root(), env.stats.clone());
2061 assert_data_tree!(inspector, root:{
2062 query_stats: {}
2063 });
2064
2065 env.run_lookup(|proxy| async move {
2066 assert_eq!(
2068 proxy
2069 .lookup_ip(
2070 REMOTE_IPV4_HOST,
2071 &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
2072 )
2073 .await
2074 .expect("lookup_ip"),
2075 Ok(fname::LookupResult {
2076 addresses: Some(vec![map_ip(IPV4_HOST)]),
2077 ..Default::default()
2078 }),
2079 );
2080 })
2081 .await;
2082 env.run_lookup(|proxy| async move {
2083 assert_eq!(
2085 proxy
2086 .lookup_ip(
2087 REMOTE_IPV4_HOST,
2088 &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
2089 )
2090 .await
2091 .expect("lookup_ip"),
2092 Err(fname::LookupError::NotFound),
2093 );
2094 })
2095 .await;
2096 assert_data_tree!(inspector, root:{
2097 query_stats: {
2098 "window 1": {
2099 start_time_nanos: NonZeroUintProperty,
2100 successful_queries: 1u64,
2101 failed_queries: 1u64,
2102 average_success_duration_micros: NonZeroUintProperty,
2103 average_failure_duration_micros: NonZeroUintProperty,
2104 errors: {
2105 Message: 0u64,
2106 NoConnections: 0u64,
2107 NoRecordsFoundResponseCodeCounts: {
2108 NoError: 1u64,
2109 },
2110 Io: 0u64,
2111 Proto: 0u64,
2112 Timeout: 0u64,
2113 UnhandledResolveErrorKindCounts: {},
2114 },
2115 address_counts: {
2116 "1": 1u64,
2117 },
2118 },
2119 }
2120 });
2121 }
2122
2123 fn run_fake_lookup(
2124 exec: &mut fasync::TestExecutor,
2125 stats: Arc<QueryStats>,
2126 result: QueryResult<'_>,
2127 delay: zx::MonotonicDuration,
2128 ) {
2129 let start_time = fasync::MonotonicInstant::now();
2130 exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2131 let update_stats = stats.finish_query(start_time, result);
2132 let mut update_stats = pin!(update_stats);
2133 assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2134 }
2135
2136 const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2138
2139 #[test]
2140 fn test_query_stats_inspect_average() {
2141 let mut exec = fasync::TestExecutor::new_with_fake_time();
2142 const START_NANOS: i64 = 1_234_567;
2143 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2144
2145 let stats = Arc::new(QueryStats::new());
2146 let inspector = fuchsia_inspect::Inspector::default();
2147 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2148 const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2149 const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2150 zx::MonotonicDuration::from_seconds(30);
2151 for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2152 run_fake_lookup(
2153 &mut exec,
2154 stats.clone(),
2155 Ok(NON_ZERO_USIZE_ONE),
2156 zx::MonotonicDuration::from_nanos(0),
2157 );
2158 run_fake_lookup(
2159 &mut exec,
2160 stats.clone(),
2161 Ok(NON_ZERO_USIZE_ONE),
2162 SUCCESSFUL_QUERY_DURATION,
2163 );
2164 exec.set_fake_time(fasync::MonotonicInstant::after(
2165 STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2166 ));
2167 }
2168 let mut expected = tree_assertion!(query_stats: {});
2169 for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2170 let name = &format!("window {}", i + 1);
2171 let child = tree_assertion!(var name: {
2172 start_time_nanos: u64::try_from(
2173 START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2174 ).unwrap(),
2175 successful_queries: 2u64,
2176 failed_queries: 0u64,
2177 average_success_duration_micros: u64::try_from(
2178 SUCCESSFUL_QUERY_DURATION.into_micros()
2179 ).unwrap() / 2,
2180 errors: {
2181 Message: 0u64,
2182 NoConnections: 0u64,
2183 NoRecordsFoundResponseCodeCounts: {},
2184 Io: 0u64,
2185 Proto: 0u64,
2186 Timeout: 0u64,
2187 UnhandledResolveErrorKindCounts: {},
2188 },
2189 address_counts: {
2190 "1": 2u64,
2191 },
2192 });
2193 expected.add_child_assertion(child);
2194 }
2195 assert_data_tree!(@executor exec, inspector, root: {
2196 expected,
2197 });
2198 }
2199
2200 #[test]
2201 fn test_query_stats_inspect_error_counters() {
2202 let mut exec = fasync::TestExecutor::new_with_fake_time();
2203 const START_NANOS: i64 = 1_234_567;
2204 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2205
2206 let stats = Arc::new(QueryStats::new());
2207 let inspector = fuchsia_inspect::Inspector::default();
2208 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2209 const FAILED_QUERY_COUNT: u64 = 10;
2210 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2211 zx::MonotonicDuration::from_millis(500);
2212 for _ in 0..FAILED_QUERY_COUNT {
2213 run_fake_lookup(
2214 &mut exec,
2215 stats.clone(),
2216 Err(&ResolveErrorKind::Timeout),
2217 FAILED_QUERY_DURATION,
2218 );
2219 }
2220 assert_data_tree!(@executor exec, inspector, root:{
2221 query_stats: {
2222 "window 1": {
2223 start_time_nanos: u64::try_from(
2224 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2225 ).unwrap(),
2226 successful_queries: 0u64,
2227 failed_queries: FAILED_QUERY_COUNT,
2228 average_failure_duration_micros: u64::try_from(
2229 FAILED_QUERY_DURATION.into_micros()
2230 ).unwrap(),
2231 errors: {
2232 Message: 0u64,
2233 NoConnections: 0u64,
2234 NoRecordsFoundResponseCodeCounts: {},
2235 Io: 0u64,
2236 Proto: 0u64,
2237 Timeout: FAILED_QUERY_COUNT,
2238 UnhandledResolveErrorKindCounts: {},
2239 },
2240 address_counts: {},
2241 },
2242 }
2243 });
2244 }
2245
2246 #[test]
2247 fn test_query_stats_inspect_no_records_found() {
2248 let mut exec = fasync::TestExecutor::new_with_fake_time();
2249 const START_NANOS: i64 = 1_234_567;
2250 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2251
2252 let stats = Arc::new(QueryStats::new());
2253 let inspector = fuchsia_inspect::Inspector::default();
2254 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2255 const FAILED_QUERY_COUNT: u64 = 10;
2256 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2257 zx::MonotonicDuration::from_millis(500);
2258
2259 let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2260 run_fake_lookup(
2261 &mut exec,
2262 stats.clone(),
2263 Err(&ResolveErrorKind::NoRecordsFound {
2264 query: Box::new(Query::default()),
2265 soa: None,
2266 negative_ttl: None,
2267 response_code,
2268 trusted: false,
2269 }),
2270 FAILED_QUERY_DURATION,
2271 )
2272 };
2273
2274 for _ in 0..FAILED_QUERY_COUNT {
2275 run_fake_no_records_lookup(ResponseCode::NXDomain);
2276 run_fake_no_records_lookup(ResponseCode::Refused);
2277 run_fake_no_records_lookup(4096.into());
2278 run_fake_no_records_lookup(4097.into());
2279 }
2280
2281 assert_data_tree!(@executor exec, inspector, root:{
2282 query_stats: {
2283 "window 1": {
2284 start_time_nanos: u64::try_from(
2285 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2286 ).unwrap(),
2287 successful_queries: 0u64,
2288 failed_queries: FAILED_QUERY_COUNT * 4,
2289 average_failure_duration_micros: u64::try_from(
2290 FAILED_QUERY_DURATION.into_micros()
2291 ).unwrap(),
2292 errors: {
2293 Message: 0u64,
2294 NoConnections: 0u64,
2295 NoRecordsFoundResponseCodeCounts: {
2296 NXDomain: FAILED_QUERY_COUNT,
2297 Refused: FAILED_QUERY_COUNT,
2298 "Unknown(4096)": FAILED_QUERY_COUNT,
2299 "Unknown(4097)": FAILED_QUERY_COUNT,
2300 },
2301 Io: 0u64,
2302 Proto: 0u64,
2303 Timeout: 0u64,
2304 UnhandledResolveErrorKindCounts: {},
2305 },
2306 address_counts: {},
2307 },
2308 }
2309 });
2310 }
2311
2312 #[test]
2313 fn test_query_stats_resolved_address_counts() {
2314 let mut exec = fasync::TestExecutor::new_with_fake_time();
2315 const START_NANOS: i64 = 1_234_567;
2316 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2317
2318 let stats = Arc::new(QueryStats::new());
2319 let inspector = fuchsia_inspect::Inspector::default();
2320 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2321
2322 let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2328 const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2329 for (count, occurrences) in address_counts.iter() {
2330 for _ in 0..*occurrences {
2331 run_fake_lookup(
2332 &mut exec,
2333 stats.clone(),
2334 Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2335 QUERY_DURATION,
2336 );
2337 }
2338 }
2339
2340 let mut expected_address_counts = tree_assertion!(address_counts: {});
2341 for (count, occurrences) in address_counts.iter() {
2342 expected_address_counts
2343 .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2344 }
2345 assert_data_tree!(@executor exec, inspector, root: {
2346 query_stats: {
2347 "window 1": {
2348 start_time_nanos: u64::try_from(
2349 START_NANOS + QUERY_DURATION.into_nanos()
2350 ).unwrap(),
2351 successful_queries: address_counts.values().sum::<u64>(),
2352 failed_queries: 0u64,
2353 average_success_duration_micros: u64::try_from(
2354 QUERY_DURATION.into_micros()
2355 ).unwrap(),
2356 errors: {
2357 Message: 0u64,
2358 NoConnections: 0u64,
2359 NoRecordsFoundResponseCodeCounts: {},
2360 Io: 0u64,
2361 Proto: 0u64,
2362 Timeout: 0u64,
2363 UnhandledResolveErrorKindCounts: {},
2364 },
2365 expected_address_counts,
2366 },
2367 },
2368 });
2369 }
2370
2371 #[test]
2372 fn test_query_stats_inspect_oldest_stats_erased() {
2373 let mut exec = fasync::TestExecutor::new_with_fake_time();
2374 const START_NANOS: i64 = 1_234_567;
2375 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2376
2377 let stats = Arc::new(QueryStats::new());
2378 let inspector = fuchsia_inspect::Inspector::default();
2379 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2380 const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2381 for _ in 0..STAT_WINDOW_COUNT {
2382 let () =
2383 run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2384 let () =
2385 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2386 }
2387 for _ in 0..STAT_WINDOW_COUNT {
2388 run_fake_lookup(&mut exec, stats.clone(), Ok(NON_ZERO_USIZE_ONE), DELAY);
2389 let () =
2390 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2391 }
2392 let mut expected = tree_assertion!(query_stats: {});
2395 let start_offset = START_NANOS
2396 + DELAY.into_nanos()
2397 + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2398 for i in 0..STAT_WINDOW_COUNT {
2399 let name = &format!("window {}", i + 1);
2400 let child = tree_assertion!(var name: {
2401 start_time_nanos: u64::try_from(
2402 start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2403 ).unwrap(),
2404 successful_queries: 1u64,
2405 failed_queries: 0u64,
2406 average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2407 errors: {
2408 Message: 0u64,
2409 NoConnections: 0u64,
2410 NoRecordsFoundResponseCodeCounts: {},
2411 Io: 0u64,
2412 Proto: 0u64,
2413 Timeout: 0u64,
2414 UnhandledResolveErrorKindCounts: {},
2415 },
2416 address_counts: {
2417 "1": 1u64,
2418 },
2419 });
2420 expected.add_child_assertion(child);
2421 }
2422 assert_data_tree!(@executor exec, inspector, root: {
2423 expected,
2424 });
2425 }
2426
2427 struct BlockingResolver {}
2428
2429 #[async_trait]
2430 impl ResolverLookup for BlockingResolver {
2431 fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2432 BlockingResolver {}
2433 }
2434
2435 async fn lookup<N: IntoName + Send>(
2436 &self,
2437 _name: N,
2438 _record_type: RecordType,
2439 ) -> Result<lookup::Lookup, ResolveError> {
2440 futures::future::pending().await
2441 }
2442
2443 async fn reverse_lookup(
2444 &self,
2445 _addr: IpAddr,
2446 ) -> Result<lookup::ReverseLookup, ResolveError> {
2447 panic!("BlockingResolver does not handle reverse lookup")
2448 }
2449 }
2450
2451 #[fasync::run_singlethreaded(test)]
2452 async fn test_parallel_query_limit() {
2453 let requests = {
2457 let (name_lookup_proxy, name_lookup_stream) =
2458 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2459 const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2460 for _ in 0..NUM_REQUESTS {
2461 let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2465 name_lookup_proxy.lookup_ip(
2466 LOCAL_HOST,
2467 &fname::LookupIpOptions {
2468 ipv4_lookup: Some(true),
2469 ipv6_lookup: Some(true),
2470 ..Default::default()
2471 },
2472 );
2473 }
2474 drop(name_lookup_proxy);
2476 let requests = name_lookup_stream
2477 .map(|request| match request.expect("channel error") {
2478 LookupRequest::LookupIp { hostname, options, responder } => {
2479 IpLookupRequest { hostname, options, responder }
2480 }
2481 req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2482 })
2483 .collect::<Vec<_>>()
2484 .await;
2485 assert_eq!(requests.len(), NUM_REQUESTS);
2486 requests
2487 };
2488
2489 let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2490
2491 const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2497 const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2498 let mut send_fut = pin!(
2499 async {
2500 for (i, req) in requests.into_iter().enumerate() {
2501 match i {
2502 BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2503 LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2504 _ => assert_matches!(sender.send(req).await, Ok(())),
2505 }
2506 }
2507 }
2508 .fuse()
2509 );
2510 let mut recv_fut = pin!({
2511 let resolver = SharedResolver::new(BlockingResolver::new(
2512 ResolverConfig::default(),
2513 ResolverOpts::default(),
2514 ));
2515 let stats = Arc::new(QueryStats::new());
2516 let (routes_proxy, _routes_stream) =
2517 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2518 async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2519 .fuse()
2520 });
2521 futures::select! {
2522 () = send_fut => {},
2523 () = recv_fut => panic!("recv_fut should never complete"),
2524 };
2525 }
2526
2527 #[test]
2528 fn test_failure_stats() {
2529 use anyhow::anyhow;
2530 use trust_dns_proto::error::ProtoError;
2531 use trust_dns_proto::op::Query;
2532
2533 let mut stats = FailureStats::default();
2534 for (error_kind, expected) in &[
2535 (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2536 (
2537 ResolveErrorKind::Msg("foo".to_string()),
2538 FailureStats { message: 2, ..Default::default() },
2539 ),
2540 (
2541 ResolveErrorKind::NoRecordsFound {
2542 query: Box::new(Query::default()),
2543 soa: None,
2544 negative_ttl: None,
2545 response_code: ResponseCode::Refused,
2546 trusted: false,
2547 },
2548 FailureStats {
2549 message: 2,
2550 no_records_found: NoRecordsFoundStats {
2551 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2552 },
2553 ..Default::default()
2554 },
2555 ),
2556 (
2557 ResolveErrorKind::Io(std::io::Error::new(
2558 std::io::ErrorKind::NotFound,
2559 anyhow!("foo"),
2560 )),
2561 FailureStats {
2562 message: 2,
2563 no_records_found: NoRecordsFoundStats {
2564 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2565 },
2566 io: 1,
2567 ..Default::default()
2568 },
2569 ),
2570 (
2571 ResolveErrorKind::Proto(ProtoError::from("foo")),
2572 FailureStats {
2573 message: 2,
2574 no_records_found: NoRecordsFoundStats {
2575 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2576 },
2577 io: 1,
2578 proto: 1,
2579 ..Default::default()
2580 },
2581 ),
2582 (
2583 ResolveErrorKind::NoConnections,
2584 FailureStats {
2585 message: 2,
2586 no_connections: 1,
2587 no_records_found: NoRecordsFoundStats {
2588 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2589 },
2590 io: 1,
2591 proto: 1,
2592 ..Default::default()
2593 },
2594 ),
2595 (
2596 ResolveErrorKind::Timeout,
2597 FailureStats {
2598 message: 2,
2599 no_connections: 1,
2600 no_records_found: NoRecordsFoundStats {
2601 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2602 },
2603 io: 1,
2604 proto: 1,
2605 timeout: 1,
2606 unhandled_resolve_error_kind: Default::default(),
2607 },
2608 ),
2609 (
2610 ResolveErrorKind::NoRecordsFound {
2611 query: Box::new(Query::default()),
2612 soa: None,
2613 negative_ttl: None,
2614 response_code: ResponseCode::NXDomain,
2615 trusted: false,
2616 },
2617 FailureStats {
2618 message: 2,
2619 no_connections: 1,
2620 no_records_found: NoRecordsFoundStats {
2621 response_code_counts: [
2622 (ResponseCode::NXDomain.into(), 1),
2623 (ResponseCode::Refused.into(), 1),
2624 ]
2625 .into(),
2626 },
2627 io: 1,
2628 proto: 1,
2629 timeout: 1,
2630 unhandled_resolve_error_kind: Default::default(),
2631 },
2632 ),
2633 (
2634 ResolveErrorKind::NoRecordsFound {
2635 query: Box::new(Query::default()),
2636 soa: None,
2637 negative_ttl: None,
2638 response_code: ResponseCode::NXDomain,
2639 trusted: false,
2640 },
2641 FailureStats {
2642 message: 2,
2643 no_connections: 1,
2644 no_records_found: NoRecordsFoundStats {
2645 response_code_counts: [
2646 (ResponseCode::NXDomain.into(), 2),
2647 (ResponseCode::Refused.into(), 1),
2648 ]
2649 .into(),
2650 },
2651 io: 1,
2652 proto: 1,
2653 timeout: 1,
2654 unhandled_resolve_error_kind: Default::default(),
2655 },
2656 ),
2657 ][..]
2658 {
2659 stats.increment(error_kind);
2660 assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2661 }
2662 }
2663
2664 fn test_das_helper(
2665 l_addr: fnet::IpAddress,
2666 l_src: Option<fnet::IpAddress>,
2667 r_addr: fnet::IpAddress,
2668 r_src: Option<fnet::IpAddress>,
2669 want: std::cmp::Ordering,
2670 ) {
2671 let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2672 let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2673 assert_eq!(
2674 left.cmp(&right),
2675 want,
2676 "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2677 want,
2678 l_addr,
2679 l_src,
2680 left,
2681 r_addr,
2682 r_src,
2683 right
2684 );
2685 }
2686
2687 macro_rules! add_das_test {
2688 ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2689 #[test]
2690 fn $name() {
2691 test_das_helper(
2692 $pref_dst,
2693 $pref_src,
2694 $other_dst,
2695 $other_src,
2696 std::cmp::Ordering::Less,
2697 )
2698 }
2699 };
2700 }
2701
2702 add_das_test!(
2703 prefer_reachable,
2704 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2705 other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2706 );
2707
2708 add_das_test!(
2711 prefer_matching_scope,
2712 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2713 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2714 );
2715
2716 add_das_test!(
2717 prefer_matching_label,
2718 preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2719 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2720 );
2721
2722 add_das_test!(
2723 prefer_higher_precedence_1,
2724 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2725 other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2726 );
2727
2728 add_das_test!(
2729 prefer_higher_precedence_2,
2730 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2731 other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2732 );
2733
2734 add_das_test!(
2735 prefer_smaller_scope,
2736 preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2737 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2738 );
2739
2740 add_das_test!(
2741 prefer_longest_matching_prefix,
2742 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2743 other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2744 );
2745
2746 #[test]
2747 fn test_das_equals() {
2748 for (dst, src) in [
2749 (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2750 (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2751 ]
2752 .iter()
2753 {
2754 test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2755 test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2756 }
2757 }
2758
2759 #[test]
2760 fn test_valid_policy_table() {
2761 assert_eq!(
2763 POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2764 net_types::ip::Subnet::new(net_types::ip::Ipv6::UNSPECIFIED_ADDRESS, 0)
2765 .expect("invalid subnet")
2766 );
2767 POLICY_TABLE.windows(2).for_each(|w| {
2769 let Policy { prefix: cur, precedence: _, label: _ } = w[0];
2770 let Policy { prefix: nxt, precedence: _, label: _ } = w[1];
2771 assert!(
2772 cur.prefix() >= nxt.prefix(),
2773 "bad ordering of prefixes, {} must come after {}",
2774 cur,
2775 nxt
2776 )
2777 });
2778 for policy in POLICY_TABLE.iter() {
2781 assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2782 }
2783 }
2784
2785 #[fasync::run_singlethreaded(test)]
2786 async fn test_sort_preferred_addresses() {
2787 const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2788 (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2789 (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2790 (fidl_ip!("192.168.50.22"), None),
2791 (fidl_ip!("2001::2"), None),
2792 (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2793 ];
2794 const SORTED: [IpAddr; 5] = [
2797 std_ip!("::1"),
2798 std_ip!("2001:db8:1::1"),
2799 std_ip!("127.0.0.1"),
2800 std_ip!("192.168.50.22"),
2801 std_ip!("2001::2"),
2802 ];
2803 let (routes_proxy, routes_stream) =
2804 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2805 let routes_fut =
2806 routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2807 let (destination, responder) = assert_matches!(
2808 req,
2809 fnet_routes::StateRequest::Resolve { destination, responder }
2810 => (destination, responder)
2811 );
2812 let result = TEST_IPS
2813 .iter()
2814 .enumerate()
2815 .find_map(|(i, (dst, src))| {
2816 if *dst == destination && src.is_some() {
2817 let inner = fnet_routes::Destination {
2818 address: Some(*dst),
2819 source_address: *src,
2820 ..Default::default()
2821 };
2822 if i % 2 == 0 {
2825 Some(fnet_routes::Resolved::Direct(inner))
2826 } else {
2827 Some(fnet_routes::Resolved::Gateway(inner))
2828 }
2829 } else {
2830 None
2831 }
2832 })
2833 .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
2834 futures::future::ready(
2835 responder
2836 .send(result.as_ref().map_err(|e| *e))
2837 .context("failed to send Resolve response"),
2838 )
2839 });
2840
2841 let ((), ()) = futures::future::try_join(routes_fut, async move {
2842 let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
2843 let addrs = sort_preferred_addresses(addrs, &routes_proxy)
2844 .await
2845 .expect("failed to sort addresses");
2846 let addrs = addrs
2847 .into_iter()
2848 .map(|a| {
2849 let net_ext::IpAddress(a) = a.into();
2850 a
2851 })
2852 .collect::<Vec<_>>();
2853 assert_eq!(&addrs[..], &SORTED[..]);
2854 Ok(())
2855 })
2856 .await
2857 .expect("error running futures");
2858 }
2859
2860 #[fasync::run_singlethreaded(test)]
2861 async fn test_lookupip() {
2862 let routes_handler = |req| {
2864 let (destination, responder) = assert_matches!(
2865 req,
2866 fnet_routes::StateRequest::Resolve { destination, responder }
2867 => (destination, responder)
2868 );
2869 let resolved;
2870 let response = if destination == map_ip(IPV6_HOST) {
2871 resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
2872 address: Some(destination),
2873 source_address: Some(destination),
2874 ..Default::default()
2875 });
2876 Ok(&resolved)
2877 } else {
2878 Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
2879 };
2880 responder.send(response).expect("failed to send Resolve FIDL response");
2881 };
2882 TestEnvironment::default()
2883 .run_lookup_with_routes_handler(
2884 |proxy| async move {
2885 assert_eq!(
2887 proxy
2888 .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
2889 .await
2890 .expect("lookup_ip"),
2891 Err(fname::LookupError::InvalidArgs)
2892 );
2893 assert_eq!(
2895 proxy
2896 .lookup_ip(
2897 REMOTE_IPV4_HOST,
2898 &fname::LookupIpOptions {
2899 ipv4_lookup: Some(false),
2900 ipv6_lookup: Some(false),
2901 ..Default::default()
2902 }
2903 )
2904 .await
2905 .expect("lookup_ip"),
2906 Err(fname::LookupError::InvalidArgs)
2907 );
2908 assert_eq!(
2910 proxy
2911 .lookup_ip(
2912 REMOTE_IPV4_HOST,
2913 &fname::LookupIpOptions {
2914 ipv4_lookup: Some(false),
2915 ipv6_lookup: Some(true),
2916 ..Default::default()
2917 }
2918 )
2919 .await
2920 .expect("lookup_ip"),
2921 Err(fname::LookupError::NotFound)
2922 );
2923 assert_eq!(
2925 proxy
2926 .lookup_ip(
2927 REMOTE_IPV4_HOST,
2928 &fname::LookupIpOptions {
2929 ipv4_lookup: Some(true),
2930 ipv6_lookup: Some(true),
2931 ..Default::default()
2932 }
2933 )
2934 .await
2935 .expect("lookup_ip"),
2936 Ok(fname::LookupResult {
2937 addresses: Some(vec![map_ip(IPV4_HOST)]),
2938 ..Default::default()
2939 })
2940 );
2941 assert_eq!(
2943 proxy
2944 .lookup_ip(
2945 REMOTE_IPV4_IPV6_HOST,
2946 &fname::LookupIpOptions {
2947 ipv4_lookup: Some(true),
2948 ipv6_lookup: Some(true),
2949 ..Default::default()
2950 }
2951 )
2952 .await
2953 .expect("lookup_ip"),
2954 Ok(fname::LookupResult {
2955 addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
2956 ..Default::default()
2957 })
2958 );
2959 assert_eq!(
2961 proxy
2962 .lookup_ip(
2963 REMOTE_IPV4_IPV6_HOST,
2964 &fname::LookupIpOptions {
2965 ipv4_lookup: Some(true),
2966 ipv6_lookup: Some(true),
2967 sort_addresses: Some(true),
2968 ..Default::default()
2969 }
2970 )
2971 .await
2972 .expect("lookup_ip"),
2973 Ok(fname::LookupResult {
2974 addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
2975 ..Default::default()
2976 })
2977 );
2978 },
2979 routes_handler,
2980 )
2981 .await
2982 }
2983}