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