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