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? {
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 let () = addrs_info.sort_by(|(_laddr, left), (_raddr, right)| left.cmp(right));
582 let () = addrs.extend(addrs_info.into_iter().map(|(addr, _)| addr));
584 Ok(addrs)
585}
586
587#[derive(Debug)]
588struct Policy {
589 prefix: net_types::ip::Subnet<net_types::ip::Ipv6Addr>,
590 precedence: usize,
591 label: usize,
592}
593
594macro_rules! decl_policy {
595 ($ip:tt/$prefix:expr => $precedence:expr, $label:expr) => {
596 Policy {
597 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 let () = sender
815 .clone()
816 .send(IpLookupRequest { hostname, options, responder })
817 .await
818 .expect("receiver should not be closed");
819 Ok(())
820 }
821 LookupRequest::LookupHostname { addr, responder } => responder
822 .send(handle_lookup_hostname(&resolver, addr).await.as_deref().map_err(|e| *e)),
823 }
824 })
825 .await
826}
827
828const MAX_PARALLEL_REQUESTS: usize = 256;
829
830fn create_ip_lookup_fut<T: ResolverLookup>(
831 resolver: &SharedResolver<T>,
832 stats: Arc<QueryStats>,
833 routes: fnet_routes::StateProxy,
834 recv: mpsc::Receiver<IpLookupRequest>,
835) -> impl futures::Future<Output = ()> + '_ {
836 recv.for_each_concurrent(
837 MAX_PARALLEL_REQUESTS,
838 move |IpLookupRequest { hostname, options, responder }| {
839 let stats = stats.clone();
840 let routes = routes.clone();
841 async move {
842 let fname::LookupIpOptions {
843 ipv4_lookup,
844 ipv6_lookup,
845 sort_addresses,
846 canonical_name_lookup,
847 ..
848 } = options;
849 let ipv4_lookup = ipv4_lookup.unwrap_or(false);
850 let ipv6_lookup = ipv6_lookup.unwrap_or(false);
851 let sort_addresses = sort_addresses.unwrap_or(false);
852 let canonical_name_lookup = canonical_name_lookup.unwrap_or(false);
853 let lookup_result = (|| async {
854 let hostname = hostname.as_str();
855 match IpAddr::from_str(hostname) {
873 Ok(addr) => {
874 let _: IpAddr = addr;
875 return Err(fname::LookupError::InvalidArgs);
876 }
877 Err(std::net::AddrParseError { .. }) => {}
878 };
879 let resolver = resolver.read();
880 let start_time = fasync::MonotonicInstant::now();
881 let (ret1, ret2, ret3) = futures::future::join3(
882 futures::future::OptionFuture::from(
883 ipv4_lookup.then(|| {
884 resolver
885 .lookup(hostname, RecordType::A)
886 .map_err(|e| (LookupIpErrorSource::Ipv4, e))
887 }),
888 ),
889 futures::future::OptionFuture::from(
890 ipv6_lookup.then(|| {
891 resolver
892 .lookup(hostname, RecordType::AAAA)
893 .map_err(|e| (LookupIpErrorSource::Ipv6, e))
894 }),
895 ),
896 futures::future::OptionFuture::from(
897 canonical_name_lookup
898 .then(|| {
899 resolver
900 .lookup(hostname, RecordType::CNAME)
901 .map_err(|e| (LookupIpErrorSource::CanonicalName, e))
902 }),
903 ),
904 )
905 .await;
906 let result = [ret1, ret2, ret3];
907 if result.iter().all(Option::is_none) {
908 return Err(fname::LookupError::InvalidArgs);
909 }
910 let (addrs, cnames, error) =
911 result.into_iter().filter_map(std::convert::identity).fold(
912 (Vec::new(), Vec::new(), LookupIpErrorsFromSource::default()),
913 |(mut addrs, mut cnames, mut error), result| {
914 let () = match result {
915 Err((src, err)) => {
916 error.accumulate(src, err);
917 },
918 Ok(lookup) => lookup.iter().for_each(|rdata| match rdata {
919 RData::A(addr) if ipv4_lookup => addrs
920 .push(net_ext::IpAddress(IpAddr::V4(*addr)).into()),
921 RData::AAAA(addr) if ipv6_lookup => addrs
922 .push(net_ext::IpAddress(IpAddr::V6(*addr)).into()),
923 RData::CNAME(name) => {
924 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 let () = stats
966 .finish_query(
967 start_time,
968 count.as_ref().copied().map_err(|e| e.kind()),
969 )
970 .await;
971 match count {
972 Ok(_) => {},
973 Err(_any_err) => {
974 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 let () = update_resolver(resolver, servers);
1046 Ok(())
1047 }
1048 UpdateServersResult::NoChange => Ok(()),
1049 UpdateServersResult::InvalidsServers => {
1050 Err(zx::Status::INVALID_ARGS.into_raw())
1051 }
1052 };
1053 let () = responder.send(response)?;
1054 }
1055 LookupAdminRequest::GetDnsServers { responder } => {
1056 let () = responder.send(&state.servers())?;
1057 }
1058 }
1059 Ok(())
1060 })
1061 .await
1062}
1063
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 let () = child.record_string("address", format!("{}", addr));
1079 let () = 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 let () = child.record_uint("start_time_nanos", nanos);
1115 },
1116 Err(e) => warn!(
1117 "error computing `start_time_nanos`: {:?}.into_nanos() from i64 -> u64 failed: {}",
1118 start, e
1119 ),
1120 }
1121 let () = child.record_uint("successful_queries", *success_count);
1122 let () = child.record_uint("failed_queries", *failure_count);
1123 let record_average = |name: &str, total: zx::MonotonicDuration, count: u64| {
1124 if count == 0 {
1126 return;
1127 }
1128 match u64::try_from(total.into_micros()) {
1129 Ok(micros) => child.record_uint(name, micros / count),
1130 Err(e) => warn!(
1131 "error computing `{}`: {:?}.into_micros() from i64 -> u64 failed: {}",
1132 name, success_elapsed_time, e
1133 ),
1134 }
1135 };
1136 let () = record_average(
1137 "average_success_duration_micros",
1138 *success_elapsed_time,
1139 *success_count,
1140 );
1141 let () = record_average(
1142 "average_failure_duration_micros",
1143 *failure_elapsed_time,
1144 *failure_count,
1145 );
1146 let FailureStats {
1147 message,
1148 no_connections,
1149 no_records_found: NoRecordsFoundStats {
1150 response_code_counts,
1151 },
1152 io,
1153 proto,
1154 timeout,
1155 unhandled_resolve_error_kind: UnhandledResolveErrorKindStats {
1156 resolve_error_kind_counts,
1157 },
1158 } = failure_stats;
1159 let errors = child.create_child("errors");
1160 let () = errors.record_uint("Message", *message);
1161 let () = errors.record_uint("NoConnections", *no_connections);
1162 let () = errors.record_uint("Io", *io);
1163 let () = errors.record_uint("Proto", *proto);
1164 let () = errors.record_uint("Timeout", *timeout);
1165
1166 let no_records_found_response_codes =
1167 errors.create_child("NoRecordsFoundResponseCodeCounts");
1168 for (HashableResponseCode { response_code }, count) in response_code_counts {
1169 let () = no_records_found_response_codes.record_uint(
1170 format!("{:?}", response_code),
1171 *count,
1172 );
1173 }
1174 let () = errors.record(no_records_found_response_codes);
1175
1176 let unhandled_resolve_error_kinds =
1177 errors.create_child("UnhandledResolveErrorKindCounts");
1178 for (error_kind, count) in resolve_error_kind_counts {
1179 let () = unhandled_resolve_error_kinds.record_uint(error_kind, *count);
1180 }
1181 let () = errors.record(unhandled_resolve_error_kinds);
1182
1183 let () = child.record(errors);
1184
1185 let address_counts_node = child.create_child("address_counts");
1186 for (count, occurrences) in address_counts_histogram {
1187 address_counts_node.record_uint(count.to_string(), *occurrences);
1188 }
1189 child.record(address_counts_node);
1190
1191 let () = node.root().record(child);
1192 }
1193 Ok(node)
1194 }
1195 .boxed()
1196 })
1197}
1198
1199#[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 let () = proxy
1922 .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
1923 .await
1924 .expect("Failed to call SetDnsServers")
1925 .expect("SetDnsServers error");
1926 })
1927 .await;
1928 assert_eq!(
1929 env.shared_resolver.read().config.name_servers().to_vec(),
1930 vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
1931 .into_iter()
1932 .map(|s| {
1933 let net_ext::SocketAddress(s) = s.into();
1934 s
1935 })
1936 .flat_map(|x| to_server_configs(x).to_vec().into_iter())
1937 .collect::<Vec<_>>()
1938 );
1939
1940 env.run_admin(|proxy| async move {
1942 let () = proxy
1943 .set_dns_servers(&[])
1944 .await
1945 .expect("Failed to call SetDnsServers")
1946 .expect("SetDnsServers error");
1947 })
1948 .await;
1949 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
1950 }
1951
1952 #[fasync::run_singlethreaded(test)]
1953 async fn test_set_server_names_error() {
1954 let env = TestEnvironment::default();
1955 assert_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 let () = proxy
1996 .set_dns_servers(expect)
1997 .await
1998 .expect("FIDL error")
1999 .expect("set_servers failed");
2000 assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
2001 })
2002 .await;
2003 }
2004
2005 #[fasync::run_singlethreaded(test)]
2006 async fn test_config_inspect() {
2007 let env = TestEnvironment::default();
2008 let inspector = fuchsia_inspect::Inspector::default();
2009 let _config_state_node =
2010 add_config_state_inspect(inspector.root(), env.config_state.clone());
2011 assert_data_tree!(inspector, root:{
2012 servers: {}
2013 });
2014 env.run_admin(|proxy| async move {
2015 let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2016 let () = proxy
2017 .set_dns_servers(servers)
2018 .await
2019 .expect("FIDL error")
2020 .expect("set_servers failed");
2021 })
2022 .await;
2023 assert_data_tree!(inspector, root:{
2024 servers: {
2025 "0": {
2026 address: "[2001:4860:4860::4444%2]:53",
2027 },
2028 "1": {
2029 address: "8.8.4.4:53",
2030 },
2031 "2": {
2032 address: "[2002:4860:4860::4444%3]:53",
2033 },
2034 "3": {
2035 address: "8.8.8.8:53",
2036 },
2037 }
2038 });
2039 }
2040
2041 #[test]
2042 fn test_unhandled_resolve_error_kind_stats() {
2043 use ResolveErrorKind::{Msg, Timeout};
2044 let mut unhandled_resolve_error_kind_stats = UnhandledResolveErrorKindStats::default();
2045 assert_eq!(
2046 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2047 "Msg"
2048 );
2049 assert_eq!(
2050 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2051 "Msg"
2052 );
2053 assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2054 assert_eq!(
2055 unhandled_resolve_error_kind_stats,
2056 UnhandledResolveErrorKindStats {
2057 resolve_error_kind_counts: [(String::from("Msg"), 2), (String::from("Timeout"), 1)]
2058 .into()
2059 }
2060 )
2061 }
2062
2063 #[fasync::run_singlethreaded(test)]
2064 async fn test_query_stats_updated() {
2065 let env = TestEnvironment::default();
2066 let inspector = fuchsia_inspect::Inspector::default();
2067 let _query_stats_inspect_node =
2068 add_query_stats_inspect(inspector.root(), env.stats.clone());
2069 assert_data_tree!(inspector, root:{
2070 query_stats: {}
2071 });
2072
2073 let () = env
2074 .run_lookup(|proxy| async move {
2075 assert_eq!(
2077 proxy
2078 .lookup_ip(
2079 REMOTE_IPV4_HOST,
2080 &fname::LookupIpOptions {
2081 ipv4_lookup: Some(true),
2082 ..Default::default()
2083 }
2084 )
2085 .await
2086 .expect("lookup_ip"),
2087 Ok(fname::LookupResult {
2088 addresses: Some(vec![map_ip(IPV4_HOST)]),
2089 ..Default::default()
2090 }),
2091 );
2092 })
2093 .await;
2094 let () = env
2095 .run_lookup(|proxy| async move {
2096 assert_eq!(
2098 proxy
2099 .lookup_ip(
2100 REMOTE_IPV4_HOST,
2101 &fname::LookupIpOptions {
2102 ipv6_lookup: Some(true),
2103 ..Default::default()
2104 }
2105 )
2106 .await
2107 .expect("lookup_ip"),
2108 Err(fname::LookupError::NotFound),
2109 );
2110 })
2111 .await;
2112 assert_data_tree!(inspector, root:{
2113 query_stats: {
2114 "window 1": {
2115 start_time_nanos: NonZeroUintProperty,
2116 successful_queries: 1u64,
2117 failed_queries: 1u64,
2118 average_success_duration_micros: NonZeroUintProperty,
2119 average_failure_duration_micros: NonZeroUintProperty,
2120 errors: {
2121 Message: 0u64,
2122 NoConnections: 0u64,
2123 NoRecordsFoundResponseCodeCounts: {
2124 NoError: 1u64,
2125 },
2126 Io: 0u64,
2127 Proto: 0u64,
2128 Timeout: 0u64,
2129 UnhandledResolveErrorKindCounts: {},
2130 },
2131 address_counts: {
2132 "1": 1u64,
2133 },
2134 },
2135 }
2136 });
2137 }
2138
2139 fn run_fake_lookup(
2140 exec: &mut fasync::TestExecutor,
2141 stats: Arc<QueryStats>,
2142 result: QueryResult<'_>,
2143 delay: zx::MonotonicDuration,
2144 ) {
2145 let start_time = fasync::MonotonicInstant::now();
2146 let () = exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2147 let update_stats = stats.finish_query(start_time, result);
2148 let mut update_stats = pin!(update_stats);
2149 assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2150 }
2151
2152 const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2154
2155 #[test]
2156 fn test_query_stats_inspect_average() {
2157 let mut exec = fasync::TestExecutor::new_with_fake_time();
2158 const START_NANOS: i64 = 1_234_567;
2159 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2160
2161 let stats = Arc::new(QueryStats::new());
2162 let inspector = fuchsia_inspect::Inspector::default();
2163 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2164 const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2165 const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2166 zx::MonotonicDuration::from_seconds(30);
2167 for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2168 let () = run_fake_lookup(
2169 &mut exec,
2170 stats.clone(),
2171 Ok(NON_ZERO_USIZE_ONE),
2172 zx::MonotonicDuration::from_nanos(0),
2173 );
2174 let () = run_fake_lookup(
2175 &mut exec,
2176 stats.clone(),
2177 Ok(NON_ZERO_USIZE_ONE),
2178 SUCCESSFUL_QUERY_DURATION,
2179 );
2180 let () = exec.set_fake_time(fasync::MonotonicInstant::after(
2181 STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2182 ));
2183 }
2184 let mut expected = tree_assertion!(query_stats: {});
2185 for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2186 let name = &format!("window {}", i + 1);
2187 let child = tree_assertion!(var name: {
2188 start_time_nanos: u64::try_from(
2189 START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2190 ).unwrap(),
2191 successful_queries: 2u64,
2192 failed_queries: 0u64,
2193 average_success_duration_micros: u64::try_from(
2194 SUCCESSFUL_QUERY_DURATION.into_micros()
2195 ).unwrap() / 2,
2196 errors: {
2197 Message: 0u64,
2198 NoConnections: 0u64,
2199 NoRecordsFoundResponseCodeCounts: {},
2200 Io: 0u64,
2201 Proto: 0u64,
2202 Timeout: 0u64,
2203 UnhandledResolveErrorKindCounts: {},
2204 },
2205 address_counts: {
2206 "1": 2u64,
2207 },
2208 });
2209 expected.add_child_assertion(child);
2210 }
2211 assert_data_tree!(@executor exec, inspector, root: {
2212 expected,
2213 });
2214 }
2215
2216 #[test]
2217 fn test_query_stats_inspect_error_counters() {
2218 let mut exec = fasync::TestExecutor::new_with_fake_time();
2219 const START_NANOS: i64 = 1_234_567;
2220 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2221
2222 let stats = Arc::new(QueryStats::new());
2223 let inspector = fuchsia_inspect::Inspector::default();
2224 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2225 const FAILED_QUERY_COUNT: u64 = 10;
2226 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2227 zx::MonotonicDuration::from_millis(500);
2228 for _ in 0..FAILED_QUERY_COUNT {
2229 let () = run_fake_lookup(
2230 &mut exec,
2231 stats.clone(),
2232 Err(&ResolveErrorKind::Timeout),
2233 FAILED_QUERY_DURATION,
2234 );
2235 }
2236 assert_data_tree!(@executor exec, inspector, root:{
2237 query_stats: {
2238 "window 1": {
2239 start_time_nanos: u64::try_from(
2240 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2241 ).unwrap(),
2242 successful_queries: 0u64,
2243 failed_queries: FAILED_QUERY_COUNT,
2244 average_failure_duration_micros: u64::try_from(
2245 FAILED_QUERY_DURATION.into_micros()
2246 ).unwrap(),
2247 errors: {
2248 Message: 0u64,
2249 NoConnections: 0u64,
2250 NoRecordsFoundResponseCodeCounts: {},
2251 Io: 0u64,
2252 Proto: 0u64,
2253 Timeout: FAILED_QUERY_COUNT,
2254 UnhandledResolveErrorKindCounts: {},
2255 },
2256 address_counts: {},
2257 },
2258 }
2259 });
2260 }
2261
2262 #[test]
2263 fn test_query_stats_inspect_no_records_found() {
2264 let mut exec = fasync::TestExecutor::new_with_fake_time();
2265 const START_NANOS: i64 = 1_234_567;
2266 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2267
2268 let stats = Arc::new(QueryStats::new());
2269 let inspector = fuchsia_inspect::Inspector::default();
2270 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2271 const FAILED_QUERY_COUNT: u64 = 10;
2272 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2273 zx::MonotonicDuration::from_millis(500);
2274
2275 let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2276 run_fake_lookup(
2277 &mut exec,
2278 stats.clone(),
2279 Err(&ResolveErrorKind::NoRecordsFound {
2280 query: Box::new(Query::default()),
2281 soa: None,
2282 negative_ttl: None,
2283 response_code,
2284 trusted: false,
2285 }),
2286 FAILED_QUERY_DURATION,
2287 )
2288 };
2289
2290 for _ in 0..FAILED_QUERY_COUNT {
2291 let () = run_fake_no_records_lookup(ResponseCode::NXDomain);
2292 let () = run_fake_no_records_lookup(ResponseCode::Refused);
2293 let () = run_fake_no_records_lookup(4096.into());
2294 let () = run_fake_no_records_lookup(4097.into());
2295 }
2296
2297 assert_data_tree!(@executor exec, inspector, root:{
2298 query_stats: {
2299 "window 1": {
2300 start_time_nanos: u64::try_from(
2301 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2302 ).unwrap(),
2303 successful_queries: 0u64,
2304 failed_queries: FAILED_QUERY_COUNT * 4,
2305 average_failure_duration_micros: u64::try_from(
2306 FAILED_QUERY_DURATION.into_micros()
2307 ).unwrap(),
2308 errors: {
2309 Message: 0u64,
2310 NoConnections: 0u64,
2311 NoRecordsFoundResponseCodeCounts: {
2312 NXDomain: FAILED_QUERY_COUNT,
2313 Refused: FAILED_QUERY_COUNT,
2314 "Unknown(4096)": FAILED_QUERY_COUNT,
2315 "Unknown(4097)": FAILED_QUERY_COUNT,
2316 },
2317 Io: 0u64,
2318 Proto: 0u64,
2319 Timeout: 0u64,
2320 UnhandledResolveErrorKindCounts: {},
2321 },
2322 address_counts: {},
2323 },
2324 }
2325 });
2326 }
2327
2328 #[test]
2329 fn test_query_stats_resolved_address_counts() {
2330 let mut exec = fasync::TestExecutor::new_with_fake_time();
2331 const START_NANOS: i64 = 1_234_567;
2332 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2333
2334 let stats = Arc::new(QueryStats::new());
2335 let inspector = fuchsia_inspect::Inspector::default();
2336 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2337
2338 let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2344 const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2345 for (count, occurrences) in address_counts.iter() {
2346 for _ in 0..*occurrences {
2347 run_fake_lookup(
2348 &mut exec,
2349 stats.clone(),
2350 Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2351 QUERY_DURATION,
2352 );
2353 }
2354 }
2355
2356 let mut expected_address_counts = tree_assertion!(address_counts: {});
2357 for (count, occurrences) in address_counts.iter() {
2358 expected_address_counts
2359 .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2360 }
2361 assert_data_tree!(@executor exec, inspector, root: {
2362 query_stats: {
2363 "window 1": {
2364 start_time_nanos: u64::try_from(
2365 START_NANOS + QUERY_DURATION.into_nanos()
2366 ).unwrap(),
2367 successful_queries: address_counts.values().sum::<u64>(),
2368 failed_queries: 0u64,
2369 average_success_duration_micros: u64::try_from(
2370 QUERY_DURATION.into_micros()
2371 ).unwrap(),
2372 errors: {
2373 Message: 0u64,
2374 NoConnections: 0u64,
2375 NoRecordsFoundResponseCodeCounts: {},
2376 Io: 0u64,
2377 Proto: 0u64,
2378 Timeout: 0u64,
2379 UnhandledResolveErrorKindCounts: {},
2380 },
2381 expected_address_counts,
2382 },
2383 },
2384 });
2385 }
2386
2387 #[test]
2388 fn test_query_stats_inspect_oldest_stats_erased() {
2389 let mut exec = fasync::TestExecutor::new_with_fake_time();
2390 const START_NANOS: i64 = 1_234_567;
2391 let () = exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2392
2393 let stats = Arc::new(QueryStats::new());
2394 let inspector = fuchsia_inspect::Inspector::default();
2395 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2396 const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2397 for _ in 0..STAT_WINDOW_COUNT {
2398 let () =
2399 run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2400 let () =
2401 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2402 }
2403 for _ in 0..STAT_WINDOW_COUNT {
2404 let () = run_fake_lookup(
2405 &mut exec,
2406 stats.clone(),
2407 Ok(NON_ZERO_USIZE_ONE),
2408 DELAY,
2409 );
2410 let () =
2411 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2412 }
2413 let mut expected = tree_assertion!(query_stats: {});
2416 let start_offset = START_NANOS
2417 + DELAY.into_nanos()
2418 + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2419 for i in 0..STAT_WINDOW_COUNT {
2420 let name = &format!("window {}", i + 1);
2421 let child = tree_assertion!(var name: {
2422 start_time_nanos: u64::try_from(
2423 start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2424 ).unwrap(),
2425 successful_queries: 1u64,
2426 failed_queries: 0u64,
2427 average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2428 errors: {
2429 Message: 0u64,
2430 NoConnections: 0u64,
2431 NoRecordsFoundResponseCodeCounts: {},
2432 Io: 0u64,
2433 Proto: 0u64,
2434 Timeout: 0u64,
2435 UnhandledResolveErrorKindCounts: {},
2436 },
2437 address_counts: {
2438 "1": 1u64,
2439 },
2440 });
2441 expected.add_child_assertion(child);
2442 }
2443 assert_data_tree!(@executor exec, inspector, root: {
2444 expected,
2445 });
2446 }
2447
2448 struct BlockingResolver {}
2449
2450 #[async_trait]
2451 impl ResolverLookup for BlockingResolver {
2452 fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2453 BlockingResolver {}
2454 }
2455
2456 async fn lookup<N: IntoName + Send>(
2457 &self,
2458 _name: N,
2459 _record_type: RecordType,
2460 ) -> Result<lookup::Lookup, ResolveError> {
2461 futures::future::pending().await
2462 }
2463
2464 async fn reverse_lookup(
2465 &self,
2466 _addr: IpAddr,
2467 ) -> Result<lookup::ReverseLookup, ResolveError> {
2468 panic!("BlockingResolver does not handle reverse lookup")
2469 }
2470 }
2471
2472 #[fasync::run_singlethreaded(test)]
2473 async fn test_parallel_query_limit() {
2474 let requests = {
2478 let (name_lookup_proxy, name_lookup_stream) =
2479 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2480 const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2481 for _ in 0..NUM_REQUESTS {
2482 let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2486 name_lookup_proxy.lookup_ip(
2487 LOCAL_HOST,
2488 &fname::LookupIpOptions {
2489 ipv4_lookup: Some(true),
2490 ipv6_lookup: Some(true),
2491 ..Default::default()
2492 },
2493 );
2494 }
2495 drop(name_lookup_proxy);
2497 let requests = name_lookup_stream
2498 .map(|request| match request.expect("channel error") {
2499 LookupRequest::LookupIp { hostname, options, responder } => {
2500 IpLookupRequest { hostname, options, responder }
2501 }
2502 req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2503 })
2504 .collect::<Vec<_>>()
2505 .await;
2506 assert_eq!(requests.len(), NUM_REQUESTS);
2507 requests
2508 };
2509
2510 let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2511
2512 const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2518 const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2519 let mut send_fut = pin!(
2520 async {
2521 for (i, req) in requests.into_iter().enumerate() {
2522 match i {
2523 BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2524 LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2525 _ => assert_matches!(sender.send(req).await, Ok(())),
2526 }
2527 }
2528 }
2529 .fuse()
2530 );
2531 let mut recv_fut = pin!({
2532 let resolver = SharedResolver::new(BlockingResolver::new(
2533 ResolverConfig::default(),
2534 ResolverOpts::default(),
2535 ));
2536 let stats = Arc::new(QueryStats::new());
2537 let (routes_proxy, _routes_stream) =
2538 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2539 async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2540 .fuse()
2541 });
2542 futures::select! {
2543 () = send_fut => {},
2544 () = recv_fut => panic!("recv_fut should never complete"),
2545 };
2546 }
2547
2548 #[test]
2549 fn test_failure_stats() {
2550 use anyhow::anyhow;
2551 use trust_dns_proto::error::ProtoError;
2552 use trust_dns_proto::op::Query;
2553
2554 let mut stats = FailureStats::default();
2555 for (error_kind, expected) in &[
2556 (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2557 (
2558 ResolveErrorKind::Msg("foo".to_string()),
2559 FailureStats { message: 2, ..Default::default() },
2560 ),
2561 (
2562 ResolveErrorKind::NoRecordsFound {
2563 query: Box::new(Query::default()),
2564 soa: None,
2565 negative_ttl: None,
2566 response_code: ResponseCode::Refused,
2567 trusted: false,
2568 },
2569 FailureStats {
2570 message: 2,
2571 no_records_found: NoRecordsFoundStats {
2572 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2573 },
2574 ..Default::default()
2575 },
2576 ),
2577 (
2578 ResolveErrorKind::Io(std::io::Error::new(
2579 std::io::ErrorKind::NotFound,
2580 anyhow!("foo"),
2581 )),
2582 FailureStats {
2583 message: 2,
2584 no_records_found: NoRecordsFoundStats {
2585 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2586 },
2587 io: 1,
2588 ..Default::default()
2589 },
2590 ),
2591 (
2592 ResolveErrorKind::Proto(ProtoError::from("foo")),
2593 FailureStats {
2594 message: 2,
2595 no_records_found: NoRecordsFoundStats {
2596 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2597 },
2598 io: 1,
2599 proto: 1,
2600 ..Default::default()
2601 },
2602 ),
2603 (
2604 ResolveErrorKind::NoConnections,
2605 FailureStats {
2606 message: 2,
2607 no_connections: 1,
2608 no_records_found: NoRecordsFoundStats {
2609 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2610 },
2611 io: 1,
2612 proto: 1,
2613 ..Default::default()
2614 },
2615 ),
2616 (
2617 ResolveErrorKind::Timeout,
2618 FailureStats {
2619 message: 2,
2620 no_connections: 1,
2621 no_records_found: NoRecordsFoundStats {
2622 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2623 },
2624 io: 1,
2625 proto: 1,
2626 timeout: 1,
2627 unhandled_resolve_error_kind: Default::default(),
2628 },
2629 ),
2630 (
2631 ResolveErrorKind::NoRecordsFound {
2632 query: Box::new(Query::default()),
2633 soa: None,
2634 negative_ttl: None,
2635 response_code: ResponseCode::NXDomain,
2636 trusted: false,
2637 },
2638 FailureStats {
2639 message: 2,
2640 no_connections: 1,
2641 no_records_found: NoRecordsFoundStats {
2642 response_code_counts: [
2643 (ResponseCode::NXDomain.into(), 1),
2644 (ResponseCode::Refused.into(), 1),
2645 ]
2646 .into(),
2647 },
2648 io: 1,
2649 proto: 1,
2650 timeout: 1,
2651 unhandled_resolve_error_kind: Default::default(),
2652 },
2653 ),
2654 (
2655 ResolveErrorKind::NoRecordsFound {
2656 query: Box::new(Query::default()),
2657 soa: None,
2658 negative_ttl: None,
2659 response_code: ResponseCode::NXDomain,
2660 trusted: false,
2661 },
2662 FailureStats {
2663 message: 2,
2664 no_connections: 1,
2665 no_records_found: NoRecordsFoundStats {
2666 response_code_counts: [
2667 (ResponseCode::NXDomain.into(), 2),
2668 (ResponseCode::Refused.into(), 1),
2669 ]
2670 .into(),
2671 },
2672 io: 1,
2673 proto: 1,
2674 timeout: 1,
2675 unhandled_resolve_error_kind: Default::default(),
2676 },
2677 ),
2678 ][..]
2679 {
2680 let () = stats.increment(error_kind);
2681 assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2682 }
2683 }
2684
2685 fn test_das_helper(
2686 l_addr: fnet::IpAddress,
2687 l_src: Option<fnet::IpAddress>,
2688 r_addr: fnet::IpAddress,
2689 r_src: Option<fnet::IpAddress>,
2690 want: std::cmp::Ordering,
2691 ) {
2692 let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2693 let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2694 assert_eq!(
2695 left.cmp(&right),
2696 want,
2697 "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2698 want,
2699 l_addr,
2700 l_src,
2701 left,
2702 r_addr,
2703 r_src,
2704 right
2705 );
2706 }
2707
2708 macro_rules! add_das_test {
2709 ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2710 #[test]
2711 fn $name() {
2712 test_das_helper(
2713 $pref_dst,
2714 $pref_src,
2715 $other_dst,
2716 $other_src,
2717 std::cmp::Ordering::Less,
2718 )
2719 }
2720 };
2721 }
2722
2723 add_das_test!(
2724 prefer_reachable,
2725 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2726 other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2727 );
2728
2729 add_das_test!(
2732 prefer_matching_scope,
2733 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2734 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2735 );
2736
2737 add_das_test!(
2738 prefer_matching_label,
2739 preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2740 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2741 );
2742
2743 add_das_test!(
2744 prefer_higher_precedence_1,
2745 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2746 other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2747 );
2748
2749 add_das_test!(
2750 prefer_higher_precedence_2,
2751 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2752 other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2753 );
2754
2755 add_das_test!(
2756 prefer_smaller_scope,
2757 preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2758 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2759 );
2760
2761 add_das_test!(
2762 prefer_longest_matching_prefix,
2763 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2764 other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2765 );
2766
2767 #[test]
2768 fn test_das_equals() {
2769 for (dst, src) in [
2770 (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2771 (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2772 ]
2773 .iter()
2774 {
2775 let () = test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2776 let () = test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2777 }
2778 }
2779
2780 #[test]
2781 fn test_valid_policy_table() {
2782 assert_eq!(
2784 POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2785 net_types::ip::Subnet::new(net_types::ip::Ipv6::UNSPECIFIED_ADDRESS, 0)
2786 .expect("invalid subnet")
2787 );
2788 let () = POLICY_TABLE.windows(2).for_each(|w| {
2790 let Policy { prefix: cur, precedence: _, label: _ } = w[0];
2791 let Policy { prefix: nxt, precedence: _, label: _ } = w[1];
2792 assert!(
2793 cur.prefix() >= nxt.prefix(),
2794 "bad ordering of prefixes, {} must come after {}",
2795 cur,
2796 nxt
2797 )
2798 });
2799 for policy in POLICY_TABLE.iter() {
2802 assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2803 }
2804 }
2805
2806 #[fasync::run_singlethreaded(test)]
2807 async fn test_sort_preferred_addresses() {
2808 const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2809 (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2810 (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2811 (fidl_ip!("192.168.50.22"), None),
2812 (fidl_ip!("2001::2"), None),
2813 (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2814 ];
2815 const SORTED: [IpAddr; 5] = [
2818 std_ip!("::1"),
2819 std_ip!("2001:db8:1::1"),
2820 std_ip!("127.0.0.1"),
2821 std_ip!("192.168.50.22"),
2822 std_ip!("2001::2"),
2823 ];
2824 let (routes_proxy, routes_stream) =
2825 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2826 let routes_fut =
2827 routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2828 let (destination, responder) = assert_matches!(
2829 req,
2830 fnet_routes::StateRequest::Resolve { destination, responder }
2831 => (destination, responder)
2832 );
2833 let result = TEST_IPS
2834 .iter()
2835 .enumerate()
2836 .find_map(|(i, (dst, src))| {
2837 if *dst == destination && src.is_some() {
2838 let inner = fnet_routes::Destination {
2839 address: Some(*dst),
2840 source_address: *src,
2841 ..Default::default()
2842 };
2843 if i % 2 == 0 {
2846 Some(fnet_routes::Resolved::Direct(inner))
2847 } else {
2848 Some(fnet_routes::Resolved::Gateway(inner))
2849 }
2850 } else {
2851 None
2852 }
2853 })
2854 .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
2855 futures::future::ready(
2856 responder
2857 .send(result.as_ref().map_err(|e| *e))
2858 .context("failed to send Resolve response"),
2859 )
2860 });
2861
2862 let ((), ()) = futures::future::try_join(routes_fut, async move {
2863 let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
2864 let addrs = sort_preferred_addresses(addrs, &routes_proxy)
2865 .await
2866 .expect("failed to sort addresses");
2867 let addrs = addrs
2868 .into_iter()
2869 .map(|a| {
2870 let net_ext::IpAddress(a) = a.into();
2871 a
2872 })
2873 .collect::<Vec<_>>();
2874 assert_eq!(&addrs[..], &SORTED[..]);
2875 Ok(())
2876 })
2877 .await
2878 .expect("error running futures");
2879 }
2880
2881 #[fasync::run_singlethreaded(test)]
2882 async fn test_lookupip() {
2883 let routes_handler = |req| {
2885 let (destination, responder) = assert_matches!(
2886 req,
2887 fnet_routes::StateRequest::Resolve { destination, responder }
2888 => (destination, responder)
2889 );
2890 let resolved;
2891 let response = if destination == map_ip(IPV6_HOST) {
2892 resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
2893 address: Some(destination),
2894 source_address: Some(destination),
2895 ..Default::default()
2896 });
2897 Ok(&resolved)
2898 } else {
2899 Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
2900 };
2901 let () = responder.send(response).expect("failed to send Resolve FIDL response");
2902 };
2903 TestEnvironment::default()
2904 .run_lookup_with_routes_handler(
2905 |proxy| async move {
2906 assert_eq!(
2908 proxy
2909 .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
2910 .await
2911 .expect("lookup_ip"),
2912 Err(fname::LookupError::InvalidArgs)
2913 );
2914 assert_eq!(
2916 proxy
2917 .lookup_ip(
2918 REMOTE_IPV4_HOST,
2919 &fname::LookupIpOptions {
2920 ipv4_lookup: Some(false),
2921 ipv6_lookup: Some(false),
2922 ..Default::default()
2923 }
2924 )
2925 .await
2926 .expect("lookup_ip"),
2927 Err(fname::LookupError::InvalidArgs)
2928 );
2929 assert_eq!(
2931 proxy
2932 .lookup_ip(
2933 REMOTE_IPV4_HOST,
2934 &fname::LookupIpOptions {
2935 ipv4_lookup: Some(false),
2936 ipv6_lookup: Some(true),
2937 ..Default::default()
2938 }
2939 )
2940 .await
2941 .expect("lookup_ip"),
2942 Err(fname::LookupError::NotFound)
2943 );
2944 assert_eq!(
2946 proxy
2947 .lookup_ip(
2948 REMOTE_IPV4_HOST,
2949 &fname::LookupIpOptions {
2950 ipv4_lookup: Some(true),
2951 ipv6_lookup: Some(true),
2952 ..Default::default()
2953 }
2954 )
2955 .await
2956 .expect("lookup_ip"),
2957 Ok(fname::LookupResult {
2958 addresses: Some(vec![map_ip(IPV4_HOST)]),
2959 ..Default::default()
2960 })
2961 );
2962 assert_eq!(
2964 proxy
2965 .lookup_ip(
2966 REMOTE_IPV4_IPV6_HOST,
2967 &fname::LookupIpOptions {
2968 ipv4_lookup: Some(true),
2969 ipv6_lookup: Some(true),
2970 ..Default::default()
2971 }
2972 )
2973 .await
2974 .expect("lookup_ip"),
2975 Ok(fname::LookupResult {
2976 addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
2977 ..Default::default()
2978 })
2979 );
2980 assert_eq!(
2982 proxy
2983 .lookup_ip(
2984 REMOTE_IPV4_IPV6_HOST,
2985 &fname::LookupIpOptions {
2986 ipv4_lookup: Some(true),
2987 ipv6_lookup: Some(true),
2988 sort_addresses: Some(true),
2989 ..Default::default()
2990 }
2991 )
2992 .await
2993 .expect("lookup_ip"),
2994 Ok(fname::LookupResult {
2995 addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
2996 ..Default::default()
2997 })
2998 );
2999 },
3000 routes_handler,
3001 )
3002 .await
3003 }
3004}