resolved-dns-scope.c revision 9436e8cae4709b50ed57f2f5858a3ffad03d5d32
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt/***
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt This file is part of systemd.
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt Copyright 2014 Lennart Poettering
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
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
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
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***/
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#include <netinet/tcp.h>
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt#include "missing.h"
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt#include "strv.h"
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt#include "socket-util.h"
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt#include "af-list.h"
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#include "random-util.h"
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt#include "hostname-util.h"
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#include "dns-domain.h"
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#include "resolved-llmnr.h"
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#include "resolved-dns-scope.h"
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt#define MULTICAST_RATELIMIT_BURST 1000
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flyktint dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt DnsScope *s;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt assert(m);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt assert(ret);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt s = new0(DnsScope, 1);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt if (!s)
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt return -ENOMEM;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt s->manager = m;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt s->link = l;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt s->protocol = protocol;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt s->family = family;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt LIST_PREPEND(scopes, m->dns_scopes, s);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt dns_scope_llmnr_membership(s, true);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
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
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* Enforce ratelimiting for the multicast protocols */
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt *ret = s;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt return 0;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt}
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik FlyktDnsScope* dns_scope_free(DnsScope *s) {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt DnsTransaction *t;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt DnsResourceRecord *rr;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt if (!s)
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt return NULL;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
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
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt dns_scope_llmnr_membership(s, false);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt while ((t = s->transactions)) {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* Abort the transaction, but make sure it is not
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt * freed while we still look at it */
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt t->block_gc++;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt t->block_gc--;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_transaction_free(t);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt }
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_resource_record_unref(rr);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt ordered_hashmap_free(s->conflict_queue);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt sd_event_source_unref(s->conflict_event_source);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_cache_flush(&s->cache);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt dns_zone_flush(&s->zone);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt LIST_REMOVE(scopes, s->manager->dns_scopes, s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt strv_free(s->domains);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt free(s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return NULL;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt}
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik FlyktDnsServer *dns_scope_get_dns_server(DnsScope *s) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert(s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt if (s->protocol != DNS_PROTOCOL_DNS)
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return NULL;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt if (s->link)
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return link_get_dns_server(s->link);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt else
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return manager_get_dns_server(s->manager);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt}
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flyktvoid dns_scope_next_dns_server(DnsScope *s) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert(s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt if (s->protocol != DNS_PROTOCOL_DNS)
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt if (s->link)
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt link_next_dns_server(s->link);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt else
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt manager_next_dns_server(s->manager);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt}
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flyktint dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt union in_addr_union addr;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt int ifindex = 0, r;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt int family;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt uint16_t port;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt uint32_t mtu;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert(s);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert(p);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert(p->protocol == s->protocol);
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt if (s->link) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt mtu = s->link->mtu;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt ifindex = s->link->ifindex;
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt } else
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt mtu = manager_find_mtu(s->manager);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (s->protocol == DNS_PROTOCOL_DNS) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (DNS_PACKET_QDCOUNT(p) > 1)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EOPNOTSUPP;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EMSGSIZE;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EMSGSIZE;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = manager_write(s->manager, fd, p);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return r;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (DNS_PACKET_QDCOUNT(p) > 1)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EOPNOTSUPP;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (!ratelimit_test(&s->ratelimit))
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EBUSY;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt family = s->family;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt port = LLMNR_PORT;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (family == AF_INET) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt fd = manager_llmnr_ipv4_udp_fd(s->manager);
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt } else if (family == AF_INET6) {
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt fd = manager_llmnr_ipv6_udp_fd(s->manager);
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt } else
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return -EAFNOSUPPORT;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (fd < 0)
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return fd;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (r < 0)
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return r;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt } else
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return -EAFNOSUPPORT;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return 1;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt}
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flyktstatic int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt DnsServer *srv = NULL;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt _cleanup_close_ int fd = -1;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt union sockaddr_union sa = {};
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt socklen_t salen;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt static const int one = 1;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt int ret, r;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt assert(s);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt assert((family == AF_UNSPEC) == !address);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (family == AF_UNSPEC) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt srv = dns_scope_get_dns_server(s);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (!srv)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -ESRCH;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.sa.sa_family = srv->family;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (srv->family == AF_INET) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in.sin_port = htobe16(port);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in.sin_addr = srv->address.in;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt salen = sizeof(sa.in);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (srv->family == AF_INET6) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_port = htobe16(port);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_addr = srv->address.in6;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt salen = sizeof(sa.in6);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EAFNOSUPPORT;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.sa.sa_family = family;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (family == AF_INET) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in.sin_port = htobe16(port);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in.sin_addr = address->in;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt salen = sizeof(sa.in);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (family == AF_INET6) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_port = htobe16(port);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_addr = address->in6;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt salen = sizeof(sa.in6);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -EAFNOSUPPORT;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (fd < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (type == SOCK_STREAM) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (s->link) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt uint32_t ifindex = htobe32(s->link->ifindex);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (sa.sa.sa_family == AF_INET) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (sa.sa.sa_family == AF_INET6) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (s->protocol == DNS_PROTOCOL_LLMNR) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (sa.sa.sa_family == AF_INET) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt } else if (sa.sa.sa_family == AF_INET6) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt r = connect(fd, &sa.sa, salen);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (r < 0 && errno != EINPROGRESS)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return -errno;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (server)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt *server = srv;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt ret = fd;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt fd = -1;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return ret;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt}
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
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 Flykt}
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
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 Flykt}
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik FlyktDnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt char **i;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt assert(s);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt assert(domain);
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return DNS_SCOPE_NO;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt return DNS_SCOPE_NO;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt STRV_FOREACH(i, s->domains)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (dns_name_endswith(domain, *i) > 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return DNS_SCOPE_YES;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (dns_name_root(domain) != 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return DNS_SCOPE_NO;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (is_localhost(domain))
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return DNS_SCOPE_NO;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
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 return DNS_SCOPE_NO;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (s->protocol == DNS_PROTOCOL_DNS) {
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 dns_name_single_label(domain) == 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return DNS_SCOPE_MAYBE;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return DNS_SCOPE_NO;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (s->protocol == DNS_PROTOCOL_MDNS) {
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 return DNS_SCOPE_MAYBE;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return DNS_SCOPE_NO;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt }
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (s->protocol == DNS_PROTOCOL_LLMNR) {
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt dns_name_endswith(domain, "ip6.arpa") > 0 ||
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt (dns_name_single_label(domain) > 0 &&
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return DNS_SCOPE_MAYBE;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt return DNS_SCOPE_NO;
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt }
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert_not_reached("Unknown scope protocol");
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt}
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktint dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(s);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(key);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->protocol == DNS_PROTOCOL_DNS)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return true;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt /* On mDNS and LLMNR, send A and AAAA queries only on the
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt * respective scopes */
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return false;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return false;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return true;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt}
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktint dns_scope_llmnr_membership(DnsScope *s, bool b) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt int fd;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(s);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->protocol != DNS_PROTOCOL_LLMNR)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return 0;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(s->link);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (s->family == AF_INET) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt struct ip_mreqn mreqn = {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt .imr_ifindex = s->link->ifindex,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt };
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt fd = manager_llmnr_ipv4_udp_fd(s->manager);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (fd < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return fd;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt /* Always first try to drop membership before we add
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt * one. This is necessary on some devices, such as
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt * veth. */
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (b)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return -errno;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt } else if (s->family == AF_INET6) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt struct ipv6_mreq mreq = {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt .ipv6mr_interface = s->link->ifindex,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt };
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt fd = manager_llmnr_ipv6_udp_fd(s->manager);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (fd < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return fd;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (b)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return -errno;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt } else
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return -EAFNOSUPPORT;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return 0;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt}
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flyktstatic int dns_scope_make_reply_packet(
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt DnsScope *s,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt uint16_t id,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt int rcode,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt DnsQuestion *q,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt DnsAnswer *answer,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt DnsAnswer *soa,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt bool tentative,
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt DnsPacket **ret) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt unsigned i;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt int r;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(s);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt assert(ret);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if ((!q || q->n_keys <= 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt && (!answer || answer->n_rrs <= 0)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt && (!soa || soa->n_rrs <= 0))
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt return -EINVAL;
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt r = dns_packet_new(&p, s->protocol, 0);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (r < 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return r;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->id = id;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 1 /* qr */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* opcode */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* c */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* tc */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt tentative,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (ra) */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (ad) */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt 0 /* (cd) */,
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt rcode));
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (q) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt for (i = 0; i < q->n_keys; i++) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt r = dns_packet_append_key(p, q->keys[i], NULL);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return r;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (answer) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt for (i = 0; i < answer->n_rrs; i++) {
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt r = dns_packet_append_rr(p, answer->rrs[i], NULL);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (r < 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return r;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (soa) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt for (i = 0; i < soa->n_rrs; i++) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_packet_append_rr(p, soa->rrs[i], NULL);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return r;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt *ret = p;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt p = NULL;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return 0;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt}
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flyktstatic void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt unsigned n;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt assert(s);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt assert(p);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->question)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt for (n = 0; n < p->question->n_keys; n++)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt if (p->answer)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt for (n = 0; n < p->answer->n_rrs; n++)
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key);
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt}
5e256ea7d3d556b3a1fb5c1faa94ec6a8833e53ePatrik Flykt
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 bool tentative = false;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt int r, fd;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt assert(s);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt assert(p);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->protocol != DNS_PROTOCOL_LLMNR)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
947527f8326d3591f252c48fee5426a563f03544Patrik Flykt if (p->ipproto == IPPROTO_UDP) {
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
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_packet_extract(p);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r < 0) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (DNS_PACKET_LLMNR_C(p)) {
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt /* Somebody notified us about a possible conflict */
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_scope_verify_conflicts(s, p);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
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 return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (r == 0)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (answer)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
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");
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt return;
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (stream)
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt r = dns_stream_write_packet(stream, reply);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt else {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt if (!ratelimit_test(&s->ratelimit))
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt return;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt if (p->family == AF_INET)
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt fd = manager_llmnr_ipv4_udp_fd(s->manager);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt else if (p->family == AF_INET6)
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt fd = manager_llmnr_ipv6_udp_fd(s->manager);
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt else {
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt log_debug("Unknown protocol");
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return;
859cca44f834ab1cc3e41fa6b94744f1856ab027Patrik Flykt }
2ea8857effb833615b16d10fc7a19a7104c19e13Patrik Flykt if (fd < 0) {
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt log_debug_errno(fd, "Failed to get reply socket: %m");
f12ed3bf0b315fc88d5fbdf5bdca14b218c86e0cPatrik Flykt return;
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt }
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt
813e3a6ffcd094696001716480bbd68008cc54c8Patrik Flykt /* 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 */
r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
}
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return;
}
}
DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok) {
DnsTransaction *t;
assert(scope);
assert(question);
/* Try to find an ongoing transaction that is a equal or a
* superset of the specified question */
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;
if (dns_question_is_superset(t->question, question) > 0)
return t;
}
return NULL;
}
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;
rr = ordered_hashmap_steal_first(scope->conflict_queue);
if (!rr)
break;
r = dns_scope_make_conflict_packet(scope, rr, &p);
if (r < 0) {
log_error_errno(r, "Failed to make conflict packet: %m");
return 0;
}
r = dns_scope_emit(scope, -1, p);
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
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. */
r = ordered_hashmap_ensure_allocated(&scope->conflict_queue, &dns_resource_key_hash_ops);
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 */
r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr);
if (r == -EEXIST || r == 0)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to queue conflicting RR: %m");
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);
if (r < 0)
return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
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;
if (DNS_PACKET_LLMNR_C(p) != 0)
return;
if (DNS_PACKET_LLMNR_T(p) != 0)
return;
if (manager_our_packet(scope->manager, p))
return;
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract packet: %m");
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]);
}
}