resolved-dns-scope.c revision 9436e8cae4709b50ed57f2f5858a3ffad03d5d32
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt This file is part of systemd.
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt Copyright 2014 Lennart Poettering
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt systemd is free software; you can redistribute it and/or modify it
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt under the terms of the GNU Lesser General Public License as published by
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt the Free Software Foundation; either version 2.1 of the License, or
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt (at your option) any later version.
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt systemd is distributed in the hope that it will be useful, but
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt Lesser General Public License for more details.
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt You should have received a copy of the GNU Lesser General Public License
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flyktint dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt 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));
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* Enforce ratelimiting for the multicast protocols */
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt 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));
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt while ((t = s->transactions)) {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* Abort the transaction, but make sure it is not
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt * freed while we still look at it */
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt sd_event_source_unref(s->conflict_event_source);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt LIST_REMOVE(scopes, s->manager->dns_scopes, s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik FlyktDnsServer *dns_scope_get_dns_server(DnsScope *s) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flyktint dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
947527f8326d3591f252c48fee5426a563f03544Patrik Flyktstatic int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flyktint dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flyktint dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik FlyktDnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt /* Never resolve any loopback IP address via DNS, LLMNR or mDNS */
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktint dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return true;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt /* On mDNS and LLMNR, send A and AAAA queries only on the
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt * respective scopes */
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return false;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return false;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return true;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktint dns_scope_llmnr_membership(DnsScope *s, bool b) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt /* Always first try to drop membership before we add
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt * one. This is necessary on some devices, such as
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if ((!q || q->n_keys <= 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* opcode */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (ra) */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (ad) */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (cd) */,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt for (i = 0; i < q->n_keys; i++) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt r = dns_packet_append_key(p, q->keys[i], NULL);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt r = dns_packet_append_rr(p, answer->rrs[i], NULL);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_packet_append_rr(p, soa->rrs[i], NULL);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flyktstatic void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktvoid dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt /* Don't accept UDP queries directed to anything but
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt * the LLMNR multicast addresses. See RFC 4795,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt * section 2.5. */
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt /* Somebody notified us about a possible conflict */
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt log_debug_errno(r, "Failed to lookup key: %m");
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt log_debug_errno(r, "Failed to build reply packet: %m");
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt log_debug_errno(fd, "Failed to get reply socket: %m");
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* Note that we always immediately reply to all LLMNR
DnsTransaction *t;
if (!cache_ok &&
!t->received)
return NULL;
static int dns_scope_make_conflict_packet(
DnsScope *s,
assert(s);
*ret = p;
p = NULL;
if (!rr)
log_oom();
if (r == -EEXIST || r == 0)
assert(p);
if (DNS_PACKET_RRCOUNT(p) <= 0)
if (DNS_PACKET_LLMNR_C(p) != 0)
if (DNS_PACKET_LLMNR_T(p) != 0)
r = dns_packet_extract(p);