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-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"
2014-07-16 00:26:02 +02:00
# include "resolved-dns-domain.h"
# include "resolved-dns-scope.h"
# define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
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-07-16 00:26:02 +02:00
* ret = s ;
return 0 ;
}
DnsScope * dns_scope_free ( DnsScope * s ) {
2014-07-22 21:48:41 +02:00
DnsQueryTransaction * t ;
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 + + ;
dns_query_transaction_complete ( t , DNS_QUERY_ABORTED ) ;
t - > block_gc - - ;
2014-07-16 00:26:02 +02:00
2014-07-22 21:48:41 +02:00
dns_query_transaction_free ( t ) ;
2014-07-16 00:26:02 +02:00
}
2014-07-17 19:38:37 +02:00
dns_cache_flush ( & s - > cache ) ;
2014-07-16 00:26:02 +02:00
LIST_REMOVE ( scopes , s - > manager - > dns_scopes , s ) ;
strv_free ( s - > domains ) ;
free ( s ) ;
return NULL ;
}
DnsServer * dns_scope_get_server ( DnsScope * s ) {
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 ) ;
}
int dns_scope_send ( DnsScope * s , DnsPacket * p ) {
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 ) {
DnsServer * srv ;
2014-07-17 01:13:22 +02:00
2014-07-18 12:34:02 +02:00
srv = dns_scope_get_server ( s ) ;
if ( ! srv )
return - ESRCH ;
family = srv - > family ;
addr = srv - > address ;
port = 53 ;
if ( p - > size > DNS_PACKET_UNICAST_SIZE_MAX )
return - EMSGSIZE ;
if ( p - > size > mtu )
return - EMSGSIZE ;
if ( family = = AF_INET )
fd = manager_dns_ipv4_fd ( s - > manager ) ;
else if ( family = = AF_INET6 )
fd = manager_dns_ipv6_fd ( s - > manager ) ;
else
return - EAFNOSUPPORT ;
if ( fd < 0 )
return fd ;
} else if ( s - > protocol = = DNS_PROTOCOL_LLMNR ) {
if ( DNS_PACKET_QDCOUNT ( p ) > 1 )
return - ENOTSUP ;
family = s - > family ;
port = 5355 ;
if ( family = = AF_INET ) {
addr . in = LLMNR_MULTICAST_IPV4_ADDRESS ;
/* fd = manager_dns_ipv4_fd(s->manager); */
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 ) ;
/* fd = manager_dns_ipv6_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 ;
return 1 ;
}
2014-07-16 20:15:47 +02:00
int dns_scope_tcp_socket ( DnsScope * s ) {
_cleanup_close_ int fd = - 1 ;
union sockaddr_union sa = { } ;
socklen_t salen ;
2014-07-19 03:44:34 +02:00
int one , ret ;
2014-07-16 20:15:47 +02:00
DnsServer * srv ;
int r ;
assert ( s ) ;
srv = dns_scope_get_server ( s ) ;
if ( ! srv )
return - ESRCH ;
sa . sa . sa_family = srv - > family ;
if ( srv - > family = = AF_INET ) {
sa . in . sin_port = htobe16 ( 53 ) ;
sa . in . sin_addr = srv - > address . in ;
salen = sizeof ( sa . in ) ;
} else if ( srv - > family = = AF_INET6 ) {
sa . in6 . sin6_port = htobe16 ( 53 ) ;
sa . in6 . sin6_addr = srv - > address . in6 ;
2014-07-19 03:44:34 +02:00
sa . in6 . sin6_scope_id = s - > link ? s - > link - > ifindex : 0 ;
2014-07-16 20:15:47 +02:00
salen = sizeof ( sa . in6 ) ;
} else
return - EAFNOSUPPORT ;
fd = socket ( srv - > family , SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ;
if ( fd < 0 )
return - errno ;
one = 1 ;
setsockopt ( fd , IPPROTO_TCP , TCP_NODELAY , & one , sizeof ( one ) ) ;
r = connect ( fd , & sa . sa , salen ) ;
if ( r < 0 & & errno ! = EINPROGRESS )
return - errno ;
ret = fd ;
fd = - 1 ;
return ret ;
}
2014-07-18 12:34:02 +02:00
DnsScopeMatch dns_scope_good_domain ( DnsScope * s , const char * domain ) {
2014-07-16 00:26:02 +02:00
char * * i ;
assert ( s ) ;
assert ( domain ) ;
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-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 )
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 ;
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 ;
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 ;
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
}