2014-07-16 00:26:02 +02:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2014 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2014-07-16 20:15:47 +02:00
# include <netinet/tcp.h>
2014-07-29 14:24:02 +02:00
# include "missing.h"
2014-07-16 00:26:02 +02:00
# include "strv.h"
2014-07-16 20:15:47 +02:00
# include "socket-util.h"
2014-07-18 16:15:12 +02:00
# include "af-list.h"
2015-05-18 17:10:07 +02:00
# include "random-util.h"
# include "hostname-util.h"
2015-06-02 20:49:43 +02:00
# include "dns-domain.h"
2015-07-10 21:02:38 +02:00
# include "resolved-llmnr.h"
2014-07-16 00:26:02 +02:00
# include "resolved-dns-scope.h"
2014-08-05 16:34:45 +02:00
# define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
# define MULTICAST_RATELIMIT_BURST 1000
2014-07-18 16:09:30 +02:00
int dns_scope_new ( Manager * m , DnsScope * * ret , Link * l , DnsProtocol protocol , int family ) {
2014-07-16 00:26:02 +02:00
DnsScope * s ;
assert ( m ) ;
assert ( ret ) ;
s = new0 ( DnsScope , 1 ) ;
if ( ! s )
return - ENOMEM ;
s - > manager = m ;
2014-07-18 12:34:02 +02:00
s - > link = l ;
s - > protocol = protocol ;
s - > family = family ;
2014-07-16 00:26:02 +02:00
LIST_PREPEND ( scopes , m - > dns_scopes , s ) ;
2014-07-18 12:34:02 +02:00
dns_scope_llmnr_membership ( s , true ) ;
2014-07-18 16:15:12 +02:00
log_debug ( " New scope on link %s, protocol %s, family %s " , l ? l - > name : " * " , dns_protocol_to_string ( protocol ) , family = = AF_UNSPEC ? " * " : af_to_name ( family ) ) ;
2014-07-18 12:34:02 +02:00
2014-08-05 16:34:45 +02:00
/* Enforce ratelimiting for the multicast protocols */
RATELIMIT_INIT ( s - > ratelimit , MULTICAST_RATELIMIT_INTERVAL_USEC , MULTICAST_RATELIMIT_BURST ) ;
2014-07-16 00:26:02 +02:00
* ret = s ;
return 0 ;
}
DnsScope * dns_scope_free ( DnsScope * s ) {
2014-07-31 17:46:40 +02:00
DnsTransaction * t ;
2014-08-06 16:15:35 +02:00
DnsResourceRecord * rr ;
2014-07-22 21:48:41 +02:00
2014-07-16 00:26:02 +02:00
if ( ! s )
return NULL ;
2014-07-18 16:15:12 +02:00
log_debug ( " Removing scope on link %s, protocol %s, family %s " , s - > link ? s - > link - > name : " * " , dns_protocol_to_string ( s - > protocol ) , s - > family = = AF_UNSPEC ? " * " : af_to_name ( s - > family ) ) ;
2014-07-18 12:34:02 +02:00
dns_scope_llmnr_membership ( s , false ) ;
2014-07-22 21:48:41 +02:00
while ( ( t = s - > transactions ) ) {
/* Abort the transaction, but make sure it is not
* freed while we still look at it */
2014-07-16 00:26:02 +02:00
2014-07-22 21:48:41 +02:00
t - > block_gc + + ;
2014-07-31 17:46:40 +02:00
dns_transaction_complete ( t , DNS_TRANSACTION_ABORTED ) ;
2014-07-22 21:48:41 +02:00
t - > block_gc - - ;
2014-07-16 00:26:02 +02:00
2014-07-31 17:46:40 +02:00
dns_transaction_free ( t ) ;
2014-07-16 00:26:02 +02:00
}
2014-08-22 13:56:51 +02:00
while ( ( rr = ordered_hashmap_steal_first ( s - > conflict_queue ) ) )
2014-08-06 16:15:35 +02:00
dns_resource_record_unref ( rr ) ;
2014-08-22 13:56:51 +02:00
ordered_hashmap_free ( s - > conflict_queue ) ;
2014-08-06 16:15:35 +02:00
sd_event_source_unref ( s - > conflict_event_source ) ;
2014-07-17 19:38:37 +02:00
dns_cache_flush ( & s - > cache ) ;
2014-07-29 14:24:02 +02:00
dns_zone_flush ( & s - > zone ) ;
2014-07-17 19:38:37 +02:00
2014-07-16 00:26:02 +02:00
LIST_REMOVE ( scopes , s - > manager - > dns_scopes , s ) ;
strv_free ( s - > domains ) ;
free ( s ) ;
return NULL ;
}
2014-08-01 18:09:07 +02:00
DnsServer * dns_scope_get_dns_server ( DnsScope * s ) {
2014-07-16 00:26:02 +02:00
assert ( s ) ;
2014-07-18 12:34:02 +02:00
if ( s - > protocol ! = DNS_PROTOCOL_DNS )
return NULL ;
2014-07-16 00:26:02 +02:00
if ( s - > link )
return link_get_dns_server ( s - > link ) ;
else
return manager_get_dns_server ( s - > manager ) ;
}
void dns_scope_next_dns_server ( DnsScope * s ) {
assert ( s ) ;
2014-07-18 12:34:02 +02:00
if ( s - > protocol ! = DNS_PROTOCOL_DNS )
return ;
2014-07-16 00:26:02 +02:00
if ( s - > link )
link_next_dns_server ( s - > link ) ;
else
manager_next_dns_server ( s - > manager ) ;
}
2015-07-09 14:19:55 +02:00
int dns_scope_emit ( DnsScope * s , DnsTransaction * t , DnsPacket * p , DnsServer * * server ) {
2015-06-24 18:54:12 +02:00
DnsServer * srv = NULL ;
2014-07-18 12:34:02 +02:00
union in_addr_union addr ;
int ifindex = 0 , r ;
2014-07-18 16:09:30 +02:00
int family ;
2014-07-18 12:34:02 +02:00
uint16_t port ;
uint32_t mtu ;
int fd ;
2014-07-16 00:26:02 +02:00
assert ( s ) ;
assert ( p ) ;
2014-07-18 12:34:02 +02:00
assert ( p - > protocol = = s - > protocol ) ;
2014-07-16 20:15:47 +02:00
if ( s - > link ) {
2014-07-18 12:34:02 +02:00
mtu = s - > link - > mtu ;
2014-07-16 00:26:02 +02:00
ifindex = s - > link - > ifindex ;
2014-07-18 12:34:02 +02:00
} else
2014-07-17 01:13:22 +02:00
mtu = manager_find_mtu ( s - > manager ) ;
2014-07-16 00:26:02 +02:00
2014-07-18 12:34:02 +02:00
if ( s - > protocol = = DNS_PROTOCOL_DNS ) {
2014-07-29 14:24:02 +02:00
if ( DNS_PACKET_QDCOUNT ( p ) > 1 )
2015-03-13 14:08:00 +01:00
return - EOPNOTSUPP ;
2014-07-29 14:24:02 +02:00
2014-08-01 18:09:07 +02:00
srv = dns_scope_get_dns_server ( s ) ;
2014-07-18 12:34:02 +02:00
if ( ! srv )
return - ESRCH ;
family = srv - > family ;
addr = srv - > address ;
port = 53 ;
if ( p - > size > DNS_PACKET_UNICAST_SIZE_MAX )
return - EMSGSIZE ;
2015-06-24 21:22:46 +02:00
if ( p - > size + UDP_PACKET_HEADER_SIZE > mtu )
2014-07-18 12:34:02 +02:00
return - EMSGSIZE ;
if ( family = = AF_INET )
2015-07-09 14:19:55 +02:00
fd = transaction_dns_ipv4_fd ( t ) ;
2014-07-18 12:34:02 +02:00
else if ( family = = AF_INET6 )
2015-07-09 14:19:55 +02:00
fd = transaction_dns_ipv6_fd ( t ) ;
2014-07-18 12:34:02 +02:00
else
return - EAFNOSUPPORT ;
if ( fd < 0 )
return fd ;
} else if ( s - > protocol = = DNS_PROTOCOL_LLMNR ) {
if ( DNS_PACKET_QDCOUNT ( p ) > 1 )
2015-03-13 14:08:00 +01:00
return - EOPNOTSUPP ;
2014-07-18 12:34:02 +02:00
2014-08-05 16:34:45 +02:00
if ( ! ratelimit_test ( & s - > ratelimit ) )
return - EBUSY ;
2014-07-18 12:34:02 +02:00
family = s - > family ;
2015-07-10 21:28:09 +02:00
port = LLMNR_PORT ;
2014-07-18 12:34:02 +02:00
if ( family = = AF_INET ) {
addr . in = LLMNR_MULTICAST_IPV4_ADDRESS ;
fd = manager_llmnr_ipv4_udp_fd ( s - > manager ) ;
} else if ( family = = AF_INET6 ) {
addr . in6 = LLMNR_MULTICAST_IPV6_ADDRESS ;
fd = manager_llmnr_ipv6_udp_fd ( s - > manager ) ;
} else
return - EAFNOSUPPORT ;
if ( fd < 0 )
return fd ;
} else
2014-07-16 00:26:02 +02:00
return - EAFNOSUPPORT ;
2014-07-18 12:34:02 +02:00
r = manager_send ( s - > manager , fd , ifindex , family , & addr , port , p ) ;
2014-07-16 00:26:02 +02:00
if ( r < 0 )
return r ;
2015-06-24 18:54:12 +02:00
if ( server )
* server = srv ;
2014-07-16 00:26:02 +02:00
return 1 ;
}
2015-06-24 18:54:12 +02:00
int dns_scope_tcp_socket ( DnsScope * s , int family , const union in_addr_union * address , uint16_t port , DnsServer * * server ) {
DnsServer * srv = NULL ;
2014-07-16 20:15:47 +02:00
_cleanup_close_ int fd = - 1 ;
union sockaddr_union sa = { } ;
socklen_t salen ;
2014-07-29 14:24:02 +02:00
static const int one = 1 ;
int ret , r ;
2014-07-16 20:15:47 +02:00
assert ( s ) ;
2014-07-29 14:24:02 +02:00
assert ( ( family = = AF_UNSPEC ) = = ! address ) ;
2014-07-16 20:15:47 +02:00
2014-07-29 14:24:02 +02:00
if ( family = = AF_UNSPEC ) {
2014-08-01 18:09:07 +02:00
srv = dns_scope_get_dns_server ( s ) ;
2014-07-29 14:24:02 +02:00
if ( ! srv )
return - ESRCH ;
sa . sa . sa_family = srv - > family ;
if ( srv - > family = = AF_INET ) {
sa . in . sin_port = htobe16 ( port ) ;
sa . in . sin_addr = srv - > address . in ;
salen = sizeof ( sa . in ) ;
} else if ( srv - > family = = AF_INET6 ) {
sa . in6 . sin6_port = htobe16 ( port ) ;
sa . in6 . sin6_addr = srv - > address . in6 ;
sa . in6 . sin6_scope_id = s - > link ? s - > link - > ifindex : 0 ;
salen = sizeof ( sa . in6 ) ;
} else
return - EAFNOSUPPORT ;
} else {
sa . sa . sa_family = family ;
if ( family = = AF_INET ) {
sa . in . sin_port = htobe16 ( port ) ;
sa . in . sin_addr = address - > in ;
salen = sizeof ( sa . in ) ;
} else if ( family = = AF_INET6 ) {
sa . in6 . sin6_port = htobe16 ( port ) ;
sa . in6 . sin6_addr = address - > in6 ;
sa . in6 . sin6_scope_id = s - > link ? s - > link - > ifindex : 0 ;
salen = sizeof ( sa . in6 ) ;
} else
return - EAFNOSUPPORT ;
}
2014-07-16 20:15:47 +02:00
2014-07-29 14:24:02 +02:00
fd = socket ( sa . sa . sa_family , SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ;
2014-07-16 20:15:47 +02:00
if ( fd < 0 )
return - errno ;
2014-07-29 14:24:02 +02:00
r = setsockopt ( fd , IPPROTO_TCP , TCP_NODELAY , & one , sizeof ( one ) ) ;
if ( r < 0 )
return - errno ;
if ( s - > link ) {
uint32_t ifindex = htobe32 ( s - > link - > ifindex ) ;
if ( sa . sa . sa_family = = AF_INET ) {
r = setsockopt ( fd , IPPROTO_IP , IP_UNICAST_IF , & ifindex , sizeof ( ifindex ) ) ;
if ( r < 0 )
return - errno ;
} else if ( sa . sa . sa_family = = AF_INET6 ) {
r = setsockopt ( fd , IPPROTO_IPV6 , IPV6_UNICAST_IF , & ifindex , sizeof ( ifindex ) ) ;
if ( r < 0 )
return - errno ;
}
}
if ( s - > protocol = = DNS_PROTOCOL_LLMNR ) {
2014-07-29 23:51:34 +02:00
/* RFC 4795, section 2.5 requires the TTL to be set to 1 */
2014-07-29 14:24:02 +02:00
if ( sa . sa . sa_family = = AF_INET ) {
r = setsockopt ( fd , IPPROTO_IP , IP_TTL , & one , sizeof ( one ) ) ;
if ( r < 0 )
return - errno ;
} else if ( sa . sa . sa_family = = AF_INET6 ) {
r = setsockopt ( fd , IPPROTO_IPV6 , IPV6_UNICAST_HOPS , & one , sizeof ( one ) ) ;
if ( r < 0 )
return - errno ;
}
}
2014-07-16 20:15:47 +02:00
r = connect ( fd , & sa . sa , salen ) ;
if ( r < 0 & & errno ! = EINPROGRESS )
return - errno ;
2015-06-24 18:54:12 +02:00
if ( server )
* server = srv ;
2014-07-16 20:15:47 +02:00
ret = fd ;
fd = - 1 ;
2014-07-29 14:24:02 +02:00
2014-07-16 20:15:47 +02:00
return ret ;
}
2014-08-14 01:00:15 +02:00
DnsScopeMatch dns_scope_good_domain ( DnsScope * s , int ifindex , uint64_t flags , const char * domain ) {
2014-07-16 00:26:02 +02:00
char * * i ;
assert ( s ) ;
assert ( domain ) ;
2014-08-14 01:00:15 +02:00
if ( ifindex ! = 0 & & ( ! s - > link | | s - > link - > ifindex ! = ifindex ) )
return DNS_SCOPE_NO ;
if ( ( SD_RESOLVED_FLAGS_MAKE ( s - > protocol , s - > family ) & flags ) = = 0 )
return DNS_SCOPE_NO ;
2014-07-16 00:26:02 +02:00
STRV_FOREACH ( i , s - > domains )
2014-07-22 21:48:41 +02:00
if ( dns_name_endswith ( domain , * i ) > 0 )
2014-07-16 00:26:02 +02:00
return DNS_SCOPE_YES ;
2014-07-22 21:48:41 +02:00
if ( dns_name_root ( domain ) ! = 0 )
2014-07-16 00:26:02 +02:00
return DNS_SCOPE_NO ;
2014-07-17 01:14:07 +02:00
if ( is_localhost ( domain ) )
return DNS_SCOPE_NO ;
2014-07-18 12:34:02 +02:00
if ( s - > protocol = = DNS_PROTOCOL_DNS ) {
2014-07-22 21:48:41 +02:00
if ( dns_name_endswith ( domain , " 254.169.in-addr.arpa " ) = = 0 & &
dns_name_endswith ( domain , " 0.8.e.f.ip6.arpa " ) = = 0 & &
dns_name_single_label ( domain ) = = 0 )
return DNS_SCOPE_MAYBE ;
2014-07-18 12:34:02 +02:00
2014-07-22 21:48:41 +02:00
return DNS_SCOPE_NO ;
2014-07-18 12:34:02 +02:00
}
if ( s - > protocol = = DNS_PROTOCOL_MDNS ) {
2014-07-22 21:48:41 +02:00
if ( dns_name_endswith ( domain , " 254.169.in-addr.arpa " ) > 0 | |
dns_name_endswith ( domain , " 0.8.e.f.ip6.arpa " ) > 0 | |
( dns_name_endswith ( domain , " local " ) > 0 & & dns_name_equal ( domain , " local " ) = = 0 ) )
2014-07-16 00:26:02 +02:00
return DNS_SCOPE_MAYBE ;
return DNS_SCOPE_NO ;
}
2014-07-18 12:34:02 +02:00
if ( s - > protocol = = DNS_PROTOCOL_LLMNR ) {
2014-07-29 19:49:45 +02:00
if ( dns_name_endswith ( domain , " in-addr.arpa " ) > 0 | |
dns_name_endswith ( domain , " ip6.arpa " ) > 0 | |
2014-12-03 22:23:41 +01:00
( dns_name_single_label ( domain ) > 0 & &
dns_name_equal ( domain , " gateway " ) < = 0 ) ) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
2014-07-18 12:34:02 +02:00
return DNS_SCOPE_MAYBE ;
2014-07-16 00:26:02 +02:00
2014-07-18 12:34:02 +02:00
return DNS_SCOPE_NO ;
2014-07-16 00:26:02 +02:00
}
2014-07-18 12:34:02 +02:00
assert_not_reached ( " Unknown scope protocol " ) ;
}
int dns_scope_good_key ( DnsScope * s , DnsResourceKey * key ) {
assert ( s ) ;
assert ( key ) ;
if ( s - > protocol = = DNS_PROTOCOL_DNS )
return true ;
/* On mDNS and LLMNR, send A and AAAA queries only on the
* respective scopes */
if ( s - > family = = AF_INET & & key - > class = = DNS_CLASS_IN & & key - > type = = DNS_TYPE_AAAA )
return false ;
if ( s - > family = = AF_INET6 & & key - > class = = DNS_CLASS_IN & & key - > type = = DNS_TYPE_A )
return false ;
return true ;
}
int dns_scope_llmnr_membership ( DnsScope * s , bool b ) {
int fd ;
2014-08-14 19:56:22 +02:00
assert ( s ) ;
2014-08-15 16:42:06 +02:00
if ( s - > protocol ! = DNS_PROTOCOL_LLMNR )
return 0 ;
2014-08-14 19:56:22 +02:00
assert ( s - > link ) ;
2014-07-18 12:34:02 +02:00
if ( s - > family = = AF_INET ) {
struct ip_mreqn mreqn = {
. imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS ,
. imr_ifindex = s - > link - > ifindex ,
} ;
fd = manager_llmnr_ipv4_udp_fd ( s - > manager ) ;
if ( fd < 0 )
return fd ;
2014-08-01 19:48:02 +02:00
/* Always first try to drop membership before we add
* one . This is necessary on some devices , such as
* veth . */
if ( b )
2015-03-14 03:20:01 +01:00
( void ) setsockopt ( fd , IPPROTO_IP , IP_DROP_MEMBERSHIP , & mreqn , sizeof ( mreqn ) ) ;
2014-08-01 19:48:02 +02:00
2014-07-18 12:34:02 +02:00
if ( setsockopt ( fd , IPPROTO_IP , b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP , & mreqn , sizeof ( mreqn ) ) < 0 )
return - errno ;
} else if ( s - > family = = AF_INET6 ) {
struct ipv6_mreq mreq = {
. ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS ,
. ipv6mr_interface = s - > link - > ifindex ,
} ;
fd = manager_llmnr_ipv6_udp_fd ( s - > manager ) ;
if ( fd < 0 )
return fd ;
2014-08-01 19:48:02 +02:00
if ( b )
2015-03-14 03:20:01 +01:00
( void ) setsockopt ( fd , IPPROTO_IPV6 , IPV6_DROP_MEMBERSHIP , & mreq , sizeof ( mreq ) ) ;
2014-08-01 19:48:02 +02:00
2014-07-18 12:34:02 +02:00
if ( setsockopt ( fd , IPPROTO_IPV6 , b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP , & mreq , sizeof ( mreq ) ) < 0 )
return - errno ;
} else
return - EAFNOSUPPORT ;
return 0 ;
2014-07-16 00:26:02 +02:00
}
2014-07-29 14:24:02 +02:00
int dns_scope_good_dns_server ( DnsScope * s , int family , const union in_addr_union * address ) {
assert ( s ) ;
assert ( address ) ;
if ( s - > protocol ! = DNS_PROTOCOL_DNS )
return 1 ;
if ( s - > link )
return ! ! link_find_dns_server ( s - > link , family , address ) ;
else
2014-08-01 18:09:07 +02:00
return ! ! manager_find_dns_server ( s - > manager , family , address ) ;
2014-07-29 14:24:02 +02:00
}
2014-07-31 17:46:40 +02:00
static int dns_scope_make_reply_packet (
DnsScope * s ,
uint16_t id ,
int rcode ,
DnsQuestion * q ,
DnsAnswer * answer ,
DnsAnswer * soa ,
bool tentative ,
DnsPacket * * ret ) {
2014-07-29 14:24:02 +02:00
_cleanup_ ( dns_packet_unrefp ) DnsPacket * p = NULL ;
unsigned i ;
int r ;
assert ( s ) ;
2014-08-06 16:15:35 +02:00
assert ( ret ) ;
2014-07-29 14:24:02 +02:00
2014-08-03 22:41:25 +02:00
if ( ( ! q | | q - > n_keys < = 0 )
& & ( ! answer | | answer - > n_rrs < = 0 )
& & ( ! soa | | soa - > n_rrs < = 0 ) )
2014-07-29 14:24:02 +02:00
return - EINVAL ;
r = dns_packet_new ( & p , s - > protocol , 0 ) ;
if ( r < 0 )
return r ;
DNS_PACKET_HEADER ( p ) - > id = id ;
2014-07-29 19:50:28 +02:00
DNS_PACKET_HEADER ( p ) - > flags = htobe16 ( DNS_PACKET_MAKE_FLAGS (
1 /* qr */ ,
0 /* opcode */ ,
0 /* c */ ,
0 /* tc */ ,
2014-07-31 17:46:40 +02:00
tentative ,
2014-07-29 19:50:28 +02:00
0 /* (ra) */ ,
0 /* (ad) */ ,
0 /* (cd) */ ,
rcode ) ) ;
2014-07-29 14:24:02 +02:00
if ( q ) {
for ( i = 0 ; i < q - > n_keys ; i + + ) {
r = dns_packet_append_key ( p , q - > keys [ i ] , NULL ) ;
if ( r < 0 )
return r ;
}
DNS_PACKET_HEADER ( p ) - > qdcount = htobe16 ( q - > n_keys ) ;
}
2014-07-30 16:30:25 +02:00
if ( answer ) {
for ( i = 0 ; i < answer - > n_rrs ; i + + ) {
r = dns_packet_append_rr ( p , answer - > rrs [ i ] , NULL ) ;
2014-07-29 14:24:02 +02:00
if ( r < 0 )
return r ;
}
2014-07-30 16:30:25 +02:00
DNS_PACKET_HEADER ( p ) - > ancount = htobe16 ( answer - > n_rrs ) ;
}
if ( soa ) {
for ( i = 0 ; i < soa - > n_rrs ; i + + ) {
r = dns_packet_append_rr ( p , soa - > rrs [ i ] , NULL ) ;
if ( r < 0 )
return r ;
}
DNS_PACKET_HEADER ( p ) - > arcount = htobe16 ( soa - > n_rrs ) ;
2014-07-29 14:24:02 +02:00
}
* ret = p ;
p = NULL ;
return 0 ;
}
2014-08-06 16:15:35 +02:00
static void dns_scope_verify_conflicts ( DnsScope * s , DnsPacket * p ) {
unsigned n ;
assert ( s ) ;
assert ( p ) ;
if ( p - > question )
for ( n = 0 ; n < p - > question - > n_keys ; n + + )
dns_zone_verify_conflicts ( & s - > zone , p - > question - > keys [ n ] ) ;
if ( p - > answer )
for ( n = 0 ; n < p - > answer - > n_rrs ; n + + )
dns_zone_verify_conflicts ( & s - > zone , p - > answer - > rrs [ n ] - > key ) ;
}
2014-07-29 14:24:02 +02:00
void dns_scope_process_query ( DnsScope * s , DnsStream * stream , DnsPacket * p ) {
_cleanup_ ( dns_packet_unrefp ) DnsPacket * reply = NULL ;
2014-07-30 16:30:25 +02:00
_cleanup_ ( dns_answer_unrefp ) DnsAnswer * answer = NULL , * soa = NULL ;
2014-07-31 17:46:40 +02:00
bool tentative = false ;
2014-07-29 14:24:02 +02:00
int r , fd ;
assert ( s ) ;
assert ( p ) ;
if ( p - > protocol ! = DNS_PROTOCOL_LLMNR )
return ;
2014-07-29 23:52:57 +02:00
if ( p - > ipproto = = IPPROTO_UDP ) {
/* Don't accept UDP queries directed to anything but
* the LLMNR multicast addresses . See RFC 4795 ,
2014-12-10 20:00:04 +01:00
* section 2.5 . */
2014-07-29 23:52:57 +02:00
if ( p - > family = = AF_INET & & ! in_addr_equal ( AF_INET , & p - > destination , ( union in_addr_union * ) & LLMNR_MULTICAST_IPV4_ADDRESS ) )
return ;
if ( p - > family = = AF_INET6 & & ! in_addr_equal ( AF_INET6 , & p - > destination , ( union in_addr_union * ) & LLMNR_MULTICAST_IPV6_ADDRESS ) )
return ;
}
2014-07-29 14:24:02 +02:00
r = dns_packet_extract ( p ) ;
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to extract resources from incoming packet: %m " ) ;
2014-07-29 14:24:02 +02:00
return ;
}
2015-07-11 02:35:16 +02:00
if ( DNS_PACKET_LLMNR_C ( p ) ) {
2014-08-06 16:15:35 +02:00
/* Somebody notified us about a possible conflict */
dns_scope_verify_conflicts ( s , p ) ;
2014-07-29 19:50:28 +02:00
return ;
}
2014-07-31 17:46:40 +02:00
r = dns_zone_lookup ( & s - > zone , p - > question , & answer , & soa , & tentative ) ;
2014-07-29 14:24:02 +02:00
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to lookup key: %m " ) ;
2014-07-29 14:24:02 +02:00
return ;
}
if ( r = = 0 )
return ;
2014-07-30 19:24:05 +02:00
if ( answer )
dns_answer_order_by_scope ( answer , in_addr_is_link_local ( p - > family , & p - > sender ) > 0 ) ;
2014-07-30 00:48:59 +02:00
2014-07-31 17:46:40 +02:00
r = dns_scope_make_reply_packet ( s , DNS_PACKET_ID ( p ) , DNS_RCODE_SUCCESS , p - > question , answer , soa , tentative , & reply ) ;
2014-07-29 14:24:02 +02:00
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to build reply packet: %m " ) ;
2014-07-29 14:24:02 +02:00
return ;
}
if ( stream )
r = dns_stream_write_packet ( stream , reply ) ;
else {
2014-08-05 16:34:45 +02:00
if ( ! ratelimit_test ( & s - > ratelimit ) )
return ;
2014-07-29 14:24:02 +02:00
if ( p - > family = = AF_INET )
fd = manager_llmnr_ipv4_udp_fd ( s - > manager ) ;
else if ( p - > family = = AF_INET6 )
fd = manager_llmnr_ipv6_udp_fd ( s - > manager ) ;
else {
log_debug ( " Unknown protocol " ) ;
return ;
}
if ( fd < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( fd , " Failed to get reply socket: %m " ) ;
2014-07-29 14:24:02 +02:00
return ;
}
2014-08-05 17:01:33 +02:00
/* Note that we always immediately reply to all LLMNR
* requests , and do not wait any time , since we
* verified uniqueness for all records . Also see RFC
* 4795 , Section 2.7 */
2014-07-29 14:24:02 +02:00
r = manager_send ( s - > manager , fd , p - > ifindex , p - > family , & p - > sender , p - > sender_port , reply ) ;
}
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to send reply packet: %m " ) ;
2014-07-29 14:24:02 +02:00
return ;
}
}
2014-07-31 17:46:40 +02:00
2014-08-05 01:51:40 +02:00
DnsTransaction * dns_scope_find_transaction ( DnsScope * scope , DnsQuestion * question , bool cache_ok ) {
2014-07-31 17:46:40 +02:00
DnsTransaction * t ;
assert ( scope ) ;
assert ( question ) ;
/* Try to find an ongoing transaction that is a equal or a
* superset of the specified question */
2014-08-05 01:51:40 +02:00
LIST_FOREACH ( transactions_by_scope , t , scope - > transactions ) {
/* Refuse reusing transactions that completed based on
* cached data instead of a real packet , if that ' s
* requested . */
if ( ! cache_ok & &
IN_SET ( t - > state , DNS_TRANSACTION_SUCCESS , DNS_TRANSACTION_FAILURE ) & &
! t - > received )
continue ;
2014-07-31 17:46:40 +02:00
if ( dns_question_is_superset ( t - > question , question ) > 0 )
return t ;
2014-08-05 01:51:40 +02:00
}
2014-07-31 17:46:40 +02:00
return NULL ;
}
2014-08-06 16:15:35 +02:00
static int dns_scope_make_conflict_packet (
DnsScope * s ,
DnsResourceRecord * rr ,
DnsPacket * * ret ) {
_cleanup_ ( dns_packet_unrefp ) DnsPacket * p = NULL ;
int r ;
assert ( s ) ;
assert ( rr ) ;
assert ( ret ) ;
r = dns_packet_new ( & p , s - > protocol , 0 ) ;
if ( r < 0 )
return r ;
DNS_PACKET_HEADER ( p ) - > flags = htobe16 ( DNS_PACKET_MAKE_FLAGS (
0 /* qr */ ,
0 /* opcode */ ,
1 /* conflict */ ,
0 /* tc */ ,
0 /* t */ ,
0 /* (ra) */ ,
0 /* (ad) */ ,
0 /* (cd) */ ,
0 ) ) ;
random_bytes ( & DNS_PACKET_HEADER ( p ) - > id , sizeof ( uint16_t ) ) ;
DNS_PACKET_HEADER ( p ) - > qdcount = htobe16 ( 1 ) ;
DNS_PACKET_HEADER ( p ) - > arcount = htobe16 ( 1 ) ;
r = dns_packet_append_key ( p , rr - > key , NULL ) ;
if ( r < 0 )
return r ;
r = dns_packet_append_rr ( p , rr , NULL ) ;
if ( r < 0 )
return r ;
* ret = p ;
p = NULL ;
return 0 ;
}
static int on_conflict_dispatch ( sd_event_source * es , usec_t usec , void * userdata ) {
DnsScope * scope = userdata ;
int r ;
assert ( es ) ;
assert ( scope ) ;
scope - > conflict_event_source = sd_event_source_unref ( scope - > conflict_event_source ) ;
for ( ; ; ) {
_cleanup_ ( dns_resource_record_unrefp ) DnsResourceRecord * rr = NULL ;
_cleanup_ ( dns_packet_unrefp ) DnsPacket * p = NULL ;
2014-08-22 13:56:51 +02:00
rr = ordered_hashmap_steal_first ( scope - > conflict_queue ) ;
2014-08-06 16:15:35 +02:00
if ( ! rr )
break ;
r = dns_scope_make_conflict_packet ( scope , rr , & p ) ;
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_error_errno ( r , " Failed to make conflict packet: %m " ) ;
2014-08-06 16:15:35 +02:00
return 0 ;
}
2015-07-09 14:19:55 +02:00
r = dns_scope_emit ( scope , NULL , p , NULL ) ;
2014-08-06 16:15:35 +02:00
if ( r < 0 )
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to send conflict packet: %m " ) ;
2014-08-06 16:15:35 +02:00
}
return 0 ;
}
int dns_scope_notify_conflict ( DnsScope * scope , DnsResourceRecord * rr ) {
usec_t jitter ;
int r ;
assert ( scope ) ;
assert ( rr ) ;
/* We don't send these queries immediately. Instead, we queue
* them , and send them after some jitter delay . */
2014-08-22 13:56:51 +02:00
r = ordered_hashmap_ensure_allocated ( & scope - > conflict_queue , & dns_resource_key_hash_ops ) ;
2014-08-06 16:15:35 +02:00
if ( r < 0 ) {
log_oom ( ) ;
return r ;
}
/* We only place one RR per key in the conflict
* messages , not all of them . That should be enough to
* indicate where there might be a conflict */
2014-08-22 13:56:51 +02:00
r = ordered_hashmap_put ( scope - > conflict_queue , rr - > key , rr ) ;
2014-08-06 16:15:35 +02:00
if ( r = = - EEXIST | | r = = 0 )
return 0 ;
2014-11-28 18:50:43 +01:00
if ( r < 0 )
return log_debug_errno ( r , " Failed to queue conflicting RR: %m " ) ;
2014-08-06 16:15:35 +02:00
dns_resource_record_ref ( rr ) ;
if ( scope - > conflict_event_source )
return 0 ;
random_bytes ( & jitter , sizeof ( jitter ) ) ;
jitter % = LLMNR_JITTER_INTERVAL_USEC ;
r = sd_event_add_time ( scope - > manager - > event ,
& scope - > conflict_event_source ,
clock_boottime_or_monotonic ( ) ,
now ( clock_boottime_or_monotonic ( ) ) + jitter ,
LLMNR_JITTER_INTERVAL_USEC ,
on_conflict_dispatch , scope ) ;
2014-11-28 18:50:43 +01:00
if ( r < 0 )
return log_debug_errno ( r , " Failed to add conflict dispatch event: %m " ) ;
2014-08-06 16:15:35 +02:00
return 0 ;
}
void dns_scope_check_conflicts ( DnsScope * scope , DnsPacket * p ) {
unsigned i ;
int r ;
assert ( scope ) ;
assert ( p ) ;
if ( p - > protocol ! = DNS_PROTOCOL_LLMNR )
return ;
if ( DNS_PACKET_RRCOUNT ( p ) < = 0 )
return ;
2015-07-11 02:35:16 +02:00
if ( DNS_PACKET_LLMNR_C ( p ) ! = 0 )
2014-08-06 16:15:35 +02:00
return ;
2015-07-11 02:35:16 +02:00
if ( DNS_PACKET_LLMNR_T ( p ) ! = 0 )
2014-08-06 16:15:35 +02:00
return ;
if ( manager_our_packet ( scope - > manager , p ) )
return ;
r = dns_packet_extract ( p ) ;
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_debug_errno ( r , " Failed to extract packet: %m " ) ;
2014-08-06 16:15:35 +02:00
return ;
}
log_debug ( " Checking for conflicts... " ) ;
for ( i = 0 ; i < p - > answer - > n_rrs ; i + + ) {
/* Check for conflicts against the local zone. If we
* found one , we won ' t check any further */
r = dns_zone_check_conflicts ( & scope - > zone , p - > answer - > rrs [ i ] ) ;
if ( r ! = 0 )
continue ;
/* Check for conflicts against the local cache. If so,
* send out an advisory query , to inform everybody */
r = dns_cache_check_conflicts ( & scope - > cache , p - > answer - > rrs [ i ] , p - > family , & p - > sender ) ;
if ( r < = 0 )
continue ;
dns_scope_notify_conflict ( scope , p - > answer - > rrs [ i ] ) ;
}
}