resolved-dns-scope.c revision b43d96b0764e63088429f746cd9e515f55286460
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/***
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen This file is part of systemd.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Copyright 2014 Lennart Poettering
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen systemd is free software; you can redistribute it and/or modify it
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen under the terms of the GNU Lesser General Public License as published by
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen (at your option) any later version.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen systemd is distributed in the hope that it will be useful, but
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Lesser General Public License for more details.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen You should have received a copy of the GNU Lesser General Public License
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen***/
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include <netinet/tcp.h>
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include "af-list.h"
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen#include "alloc-util.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "dns-domain.h"
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen#include "fd-util.h"
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen#include "hostname-util.h"
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen#include "missing.h"
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen#include "random-util.h"
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include "resolved-dns-scope.h"
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include "resolved-llmnr.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "resolved-mdns.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "socket-util.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "strv.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#define MULTICAST_RATELIMIT_BURST 1000
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
3aeb37bc4f32b5edc334f2ac7c5d3c7b0a121328Tom Gundersen#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC)
c6f7c917a1b494d4455800823472227463f87438Tom Gundersen#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC)
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenint dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen DnsScope *s;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(m);
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen assert(ret);
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen s = new0(DnsScope, 1);
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen if (!s)
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return -ENOMEM;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen s->manager = m;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen s->link = l;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen s->protocol = protocol;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek s->family = family;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek LIST_PREPEND(scopes, m->dns_scopes, s);
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek dns_scope_llmnr_membership(s, true);
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek dns_scope_mdns_membership(s, true);
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek 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));
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen /* Enforce ratelimiting for the multicast protocols */
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen *ret = s;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return 0;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void dns_scope_abort_transactions(DnsScope *s) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen while (s->transactions) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen DnsTransaction *t = s->transactions;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* Abort the transaction, but make sure it is not
977085794d2996320e345433403de75f662b0622Tom Gundersen * freed while we still look at it */
977085794d2996320e345433403de75f662b0622Tom Gundersen
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen t->block_gc++;
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen t->block_gc--;
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen dns_transaction_free(t);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom GundersenDnsScope* dns_scope_free(DnsScope *s) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen DnsResourceRecord *rr;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (!s)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen 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));
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen dns_scope_llmnr_membership(s, false);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen dns_scope_mdns_membership(s, false);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen dns_scope_abort_transactions(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen while (s->query_candidates)
d2df0d0ed3a88e491405b403e6022e6619750130Tom Gundersen dns_query_candidate_free(s->query_candidates);
edf029b7fd9a5853a87d3ca99aac2922bb8a277eTom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen hashmap_free(s->transactions_by_key);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen dns_resource_record_unref(rr);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen ordered_hashmap_free(s->conflict_queue);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen sd_event_source_unref(s->conflict_event_source);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen dns_cache_flush(&s->cache);
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering dns_zone_flush(&s->zone);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen LIST_REMOVE(scopes, s->manager->dns_scopes, s);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen free(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom GundersenDnsServer *dns_scope_get_dns_server(DnsScope *s) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (s->protocol != DNS_PROTOCOL_DNS)
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek return NULL;
6e37cd2f4af8928d905203108a4331e375d7127cThomas Hindoe Paaboel Andersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (s->link)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return link_get_dns_server(s->link);
187dc6e554f2d5b4b5a3bee72c73ff5df6418aa6Thomas Hindoe Paaboel Andersen else
187dc6e554f2d5b4b5a3bee72c73ff5df6418aa6Thomas Hindoe Paaboel Andersen return manager_get_dns_server(s->manager);
187dc6e554f2d5b4b5a3bee72c73ff5df6418aa6Thomas Hindoe Paaboel Andersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenvoid dns_scope_next_dns_server(DnsScope *s) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (s->protocol != DNS_PROTOCOL_DNS)
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek return;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (s->link)
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek link_next_dns_server(s->link);
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek else
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek manager_next_dns_server(s->manager);
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek}
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmek
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenvoid dns_scope_packet_received(DnsScope *s, usec_t rtt) {
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek assert(s);
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (rtt <= s->max_rtt)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen s->max_rtt = rtt;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC);
e9f3d2d508bfd9fb5b54e82994bda365a71eb864Zbigniew Jędrzejewski-Szmek}
e9f3d2d508bfd9fb5b54e82994bda365a71eb864Zbigniew Jędrzejewski-Szmek
e9f3d2d508bfd9fb5b54e82994bda365a71eb864Zbigniew Jędrzejewski-Szmekvoid dns_scope_packet_lost(DnsScope *s, usec_t usec) {
36f822c4bd077f9121757e24b6516e5c7ada63b5Zbigniew Jędrzejewski-Szmek assert(s);
36f822c4bd077f9121757e24b6516e5c7ada63b5Zbigniew Jędrzejewski-Szmek
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek if (s->resend_timeout <= usec)
36f822c4bd077f9121757e24b6516e5c7ada63b5Zbigniew Jędrzejewski-Szmek s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
98a375f6d5cac24eb80d6d4e00699851324afdecTom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenint dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen union in_addr_union addr;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int ifindex = 0, r;
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek int family;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen uint32_t mtu;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen size_t saved_size = 0;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(s);
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen assert(p);
f8a0bb5285024b6ce372c3157e761e6543ebdcd2Andreas Henriksson assert(p->protocol == s->protocol);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen if (s->link) {
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen mtu = s->link->mtu;
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering ifindex = s->link->ifindex;
b5884878a2874447b2a9f07f324a7cd909d96d48Lennart Poettering } else
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt mtu = manager_find_mtu(s->manager);
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering
b5884878a2874447b2a9f07f324a7cd909d96d48Lennart Poettering switch (s->protocol) {
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen case DNS_PROTOCOL_DNS:
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek assert(server);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen if (DNS_PACKET_QDCOUNT(p) > 1)
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen return -EOPNOTSUPP;
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen bool edns_do;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen size_t packet_size;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
edf029b7fd9a5853a87d3ca99aac2922bb8a277eTom Gundersen edns_do = server->possible_features >= DNS_SERVER_FEATURE_LEVEL_DO;
edf029b7fd9a5853a87d3ca99aac2922bb8a277eTom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_LARGE)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen else
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen packet_size = server->received_udp_packet_max;
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen
3f85ef0f05ffc51e19f86fb83a1c51e8e3cd6817Harald Hoyer r = dns_packet_append_opt_rr(p, packet_size, edns_do, &saved_size);
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen if (r < 0)
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen return r;
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) + 1);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt return -EMSGSIZE;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return -EMSGSIZE;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = manager_write(s->manager, fd, p);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (saved_size > 0) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen dns_packet_truncate(p, saved_size);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) - 1);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen break;
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen case DNS_PROTOCOL_LLMNR:
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (DNS_PACKET_QDCOUNT(p) > 1)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return -EOPNOTSUPP;
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen if (!ratelimit_test(&s->ratelimit))
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen return -EBUSY;
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen family = s->family;
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen if (family == AF_INET) {
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
b3e013148603aa670bc2c060ac63d48e54d76fc2Tom Gundersen fd = manager_llmnr_ipv4_udp_fd(s->manager);
edbb03e95a3c31bf719d5c6c46eec14d0bcb9c8fTom Gundersen } else if (family == AF_INET6) {
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
edbb03e95a3c31bf719d5c6c46eec14d0bcb9c8fTom Gundersen fd = manager_llmnr_ipv6_udp_fd(s->manager);
eb7040ec50fbfe5aad9eaf305bd442a4a235abaaTom Gundersen } else
b3e013148603aa670bc2c060ac63d48e54d76fc2Tom Gundersen return -EAFNOSUPPORT;
9b1c2626cef16722603bded9bb52033aba34dd74Tom Gundersen if (fd < 0)
bf175aafd20c9ef974709ef12c5acf836121af33Tom Gundersen return fd;
b3e013148603aa670bc2c060ac63d48e54d76fc2Tom Gundersen
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen if (r < 0)
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen return r;
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen break;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen case DNS_PROTOCOL_MDNS:
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (!ratelimit_test(&s->ratelimit))
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return -EBUSY;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen family = s->family;
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (family == AF_INET) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen fd = manager_mdns_ipv4_fd(s->manager);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen } else if (family == AF_INET6) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen fd = manager_mdns_ipv6_fd(s->manager);
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen } else
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return -EAFNOSUPPORT;
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (fd < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return fd;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (r < 0)
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen return r;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen break;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen default:
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen return -EAFNOSUPPORT;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen }
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen return 1;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen}
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersenstatic int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen DnsServer *srv = NULL;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen _cleanup_close_ int fd = -1;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen union sockaddr_union sa = {};
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen socklen_t salen;
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen static const int one = 1;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen int ret, r;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen assert(s);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen assert((family == AF_UNSPEC) == !address);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen if (family == AF_UNSPEC) {
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen srv = dns_scope_get_dns_server(s);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (!srv)
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen return -ESRCH;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen srv->possible_features = dns_server_possible_features(srv);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (type == SOCK_DGRAM && srv->possible_features < DNS_SERVER_FEATURE_LEVEL_UDP)
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen return -EAGAIN;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen sa.sa.sa_family = srv->family;
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (srv->family == AF_INET) {
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen sa.in.sin_port = htobe16(port);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen sa.in.sin_addr = srv->address.in;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen salen = sizeof(sa.in);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen } else if (srv->family == AF_INET6) {
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen sa.in6.sin6_port = htobe16(port);
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen sa.in6.sin6_addr = srv->address.in6;
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen salen = sizeof(sa.in6);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen } else
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering return -EAFNOSUPPORT;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen } else {
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering sa.sa.sa_family = family;
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering
b5db00e52ee2e20578839e4e4488f7b9af9abc38Umut Tezduyar Lindskog if (family == AF_INET) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen sa.in.sin_port = htobe16(port);
55428d84f31b52da1c50b7469f14e15740547f20Tom Gundersen sa.in.sin_addr = address->in;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen salen = sizeof(sa.in);
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering } else if (family == AF_INET6) {
9bf3b53533cdc9b95c921b71da755401f223f765Lennart Poettering sa.in6.sin6_port = htobe16(port);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen sa.in6.sin6_addr = address->in6;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen salen = sizeof(sa.in6);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen } else
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen return -EAFNOSUPPORT;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen }
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (fd < 0)
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen return -errno;
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen if (type == SOCK_STREAM) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (r < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return -errno;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen }
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (s->link) {
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen uint32_t ifindex = htobe32(s->link->ifindex);
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen if (sa.sa.sa_family == AF_INET) {
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen if (r < 0)
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen return -errno;
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen } else if (sa.sa.sa_family == AF_INET6) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0)
aedca89268ed4fd6be41e55a605f011033ad1fb5Tom Gundersen return -errno;
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen }
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen }
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering if (s->protocol == DNS_PROTOCOL_LLMNR) {
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen
aedca89268ed4fd6be41e55a605f011033ad1fb5Tom Gundersen if (sa.sa.sa_family == AF_INET) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering if (r < 0)
755bde375f4db393ad06e73340bfcf4d0cf91bb2Lennart Poettering return -errno;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (sa.sa.sa_family == AF_INET6) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (r < 0)
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return -errno;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen }
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen }
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen r = connect(fd, &sa.sa, salen);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (r < 0 && errno != EINPROGRESS)
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return -errno;
68ba38770640413b4fa06773447666eb88a38d4cTom Gundersen
68ba38770640413b4fa06773447666eb88a38d4cTom Gundersen if (server)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen *server = srv;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen ret = fd;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen fd = -1;
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersen
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersen return ret;
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersen}
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenint dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen}
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersenint dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen}
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom GundersenDnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen DnsSearchDomain *d;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen assert(s);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen assert(domain);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen /* Checks if the specified domain is something to look up on
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen * this scope. Note that this accepts non-qualified hostnames,
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen * i.e. those without any search path prefixed yet. */
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen return DNS_SCOPE_NO;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen return DNS_SCOPE_NO;
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen /* Never resolve any loopback hostname or IP address via DNS,
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen * LLMNR or mDNS. Instead, always rely on synthesized RRs for
92d927f850d4b668b44f3e5f41e266d934d03726Tom Gundersen * these. */
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (is_localhost(domain) ||
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen 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)
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen return DNS_SCOPE_NO;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen /* Never respond to some of the domains listed in RFC6303 */
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 ||
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 ||
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen dns_name_equal(domain, "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.0.ip6.arpa") > 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return DNS_SCOPE_NO;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen /* Always honour search domains for routing queries. Note that
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen * we return DNS_SCOPE_YES here, rather than just
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen * DNS_SCOPE_MAYBE, which means wildcard scopes won't be
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen * considered anymore. */
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (dns_name_endswith(domain, d->name) > 0)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return DNS_SCOPE_YES;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen switch (s->protocol) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen case DNS_PROTOCOL_DNS:
aedca89268ed4fd6be41e55a605f011033ad1fb5Tom Gundersen
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen /* Exclude link-local IP ranges */
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 &&
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 &&
d95b83b87d7d7c50e550f7128827f73a321c8934Tom Gundersen dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 &&
d95b83b87d7d7c50e550f7128827f73a321c8934Tom Gundersen dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0 &&
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* If networks use .local in their private setups, they are supposed to also add .local to their search
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * domains, which we already checked above. Otherwise, we consider .local specific to mDNS and won't
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen * send such queries ordinary DNS servers. */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen dns_name_endswith(domain, "local") == 0)
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen return DNS_SCOPE_MAYBE;
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen return DNS_SCOPE_NO;
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen case DNS_PROTOCOL_MDNS:
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
aedca89268ed4fd6be41e55a605f011033ad1fb5Tom Gundersen dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen return DNS_SCOPE_MAYBE;
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen return DNS_SCOPE_NO;
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen case DNS_PROTOCOL_LLMNR:
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
2c5859afecee81e345fc9526b1083bf79990ffb8Daniel Mack (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen return DNS_SCOPE_MAYBE;
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen return DNS_SCOPE_NO;
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen default:
2c5859afecee81e345fc9526b1083bf79990ffb8Daniel Mack assert_not_reached("Unknown scope protocol");
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen }
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersen}
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersenint dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen assert(s);
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen assert(key);
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* Check if it makes sense to resolve the specified key on
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen * this scope. Note that this call assumes as fully qualified
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen * name, i.e. the search suffixes already appended. */
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen if (s->protocol == DNS_PROTOCOL_DNS) {
/* On classic DNS, lookin up non-address RRs is always
* fine. (Specifically, we want to permit looking up
* DNSKEY and DS records on the root and top-level
* domains.) */
if (!dns_resource_key_is_address(key))
return true;
/* However, we refuse to look up A and AAAA RRs on the
* root and single-label domains, under the assumption
* that those should be resolved via LLMNR or search
* path only, and should not be leaked onto the
* internet. */
return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) ||
dns_name_is_root(DNS_RESOURCE_KEY_NAME(key)));
}
/* 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;
}
static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
int fd;
assert(s);
assert(s->link);
if (s->family == AF_INET) {
struct ip_mreqn mreqn = {
.imr_multiaddr = in,
.imr_ifindex = s->link->ifindex,
};
fd = manager_llmnr_ipv4_udp_fd(s->manager);
if (fd < 0)
return fd;
/* Always first try to drop membership before we add
* one. This is necessary on some devices, such as
* veth. */
if (b)
(void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
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 = in6,
.ipv6mr_interface = s->link->ifindex,
};
fd = manager_llmnr_ipv6_udp_fd(s->manager);
if (fd < 0)
return fd;
if (b)
(void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
} else
return -EAFNOSUPPORT;
return 0;
}
int dns_scope_llmnr_membership(DnsScope *s, bool b) {
if (s->protocol != DNS_PROTOCOL_LLMNR)
return 0;
return dns_scope_multicast_membership(s, b, LLMNR_MULTICAST_IPV4_ADDRESS, LLMNR_MULTICAST_IPV6_ADDRESS);
}
int dns_scope_mdns_membership(DnsScope *s, bool b) {
if (s->protocol != DNS_PROTOCOL_MDNS)
return 0;
return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS);
}
static int dns_scope_make_reply_packet(
DnsScope *s,
uint16_t id,
int rcode,
DnsQuestion *q,
DnsAnswer *answer,
DnsAnswer *soa,
bool tentative,
DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
unsigned i;
int r;
assert(s);
assert(ret);
if ((!q || q->n_keys <= 0)
&& (!answer || answer->n_rrs <= 0)
&& (!soa || soa->n_rrs <= 0))
return -EINVAL;
r = dns_packet_new(&p, s->protocol, 0);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->id = id;
DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
1 /* qr */,
0 /* opcode */,
0 /* c */,
0 /* tc */,
tentative,
0 /* (ra) */,
0 /* (ad) */,
0 /* (cd) */,
rcode));
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);
}
if (answer) {
for (i = 0; i < answer->n_rrs; i++) {
r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
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->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
}
*ret = p;
p = NULL;
return 0;
}
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->items[n].rr->key);
}
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
assert(s);
assert(p);
if (p->protocol != DNS_PROTOCOL_LLMNR)
return;
if (p->ipproto == IPPROTO_UDP) {
/* Don't accept UDP queries directed to anything but
* the LLMNR multicast addresses. See RFC 4795,
* section 2.5. */
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;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
return;
}
if (DNS_PACKET_LLMNR_C(p)) {
/* Somebody notified us about a possible conflict */
dns_scope_verify_conflicts(s, p);
return;
}
assert(p->question->n_keys == 1);
key = p->question->keys[0];
r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return;
}
if (r == 0)
return;
if (answer)
dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
if (r < 0) {
log_debug_errno(r, "Failed to build reply packet: %m");
return;
}
if (stream)
r = dns_stream_write_packet(stream, reply);
else {
if (!ratelimit_test(&s->ratelimit))
return;
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) {
log_debug_errno(fd, "Failed to get reply socket: %m");
return;
}
/* 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, DnsResourceKey *key, bool cache_ok) {
DnsTransaction *t;
assert(scope);
assert(key);
/* Try to find an ongoing transaction that is a equal to the
* specified question */
t = hashmap_get(scope->transactions_by_key, key);
if (!t)
return NULL;
/* 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->answer_source != DNS_TRANSACTION_NETWORK)
return NULL;
return t;
}
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, 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, NULL, 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->items[i].rr);
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->items[i].rr, p->family, &p->sender);
if (r <= 0)
continue;
dns_scope_notify_conflict(scope, p->answer->items[i].rr);
}
}
void dns_scope_dump(DnsScope *s, FILE *f) {
assert(s);
if (!f)
f = stdout;
fputs("[Scope protocol=", f);
fputs(dns_protocol_to_string(s->protocol), f);
if (s->link) {
fputs(" interface=", f);
fputs(s->link->name, f);
}
if (s->family != AF_UNSPEC) {
fputs(" family=", f);
fputs(af_to_name(s->family), f);
}
fputs("]\n", f);
if (!dns_zone_is_empty(&s->zone)) {
fputs("ZONE:\n", f);
dns_zone_dump(&s->zone, f);
}
if (!dns_cache_is_empty(&s->cache)) {
fputs("CACHE:\n", f);
dns_cache_dump(&s->cache, f);
}
}
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
assert(s);
if (s->protocol != DNS_PROTOCOL_DNS)
return NULL;
if (s->link)
return s->link->search_domains;
return s->manager->search_domains;
}
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
assert(s);
if (s->protocol != DNS_PROTOCOL_DNS)
return false;
return dns_name_is_single_label(name);
}