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 itertools::Itertools as _;
1368 use net_declare::{fidl_ip, std_ip, std_ip_v4, std_ip_v6, std_socket_addr};
1369 use net_types::ip::Ip as _;
1370 use test_case::test_case;
1371 use trust_dns_proto::op::Query;
1372 use trust_dns_proto::rr::{Name, Record};
1373 use trust_dns_resolver::config::Protocol;
1374 use trust_dns_resolver::lookup::{Lookup, ReverseLookup};
1375
1376 use super::*;
1377
1378 const IPV4_LOOPBACK: fnet::IpAddress = fidl_ip!("127.0.0.1");
1379 const IPV6_LOOPBACK: fnet::IpAddress = fidl_ip!("::1");
1380 const LOCAL_HOST: &str = "localhost.";
1381
1382 const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1384 const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1386
1387 const REMOTE_IPV4_HOST: &str = "www.foo.com";
1389 const REMOTE_IPV6_HOST: &str = "www.bar.com";
1391 const REMOTE_IPV4_HOST_ALIAS: &str = "www.alsofoo.com";
1392 const REMOTE_IPV6_HOST_ALIAS: &str = "www.alsobar.com";
1393 const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1395 const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1397 const NO_RECORDS_AND_NO_ERROR_HOST: &str = "www.no-records-and-no-error.com";
1399
1400 async fn setup_namelookup_service() -> (fname::LookupProxy, impl futures::Future<Output = ()>) {
1401 let (name_lookup_proxy, stream) =
1402 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1403
1404 let mut resolver_opts = ResolverOpts::default();
1405 resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1406
1407 let resolver = SharedResolver::new(
1408 Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1409 .expect("failed to create resolver"),
1410 );
1411 let stats = Arc::new(QueryStats::new());
1412 let (routes_proxy, routes_stream) =
1413 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1414 let routes_fut =
1415 routes_stream.try_for_each(|req| -> futures::future::Ready<Result<(), fidl::Error>> {
1416 panic!("Should not call routes/State. Received request {:?}", req)
1417 });
1418 let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1419
1420 (name_lookup_proxy, async move {
1421 futures::future::try_join3(
1422 run_lookup(&resolver, stream, sender),
1423 routes_fut,
1424 create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).map(Ok),
1425 )
1426 .map(|r| match r {
1427 Ok(((), (), ())) => (),
1428 Err(e) => panic!("namelookup service error {:?}", e),
1429 })
1430 .await
1431 })
1432 }
1433
1434 #[fasync::run_singlethreaded(test)]
1435 async fn test_lookupip_localhost() {
1436 let (proxy, fut) = setup_namelookup_service().await;
1437 let ((), ()) = futures::future::join(fut, async move {
1438 assert_eq!(
1440 proxy
1441 .lookup_ip(
1442 LOCAL_HOST,
1443 &fname::LookupIpOptions {
1444 ipv4_lookup: Some(true),
1445 ipv6_lookup: Some(true),
1446 ..Default::default()
1447 }
1448 )
1449 .await
1450 .expect("lookup_ip"),
1451 Ok(fname::LookupResult {
1452 addresses: Some(vec![IPV4_LOOPBACK, IPV6_LOOPBACK]),
1453 ..Default::default()
1454 }),
1455 );
1456
1457 assert_eq!(
1459 proxy
1460 .lookup_ip(
1461 LOCAL_HOST,
1462 &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
1463 )
1464 .await
1465 .expect("lookup_ip"),
1466 Ok(fname::LookupResult {
1467 addresses: Some(vec![IPV4_LOOPBACK]),
1468 ..Default::default()
1469 }),
1470 );
1471
1472 assert_eq!(
1474 proxy
1475 .lookup_ip(
1476 LOCAL_HOST,
1477 &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
1478 )
1479 .await
1480 .expect("lookup_ip"),
1481 Ok(fname::LookupResult {
1482 addresses: Some(vec![IPV6_LOOPBACK]),
1483 ..Default::default()
1484 }),
1485 );
1486 })
1487 .await;
1488 }
1489
1490 #[fasync::run_singlethreaded(test)]
1491 async fn test_lookuphostname_localhost() {
1492 let (proxy, fut) = setup_namelookup_service().await;
1493 let ((), ()) = futures::future::join(fut, async move {
1494 let hostname = IPV4_LOOPBACK;
1495 assert_eq!(
1496 proxy.lookup_hostname(&hostname).await.expect("lookup_hostname").as_deref(),
1497 Ok(LOCAL_HOST)
1498 );
1499 })
1500 .await;
1501 }
1502
1503 #[derive(Debug, Clone)]
1504 struct MockResolver {
1505 config: ResolverConfig,
1506 repeat: u16,
1507 name_server_stats: Vec<NameServerStats>,
1508 }
1509
1510 impl ResolverLookup for MockResolver {
1511 fn new(config: ResolverConfig, _options: ResolverOpts) -> Self {
1512 Self { config, repeat: 1, name_server_stats: Vec::new() }
1513 }
1514
1515 async fn lookup<N: IntoName + Send>(
1516 &self,
1517 name: N,
1518 record_type: RecordType,
1519 ) -> Result<lookup::Lookup, ResolveError> {
1520 let Self { config: _, repeat, name_server_stats: _ } = self;
1521
1522 let name = name.into_name()?;
1523 let host_name = name.to_utf8();
1524
1525 if host_name == NO_RECORDS_AND_NO_ERROR_HOST {
1526 return Ok(Lookup::new_with_max_ttl(Query::default(), Arc::new([])));
1527 }
1528 let rdatas = match record_type {
1529 RecordType::A => [REMOTE_IPV4_HOST, REMOTE_IPV4_IPV6_HOST]
1530 .contains(&host_name.as_str())
1531 .then_some(RData::A(IPV4_HOST)),
1532 RecordType::AAAA => [REMOTE_IPV6_HOST, REMOTE_IPV4_IPV6_HOST]
1533 .contains(&host_name.as_str())
1534 .then_some(RData::AAAA(IPV6_HOST)),
1535 RecordType::CNAME => match host_name.as_str() {
1536 REMOTE_IPV4_HOST_ALIAS => Some(REMOTE_IPV4_HOST),
1537 REMOTE_IPV6_HOST_ALIAS => Some(REMOTE_IPV6_HOST),
1538 _ => None,
1539 }
1540 .map(Name::from_str)
1541 .transpose()
1542 .unwrap()
1543 .map(RData::CNAME),
1544 record_type => {
1545 panic!("unexpected record type {:?}", record_type)
1546 }
1547 }
1548 .into_iter();
1549
1550 let len = rdatas.len() * usize::from(*repeat);
1551 let records: Vec<Record> = rdatas
1552 .map(|rdata| {
1553 Record::from_rdata(
1554 Name::new(),
1555 60,
1558 rdata,
1559 )
1560 })
1561 .cycle()
1562 .take(len)
1563 .collect();
1564
1565 if records.is_empty() {
1566 let mut response = trust_dns_proto::op::Message::new();
1567 let _: &mut trust_dns_proto::op::Message =
1568 response.set_response_code(ResponseCode::NoError);
1569 let error = ResolveError::from_response(response.into(), false)
1570 .expect_err("response with no records should be a NoRecordsFound error");
1571 return Err(error);
1572 }
1573
1574 Ok(Lookup::new_with_max_ttl(Query::default(), records.into()))
1575 }
1576
1577 async fn reverse_lookup(
1578 &self,
1579 addr: IpAddr,
1580 ) -> Result<lookup::ReverseLookup, ResolveError> {
1581 let lookup = if addr == IPV4_HOST {
1582 Lookup::from_rdata(
1583 Query::default(),
1584 RData::PTR(Name::from_str(REMOTE_IPV4_HOST).unwrap()),
1585 )
1586 } else if addr == IPV6_HOST {
1587 Lookup::new_with_max_ttl(
1588 Query::default(),
1589 Arc::new([
1590 Record::from_rdata(
1591 Name::new(),
1592 60, RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1595 ),
1596 Record::from_rdata(
1597 Name::new(),
1598 60, RData::PTR(Name::from_str(REMOTE_IPV6_HOST_EXTRA).unwrap()),
1601 ),
1602 ]),
1603 )
1604 } else {
1605 Lookup::new_with_max_ttl(Query::default(), Arc::new([]))
1606 };
1607 Ok(ReverseLookup::from(lookup))
1608 }
1609 }
1610
1611 impl NameServerStatsProvider for MockResolver {
1612 fn name_server_stats(&self) -> Vec<NameServerStats> {
1613 self.name_server_stats.clone()
1614 }
1615 }
1616
1617 struct TestEnvironment {
1618 shared_resolver: SharedResolver<MockResolver>,
1619 config_state: Arc<dns::config::ServerConfigState>,
1620 stats: Arc<QueryStats>,
1621 }
1622
1623 impl Default for TestEnvironment {
1624 fn default() -> Self {
1625 Self::new(1)
1626 }
1627 }
1628
1629 impl TestEnvironment {
1630 fn new(repeat: u16) -> Self {
1631 Self {
1632 shared_resolver: SharedResolver::new(MockResolver {
1633 config: ResolverConfig::from_parts(
1634 None,
1635 vec![],
1636 NameServerConfigGroup::with_capacity(0),
1639 ),
1640 repeat,
1641 name_server_stats: Vec::new(),
1642 }),
1643 config_state: Arc::new(dns::config::ServerConfigState::new()),
1644 stats: Arc::new(QueryStats::new()),
1645 }
1646 }
1647
1648 async fn run_lookup<F, Fut>(&self, f: F)
1649 where
1650 Fut: futures::Future<Output = ()>,
1651 F: FnOnce(fname::LookupProxy) -> Fut,
1652 {
1653 self.run_lookup_with_routes_handler(f, |req| {
1654 panic!("Should not call routes/State. Received request {:?}", req)
1655 })
1656 .await
1657 }
1658
1659 async fn run_lookup_with_routes_handler<F, Fut, R>(&self, f: F, handle_routes: R)
1660 where
1661 Fut: futures::Future<Output = ()>,
1662 F: FnOnce(fname::LookupProxy) -> Fut,
1663 R: Fn(fnet_routes::StateRequest),
1664 {
1665 let (name_lookup_proxy, name_lookup_stream) =
1666 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1667
1668 let (routes_proxy, routes_stream) =
1669 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1670
1671 let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1672 let Self { shared_resolver, config_state: _, stats } = self;
1673 let ((), (), (), ()) = futures::future::try_join4(
1674 run_lookup(shared_resolver, name_lookup_stream, sender),
1675 f(name_lookup_proxy).map(Ok),
1676 routes_stream.try_for_each(|req| futures::future::ok(handle_routes(req))),
1677 create_ip_lookup_fut(shared_resolver, stats.clone(), routes_proxy, recv).map(Ok),
1678 )
1679 .await
1680 .expect("Error running lookup future");
1681 }
1682
1683 async fn run_admin<F, Fut>(&self, f: F)
1684 where
1685 Fut: futures::Future<Output = ()>,
1686 F: FnOnce(fname::LookupAdminProxy) -> Fut,
1687 {
1688 let (lookup_admin_proxy, lookup_admin_stream) =
1689 fidl::endpoints::create_proxy_and_stream::<fname::LookupAdminMarker>();
1690 let Self { shared_resolver, config_state, stats: _ } = self;
1691 let ((), ()) = futures::future::try_join(
1692 run_lookup_admin(shared_resolver, config_state, lookup_admin_stream)
1693 .map_err(anyhow::Error::from),
1694 f(lookup_admin_proxy).map(Ok),
1695 )
1696 .await
1697 .expect("Error running admin future");
1698 }
1699 }
1700
1701 fn map_ip<T: Into<IpAddr>>(addr: T) -> fnet::IpAddress {
1702 net_ext::IpAddress(addr.into()).into()
1703 }
1704
1705 #[fasync::run_singlethreaded(test)]
1706 async fn test_no_records_and_no_error() {
1707 TestEnvironment::default()
1708 .run_lookup(|proxy| async move {
1709 let proxy = &proxy;
1710 futures::stream::iter([(true, true), (true, false), (false, true)])
1711 .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1712 assert_eq!(
1717 proxy
1718 .lookup_ip(
1719 NO_RECORDS_AND_NO_ERROR_HOST,
1720 &fname::LookupIpOptions {
1721 ipv4_lookup: Some(ipv4_lookup),
1722 ipv6_lookup: Some(ipv6_lookup),
1723 ..Default::default()
1724 }
1725 )
1726 .await
1727 .expect("lookup_ip"),
1728 Err(fname::LookupError::NotFound),
1729 );
1730 })
1731 .await
1732 })
1733 .await;
1734 }
1735
1736 #[fasync::run_singlethreaded(test)]
1737 async fn test_lookupip_remotehost_overflow() {
1738 const REPEAT: u16 = fname::MAX_ADDRESSES / 2 + 1;
1740 let expected = std::iter::empty()
1741 .chain(std::iter::repeat(map_ip(IPV4_HOST)).take(REPEAT.into()))
1742 .chain(std::iter::repeat(map_ip(IPV6_HOST)).take(REPEAT.into()))
1743 .take(fname::MAX_ADDRESSES.into())
1744 .collect::<Vec<_>>();
1745 assert_eq!(expected.len(), usize::from(fname::MAX_ADDRESSES));
1746 TestEnvironment::new(REPEAT)
1747 .run_lookup(|proxy| async move {
1748 assert_eq!(
1749 proxy
1750 .lookup_ip(
1751 REMOTE_IPV4_IPV6_HOST,
1752 &fname::LookupIpOptions {
1753 ipv4_lookup: Some(true),
1754 ipv6_lookup: Some(true),
1755 ..Default::default()
1756 }
1757 )
1758 .await
1759 .expect("lookup_ip"),
1760 Ok(fname::LookupResult { addresses: Some(expected), ..Default::default() })
1761 );
1762 })
1763 .await;
1764 }
1765
1766 #[fasync::run_singlethreaded(test)]
1767 async fn test_lookupip_remotehost_ipv4() {
1768 TestEnvironment::default()
1769 .run_lookup(|proxy| async move {
1770 assert_eq!(
1772 proxy
1773 .lookup_ip(
1774 REMOTE_IPV4_HOST,
1775 &fname::LookupIpOptions {
1776 ipv4_lookup: Some(true),
1777 ipv6_lookup: Some(true),
1778 ..Default::default()
1779 }
1780 )
1781 .await
1782 .expect("lookup_ip"),
1783 Ok(fname::LookupResult {
1784 addresses: Some(vec![map_ip(IPV4_HOST)]),
1785 ..Default::default()
1786 }),
1787 );
1788
1789 assert_eq!(
1791 proxy
1792 .lookup_ip(
1793 REMOTE_IPV4_HOST,
1794 &fname::LookupIpOptions {
1795 ipv4_lookup: Some(true),
1796 ..Default::default()
1797 }
1798 )
1799 .await
1800 .expect("lookup_ip"),
1801 Ok(fname::LookupResult {
1802 addresses: Some(vec![map_ip(IPV4_HOST)]),
1803 ..Default::default()
1804 }),
1805 );
1806
1807 assert_eq!(
1809 proxy
1810 .lookup_ip(
1811 REMOTE_IPV4_HOST,
1812 &fname::LookupIpOptions {
1813 ipv6_lookup: Some(true),
1814 ..Default::default()
1815 }
1816 )
1817 .await
1818 .expect("lookup_ip"),
1819 Err(fname::LookupError::NotFound),
1820 );
1821 })
1822 .await;
1823 }
1824
1825 #[fasync::run_singlethreaded(test)]
1826 async fn test_lookupip_remotehost_ipv6() {
1827 TestEnvironment::default()
1828 .run_lookup(|proxy| async move {
1829 assert_eq!(
1831 proxy
1832 .lookup_ip(
1833 REMOTE_IPV6_HOST,
1834 &fname::LookupIpOptions {
1835 ipv4_lookup: Some(true),
1836 ipv6_lookup: Some(true),
1837 ..Default::default()
1838 }
1839 )
1840 .await
1841 .expect("lookup_ip"),
1842 Ok(fname::LookupResult {
1843 addresses: Some(vec![map_ip(IPV6_HOST)]),
1844 ..Default::default()
1845 }),
1846 );
1847
1848 assert_eq!(
1850 proxy
1851 .lookup_ip(
1852 REMOTE_IPV6_HOST,
1853 &fname::LookupIpOptions {
1854 ipv4_lookup: Some(true),
1855 ..Default::default()
1856 }
1857 )
1858 .await
1859 .expect("lookup_ip"),
1860 Err(fname::LookupError::NotFound),
1861 );
1862
1863 assert_eq!(
1865 proxy
1866 .lookup_ip(
1867 REMOTE_IPV6_HOST,
1868 &fname::LookupIpOptions {
1869 ipv6_lookup: Some(true),
1870 ..Default::default()
1871 }
1872 )
1873 .await
1874 .expect("lookup_ip"),
1875 Ok(fname::LookupResult {
1876 addresses: Some(vec![map_ip(IPV6_HOST)]),
1877 ..Default::default()
1878 }),
1879 );
1880 })
1881 .await;
1882 }
1883
1884 #[test_case(REMOTE_IPV4_HOST_ALIAS, REMOTE_IPV4_HOST; "ipv4")]
1885 #[test_case(REMOTE_IPV6_HOST_ALIAS, REMOTE_IPV6_HOST; "ipv6")]
1886 #[fasync::run_singlethreaded(test)]
1887 async fn test_lookupip_remotehost_canonical_name(hostname: &str, expected: &str) {
1888 TestEnvironment::default()
1889 .run_lookup(|proxy| async move {
1890 assert_matches!(
1891 proxy
1892 .lookup_ip(
1893 hostname,
1894 &fname::LookupIpOptions {
1895 canonical_name_lookup: Some(true),
1896 ..Default::default()
1897 }
1898 )
1899 .await,
1900 Ok(Ok(fname::LookupResult {
1901 canonical_name: Some(cname),
1902 ..
1903 })) => assert_eq!(cname, expected)
1904 );
1905 })
1906 .await;
1907 }
1908
1909 #[fasync::run_singlethreaded(test)]
1910 async fn test_lookupip_ip_literal() {
1911 TestEnvironment::default()
1912 .run_lookup(|proxy| async move {
1913 let proxy = &proxy;
1914
1915 let range = || [true, false].into_iter();
1916
1917 futures::stream::iter(range().cartesian_product(range()))
1918 .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1919 assert_eq!(
1920 proxy
1921 .lookup_ip(
1922 "240.0.0.2",
1923 &fname::LookupIpOptions {
1924 ipv4_lookup: Some(ipv4_lookup),
1925 ipv6_lookup: Some(ipv6_lookup),
1926 ..Default::default()
1927 }
1928 )
1929 .await
1930 .expect("lookup_ip"),
1931 Err(fname::LookupError::InvalidArgs),
1932 "ipv4_lookup={},ipv6_lookup={}",
1933 ipv4_lookup,
1934 ipv6_lookup,
1935 );
1936
1937 assert_eq!(
1938 proxy
1939 .lookup_ip(
1940 "abcd::2",
1941 &fname::LookupIpOptions {
1942 ipv4_lookup: Some(ipv4_lookup),
1943 ipv6_lookup: Some(ipv6_lookup),
1944 ..Default::default()
1945 }
1946 )
1947 .await
1948 .expect("lookup_ip"),
1949 Err(fname::LookupError::InvalidArgs),
1950 "ipv4_lookup={},ipv6_lookup={}",
1951 ipv4_lookup,
1952 ipv6_lookup,
1953 );
1954 })
1955 .await
1956 })
1957 .await
1958 }
1959
1960 #[fasync::run_singlethreaded(test)]
1961 async fn test_lookup_hostname() {
1962 TestEnvironment::default()
1963 .run_lookup(|proxy| async move {
1964 assert_eq!(
1965 proxy
1966 .lookup_hostname(&map_ip(IPV4_HOST))
1967 .await
1968 .expect("lookup_hostname")
1969 .as_deref(),
1970 Ok(REMOTE_IPV4_HOST)
1971 );
1972 })
1973 .await;
1974 }
1975
1976 #[fasync::run_singlethreaded(test)]
1979 async fn test_lookup_hostname_multi() {
1980 TestEnvironment::default()
1981 .run_lookup(|proxy| async move {
1982 assert_eq!(
1983 proxy
1984 .lookup_hostname(&map_ip(IPV6_HOST))
1985 .await
1986 .expect("lookup_hostname")
1987 .as_deref(),
1988 Ok(REMOTE_IPV6_HOST)
1989 );
1990 })
1991 .await;
1992 }
1993
1994 #[fasync::run_singlethreaded(test)]
1995 async fn test_set_server_names() {
1996 let env = TestEnvironment::default();
1997
1998 let to_server_configs = |socket_addr: SocketAddr| -> [NameServerConfig; 2] {
1999 [
2000 NameServerConfig {
2001 socket_addr,
2002 protocol: Protocol::Udp,
2003 tls_dns_name: None,
2004 trust_nx_responses: false,
2005 bind_addr: None,
2006 num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2007 },
2008 NameServerConfig {
2009 socket_addr,
2010 protocol: Protocol::Tcp,
2011 tls_dns_name: None,
2012 trust_nx_responses: false,
2013 bind_addr: None,
2014 num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2015 },
2016 ]
2017 };
2018
2019 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2021
2022 env.run_admin(|proxy| async move {
2024 proxy
2025 .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
2026 .await
2027 .expect("Failed to call SetDnsServers")
2028 .expect("SetDnsServers error");
2029 })
2030 .await;
2031 assert_eq!(
2032 env.shared_resolver.read().config.name_servers().to_vec(),
2033 vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
2034 .into_iter()
2035 .map(|s| {
2036 let net_ext::SocketAddress(s) = s.into();
2037 s
2038 })
2039 .flat_map(|x| to_server_configs(x).to_vec().into_iter())
2040 .collect::<Vec<_>>()
2041 );
2042
2043 env.run_admin(|proxy| async move {
2045 proxy
2046 .set_dns_servers(&[])
2047 .await
2048 .expect("Failed to call SetDnsServers")
2049 .expect("SetDnsServers error");
2050 })
2051 .await;
2052 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
2053 }
2054
2055 #[fasync::run_singlethreaded(test)]
2056 async fn test_set_server_names_error() {
2057 let env = TestEnvironment::default();
2058 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2060
2061 env.run_admin(|proxy| async move {
2062 let status = proxy
2066 .set_dns_servers(&[fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
2067 address: fnet::Ipv4Address { addr: [224, 0, 0, 1] },
2068 port: DEFAULT_PORT,
2069 })])
2070 .await
2071 .expect("Failed to call SetDnsServers")
2072 .expect_err("SetDnsServers should fail for multicast address");
2073 assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2074
2075 let status = proxy
2077 .set_dns_servers(&[fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
2078 address: fnet::Ipv6Address { addr: [0; 16] },
2079 port: DEFAULT_PORT,
2080 zone_index: 0,
2081 })])
2082 .await
2083 .expect("Failed to call SetDnsServers")
2084 .expect_err("SetDnsServers should fail for unspecified address");
2085 assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2086 })
2087 .await;
2088
2089 assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2091 }
2092
2093 #[fasync::run_singlethreaded(test)]
2094 async fn test_get_servers() {
2095 let env = TestEnvironment::default();
2096 env.run_admin(|proxy| async move {
2097 let expect = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2098 proxy.set_dns_servers(expect).await.expect("FIDL error").expect("set_servers failed");
2099 assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
2100 })
2101 .await;
2102 }
2103
2104 #[fasync::run_singlethreaded(test)]
2105 async fn test_config_inspect() {
2106 let env = TestEnvironment::default();
2107 let inspector = fuchsia_inspect::Inspector::default();
2108 let _config_state_node =
2109 add_config_state_inspect(inspector.root(), env.config_state.clone());
2110 assert_data_tree!(inspector, root:{
2111 servers: {}
2112 });
2113 env.run_admin(|proxy| async move {
2114 let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2115 proxy.set_dns_servers(servers).await.expect("FIDL error").expect("set_servers failed");
2116 })
2117 .await;
2118 assert_data_tree!(inspector, root:{
2119 servers: {
2120 "0": {
2121 address: "[2001:4860:4860::4444%2]:53",
2122 },
2123 "1": {
2124 address: "8.8.4.4:53",
2125 },
2126 "2": {
2127 address: "[2002:4860:4860::4444%3]:53",
2128 },
2129 "3": {
2130 address: "8.8.8.8:53",
2131 },
2132 }
2133 });
2134 }
2135
2136 #[fasync::run_singlethreaded(test)]
2137 async fn test_name_server_stats_inspect() {
2138 let env = TestEnvironment::default();
2139 let inspector = fuchsia_inspect::Inspector::default();
2140 let _name_server_stats_node =
2141 add_name_server_stats_inspect(inspector.root(), env.shared_resolver.clone());
2142 assert_data_tree!(inspector, root:{
2143 name_servers: {}
2144 });
2145
2146 let addr = std_socket_addr!("1.2.3.4:53");
2147 let stats = NameServerStats {
2148 addr,
2149 proto: Protocol::Udp,
2150 failures: 1,
2151 successes: 2,
2152 success_streak: 0,
2153 recent_errors: vec![ResolveErrorKind::Timeout.into()],
2154 };
2155 env.shared_resolver.write(Arc::new(MockResolver {
2156 config: ResolverConfig::default(),
2157 repeat: 1,
2158 name_server_stats: vec![stats],
2159 }));
2160
2161 assert_data_tree!(inspector, root:{
2162 name_servers: {
2163 "0": {
2164 address: "1.2.3.4:53",
2165 protocol: "Udp",
2166 successful_queries: 2u64,
2167 failed_queries: 1u64,
2168 success_streak: 0u64,
2169 errors: {
2170 Timeout: 1u64,
2171 Message: 0u64,
2172 NoConnections: 0u64,
2173 IoErrorCounts: {},
2174 ProtoErrorCounts: {},
2175 NoRecordsFoundResponseCodeCounts: {},
2176 UnhandledResolveErrorKindCounts: {},
2177 }
2178 }
2179 }
2180 });
2181 }
2182
2183 #[test]
2184 fn test_unhandled_resolve_error_kind_stats() {
2185 use ResolveErrorKind::{Msg, Timeout};
2186 let mut unhandled_resolve_error_kind_stats = GenericErrorKindStats::default();
2187 assert_eq!(
2188 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2189 "Msg"
2190 );
2191 assert_eq!(
2192 unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2193 "Msg"
2194 );
2195 assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2196 assert_eq!(
2197 unhandled_resolve_error_kind_stats,
2198 GenericErrorKindStats([(String::from("Msg"), 2), (String::from("Timeout"), 1)].into())
2199 )
2200 }
2201
2202 #[fasync::run_singlethreaded(test)]
2203 async fn test_query_stats_updated() {
2204 let env = TestEnvironment::default();
2205 let inspector = fuchsia_inspect::Inspector::default();
2206 let _query_stats_inspect_node =
2207 add_query_stats_inspect(inspector.root(), env.stats.clone());
2208 assert_data_tree!(inspector, root:{
2209 query_stats: {}
2210 });
2211
2212 env.run_lookup(|proxy| async move {
2213 assert_eq!(
2215 proxy
2216 .lookup_ip(
2217 REMOTE_IPV4_HOST,
2218 &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
2219 )
2220 .await
2221 .expect("lookup_ip"),
2222 Ok(fname::LookupResult {
2223 addresses: Some(vec![map_ip(IPV4_HOST)]),
2224 ..Default::default()
2225 }),
2226 );
2227 })
2228 .await;
2229 env.run_lookup(|proxy| async move {
2230 assert_eq!(
2232 proxy
2233 .lookup_ip(
2234 REMOTE_IPV4_HOST,
2235 &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
2236 )
2237 .await
2238 .expect("lookup_ip"),
2239 Err(fname::LookupError::NotFound),
2240 );
2241 })
2242 .await;
2243 assert_data_tree!(inspector, root:{
2244 query_stats: {
2245 "window 1": {
2246 start_time_nanos: NonZeroUintProperty,
2247 successful_queries: 1u64,
2248 failed_queries: 1u64,
2249 average_success_duration_micros: NonZeroUintProperty,
2250 average_failure_duration_micros: NonZeroUintProperty,
2251 errors: {
2252 Message: 0u64,
2253 NoConnections: 0u64,
2254 NoRecordsFoundResponseCodeCounts: {
2255 NoError: 1u64,
2256 },
2257 IoErrorCounts: {},
2258 ProtoErrorCounts: {},
2259 Timeout: 0u64,
2260 UnhandledResolveErrorKindCounts: {},
2261 },
2262 address_counts: {
2263 "1": 1u64,
2264 },
2265 },
2266 }
2267 });
2268 }
2269
2270 fn run_fake_lookup(
2271 exec: &mut fasync::TestExecutor,
2272 stats: Arc<QueryStats>,
2273 result: QueryResult<'_>,
2274 delay: zx::MonotonicDuration,
2275 ) {
2276 let start_time = fasync::MonotonicInstant::now();
2277 exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2278 let update_stats = stats.finish_query(start_time, result);
2279 let mut update_stats = pin!(update_stats);
2280 assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2281 }
2282
2283 const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2285
2286 #[test]
2287 fn test_query_stats_inspect_average() {
2288 let mut exec = fasync::TestExecutor::new_with_fake_time();
2289 const START_NANOS: i64 = 1_234_567;
2290 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2291
2292 let stats = Arc::new(QueryStats::new());
2293 let inspector = fuchsia_inspect::Inspector::default();
2294 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2295 const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2296 const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2297 zx::MonotonicDuration::from_seconds(30);
2298 for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2299 run_fake_lookup(
2300 &mut exec,
2301 stats.clone(),
2302 Ok(NON_ZERO_USIZE_ONE),
2303 zx::MonotonicDuration::from_nanos(0),
2304 );
2305 run_fake_lookup(
2306 &mut exec,
2307 stats.clone(),
2308 Ok(NON_ZERO_USIZE_ONE),
2309 SUCCESSFUL_QUERY_DURATION,
2310 );
2311 exec.set_fake_time(fasync::MonotonicInstant::after(
2312 STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2313 ));
2314 }
2315 let mut expected = tree_assertion!(query_stats: {});
2316 for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2317 let name = &format!("window {}", i + 1);
2318 let child = tree_assertion!(var name: {
2319 start_time_nanos: u64::try_from(
2320 START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2321 ).unwrap(),
2322 successful_queries: 2u64,
2323 failed_queries: 0u64,
2324 average_success_duration_micros: u64::try_from(
2325 SUCCESSFUL_QUERY_DURATION.into_micros()
2326 ).unwrap() / 2,
2327 errors: {
2328 Message: 0u64,
2329 NoConnections: 0u64,
2330 NoRecordsFoundResponseCodeCounts: {},
2331 IoErrorCounts: {},
2332 ProtoErrorCounts: {},
2333 Timeout: 0u64,
2334 UnhandledResolveErrorKindCounts: {},
2335 },
2336 address_counts: {
2337 "1": 2u64,
2338 },
2339 });
2340 expected.add_child_assertion(child);
2341 }
2342 assert_data_tree!(@executor exec, inspector, root: {
2343 expected,
2344 });
2345 }
2346
2347 #[test]
2348 fn test_query_stats_inspect_error_counters() {
2349 let mut exec = fasync::TestExecutor::new_with_fake_time();
2350 const START_NANOS: i64 = 1_234_567;
2351 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2352
2353 let stats = Arc::new(QueryStats::new());
2354 let inspector = fuchsia_inspect::Inspector::default();
2355 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2356 const FAILED_QUERY_COUNT: u64 = 10;
2357 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2358 zx::MonotonicDuration::from_millis(500);
2359 for _ in 0..FAILED_QUERY_COUNT {
2360 run_fake_lookup(
2361 &mut exec,
2362 stats.clone(),
2363 Err(&ResolveErrorKind::Timeout),
2364 FAILED_QUERY_DURATION,
2365 );
2366 }
2367 assert_data_tree!(@executor exec, inspector, root:{
2368 query_stats: {
2369 "window 1": {
2370 start_time_nanos: u64::try_from(
2371 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2372 ).unwrap(),
2373 successful_queries: 0u64,
2374 failed_queries: FAILED_QUERY_COUNT,
2375 average_failure_duration_micros: u64::try_from(
2376 FAILED_QUERY_DURATION.into_micros()
2377 ).unwrap(),
2378 errors: {
2379 Message: 0u64,
2380 NoConnections: 0u64,
2381 NoRecordsFoundResponseCodeCounts: {},
2382 IoErrorCounts: {},
2383 ProtoErrorCounts: {},
2384 Timeout: FAILED_QUERY_COUNT,
2385 UnhandledResolveErrorKindCounts: {},
2386 },
2387 address_counts: {},
2388 },
2389 }
2390 });
2391 }
2392
2393 #[test]
2394 fn test_query_stats_inspect_no_records_found() {
2395 let mut exec = fasync::TestExecutor::new_with_fake_time();
2396 const START_NANOS: i64 = 1_234_567;
2397 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2398
2399 let stats = Arc::new(QueryStats::new());
2400 let inspector = fuchsia_inspect::Inspector::default();
2401 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2402 const FAILED_QUERY_COUNT: u64 = 10;
2403 const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2404 zx::MonotonicDuration::from_millis(500);
2405
2406 let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2407 run_fake_lookup(
2408 &mut exec,
2409 stats.clone(),
2410 Err(&ResolveErrorKind::NoRecordsFound {
2411 query: Box::new(Query::default()),
2412 soa: None,
2413 negative_ttl: None,
2414 response_code,
2415 trusted: false,
2416 }),
2417 FAILED_QUERY_DURATION,
2418 )
2419 };
2420
2421 for _ in 0..FAILED_QUERY_COUNT {
2422 run_fake_no_records_lookup(ResponseCode::NXDomain);
2423 run_fake_no_records_lookup(ResponseCode::Refused);
2424 run_fake_no_records_lookup(4096.into());
2425 run_fake_no_records_lookup(4097.into());
2426 }
2427
2428 assert_data_tree!(@executor exec, inspector, root:{
2429 query_stats: {
2430 "window 1": {
2431 start_time_nanos: u64::try_from(
2432 START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2433 ).unwrap(),
2434 successful_queries: 0u64,
2435 failed_queries: FAILED_QUERY_COUNT * 4,
2436 average_failure_duration_micros: u64::try_from(
2437 FAILED_QUERY_DURATION.into_micros()
2438 ).unwrap(),
2439 errors: {
2440 Message: 0u64,
2441 NoConnections: 0u64,
2442 NoRecordsFoundResponseCodeCounts: {
2443 NXDomain: FAILED_QUERY_COUNT,
2444 Refused: FAILED_QUERY_COUNT,
2445 "Unknown(4096)": FAILED_QUERY_COUNT,
2446 "Unknown(4097)": FAILED_QUERY_COUNT,
2447 },
2448 IoErrorCounts: {},
2449 ProtoErrorCounts: {},
2450 Timeout: 0u64,
2451 UnhandledResolveErrorKindCounts: {},
2452 },
2453 address_counts: {},
2454 },
2455 }
2456 });
2457 }
2458
2459 #[test]
2460 fn test_query_stats_resolved_address_counts() {
2461 let mut exec = fasync::TestExecutor::new_with_fake_time();
2462 const START_NANOS: i64 = 1_234_567;
2463 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2464
2465 let stats = Arc::new(QueryStats::new());
2466 let inspector = fuchsia_inspect::Inspector::default();
2467 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2468
2469 let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2475 const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2476 for (count, occurrences) in address_counts.iter() {
2477 for _ in 0..*occurrences {
2478 run_fake_lookup(
2479 &mut exec,
2480 stats.clone(),
2481 Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2482 QUERY_DURATION,
2483 );
2484 }
2485 }
2486
2487 let mut expected_address_counts = tree_assertion!(address_counts: {});
2488 for (count, occurrences) in address_counts.iter() {
2489 expected_address_counts
2490 .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2491 }
2492 assert_data_tree!(@executor exec, inspector, root: {
2493 query_stats: {
2494 "window 1": {
2495 start_time_nanos: u64::try_from(
2496 START_NANOS + QUERY_DURATION.into_nanos()
2497 ).unwrap(),
2498 successful_queries: address_counts.values().sum::<u64>(),
2499 failed_queries: 0u64,
2500 average_success_duration_micros: u64::try_from(
2501 QUERY_DURATION.into_micros()
2502 ).unwrap(),
2503 errors: {
2504 Message: 0u64,
2505 NoConnections: 0u64,
2506 NoRecordsFoundResponseCodeCounts: {},
2507 IoErrorCounts: {},
2508 ProtoErrorCounts: {},
2509 Timeout: 0u64,
2510 UnhandledResolveErrorKindCounts: {},
2511 },
2512 expected_address_counts,
2513 },
2514 },
2515 });
2516 }
2517
2518 #[test]
2519 fn test_query_stats_inspect_oldest_stats_erased() {
2520 let mut exec = fasync::TestExecutor::new_with_fake_time();
2521 const START_NANOS: i64 = 1_234_567;
2522 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2523
2524 let stats = Arc::new(QueryStats::new());
2525 let inspector = fuchsia_inspect::Inspector::default();
2526 let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2527 const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2528 for _ in 0..STAT_WINDOW_COUNT {
2529 let () =
2530 run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2531 let () =
2532 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2533 }
2534 for _ in 0..STAT_WINDOW_COUNT {
2535 run_fake_lookup(&mut exec, stats.clone(), Ok(NON_ZERO_USIZE_ONE), DELAY);
2536 let () =
2537 exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2538 }
2539 let mut expected = tree_assertion!(query_stats: {});
2542 let start_offset = START_NANOS
2543 + DELAY.into_nanos()
2544 + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2545 for i in 0..STAT_WINDOW_COUNT {
2546 let name = &format!("window {}", i + 1);
2547 let child = tree_assertion!(var name: {
2548 start_time_nanos: u64::try_from(
2549 start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2550 ).unwrap(),
2551 successful_queries: 1u64,
2552 failed_queries: 0u64,
2553 average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2554 errors: {
2555 Message: 0u64,
2556 NoConnections: 0u64,
2557 NoRecordsFoundResponseCodeCounts: {},
2558 IoErrorCounts: {},
2559 ProtoErrorCounts: {},
2560 Timeout: 0u64,
2561 UnhandledResolveErrorKindCounts: {},
2562 },
2563 address_counts: {
2564 "1": 1u64,
2565 },
2566 });
2567 expected.add_child_assertion(child);
2568 }
2569 assert_data_tree!(@executor exec, inspector, root: {
2570 expected,
2571 });
2572 }
2573
2574 struct BlockingResolver {}
2575
2576 impl ResolverLookup for BlockingResolver {
2577 fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2578 BlockingResolver {}
2579 }
2580
2581 async fn lookup<N: IntoName + Send>(
2582 &self,
2583 _name: N,
2584 _record_type: RecordType,
2585 ) -> Result<lookup::Lookup, ResolveError> {
2586 futures::future::pending().await
2587 }
2588
2589 async fn reverse_lookup(
2590 &self,
2591 _addr: IpAddr,
2592 ) -> Result<lookup::ReverseLookup, ResolveError> {
2593 panic!("BlockingResolver does not handle reverse lookup")
2594 }
2595 }
2596
2597 #[fasync::run_singlethreaded(test)]
2598 async fn test_parallel_query_limit() {
2599 let requests = {
2603 let (name_lookup_proxy, name_lookup_stream) =
2604 fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2605 const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2606 for _ in 0..NUM_REQUESTS {
2607 let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2611 name_lookup_proxy.lookup_ip(
2612 LOCAL_HOST,
2613 &fname::LookupIpOptions {
2614 ipv4_lookup: Some(true),
2615 ipv6_lookup: Some(true),
2616 ..Default::default()
2617 },
2618 );
2619 }
2620 drop(name_lookup_proxy);
2622 let requests = name_lookup_stream
2623 .map(|request| match request.expect("channel error") {
2624 LookupRequest::LookupIp { hostname, options, responder } => {
2625 IpLookupRequest { hostname, options, responder }
2626 }
2627 req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2628 })
2629 .collect::<Vec<_>>()
2630 .await;
2631 assert_eq!(requests.len(), NUM_REQUESTS);
2632 requests
2633 };
2634
2635 let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2636
2637 const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2643 const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2644 let mut send_fut = pin!(
2645 async {
2646 for (i, req) in requests.into_iter().enumerate() {
2647 match i {
2648 BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2649 LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2650 _ => assert_matches!(sender.send(req).await, Ok(())),
2651 }
2652 }
2653 }
2654 .fuse()
2655 );
2656 let mut recv_fut = pin!({
2657 let resolver = SharedResolver::new(BlockingResolver::new(
2658 ResolverConfig::default(),
2659 ResolverOpts::default(),
2660 ));
2661 let stats = Arc::new(QueryStats::new());
2662 let (routes_proxy, _routes_stream) =
2663 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2664 async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2665 .fuse()
2666 });
2667 futures::select! {
2668 () = send_fut => {},
2669 () = recv_fut => panic!("recv_fut should never complete"),
2670 };
2671 }
2672
2673 #[test]
2674 fn test_failure_stats() {
2675 use anyhow::anyhow;
2676 use trust_dns_proto::error::ProtoError;
2677 use trust_dns_proto::op::Query;
2678
2679 let mut stats = FailureStats::default();
2680 for (error_kind, expected) in &[
2681 (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2682 (
2683 ResolveErrorKind::Msg("foo".to_string()),
2684 FailureStats { message: 2, ..Default::default() },
2685 ),
2686 (
2687 ResolveErrorKind::NoRecordsFound {
2688 query: Box::new(Query::default()),
2689 soa: None,
2690 negative_ttl: None,
2691 response_code: ResponseCode::Refused,
2692 trusted: false,
2693 },
2694 FailureStats {
2695 message: 2,
2696 no_records_found: NoRecordsFoundStats {
2697 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2698 },
2699 ..Default::default()
2700 },
2701 ),
2702 (
2703 ResolveErrorKind::Io(std::io::Error::new(
2704 std::io::ErrorKind::NotFound,
2705 anyhow!("foo"),
2706 )),
2707 FailureStats {
2708 message: 2,
2709 no_records_found: NoRecordsFoundStats {
2710 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2711 },
2712 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2713 ..Default::default()
2714 },
2715 ),
2716 (
2717 ResolveErrorKind::Proto(ProtoError::from("foo")),
2718 FailureStats {
2719 message: 2,
2720 no_records_found: NoRecordsFoundStats {
2721 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2722 },
2723 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2724 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2725 ..Default::default()
2726 },
2727 ),
2728 (
2729 ResolveErrorKind::NoConnections,
2730 FailureStats {
2731 message: 2,
2732 no_connections: 1,
2733 no_records_found: NoRecordsFoundStats {
2734 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2735 },
2736 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2737 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2738 ..Default::default()
2739 },
2740 ),
2741 (
2742 ResolveErrorKind::Timeout,
2743 FailureStats {
2744 message: 2,
2745 no_connections: 1,
2746 no_records_found: NoRecordsFoundStats {
2747 response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2748 },
2749 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2750 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2751 timeout: 1,
2752 unhandled_resolve_error_kind: Default::default(),
2753 },
2754 ),
2755 (
2756 ResolveErrorKind::NoRecordsFound {
2757 query: Box::new(Query::default()),
2758 soa: None,
2759 negative_ttl: None,
2760 response_code: ResponseCode::NXDomain,
2761 trusted: false,
2762 },
2763 FailureStats {
2764 message: 2,
2765 no_connections: 1,
2766 no_records_found: NoRecordsFoundStats {
2767 response_code_counts: [
2768 (ResponseCode::NXDomain.into(), 1),
2769 (ResponseCode::Refused.into(), 1),
2770 ]
2771 .into(),
2772 },
2773 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2774 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2775 timeout: 1,
2776 unhandled_resolve_error_kind: Default::default(),
2777 },
2778 ),
2779 (
2780 ResolveErrorKind::NoRecordsFound {
2781 query: Box::new(Query::default()),
2782 soa: None,
2783 negative_ttl: None,
2784 response_code: ResponseCode::NXDomain,
2785 trusted: false,
2786 },
2787 FailureStats {
2788 message: 2,
2789 no_connections: 1,
2790 no_records_found: NoRecordsFoundStats {
2791 response_code_counts: [
2792 (ResponseCode::NXDomain.into(), 2),
2793 (ResponseCode::Refused.into(), 1),
2794 ]
2795 .into(),
2796 },
2797 io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2798 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2799 timeout: 1,
2800 unhandled_resolve_error_kind: Default::default(),
2801 },
2802 ),
2803 (
2804 ResolveErrorKind::Proto(ProtoError::from(std::io::Error::new(
2805 std::io::ErrorKind::ConnectionAborted,
2806 anyhow!("foo"),
2807 ))),
2808 FailureStats {
2809 message: 2,
2810 no_connections: 1,
2811 no_records_found: NoRecordsFoundStats {
2812 response_code_counts: [
2813 (ResponseCode::NXDomain.into(), 2),
2814 (ResponseCode::Refused.into(), 1),
2815 ]
2816 .into(),
2817 },
2818 io: IoErrorStats(
2819 [
2820 (std::io::ErrorKind::NotFound, 1),
2821 (std::io::ErrorKind::ConnectionAborted, 1),
2822 ]
2823 .into(),
2824 ),
2825 proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2826 timeout: 1,
2827 unhandled_resolve_error_kind: Default::default(),
2828 },
2829 ),
2830 ][..]
2831 {
2832 stats.increment(error_kind);
2833 assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2834 }
2835 }
2836
2837 fn test_das_helper(
2838 l_addr: fnet::IpAddress,
2839 l_src: Option<fnet::IpAddress>,
2840 r_addr: fnet::IpAddress,
2841 r_src: Option<fnet::IpAddress>,
2842 want: std::cmp::Ordering,
2843 ) {
2844 let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2845 let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2846 assert_eq!(
2847 left.cmp(&right),
2848 want,
2849 "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2850 want,
2851 l_addr,
2852 l_src,
2853 left,
2854 r_addr,
2855 r_src,
2856 right
2857 );
2858 }
2859
2860 macro_rules! add_das_test {
2861 ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2862 #[test]
2863 fn $name() {
2864 test_das_helper(
2865 $pref_dst,
2866 $pref_src,
2867 $other_dst,
2868 $other_src,
2869 std::cmp::Ordering::Less,
2870 )
2871 }
2872 };
2873 }
2874
2875 add_das_test!(
2876 prefer_reachable,
2877 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2878 other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2879 );
2880
2881 add_das_test!(
2884 prefer_matching_scope,
2885 preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2886 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2887 );
2888
2889 add_das_test!(
2890 prefer_matching_label,
2891 preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2892 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2893 );
2894
2895 add_das_test!(
2896 prefer_higher_precedence_1,
2897 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2898 other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2899 );
2900
2901 add_das_test!(
2902 prefer_higher_precedence_2,
2903 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2904 other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2905 );
2906
2907 add_das_test!(
2908 prefer_smaller_scope,
2909 preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2910 other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2911 );
2912
2913 add_das_test!(
2914 prefer_longest_matching_prefix,
2915 preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2916 other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2917 );
2918
2919 #[test]
2920 fn test_das_equals() {
2921 for (dst, src) in [
2922 (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2923 (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2924 ]
2925 .iter()
2926 {
2927 test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2928 test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2929 }
2930 }
2931
2932 #[test]
2933 fn test_valid_policy_table() {
2934 assert_eq!(
2936 POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2937 net_types::ip::Ipv6::ALL_ADDRS_SUBNET
2938 );
2939 POLICY_TABLE.array_windows().for_each(|[w0, w1]| {
2941 let Policy { prefix: cur, precedence: _, label: _ } = w0;
2942 let Policy { prefix: nxt, precedence: _, label: _ } = w1;
2943 assert!(
2944 cur.prefix() >= nxt.prefix(),
2945 "bad ordering of prefixes, {} must come after {}",
2946 cur,
2947 nxt
2948 )
2949 });
2950 for policy in POLICY_TABLE.iter() {
2953 assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2954 }
2955 }
2956
2957 #[fasync::run_singlethreaded(test)]
2958 async fn test_sort_preferred_addresses() {
2959 const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2960 (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2961 (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2962 (fidl_ip!("192.168.50.22"), None),
2963 (fidl_ip!("2001::2"), None),
2964 (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2965 ];
2966 const SORTED: [IpAddr; 5] = [
2969 std_ip!("::1"),
2970 std_ip!("2001:db8:1::1"),
2971 std_ip!("127.0.0.1"),
2972 std_ip!("192.168.50.22"),
2973 std_ip!("2001::2"),
2974 ];
2975 let (routes_proxy, routes_stream) =
2976 fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2977 let routes_fut =
2978 routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2979 let (destination, responder) = assert_matches!(
2980 req,
2981 fnet_routes::StateRequest::Resolve { destination, responder }
2982 => (destination, responder)
2983 );
2984 let result = TEST_IPS
2985 .iter()
2986 .enumerate()
2987 .find_map(|(i, (dst, src))| {
2988 if *dst == destination && src.is_some() {
2989 let inner = fnet_routes::Destination {
2990 address: Some(*dst),
2991 source_address: *src,
2992 ..Default::default()
2993 };
2994 if i % 2 == 0 {
2997 Some(fnet_routes::Resolved::Direct(inner))
2998 } else {
2999 Some(fnet_routes::Resolved::Gateway(inner))
3000 }
3001 } else {
3002 None
3003 }
3004 })
3005 .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
3006 futures::future::ready(
3007 responder
3008 .send(result.as_ref().map_err(|e| *e))
3009 .context("failed to send Resolve response"),
3010 )
3011 });
3012
3013 let ((), ()) = futures::future::try_join(routes_fut, async move {
3014 let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
3015 let addrs = sort_preferred_addresses(addrs, &routes_proxy)
3016 .await
3017 .expect("failed to sort addresses");
3018 let addrs = addrs
3019 .into_iter()
3020 .map(|a| {
3021 let net_ext::IpAddress(a) = a.into();
3022 a
3023 })
3024 .collect::<Vec<_>>();
3025 assert_eq!(&addrs[..], &SORTED[..]);
3026 Ok(())
3027 })
3028 .await
3029 .expect("error running futures");
3030 }
3031
3032 #[fasync::run_singlethreaded(test)]
3033 async fn test_lookupip() {
3034 let routes_handler = |req| {
3036 let (destination, responder) = assert_matches!(
3037 req,
3038 fnet_routes::StateRequest::Resolve { destination, responder }
3039 => (destination, responder)
3040 );
3041 let resolved;
3042 let response = if destination == map_ip(IPV6_HOST) {
3043 resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
3044 address: Some(destination),
3045 source_address: Some(destination),
3046 ..Default::default()
3047 });
3048 Ok(&resolved)
3049 } else {
3050 Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
3051 };
3052 responder.send(response).expect("failed to send Resolve FIDL response");
3053 };
3054 TestEnvironment::default()
3055 .run_lookup_with_routes_handler(
3056 |proxy| async move {
3057 assert_eq!(
3059 proxy
3060 .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
3061 .await
3062 .expect("lookup_ip"),
3063 Err(fname::LookupError::InvalidArgs)
3064 );
3065 assert_eq!(
3067 proxy
3068 .lookup_ip(
3069 REMOTE_IPV4_HOST,
3070 &fname::LookupIpOptions {
3071 ipv4_lookup: Some(false),
3072 ipv6_lookup: Some(false),
3073 ..Default::default()
3074 }
3075 )
3076 .await
3077 .expect("lookup_ip"),
3078 Err(fname::LookupError::InvalidArgs)
3079 );
3080 assert_eq!(
3082 proxy
3083 .lookup_ip(
3084 REMOTE_IPV4_HOST,
3085 &fname::LookupIpOptions {
3086 ipv4_lookup: Some(false),
3087 ipv6_lookup: Some(true),
3088 ..Default::default()
3089 }
3090 )
3091 .await
3092 .expect("lookup_ip"),
3093 Err(fname::LookupError::NotFound)
3094 );
3095 assert_eq!(
3097 proxy
3098 .lookup_ip(
3099 REMOTE_IPV4_HOST,
3100 &fname::LookupIpOptions {
3101 ipv4_lookup: Some(true),
3102 ipv6_lookup: Some(true),
3103 ..Default::default()
3104 }
3105 )
3106 .await
3107 .expect("lookup_ip"),
3108 Ok(fname::LookupResult {
3109 addresses: Some(vec![map_ip(IPV4_HOST)]),
3110 ..Default::default()
3111 })
3112 );
3113 assert_eq!(
3115 proxy
3116 .lookup_ip(
3117 REMOTE_IPV4_IPV6_HOST,
3118 &fname::LookupIpOptions {
3119 ipv4_lookup: Some(true),
3120 ipv6_lookup: Some(true),
3121 ..Default::default()
3122 }
3123 )
3124 .await
3125 .expect("lookup_ip"),
3126 Ok(fname::LookupResult {
3127 addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
3128 ..Default::default()
3129 })
3130 );
3131 assert_eq!(
3133 proxy
3134 .lookup_ip(
3135 REMOTE_IPV4_IPV6_HOST,
3136 &fname::LookupIpOptions {
3137 ipv4_lookup: Some(true),
3138 ipv6_lookup: Some(true),
3139 sort_addresses: Some(true),
3140 ..Default::default()
3141 }
3142 )
3143 .await
3144 .expect("lookup_ip"),
3145 Ok(fname::LookupResult {
3146 addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
3147 ..Default::default()
3148 })
3149 );
3150 },
3151 routes_handler,
3152 )
3153 .await
3154 }
3155}