@@ -477,6 +477,65 @@ static DnsScopeMatch match_link_local_reverse_lookups(const char *domain) {
477477 return _DNS_SCOPE_MATCH_INVALID ;
478478}
479479
480+ static DnsScopeMatch match_subnet_reverse_lookups (
481+ DnsScope * s ,
482+ const char * domain ,
483+ bool exclude_own ) {
484+
485+ union in_addr_union ia ;
486+ LinkAddress * a ;
487+ int f , r ;
488+
489+ assert (s );
490+ assert (domain );
491+
492+ /* Checks whether the specified domain is a reverse address domain (i.e. in the .in-addr.arpa or
493+ * .ip6.arpa area), and if so, whether the address matches any of the local subnets of the link the
494+ * scope is associated with. If so, our scope should consider itself relevant for any lookup in the
495+ * domain, since it apparently refers to hosts on this link's subnet.
496+ *
497+ * If 'exclude_own' is true this will return DNS_SCOPE_NO for any IP addresses assigned locally. This
498+ * is useful for LLMNR/mDNS as we never want to look up our own hostname on LLMNR/mDNS but always use
499+ * the locally synthesized one. */
500+
501+ if (!s -> link )
502+ return _DNS_SCOPE_MATCH_INVALID ; /* No link, hence no local addresses to check */
503+
504+ r = dns_name_address (domain , & f , & ia );
505+ if (r < 0 )
506+ log_debug_errno (r , "Failed to determine whether '%s' is an address domain: %m" , domain );
507+ if (r <= 0 )
508+ return _DNS_SCOPE_MATCH_INVALID ;
509+
510+ if (s -> family != AF_UNSPEC && f != s -> family )
511+ return _DNS_SCOPE_MATCH_INVALID ; /* Don't look for IPv4 addresses on LLMNR/mDNS over IPv6 and vice versa */
512+
513+ LIST_FOREACH (addresses , a , s -> link -> addresses ) {
514+
515+ if (a -> family != f )
516+ continue ;
517+
518+ /* Equals our own address? nah, let's not use this scope. The local synthesizer will pick it up for us. */
519+ if (exclude_own &&
520+ in_addr_equal (f , & a -> in_addr , & ia ) > 0 )
521+ return DNS_SCOPE_NO ;
522+
523+ if (a -> prefixlen == UCHAR_MAX ) /* don't know subnet mask */
524+ continue ;
525+
526+ /* Check if the address is in the local subnet */
527+ r = in_addr_prefix_covers (f , & a -> in_addr , a -> prefixlen , & ia );
528+ if (r < 0 )
529+ log_debug_errno (r , "Failed to determine whether link address covers lookup address '%s': %m" , domain );
530+ if (r > 0 )
531+ /* Note that we only claim zero labels match. This is so that this is at the same
532+ * priority a DNS scope with "." as routing domain is. */
533+ return DNS_SCOPE_YES_BASE + 0 ;
534+ }
535+
536+ return _DNS_SCOPE_MATCH_INVALID ;
537+ }
538+
480539DnsScopeMatch dns_scope_good_domain (
481540 DnsScope * s ,
482541 int ifindex ,
@@ -533,6 +592,7 @@ DnsScopeMatch dns_scope_good_domain(
533592
534593 case DNS_PROTOCOL_DNS : {
535594 bool has_search_domains = false;
595+ DnsScopeMatch m ;
536596 int n_best = -1 ;
537597
538598 /* Never route things to scopes that lack DNS servers */
@@ -579,6 +639,13 @@ DnsScopeMatch dns_scope_good_domain(
579639 dns_name_endswith (domain , "local" ) > 0 )
580640 return DNS_SCOPE_NO ;
581641
642+ /* If the IP address to look up matches the local subnet, then implicity synthesizes
643+ * DNS_SCOPE_YES_BASE + 0 on this interface, i.e. preferably resolve IP addresses via the DNS
644+ * server belonging to this interface. */
645+ m = match_subnet_reverse_lookups (s , domain , false);
646+ if (m >= 0 )
647+ return m ;
648+
582649 /* If there was no match at all, then see if this scope is suitable as default route. */
583650 if (!dns_scope_is_default_route (s ))
584651 return DNS_SCOPE_NO ;
@@ -593,6 +660,10 @@ DnsScopeMatch dns_scope_good_domain(
593660 if (m >= 0 )
594661 return m ;
595662
663+ m = match_subnet_reverse_lookups (s , domain , true);
664+ if (m >= 0 )
665+ return m ;
666+
596667 if ((s -> family == AF_INET && dns_name_endswith (domain , "in-addr.arpa" ) > 0 ) ||
597668 (s -> family == AF_INET6 && dns_name_endswith (domain , "ip6.arpa" ) > 0 ))
598669 return DNS_SCOPE_MAYBE ;
@@ -612,6 +683,10 @@ DnsScopeMatch dns_scope_good_domain(
612683 if (m >= 0 )
613684 return m ;
614685
686+ m = match_subnet_reverse_lookups (s , domain , true);
687+ if (m >= 0 )
688+ return m ;
689+
615690 if ((s -> family == AF_INET && dns_name_endswith (domain , "in-addr.arpa" ) > 0 ) ||
616691 (s -> family == AF_INET6 && dns_name_endswith (domain , "ip6.arpa" ) > 0 ))
617692 return DNS_SCOPE_MAYBE ;
0 commit comments