summaryrefslogtreecommitdiff
path: root/src/resolve
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve')
-rw-r--r--src/resolve/.gitignore6
l---------src/resolve/Makefile1
-rw-r--r--src/resolve/RFCs59
-rw-r--r--src/resolve/dns-type.c332
-rw-r--r--src/resolve/dns-type.h162
-rw-r--r--src/resolve/org.freedesktop.resolve1.conf27
-rw-r--r--src/resolve/org.freedesktop.resolve1.service12
-rw-r--r--src/resolve/resolv.conf11
-rw-r--r--src/resolve/resolve-tool.c2025
-rw-r--r--src/resolve/resolved-bus.c1689
-rw-r--r--src/resolve/resolved-bus.h25
-rw-r--r--src/resolve/resolved-conf.c251
-rw-r--r--src/resolve/resolved-conf.h51
-rw-r--r--src/resolve/resolved-def.h38
-rw-r--r--src/resolve/resolved-dns-answer.c858
-rw-r--r--src/resolve/resolved-dns-answer.h147
-rw-r--r--src/resolve/resolved-dns-cache.c1064
-rw-r--r--src/resolve/resolved-dns-cache.h52
-rw-r--r--src/resolve/resolved-dns-dnssec.c2199
-rw-r--r--src/resolve/resolved-dns-dnssec.h102
-rw-r--r--src/resolve/resolved-dns-packet.c2301
-rw-r--r--src/resolve/resolved-dns-packet.h303
-rw-r--r--src/resolve/resolved-dns-query.c1117
-rw-r--r--src/resolve/resolved-dns-query.h141
-rw-r--r--src/resolve/resolved-dns-question.c468
-rw-r--r--src/resolve/resolved-dns-question.h73
-rw-r--r--src/resolve/resolved-dns-rr.c1835
-rw-r--r--src/resolve/resolved-dns-rr.h353
-rw-r--r--src/resolve/resolved-dns-scope.c1043
-rw-r--r--src/resolve/resolved-dns-scope.h114
-rw-r--r--src/resolve/resolved-dns-search-domain.c225
-rw-r--r--src/resolve/resolved-dns-search-domain.h74
-rw-r--r--src/resolve/resolved-dns-server.c796
-rw-r--r--src/resolve/resolved-dns-server.h149
-rw-r--r--src/resolve/resolved-dns-stream.c418
-rw-r--r--src/resolve/resolved-dns-stream.h81
-rw-r--r--src/resolve/resolved-dns-stub.c538
-rw-r--r--src/resolve/resolved-dns-stub.h28
-rw-r--r--src/resolve/resolved-dns-synthesize.c413
-rw-r--r--src/resolve/resolved-dns-synthesize.h30
-rw-r--r--src/resolve/resolved-dns-transaction.c3106
-rw-r--r--src/resolve/resolved-dns-transaction.h181
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c746
-rw-r--r--src/resolve/resolved-dns-trust-anchor.h43
-rw-r--r--src/resolve/resolved-dns-zone.c664
-rw-r--r--src/resolve/resolved-dns-zone.h81
-rw-r--r--src/resolve/resolved-etc-hosts.c448
-rw-r--r--src/resolve/resolved-etc-hosts.h28
-rw-r--r--src/resolve/resolved-gperf.gperf23
-rw-r--r--src/resolve/resolved-link-bus.c629
-rw-r--r--src/resolve/resolved-link-bus.h38
-rw-r--r--src/resolve/resolved-link.c1113
-rw-r--r--src/resolve/resolved-link.h119
-rw-r--r--src/resolve/resolved-llmnr.c471
-rw-r--r--src/resolve/resolved-llmnr.h32
-rw-r--r--src/resolve/resolved-manager.c1377
-rw-r--r--src/resolve/resolved-manager.h185
-rw-r--r--src/resolve/resolved-mdns.c287
-rw-r--r--src/resolve/resolved-mdns.h30
-rw-r--r--src/resolve/resolved-resolv-conf.c276
-rw-r--r--src/resolve/resolved-resolv-conf.h27
-rw-r--r--src/resolve/resolved.c120
-rw-r--r--src/resolve/resolved.conf.in21
-rw-r--r--src/resolve/test-data/_443._tcp.fedoraproject.org.pktsbin169 -> 0 bytes
-rw-r--r--src/resolve/test-data/_openpgpkey.fedoraproject.org.pktsbin986 -> 0 bytes
-rw-r--r--src/resolve/test-data/fake-caa.pktsbin196 -> 0 bytes
-rw-r--r--src/resolve/test-data/fedoraproject.org.pktsbin1483 -> 0 bytes
-rw-r--r--src/resolve/test-data/gandi.net.pktsbin1010 -> 0 bytes
-rw-r--r--src/resolve/test-data/google.com.pktsbin747 -> 0 bytes
-rw-r--r--src/resolve/test-data/kyhwana.org.pktsbin1803 -> 0 bytes
-rw-r--r--src/resolve/test-data/root.pktsbin1061 -> 0 bytes
-rw-r--r--src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pktsbin330 -> 0 bytes
-rw-r--r--src/resolve/test-data/teamits.com.pktsbin1021 -> 0 bytes
-rw-r--r--src/resolve/test-data/zbyszek@fedoraproject.org.pktsbin2533 -> 0 bytes
-rw-r--r--src/resolve/test-dns-packet.c132
-rw-r--r--src/resolve/test-dnssec-complex.c236
-rw-r--r--src/resolve/test-dnssec.c343
-rw-r--r--src/resolve/test-resolve-tables.c64
78 files changed, 0 insertions, 30431 deletions
diff --git a/src/resolve/.gitignore b/src/resolve/.gitignore
deleted file mode 100644
index f0835923b7..0000000000
--- a/src/resolve/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/resolved-gperf.c
-/resolved.conf
-/dns_type-from-name.gperf
-/dns_type-from-name.h
-/dns_type-list.txt
-/dns_type-to-name.h
diff --git a/src/resolve/Makefile b/src/resolve/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/resolve/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
deleted file mode 100644
index 09c85f9518..0000000000
--- a/src/resolve/RFCs
+++ /dev/null
@@ -1,59 +0,0 @@
-Y = Comprehensively Implemented, to the point appropriate for resolved
-D = Comprehensively Implemented, by a dependency of resolved
-! = Missing and something we might want to implement
-~ = Needs no explicit support or doesn't apply
-? = Is this relevant today?
- = We are working on this
-
-Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
-Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
-? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
-Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts — Application and Support
-~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes
-Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
-Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
-Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification
-Y https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
-Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV)
-D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
-Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6
-Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types
-Y https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
-Y https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
-Y https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
-! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways
-Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
-Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification
-~ https://tools.ietf.org/html/rfc4470 → Minimally Covering NSEC Records and DNSSEC On-line Signing
-Y https://tools.ietf.org/html/rfc4501 → Domain Name System Uniform Resource Identifiers
-Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs)
-~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System
-~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior
-Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR)
-Y https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors
-Y https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
-Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers
-Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
-Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework
-Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol
-Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements
-Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones
-Y https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification
-Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
- https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS
-! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
-Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names
- https://tools.ietf.org/html/rfc6762 → Multicast DNS
- https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery
-~ https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
-Y https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
-Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0))
-Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status
-Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC)
-Y https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
-Y https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors
-~ https://tools.ietf.org/html/rfc7719 → DNS Terminology
-
-Also relevant:
-
- https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
deleted file mode 100644
index d89ae28dcd..0000000000
--- a/src/resolve/dns-type.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Zbigniew Jędrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-
-#include "dns-type.h"
-#include "parse-util.h"
-#include "string-util.h"
-
-typedef const struct {
- uint16_t type;
- const char *name;
-} dns_type;
-
-static const struct dns_type_name *
-lookup_dns_type (register const char *str, register GPERF_LEN_TYPE len);
-
-#include "dns_type-from-name.h"
-#include "dns_type-to-name.h"
-
-int dns_type_from_string(const char *s) {
- const struct dns_type_name *sc;
-
- assert(s);
-
- sc = lookup_dns_type(s, strlen(s));
- if (sc)
- return sc->id;
-
- s = startswith_no_case(s, "TYPE");
- if (s) {
- unsigned x;
-
- if (safe_atou(s, &x) >= 0 &&
- x <= UINT16_MAX)
- return (int) x;
- }
-
- return _DNS_TYPE_INVALID;
-}
-
-bool dns_type_is_pseudo(uint16_t type) {
-
- /* Checks whether the specified type is a "pseudo-type". What
- * a "pseudo-type" precisely is, is defined only very weakly,
- * but apparently entails all RR types that are not actually
- * stored as RRs on the server and should hence also not be
- * cached. We use this list primarily to validate NSEC type
- * bitfields, and to verify what to cache. */
-
- return IN_SET(type,
- 0, /* A Pseudo RR type, according to RFC 2931 */
- DNS_TYPE_ANY,
- DNS_TYPE_AXFR,
- DNS_TYPE_IXFR,
- DNS_TYPE_OPT,
- DNS_TYPE_TSIG,
- DNS_TYPE_TKEY
- );
-}
-
-bool dns_class_is_pseudo(uint16_t class) {
- return class == DNS_TYPE_ANY;
-}
-
-bool dns_type_is_valid_query(uint16_t type) {
-
- /* The types valid as questions in packets */
-
- return !IN_SET(type,
- 0,
- DNS_TYPE_OPT,
- DNS_TYPE_TSIG,
- DNS_TYPE_TKEY,
-
- /* RRSIG are technically valid as questions, but we refuse doing explicit queries for them, as
- * they aren't really payload, but signatures for payload, and cannot be validated on their
- * own. After all they are the signatures, and have no signatures of their own validating
- * them. */
- DNS_TYPE_RRSIG);
-}
-
-bool dns_type_is_zone_transer(uint16_t type) {
-
- /* Zone transfers, either normal or incremental */
-
- return IN_SET(type,
- DNS_TYPE_AXFR,
- DNS_TYPE_IXFR);
-}
-
-bool dns_type_is_valid_rr(uint16_t type) {
-
- /* The types valid as RR in packets (but not necessarily
- * stored on servers). */
-
- return !IN_SET(type,
- DNS_TYPE_ANY,
- DNS_TYPE_AXFR,
- DNS_TYPE_IXFR);
-}
-
-bool dns_class_is_valid_rr(uint16_t class) {
- return class != DNS_CLASS_ANY;
-}
-
-bool dns_type_may_redirect(uint16_t type) {
- /* The following record types should never be redirected using
- * CNAME/DNAME RRs. See
- * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
-
- if (dns_type_is_pseudo(type))
- return false;
-
- return !IN_SET(type,
- DNS_TYPE_CNAME,
- DNS_TYPE_DNAME,
- DNS_TYPE_NSEC3,
- DNS_TYPE_NSEC,
- DNS_TYPE_RRSIG,
- DNS_TYPE_NXT,
- DNS_TYPE_SIG,
- DNS_TYPE_KEY);
-}
-
-bool dns_type_may_wildcard(uint16_t type) {
-
- /* The following records may not be expanded from wildcard RRsets */
-
- if (dns_type_is_pseudo(type))
- return false;
-
- return !IN_SET(type,
- DNS_TYPE_NSEC3,
- DNS_TYPE_SOA,
-
- /* Prohibited by https://tools.ietf.org/html/rfc4592#section-4.4 */
- DNS_TYPE_DNAME);
-}
-
-bool dns_type_apex_only(uint16_t type) {
-
- /* Returns true for all RR types that may only appear signed in a zone apex */
-
- return IN_SET(type,
- DNS_TYPE_SOA,
- DNS_TYPE_NS, /* this one can appear elsewhere, too, but not signed */
- DNS_TYPE_DNSKEY,
- DNS_TYPE_NSEC3PARAM);
-}
-
-bool dns_type_is_dnssec(uint16_t type) {
- return IN_SET(type,
- DNS_TYPE_DS,
- DNS_TYPE_DNSKEY,
- DNS_TYPE_RRSIG,
- DNS_TYPE_NSEC,
- DNS_TYPE_NSEC3,
- DNS_TYPE_NSEC3PARAM);
-}
-
-bool dns_type_is_obsolete(uint16_t type) {
- return IN_SET(type,
- /* Obsoleted by RFC 973 */
- DNS_TYPE_MD,
- DNS_TYPE_MF,
- DNS_TYPE_MAILA,
-
- /* Kinda obsoleted by RFC 2505 */
- DNS_TYPE_MB,
- DNS_TYPE_MG,
- DNS_TYPE_MR,
- DNS_TYPE_MINFO,
- DNS_TYPE_MAILB,
-
- /* RFC1127 kinda obsoleted this by recommending against its use */
- DNS_TYPE_WKS,
-
- /* Declared historical by RFC 6563 */
- DNS_TYPE_A6,
-
- /* Obsoleted by DNSSEC-bis */
- DNS_TYPE_NXT,
-
- /* RFC 1035 removed support for concepts that needed this from RFC 883 */
- DNS_TYPE_NULL);
-}
-
-bool dns_type_needs_authentication(uint16_t type) {
-
- /* Returns true for all (non-obsolete) RR types where records are not useful if they aren't
- * authenticated. I.e. everything that contains crypto keys. */
-
- return IN_SET(type,
- DNS_TYPE_CERT,
- DNS_TYPE_SSHFP,
- DNS_TYPE_IPSECKEY,
- DNS_TYPE_DS,
- DNS_TYPE_DNSKEY,
- DNS_TYPE_TLSA,
- DNS_TYPE_CDNSKEY,
- DNS_TYPE_OPENPGPKEY,
- DNS_TYPE_CAA);
-}
-
-int dns_type_to_af(uint16_t t) {
- switch (t) {
-
- case DNS_TYPE_A:
- return AF_INET;
-
- case DNS_TYPE_AAAA:
- return AF_INET6;
-
- case DNS_TYPE_ANY:
- return AF_UNSPEC;
-
- default:
- return -EINVAL;
- }
-}
-
-const char *dns_class_to_string(uint16_t class) {
-
- switch (class) {
-
- case DNS_CLASS_IN:
- return "IN";
-
- case DNS_CLASS_ANY:
- return "ANY";
- }
-
- return NULL;
-}
-
-int dns_class_from_string(const char *s) {
-
- if (!s)
- return _DNS_CLASS_INVALID;
-
- if (strcaseeq(s, "IN"))
- return DNS_CLASS_IN;
- else if (strcaseeq(s, "ANY"))
- return DNS_CLASS_ANY;
-
- return _DNS_CLASS_INVALID;
-}
-
-const char* tlsa_cert_usage_to_string(uint8_t cert_usage) {
-
- switch (cert_usage) {
-
- case 0:
- return "CA constraint";
-
- case 1:
- return "Service certificate constraint";
-
- case 2:
- return "Trust anchor assertion";
-
- case 3:
- return "Domain-issued certificate";
-
- case 4 ... 254:
- return "Unassigned";
-
- case 255:
- return "Private use";
- }
-
- return NULL; /* clang cannot count that we covered everything */
-}
-
-const char* tlsa_selector_to_string(uint8_t selector) {
- switch (selector) {
-
- case 0:
- return "Full Certificate";
-
- case 1:
- return "SubjectPublicKeyInfo";
-
- case 2 ... 254:
- return "Unassigned";
-
- case 255:
- return "Private use";
- }
-
- return NULL;
-}
-
-const char* tlsa_matching_type_to_string(uint8_t selector) {
-
- switch (selector) {
-
- case 0:
- return "No hash used";
-
- case 1:
- return "SHA-256";
-
- case 2:
- return "SHA-512";
-
- case 3 ... 254:
- return "Unassigned";
-
- case 255:
- return "Private use";
- }
-
- return NULL;
-}
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
deleted file mode 100644
index e675fe4ea3..0000000000
--- a/src/resolve/dns-type.h
+++ /dev/null
@@ -1,162 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Zbigniew Jędrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "macro.h"
-
-/* DNS record types, taken from
- * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
- */
-enum {
- /* Normal records */
- DNS_TYPE_A = 0x01,
- DNS_TYPE_NS,
- DNS_TYPE_MD,
- DNS_TYPE_MF,
- DNS_TYPE_CNAME,
- DNS_TYPE_SOA,
- DNS_TYPE_MB,
- DNS_TYPE_MG,
- DNS_TYPE_MR,
- DNS_TYPE_NULL,
- DNS_TYPE_WKS,
- DNS_TYPE_PTR,
- DNS_TYPE_HINFO,
- DNS_TYPE_MINFO,
- DNS_TYPE_MX,
- DNS_TYPE_TXT,
- DNS_TYPE_RP,
- DNS_TYPE_AFSDB,
- DNS_TYPE_X25,
- DNS_TYPE_ISDN,
- DNS_TYPE_RT,
- DNS_TYPE_NSAP,
- DNS_TYPE_NSAP_PTR,
- DNS_TYPE_SIG,
- DNS_TYPE_KEY,
- DNS_TYPE_PX,
- DNS_TYPE_GPOS,
- DNS_TYPE_AAAA,
- DNS_TYPE_LOC,
- DNS_TYPE_NXT,
- DNS_TYPE_EID,
- DNS_TYPE_NIMLOC,
- DNS_TYPE_SRV,
- DNS_TYPE_ATMA,
- DNS_TYPE_NAPTR,
- DNS_TYPE_KX,
- DNS_TYPE_CERT,
- DNS_TYPE_A6,
- DNS_TYPE_DNAME,
- DNS_TYPE_SINK,
- DNS_TYPE_OPT, /* EDNS0 option */
- DNS_TYPE_APL,
- DNS_TYPE_DS,
- DNS_TYPE_SSHFP,
- DNS_TYPE_IPSECKEY,
- DNS_TYPE_RRSIG,
- DNS_TYPE_NSEC,
- DNS_TYPE_DNSKEY,
- DNS_TYPE_DHCID,
- DNS_TYPE_NSEC3,
- DNS_TYPE_NSEC3PARAM,
- DNS_TYPE_TLSA,
-
- DNS_TYPE_HIP = 0x37,
- DNS_TYPE_NINFO,
- DNS_TYPE_RKEY,
- DNS_TYPE_TALINK,
- DNS_TYPE_CDS,
- DNS_TYPE_CDNSKEY,
- DNS_TYPE_OPENPGPKEY,
-
- DNS_TYPE_SPF = 0x63,
- DNS_TYPE_NID,
- DNS_TYPE_L32,
- DNS_TYPE_L64,
- DNS_TYPE_LP,
- DNS_TYPE_EUI48,
- DNS_TYPE_EUI64,
-
- DNS_TYPE_TKEY = 0xF9,
- DNS_TYPE_TSIG,
- DNS_TYPE_IXFR,
- DNS_TYPE_AXFR,
- DNS_TYPE_MAILB,
- DNS_TYPE_MAILA,
- DNS_TYPE_ANY,
- DNS_TYPE_URI,
- DNS_TYPE_CAA,
- DNS_TYPE_TA = 0x8000,
- DNS_TYPE_DLV,
-
- _DNS_TYPE_MAX,
- _DNS_TYPE_INVALID = -1
-};
-
-assert_cc(DNS_TYPE_SSHFP == 44);
-assert_cc(DNS_TYPE_TLSA == 52);
-assert_cc(DNS_TYPE_ANY == 255);
-
-/* DNS record classes, see RFC 1035 */
-enum {
- DNS_CLASS_IN = 0x01,
- DNS_CLASS_ANY = 0xFF,
-
- _DNS_CLASS_MAX,
- _DNS_CLASS_INVALID = -1
-};
-
-#define _DNS_CLASS_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t))
-#define _DNS_TYPE_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t))
-
-bool dns_type_is_pseudo(uint16_t type);
-bool dns_type_is_valid_query(uint16_t type);
-bool dns_type_is_valid_rr(uint16_t type);
-bool dns_type_may_redirect(uint16_t type);
-bool dns_type_is_dnssec(uint16_t type);
-bool dns_type_is_obsolete(uint16_t type);
-bool dns_type_may_wildcard(uint16_t type);
-bool dns_type_apex_only(uint16_t type);
-bool dns_type_needs_authentication(uint16_t type);
-bool dns_type_is_zone_transer(uint16_t type);
-int dns_type_to_af(uint16_t type);
-
-bool dns_class_is_pseudo(uint16_t class);
-bool dns_class_is_valid_rr(uint16_t class);
-
-/* TYPE?? follows http://tools.ietf.org/html/rfc3597#section-5 */
-const char *dns_type_to_string(int type);
-int dns_type_from_string(const char *s);
-
-const char *dns_class_to_string(uint16_t class);
-int dns_class_from_string(const char *name);
-
-/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.2 */
-const char *tlsa_cert_usage_to_string(uint8_t cert_usage);
-
-/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.3 */
-const char *tlsa_selector_to_string(uint8_t selector);
-
-/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */
-const char *tlsa_matching_type_to_string(uint8_t selector);
-
-/* https://tools.ietf.org/html/rfc6844#section-5.1 */
-#define CAA_FLAG_CRITICAL (1u << 7)
diff --git a/src/resolve/org.freedesktop.resolve1.conf b/src/resolve/org.freedesktop.resolve1.conf
deleted file mode 100644
index 25b09774e5..0000000000
--- a/src/resolve/org.freedesktop.resolve1.conf
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0"?> <!--*-nxml-*-->
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-
-<!--
- This file is part of systemd.
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
--->
-
-<busconfig>
-
- <policy user="systemd-resolve">
- <allow own="org.freedesktop.resolve1"/>
- <allow send_destination="org.freedesktop.resolve1"/>
- <allow receive_sender="org.freedesktop.resolve1"/>
- </policy>
-
- <policy context="default">
- <allow send_destination="org.freedesktop.resolve1"/>
- <allow receive_sender="org.freedesktop.resolve1"/>
- </policy>
-
-</busconfig>
diff --git a/src/resolve/org.freedesktop.resolve1.service b/src/resolve/org.freedesktop.resolve1.service
deleted file mode 100644
index 7ac5c323f0..0000000000
--- a/src/resolve/org.freedesktop.resolve1.service
+++ /dev/null
@@ -1,12 +0,0 @@
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-
-[D-BUS Service]
-Name=org.freedesktop.resolve1
-Exec=/bin/false
-User=root
-SystemdService=dbus-org.freedesktop.resolve1.service
diff --git a/src/resolve/resolv.conf b/src/resolve/resolv.conf
deleted file mode 100644
index b8034d6829..0000000000
--- a/src/resolve/resolv.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-# This is a static resolv.conf file for connecting local clients to
-# systemd-resolved via its DNS stub listener on 127.0.0.53.
-#
-# Third party programs must not access this file directly, but only through the
-# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,
-# replace this symlink by a static file or a different symlink.
-#
-# See systemd-resolved.service(8) for details about the supported modes of
-# operation for /etc/resolv.conf.
-
-nameserver 127.0.0.53
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
deleted file mode 100644
index 9d4d04220c..0000000000
--- a/src/resolve/resolve-tool.c
+++ /dev/null
@@ -1,2025 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Zbigniew Jędrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <getopt.h>
-#include <net/if.h>
-
-#include "sd-bus.h"
-#include "sd-netlink.h"
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "escape.h"
-#include "gcrypt-util.h"
-#include "in-addr-util.h"
-#include "netlink-util.h"
-#include "pager.h"
-#include "parse-util.h"
-#include "resolved-def.h"
-#include "resolved-dns-packet.h"
-#include "strv.h"
-#include "terminal-util.h"
-
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
-
-static int arg_family = AF_UNSPEC;
-static int arg_ifindex = 0;
-static uint16_t arg_type = 0;
-static uint16_t arg_class = 0;
-static bool arg_legend = true;
-static uint64_t arg_flags = 0;
-static bool arg_no_pager = false;
-
-typedef enum ServiceFamily {
- SERVICE_FAMILY_TCP,
- SERVICE_FAMILY_UDP,
- SERVICE_FAMILY_SCTP,
- _SERVICE_FAMILY_INVALID = -1,
-} ServiceFamily;
-static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP;
-
-typedef enum RawType {
- RAW_NONE,
- RAW_PAYLOAD,
- RAW_PACKET,
-} RawType;
-static RawType arg_raw = RAW_NONE;
-
-static enum {
- MODE_RESOLVE_HOST,
- MODE_RESOLVE_RECORD,
- MODE_RESOLVE_SERVICE,
- MODE_RESOLVE_OPENPGP,
- MODE_RESOLVE_TLSA,
- MODE_STATISTICS,
- MODE_RESET_STATISTICS,
- MODE_FLUSH_CACHES,
- MODE_STATUS,
-} arg_mode = MODE_RESOLVE_HOST;
-
-static ServiceFamily service_family_from_string(const char *s) {
- if (s == NULL || streq(s, "tcp"))
- return SERVICE_FAMILY_TCP;
- if (streq(s, "udp"))
- return SERVICE_FAMILY_UDP;
- if (streq(s, "sctp"))
- return SERVICE_FAMILY_SCTP;
- return _SERVICE_FAMILY_INVALID;
-}
-
-static const char* service_family_to_string(ServiceFamily service) {
- switch(service) {
- case SERVICE_FAMILY_TCP:
- return "_tcp";
- case SERVICE_FAMILY_UDP:
- return "_udp";
- case SERVICE_FAMILY_SCTP:
- return "_sctp";
- default:
- assert_not_reached("invalid service");
- }
-}
-
-static void print_source(uint64_t flags, usec_t rtt) {
- char rtt_str[FORMAT_TIMESTAMP_MAX];
-
- if (!arg_legend)
- return;
-
- if (flags == 0)
- return;
-
- fputs("\n-- Information acquired via", stdout);
-
- if (flags != 0)
- printf(" protocol%s%s%s%s%s",
- flags & SD_RESOLVED_DNS ? " DNS" :"",
- flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
- flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" : "",
- flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : "");
-
- assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
-
- printf(" in %s", rtt_str);
-
- fputc('.', stdout);
- fputc('\n', stdout);
-
- printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
-}
-
-static int resolve_host(sd_bus *bus, const char *name) {
-
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- const char *canonical = NULL;
- char ifname[IF_NAMESIZE] = "";
- unsigned c = 0;
- int r;
- uint64_t flags;
- usec_t ts;
-
- assert(name);
-
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
-
- r = sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveHostname");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags);
- if (r < 0)
- return bus_log_create_error(r);
-
- ts = now(CLOCK_MONOTONIC);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0)
- return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
-
- ts = now(CLOCK_MONOTONIC) - ts;
-
- r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
- _cleanup_free_ char *pretty = NULL;
- int ifindex, family;
- const void *a;
- size_t sz;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
- continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
- if (r < 0)
- return log_error_errno(r, "Failed to print address for %s: %m", name);
-
- printf("%*s%s %s%s%s\n",
- (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
- pretty,
- isempty(ifname) ? "" : "%", ifname);
-
- c++;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read(reply, "st", &canonical, &flags);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!streq(name, canonical))
- printf("%*s%s (%s)\n",
- (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ",
- canonical);
-
- if (c == 0) {
- log_error("%s: no addresses found", name);
- return -ESRCH;
- }
-
- print_source(flags, ts);
-
- return 0;
-}
-
-static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *pretty = NULL;
- char ifname[IF_NAMESIZE] = "";
- uint64_t flags;
- unsigned c = 0;
- usec_t ts;
- int r;
-
- assert(bus);
- assert(IN_SET(family, AF_INET, AF_INET6));
- assert(address);
-
- if (ifindex <= 0)
- ifindex = arg_ifindex;
-
- r = in_addr_ifindex_to_string(family, address, ifindex, &pretty);
- if (r < 0)
- return log_oom();
-
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname);
-
- r = sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveAddress");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "ii", ifindex, family);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family));
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "t", arg_flags);
- if (r < 0)
- return bus_log_create_error(r);
-
- ts = now(CLOCK_MONOTONIC);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
- return r;
- }
-
- ts = now(CLOCK_MONOTONIC) - ts;
-
- r = sd_bus_message_enter_container(reply, 'a', "(is)");
- if (r < 0)
- return bus_log_create_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) {
- const char *n;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(reply, "is", &ifindex, &n);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return r;
-
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- printf("%*s%*s%*s%s %s\n",
- (int) strlen(pretty), c == 0 ? pretty : "",
- isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%",
- (int) strlen(ifname), c == 0 ? ifname : "",
- c == 0 ? ":" : " ",
- n);
-
- c++;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read(reply, "t", &flags);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (c == 0) {
- log_error("%s: no names found", pretty);
- return -ESRCH;
- }
-
- print_source(flags, ts);
-
- return 0;
-}
-
-static int output_rr_packet(const void *d, size_t l, int ifindex) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- int r;
- char ifname[IF_NAMESIZE] = "";
-
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- return log_oom();
-
- p->refuse_compression = true;
-
- r = dns_packet_append_blob(p, d, l, NULL);
- if (r < 0)
- return log_oom();
-
- r = dns_packet_read_rr(p, &rr, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to parse RR: %m");
-
- if (arg_raw == RAW_PAYLOAD) {
- void *data;
- ssize_t k;
-
- k = dns_resource_record_payload(rr, &data);
- if (k < 0)
- return log_error_errno(k, "Cannot dump RR: %m");
- fwrite(data, 1, k, stdout);
- } else {
- const char *s;
-
- s = dns_resource_record_to_string(rr);
- if (!s)
- return log_oom();
-
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
- }
-
- return 0;
-}
-
-static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
- unsigned n = 0;
- uint64_t flags;
- int r;
- usec_t ts;
- bool needs_authentication = false;
-
- assert(name);
-
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname);
-
- r = sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveRecord");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags);
- if (r < 0)
- return bus_log_create_error(r);
-
- ts = now(CLOCK_MONOTONIC);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- if (warn_missing || r != -ENXIO)
- log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
- return r;
- }
-
- ts = now(CLOCK_MONOTONIC) - ts;
-
- r = sd_bus_message_enter_container(reply, 'a', "(iqqay)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
- uint16_t c, t;
- int ifindex;
- const void *d;
- size_t l;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read_array(reply, 'y', &d, &l);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (arg_raw == RAW_PACKET) {
- uint64_t u64 = htole64(l);
-
- fwrite(&u64, sizeof(u64), 1, stdout);
- fwrite(d, 1, l, stdout);
- } else {
- r = output_rr_packet(d, l, ifindex);
- if (r < 0)
- return r;
- }
-
- if (dns_type_needs_authentication(t))
- needs_authentication = true;
-
- n++;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read(reply, "t", &flags);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (n == 0) {
- if (warn_missing)
- log_error("%s: no records found", name);
- return -ESRCH;
- }
-
- print_source(flags, ts);
-
- if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) {
- fflush(stdout);
-
- fprintf(stderr, "\n%s"
- "WARNING: The resources shown contain cryptographic key data which could not be\n"
- " authenticated. It is not suitable to authenticate any communication.\n"
- " This is usually indication that DNSSEC authentication was not enabled\n"
- " or is not available for the selected protocol or DNS servers.%s\n",
- ansi_highlight_red(),
- ansi_normal());
- }
-
- return 0;
-}
-
-static int resolve_rfc4501(sd_bus *bus, const char *name) {
- uint16_t type = 0, class = 0;
- const char *p, *q, *n;
- int r;
-
- assert(bus);
- assert(name);
- assert(startswith(name, "dns:"));
-
- /* Parse RFC 4501 dns: URIs */
-
- p = name + 4;
-
- if (p[0] == '/') {
- const char *e;
-
- if (p[1] != '/')
- goto invalid;
-
- e = strchr(p + 2, '/');
- if (!e)
- goto invalid;
-
- if (e != p + 2)
- log_warning("DNS authority specification not supported; ignoring specified authority.");
-
- p = e + 1;
- }
-
- q = strchr(p, '?');
- if (q) {
- n = strndupa(p, q - p);
- q++;
-
- for (;;) {
- const char *f;
-
- f = startswith_no_case(q, "class=");
- if (f) {
- _cleanup_free_ char *t = NULL;
- const char *e;
-
- if (class != 0) {
- log_error("DNS class specified twice.");
- return -EINVAL;
- }
-
- e = strchrnul(f, ';');
- t = strndup(f, e - f);
- if (!t)
- return log_oom();
-
- r = dns_class_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS class %s.", t);
- return -EINVAL;
- }
-
- class = r;
-
- if (*e == ';') {
- q = e + 1;
- continue;
- }
-
- break;
- }
-
- f = startswith_no_case(q, "type=");
- if (f) {
- _cleanup_free_ char *t = NULL;
- const char *e;
-
- if (type != 0) {
- log_error("DNS type specified twice.");
- return -EINVAL;
- }
-
- e = strchrnul(f, ';');
- t = strndup(f, e - f);
- if (!t)
- return log_oom();
-
- r = dns_type_from_string(t);
- if (r < 0) {
- log_error("Unknown DNS type %s.", t);
- return -EINVAL;
- }
-
- type = r;
-
- if (*e == ';') {
- q = e + 1;
- continue;
- }
-
- break;
- }
-
- goto invalid;
- }
- } else
- n = p;
-
- if (class == 0)
- class = arg_class ?: DNS_CLASS_IN;
- if (type == 0)
- type = arg_type ?: DNS_TYPE_A;
-
- return resolve_record(bus, n, class, type, true);
-
-invalid:
- log_error("Invalid DNS URI: %s", name);
- return -EINVAL;
-}
-
-static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
- const char *canonical_name, *canonical_type, *canonical_domain;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char ifname[IF_NAMESIZE] = "";
- size_t indent, sz;
- uint64_t flags;
- const char *p;
- unsigned c;
- usec_t ts;
- int r;
-
- assert(bus);
- assert(domain);
-
- name = empty_to_null(name);
- type = empty_to_null(type);
-
- if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
-
- if (name)
- log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
- else if (type)
- log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
- else
- log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
-
- r = sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveService");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
- if (r < 0)
- return bus_log_create_error(r);
-
- ts = now(CLOCK_MONOTONIC);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0)
- return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
-
- ts = now(CLOCK_MONOTONIC) - ts;
-
- r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- indent =
- (name ? strlen(name) + 1 : 0) +
- (type ? strlen(type) + 1 : 0) +
- strlen(domain) + 2;
-
- c = 0;
- while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
- uint16_t priority, weight, port;
- const char *hostname, *canonical;
-
- r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (name)
- printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
- if (type)
- printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
-
- printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
- (int) strlen(domain), c == 0 ? domain : "",
- c == 0 ? ":" : " ",
- hostname, port,
- priority, weight);
-
- r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
- _cleanup_free_ char *pretty = NULL;
- int ifindex, family;
- const void *a;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(reply, "ii", &ifindex, &family);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read_array(reply, 'y', &a, &sz);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
- continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- return -EINVAL;
- }
-
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- r = in_addr_to_string(family, a, &pretty);
- if (r < 0)
- return log_error_errno(r, "Failed to print address for %s: %m", name);
-
- printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read(reply, "s", &canonical);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!streq(hostname, canonical))
- printf("%*s(%s)\n", (int) indent, "", canonical);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- c++;
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_enter_container(reply, 'a', "ay");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
- _cleanup_free_ char *escaped = NULL;
-
- escaped = cescape_length(p, sz);
- if (!escaped)
- return log_oom();
-
- printf("%*s%s\n", (int) indent, "", escaped);
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
- if (r < 0)
- return bus_log_parse_error(r);
-
- canonical_name = empty_to_null(canonical_name);
- canonical_type = empty_to_null(canonical_type);
-
- if (!streq_ptr(name, canonical_name) ||
- !streq_ptr(type, canonical_type) ||
- !streq_ptr(domain, canonical_domain)) {
-
- printf("%*s(", (int) indent, "");
-
- if (canonical_name)
- printf("%s/", canonical_name);
- if (canonical_type)
- printf("%s/", canonical_type);
-
- printf("%s)\n", canonical_domain);
- }
-
- print_source(flags, ts);
-
- return 0;
-}
-
-static int resolve_openpgp(sd_bus *bus, const char *address) {
- const char *domain, *full;
- int r;
- _cleanup_free_ char *hashed = NULL;
-
- assert(bus);
- assert(address);
-
- domain = strrchr(address, '@');
- if (!domain) {
- log_error("Address does not contain '@': \"%s\"", address);
- return -EINVAL;
- } else if (domain == address || domain[1] == '\0') {
- log_error("Address starts or ends with '@': \"%s\"", address);
- return -EINVAL;
- }
- domain++;
-
- r = string_hashsum_sha256(address, domain - 1 - address, &hashed);
- if (r < 0)
- return log_error_errno(r, "Hashing failed: %m");
-
- strshorten(hashed, 56);
-
- full = strjoina(hashed, "._openpgpkey.", domain);
- log_debug("Looking up \"%s\".", full);
-
- r = resolve_record(bus, full,
- arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_OPENPGPKEY, false);
-
- if (IN_SET(r, -ENXIO, -ESRCH)) { /* NXDOMAIN or NODATA? */
- hashed = NULL;
- r = string_hashsum_sha224(address, domain - 1 - address, &hashed);
- if (r < 0)
- return log_error_errno(r, "Hashing failed: %m");
-
- full = strjoina(hashed, "._openpgpkey.", domain);
- log_debug("Looking up \"%s\".", full);
-
- return resolve_record(bus, full,
- arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_OPENPGPKEY, true);
- }
-
- return r;
-}
-
-static int resolve_tlsa(sd_bus *bus, const char *address) {
- const char *port;
- uint16_t port_num = 443;
- _cleanup_free_ char *full = NULL;
- int r;
-
- assert(bus);
- assert(address);
-
- port = strrchr(address, ':');
- if (port) {
- r = safe_atou16(port + 1, &port_num);
- if (r < 0 || port_num == 0)
- return log_error_errno(r, "Invalid port \"%s\".", port + 1);
-
- address = strndupa(address, port - address);
- }
-
- r = asprintf(&full, "_%u.%s.%s",
- port_num,
- service_family_to_string(arg_service_family),
- address);
- if (r < 0)
- return log_oom();
-
- log_debug("Looking up \"%s\".", full);
-
- return resolve_record(bus, full,
- arg_class ?: DNS_CLASS_IN,
- arg_type ?: DNS_TYPE_TLSA, true);
-}
-
-static int show_statistics(sd_bus *bus) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- uint64_t n_current_transactions, n_total_transactions,
- cache_size, n_cache_hit, n_cache_miss,
- n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
- int r, dnssec_supported;
-
- assert(bus);
-
- r = sd_bus_get_property_trivial(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "DNSSECSupported",
- &error,
- 'b',
- &dnssec_supported);
- if (r < 0)
- return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r));
-
- printf("DNSSEC supported by current servers: %s%s%s\n\n",
- ansi_highlight(),
- yes_no(dnssec_supported),
- ansi_normal());
-
- r = sd_bus_get_property(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "TransactionStatistics",
- &error,
- &reply,
- "(tt)");
- if (r < 0)
- return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "(tt)",
- &n_current_transactions,
- &n_total_transactions);
- if (r < 0)
- return bus_log_parse_error(r);
-
- printf("%sTransactions%s\n"
- "Current Transactions: %" PRIu64 "\n"
- " Total Transactions: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- n_current_transactions,
- n_total_transactions);
-
- reply = sd_bus_message_unref(reply);
-
- r = sd_bus_get_property(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "CacheStatistics",
- &error,
- &reply,
- "(ttt)");
- if (r < 0)
- return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "(ttt)",
- &cache_size,
- &n_cache_hit,
- &n_cache_miss);
- if (r < 0)
- return bus_log_parse_error(r);
-
- printf("\n%sCache%s\n"
- " Current Cache Size: %" PRIu64 "\n"
- " Cache Hits: %" PRIu64 "\n"
- " Cache Misses: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- cache_size,
- n_cache_hit,
- n_cache_miss);
-
- reply = sd_bus_message_unref(reply);
-
- r = sd_bus_get_property(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "DNSSECStatistics",
- &error,
- &reply,
- "(tttt)");
- if (r < 0)
- return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "(tttt)",
- &n_dnssec_secure,
- &n_dnssec_insecure,
- &n_dnssec_bogus,
- &n_dnssec_indeterminate);
- if (r < 0)
- return bus_log_parse_error(r);
-
- printf("\n%sDNSSEC Verdicts%s\n"
- " Secure: %" PRIu64 "\n"
- " Insecure: %" PRIu64 "\n"
- " Bogus: %" PRIu64 "\n"
- " Indeterminate: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- n_dnssec_secure,
- n_dnssec_insecure,
- n_dnssec_bogus,
- n_dnssec_indeterminate);
-
- return 0;
-}
-
-static int reset_statistics(sd_bus *bus) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResetStatistics",
- &error,
- NULL,
- NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
-static int flush_caches(sd_bus *bus) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- r = sd_bus_call_method(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "FlushCaches",
- &error,
- NULL,
- NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
-static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char ***l = userdata;
- int r;
-
- assert(bus);
- assert(member);
- assert(m);
- assert(l);
-
- r = sd_bus_message_enter_container(m, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- const void *a;
- char *pretty;
- int family;
- size_t sz;
-
- r = sd_bus_message_enter_container(m, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(m, "i", &family);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("Unexpected family, ignoring.");
- continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
- continue;
- }
-
- r = in_addr_to_string(family, a, &pretty);
- if (r < 0)
- return r;
-
- r = strv_consume(l, pretty);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char ***l = userdata;
- int r;
-
- assert(bus);
- assert(member);
- assert(m);
- assert(l);
-
- r = sd_bus_message_enter_container(m, 'a', "(sb)");
- if (r < 0)
- return r;
-
- for (;;) {
- const char *domain;
- int route_only;
- char *pretty;
-
- r = sd_bus_message_read(m, "(sb)", &domain, &route_only);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (route_only)
- pretty = strappend("~", domain);
- else
- pretty = strdup(domain);
- if (!pretty)
- return -ENOMEM;
-
- r = strv_consume(l, pretty);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empty_line) {
-
- struct link_info {
- uint64_t scopes_mask;
- char *llmnr;
- char *mdns;
- char *dnssec;
- char **dns;
- char **domains;
- char **ntas;
- int dnssec_supported;
- } link_info = {};
-
- static const struct bus_properties_map property_map[] = {
- { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
- { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
- { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
- { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
- { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
- { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
- { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
- { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
- {}
- };
-
- _cleanup_free_ char *ifi = NULL, *p = NULL;
- char ifname[IF_NAMESIZE] = "";
- char **i;
- int r;
-
- assert(bus);
- assert(ifindex > 0);
- assert(empty_line);
-
- if (!name) {
- if (!if_indextoname(ifindex, ifname))
- return log_error_errno(errno, "Failed to resolve interface name for %i: %m", ifindex);
-
- name = ifname;
- }
-
- if (asprintf(&ifi, "%i", ifindex) < 0)
- return log_oom();
-
- r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p);
- if (r < 0)
- return log_oom();
-
- r = bus_map_all_properties(bus,
- "org.freedesktop.resolve1",
- p,
- property_map,
- &link_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get link data for %i: %m", ifindex);
- goto finish;
- }
-
- pager_open(arg_no_pager, false);
-
- if (*empty_line)
- fputc('\n', stdout);
-
- printf("%sLink %i (%s)%s\n",
- ansi_highlight(), ifindex, name, ansi_normal());
-
- if (link_info.scopes_mask == 0)
- printf(" Current Scopes: none\n");
- else
- printf(" Current Scopes:%s%s%s%s%s\n",
- link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "",
- link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
- link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
- link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
-
- printf(" LLMNR setting: %s\n"
- "MulticastDNS setting: %s\n"
- " DNSSEC setting: %s\n"
- " DNSSEC supported: %s\n",
- strna(link_info.llmnr),
- strna(link_info.mdns),
- strna(link_info.dnssec),
- yes_no(link_info.dnssec_supported));
-
- STRV_FOREACH(i, link_info.dns) {
- printf(" %s %s\n",
- i == link_info.dns ? "DNS Servers:" : " ",
- *i);
- }
-
- STRV_FOREACH(i, link_info.domains) {
- printf(" %s %s\n",
- i == link_info.domains ? "DNS Domain:" : " ",
- *i);
- }
-
- STRV_FOREACH(i, link_info.ntas) {
- printf(" %s %s\n",
- i == link_info.ntas ? "DNSSEC NTA:" : " ",
- *i);
- }
-
- *empty_line = true;
-
- r = 0;
-
-finish:
- strv_free(link_info.dns);
- strv_free(link_info.domains);
- free(link_info.llmnr);
- free(link_info.mdns);
- free(link_info.dnssec);
- strv_free(link_info.ntas);
- return r;
-}
-
-static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char ***l = userdata;
- int r;
-
- assert(bus);
- assert(member);
- assert(m);
- assert(l);
-
- r = sd_bus_message_enter_container(m, 'a', "(iiay)");
- if (r < 0)
- return r;
-
- for (;;) {
- const void *a;
- char *pretty;
- int family, ifindex;
- size_t sz;
-
- r = sd_bus_message_enter_container(m, 'r', "iiay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(m, "ii", &ifindex, &family);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_array(m, 'y', &a, &sz);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- if (ifindex != 0) /* only show the global ones here */
- continue;
-
- if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("Unexpected family, ignoring.");
- continue;
- }
-
- if (sz != FAMILY_ADDRESS_SIZE(family)) {
- log_debug("Address size mismatch, ignoring.");
- continue;
- }
-
- r = in_addr_to_string(family, a, &pretty);
- if (r < 0)
- return r;
-
- r = strv_consume(l, pretty);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
- char ***l = userdata;
- int r;
-
- assert(bus);
- assert(member);
- assert(m);
- assert(l);
-
- r = sd_bus_message_enter_container(m, 'a', "(isb)");
- if (r < 0)
- return r;
-
- for (;;) {
- const char *domain;
- int route_only, ifindex;
- char *pretty;
-
- r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (ifindex != 0) /* only show the global ones here */
- continue;
-
- if (route_only)
- pretty = strappend("~", domain);
- else
- pretty = strdup(domain);
- if (!pretty)
- return -ENOMEM;
-
- r = strv_consume(l, pretty);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int status_global(sd_bus *bus, bool *empty_line) {
-
- struct global_info {
- char **dns;
- char **domains;
- char **ntas;
- } global_info = {};
-
- static const struct bus_properties_map property_map[] = {
- { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
- { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
- { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
- {}
- };
-
- char **i;
- int r;
-
- assert(bus);
- assert(empty_line);
-
- r = bus_map_all_properties(bus,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- property_map,
- &global_info);
- if (r < 0) {
- log_error_errno(r, "Failed to get global data: %m");
- goto finish;
- }
-
- if (strv_isempty(global_info.dns) && strv_isempty(global_info.domains) && strv_isempty(global_info.ntas)) {
- r = 0;
- goto finish;
- }
-
- pager_open(arg_no_pager, false);
-
- printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
- STRV_FOREACH(i, global_info.dns) {
- printf(" %s %s\n",
- i == global_info.dns ? "DNS Servers:" : " ",
- *i);
- }
-
- STRV_FOREACH(i, global_info.domains) {
- printf(" %s %s\n",
- i == global_info.domains ? "DNS Domain:" : " ",
- *i);
- }
-
- strv_sort(global_info.ntas);
- STRV_FOREACH(i, global_info.ntas) {
- printf(" %s %s\n",
- i == global_info.ntas ? "DNSSEC NTA:" : " ",
- *i);
- }
-
- *empty_line = true;
-
- r = 0;
-
-finish:
- strv_free(global_info.dns);
- strv_free(global_info.domains);
- strv_free(global_info.ntas);
-
- return r;
-}
-
-static int status_all(sd_bus *bus) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- sd_netlink_message *i;
- bool empty_line = false;
- int r;
-
- assert(bus);
-
- r = status_global(bus, &empty_line);
- if (r < 0)
- return r;
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
- if (r < 0)
- return rtnl_log_create_error(r);
-
- r = sd_netlink_message_request_dump(req, true);
- if (r < 0)
- return rtnl_log_create_error(r);
-
- r = sd_netlink_call(rtnl, req, 0, &reply);
- if (r < 0)
- return log_error_errno(r, "Failed to enumerate links: %m");
-
- r = 0;
- for (i = reply; i; i = sd_netlink_message_next(i)) {
- const char *name;
- int ifindex, q;
- uint16_t type;
-
- q = sd_netlink_message_get_type(i, &type);
- if (q < 0)
- return rtnl_log_parse_error(q);
-
- if (type != RTM_NEWLINK)
- continue;
-
- q = sd_rtnl_message_link_get_ifindex(i, &ifindex);
- if (q < 0)
- return rtnl_log_parse_error(q);
-
- if (ifindex == LOOPBACK_IFINDEX)
- continue;
-
- q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
- if (q < 0)
- return rtnl_log_parse_error(q);
-
- q = status_ifindex(bus, ifindex, name, &empty_line);
- if (q < 0 && r >= 0)
- r = q;
- }
-
- return r;
-}
-
-static void help_protocol_types(void) {
- if (arg_legend)
- puts("Known protocol types:");
- puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6");
-}
-
-static void help_dns_types(void) {
- const char *t;
- int i;
-
- if (arg_legend)
- puts("Known DNS RR types:");
- for (i = 0; i < _DNS_TYPE_MAX; i++) {
- t = dns_type_to_string(i);
- if (t)
- puts(t);
- }
-}
-
-static void help_dns_classes(void) {
- const char *t;
- int i;
-
- if (arg_legend)
- puts("Known DNS RR classes:");
- for (i = 0; i < _DNS_CLASS_MAX; i++) {
- t = dns_class_to_string(i);
- if (t)
- puts(t);
- }
-}
-
-static void help(void) {
- printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n"
- "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n"
- "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n"
- "%1$s [OPTIONS...] --statistics\n"
- "%1$s [OPTIONS...] --reset-statistics\n"
- "\n"
- "Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " -4 Resolve IPv4 addresses\n"
- " -6 Resolve IPv6 addresses\n"
- " -i --interface=INTERFACE Look on interface\n"
- " -p --protocol=PROTO|help Look via protocol\n"
- " -t --type=TYPE|help Query RR with DNS type\n"
- " -c --class=CLASS|help Query RR with DNS class\n"
- " --service Resolve service (SRV)\n"
- " --service-address=BOOL Resolve address for services (default: yes)\n"
- " --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
- " --openpgp Query OpenPGP public key\n"
- " --tlsa Query TLS public key\n"
- " --cname=BOOL Follow CNAME redirects (default: yes)\n"
- " --search=BOOL Use search domains for single-label names\n"
- " (default: yes)\n"
- " --raw[=payload|packet] Dump the answer as binary data\n"
- " --legend=BOOL Print headers and additional info (default: yes)\n"
- " --statistics Show resolver statistics\n"
- " --reset-statistics Reset resolver statistics\n"
- " --status Show link and server status\n"
- " --flush-caches Flush all local DNS caches\n"
- , program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
- enum {
- ARG_VERSION = 0x100,
- ARG_LEGEND,
- ARG_SERVICE,
- ARG_CNAME,
- ARG_SERVICE_ADDRESS,
- ARG_SERVICE_TXT,
- ARG_OPENPGP,
- ARG_TLSA,
- ARG_RAW,
- ARG_SEARCH,
- ARG_STATISTICS,
- ARG_RESET_STATISTICS,
- ARG_STATUS,
- ARG_FLUSH_CACHES,
- ARG_NO_PAGER,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "type", required_argument, NULL, 't' },
- { "class", required_argument, NULL, 'c' },
- { "legend", required_argument, NULL, ARG_LEGEND },
- { "interface", required_argument, NULL, 'i' },
- { "protocol", required_argument, NULL, 'p' },
- { "cname", required_argument, NULL, ARG_CNAME },
- { "service", no_argument, NULL, ARG_SERVICE },
- { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
- { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
- { "openpgp", no_argument, NULL, ARG_OPENPGP },
- { "tlsa", optional_argument, NULL, ARG_TLSA },
- { "raw", optional_argument, NULL, ARG_RAW },
- { "search", required_argument, NULL, ARG_SEARCH },
- { "statistics", no_argument, NULL, ARG_STATISTICS, },
- { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
- { "status", no_argument, NULL, ARG_STATUS },
- { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- {}
- };
-
- int c, r;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
- switch(c) {
-
- case 'h':
- help();
- return 0; /* done */;
-
- case ARG_VERSION:
- return version();
-
- case '4':
- arg_family = AF_INET;
- break;
-
- case '6':
- arg_family = AF_INET6;
- break;
-
- case 'i': {
- int ifi;
-
- if (parse_ifindex(optarg, &ifi) >= 0)
- arg_ifindex = ifi;
- else {
- ifi = if_nametoindex(optarg);
- if (ifi <= 0)
- return log_error_errno(errno, "Unknown interface %s: %m", optarg);
-
- arg_ifindex = ifi;
- }
-
- break;
- }
-
- case 't':
- if (streq(optarg, "help")) {
- help_dns_types();
- return 0;
- }
-
- r = dns_type_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse RR record type %s", optarg);
- return r;
- }
- arg_type = (uint16_t) r;
- assert((int) arg_type == r);
-
- arg_mode = MODE_RESOLVE_RECORD;
- break;
-
- case 'c':
- if (streq(optarg, "help")) {
- help_dns_classes();
- return 0;
- }
-
- r = dns_class_from_string(optarg);
- if (r < 0) {
- log_error("Failed to parse RR record class %s", optarg);
- return r;
- }
- arg_class = (uint16_t) r;
- assert((int) arg_class == r);
-
- break;
-
- case ARG_LEGEND:
- r = parse_boolean(optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to parse --legend= argument");
-
- arg_legend = r;
- break;
-
- case 'p':
- if (streq(optarg, "help")) {
- help_protocol_types();
- return 0;
- } else if (streq(optarg, "dns"))
- arg_flags |= SD_RESOLVED_DNS;
- else if (streq(optarg, "llmnr"))
- arg_flags |= SD_RESOLVED_LLMNR;
- else if (streq(optarg, "llmnr-ipv4"))
- arg_flags |= SD_RESOLVED_LLMNR_IPV4;
- else if (streq(optarg, "llmnr-ipv6"))
- arg_flags |= SD_RESOLVED_LLMNR_IPV6;
- else {
- log_error("Unknown protocol specifier: %s", optarg);
- return -EINVAL;
- }
-
- break;
-
- case ARG_SERVICE:
- arg_mode = MODE_RESOLVE_SERVICE;
- break;
-
- case ARG_OPENPGP:
- arg_mode = MODE_RESOLVE_OPENPGP;
- break;
-
- case ARG_TLSA:
- arg_mode = MODE_RESOLVE_TLSA;
- arg_service_family = service_family_from_string(optarg);
- if (arg_service_family < 0) {
- log_error("Unknown service family \"%s\".", optarg);
- return -EINVAL;
- }
- break;
-
- case ARG_RAW:
- if (on_tty()) {
- log_error("Refusing to write binary data to tty.");
- return -ENOTTY;
- }
-
- if (optarg == NULL || streq(optarg, "payload"))
- arg_raw = RAW_PAYLOAD;
- else if (streq(optarg, "packet"))
- arg_raw = RAW_PACKET;
- else {
- log_error("Unknown --raw specifier \"%s\".", optarg);
- return -EINVAL;
- }
-
- arg_legend = false;
- break;
-
- case ARG_CNAME:
- r = parse_boolean(optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to parse --cname= argument.");
- SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0);
- break;
-
- case ARG_SERVICE_ADDRESS:
- r = parse_boolean(optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to parse --service-address= argument.");
- SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0);
- break;
-
- case ARG_SERVICE_TXT:
- r = parse_boolean(optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to parse --service-txt= argument.");
- SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0);
- break;
-
- case ARG_SEARCH:
- r = parse_boolean(optarg);
- if (r < 0)
- return log_error_errno(r, "Failed to parse --search argument.");
- SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0);
- break;
-
- case ARG_STATISTICS:
- arg_mode = MODE_STATISTICS;
- break;
-
- case ARG_RESET_STATISTICS:
- arg_mode = MODE_RESET_STATISTICS;
- break;
-
- case ARG_FLUSH_CACHES:
- arg_mode = MODE_FLUSH_CACHES;
- break;
-
- case ARG_STATUS:
- arg_mode = MODE_STATUS;
- break;
-
- case ARG_NO_PAGER:
- arg_no_pager = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- assert_not_reached("Unhandled option");
- }
-
- if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=.");
- return -EINVAL;
- }
-
- if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) {
- log_error("--service and --type= may not be combined.");
- return -EINVAL;
- }
-
- if (arg_type != 0 && arg_class == 0)
- arg_class = DNS_CLASS_IN;
-
- if (arg_class != 0 && arg_type == 0)
- arg_type = DNS_TYPE_A;
-
- return 1 /* work to do */;
-}
-
-int main(int argc, char **argv) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- int r;
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- r = sd_bus_open_system(&bus);
- if (r < 0) {
- log_error_errno(r, "sd_bus_open_system: %m");
- goto finish;
- }
-
- switch (arg_mode) {
-
- case MODE_RESOLVE_HOST:
- if (optind >= argc) {
- log_error("No arguments passed.");
- r = -EINVAL;
- goto finish;
- }
-
- while (argv[optind]) {
- int family, ifindex, k;
- union in_addr_union a;
-
- if (startswith(argv[optind], "dns:"))
- k = resolve_rfc4501(bus, argv[optind]);
- else {
- k = in_addr_ifindex_from_string_auto(argv[optind], &family, &a, &ifindex);
- if (k >= 0)
- k = resolve_address(bus, family, &a, ifindex);
- else
- k = resolve_host(bus, argv[optind]);
- }
-
- if (r == 0)
- r = k;
-
- optind++;
- }
- break;
-
- case MODE_RESOLVE_RECORD:
- if (optind >= argc) {
- log_error("No arguments passed.");
- r = -EINVAL;
- goto finish;
- }
-
- while (argv[optind]) {
- int k;
-
- k = resolve_record(bus, argv[optind], arg_class, arg_type, true);
- if (r == 0)
- r = k;
-
- optind++;
- }
- break;
-
- case MODE_RESOLVE_SERVICE:
- if (argc < optind + 1) {
- log_error("Domain specification required.");
- r = -EINVAL;
- goto finish;
-
- } else if (argc == optind + 1)
- r = resolve_service(bus, NULL, NULL, argv[optind]);
- else if (argc == optind + 2)
- r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
- else if (argc == optind + 3)
- r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
- else {
- log_error("Too many arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- break;
-
- case MODE_RESOLVE_OPENPGP:
- if (argc < optind + 1) {
- log_error("E-mail address required.");
- r = -EINVAL;
- goto finish;
-
- }
-
- r = 0;
- while (optind < argc) {
- int k;
-
- k = resolve_openpgp(bus, argv[optind++]);
- if (k < 0)
- r = k;
- }
- break;
-
- case MODE_RESOLVE_TLSA:
- if (argc < optind + 1) {
- log_error("Domain name required.");
- r = -EINVAL;
- goto finish;
-
- }
-
- r = 0;
- while (optind < argc) {
- int k;
-
- k = resolve_tlsa(bus, argv[optind++]);
- if (k < 0)
- r = k;
- }
- break;
-
- case MODE_STATISTICS:
- if (argc > optind) {
- log_error("Too many arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- r = show_statistics(bus);
- break;
-
- case MODE_RESET_STATISTICS:
- if (argc > optind) {
- log_error("Too many arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- r = reset_statistics(bus);
- break;
-
- case MODE_FLUSH_CACHES:
- if (argc > optind) {
- log_error("Too many arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- r = flush_caches(bus);
- break;
-
- case MODE_STATUS:
-
- if (argc > optind) {
- char **ifname;
- bool empty_line = false;
-
- r = 0;
- STRV_FOREACH(ifname, argv + optind) {
- int ifindex, q;
-
- q = parse_ifindex(argv[optind], &ifindex);
- if (q < 0) {
- ifindex = if_nametoindex(argv[optind]);
- if (ifindex <= 0) {
- log_error_errno(errno, "Failed to resolve interface name: %s", argv[optind]);
- continue;
- }
- }
-
- q = status_ifindex(bus, ifindex, NULL, &empty_line);
- if (q < 0 && r >= 0)
- r = q;
- }
- } else
- r = status_all(bus);
-
- break;
- }
-
-finish:
- pager_close();
-
- return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
deleted file mode 100644
index 2ca65e6953..0000000000
--- a/src/resolve/resolved-bus.c
+++ /dev/null
@@ -1,1689 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "bus-util.h"
-#include "dns-domain.h"
-#include "resolved-bus.h"
-#include "resolved-def.h"
-#include "resolved-dns-synthesize.h"
-#include "resolved-link-bus.h"
-
-static int reply_query_state(DnsQuery *q) {
-
- switch (q->state) {
-
- case DNS_TRANSACTION_NO_SERVERS:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
-
- case DNS_TRANSACTION_TIMEOUT:
- return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
-
- case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
- return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
-
- case DNS_TRANSACTION_INVALID_REPLY:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
-
- case DNS_TRANSACTION_ERRNO:
- return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m");
-
- case DNS_TRANSACTION_ABORTED:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
-
- case DNS_TRANSACTION_DNSSEC_FAILED:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s",
- dnssec_result_to_string(q->answer_dnssec_result));
-
- case DNS_TRANSACTION_NO_TRUST_ANCHOR:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known");
-
- case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
-
- case DNS_TRANSACTION_NETWORK_DOWN:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down");
-
- case DNS_TRANSACTION_NOT_FOUND:
- /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
- * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
- return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
-
- case DNS_TRANSACTION_RCODE_FAILURE: {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
- if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
- sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
- else {
- const char *rc, *n;
- char p[DECIMAL_STR_MAX(q->answer_rcode)];
-
- rc = dns_rcode_to_string(q->answer_rcode);
- if (!rc) {
- sprintf(p, "%i", q->answer_rcode);
- rc = p;
- }
-
- n = strjoina(_BUS_ERROR_DNS, rc);
- sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc);
- }
-
- return sd_bus_reply_method_error(q->request, &error);
- }
-
- case DNS_TRANSACTION_NULL:
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- case DNS_TRANSACTION_SUCCESS:
- default:
- assert_not_reached("Impossible state");
- }
-}
-
-static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
- int r;
-
- assert(reply);
- assert(rr);
-
- r = sd_bus_message_open_container(reply, 'r', "iiay");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "i", ifindex);
- if (r < 0)
- return r;
-
- if (rr->key->type == DNS_TYPE_A) {
- r = sd_bus_message_append(reply, "i", AF_INET);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
-
- } else if (rr->key->type == DNS_TYPE_AAAA) {
- r = sd_bus_message_append(reply, "i", AF_INET6);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
- } else
- return -EAFNOSUPPORT;
-
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static void bus_method_resolve_hostname_complete(DnsQuery *q) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *normalized = NULL;
- DnsResourceRecord *rr;
- unsigned added = 0;
- int ifindex, r;
-
- assert(q);
-
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
-
- r = dns_query_process_cname(q);
- if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
- goto finish;
- }
- if (r < 0)
- goto finish;
- if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
- return;
-
- r = sd_bus_message_new_method_return(q->request, &reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
- if (r < 0)
- goto finish;
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- DnsQuestion *question;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
-
- r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = append_address(reply, rr, ifindex);
- if (r < 0)
- goto finish;
-
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
-
- added++;
- }
-
- if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
- goto finish;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
-
- /* The key names are not necessarily normalized, make sure that they are when we return them to our bus
- * clients. */
- r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized);
- if (r < 0)
- goto finish;
-
- /* Return the precise spelling and uppercasing and CNAME target reported by the server */
- assert(canonical);
- r = sd_bus_message_append(
- reply, "st",
- normalized,
- SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
- if (r < 0)
- goto finish;
-
- r = sd_bus_send(q->manager->bus, reply, NULL);
-
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send hostname reply: %m");
- sd_bus_reply_method_errno(q->request, r, NULL);
- }
-
- dns_query_free(q);
-}
-
-static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) {
- assert(flags);
-
- if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
- if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
-
- if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
- *flags |= SD_RESOLVED_PROTOCOLS_ALL;
-
- return 0;
-}
-
-static int parse_as_address(sd_bus_message *m, int ifindex, const char *hostname, int family, uint64_t flags) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *canonical = NULL;
- union in_addr_union parsed;
- int r, ff, parsed_ifindex = 0;
-
- /* Check if the hostname is actually already an IP address formatted as string. In that case just parse it,
- * let's not attempt to look it up. */
-
- r = in_addr_ifindex_from_string_auto(hostname, &ff, &parsed, &parsed_ifindex);
- if (r < 0) /* not an address */
- return 0;
-
- if (family != AF_UNSPEC && ff != family)
- return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address is not of the requested family.");
- if (ifindex > 0 && parsed_ifindex > 0 && parsed_ifindex != ifindex)
- return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address interface index does not match requested interface.");
-
- if (parsed_ifindex > 0)
- ifindex = parsed_ifindex;
-
- r = sd_bus_message_new_method_return(m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'r', "iiay");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "ii", ifindex, ff);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_array(reply, 'y', &parsed, FAMILY_ADDRESS_SIZE(ff));
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- /* When an IP address is specified we just return it as canonical name, in order to avoid a DNS
- * look-up. However, we reformat it to make sure it's in a truly canonical form (i.e. on IPv6 the inner
- * omissions are always done the same way). */
- r = in_addr_ifindex_to_string(ff, &parsed, ifindex, &canonical);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "st", canonical,
- SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(flags), ff, true));
- if (r < 0)
- return r;
-
- return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL);
-}
-
-static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
- Manager *m = userdata;
- const char *hostname;
- int family, ifindex;
- uint64_t flags;
- DnsQuery *q;
- int r;
-
- assert(message);
- assert(m);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error);
- if (r < 0)
- return r;
-
- r = parse_as_address(message, ifindex, hostname, family, flags);
- if (r != 0)
- return r;
-
- r = dns_name_is_valid(hostname);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
-
- r = dns_question_new_address(&question_utf8, family, hostname, false);
- if (r < 0)
- return r;
-
- r = dns_question_new_address(&question_idna, family, hostname, true);
- if (r < 0)
- return r;
-
- r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags);
- if (r < 0)
- return r;
-
- q->request = sd_bus_message_ref(message);
- q->request_family = family;
- q->complete = bus_method_resolve_hostname_complete;
- q->suppress_unroutable_family = family == AF_UNSPEC;
-
- r = dns_query_bus_track(q, message);
- if (r < 0)
- goto fail;
-
- r = dns_query_go(q);
- if (r < 0)
- goto fail;
-
- return 1;
-
-fail:
- dns_query_free(q);
- return r;
-}
-
-static void bus_method_resolve_address_complete(DnsQuery *q) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- DnsQuestion *question;
- DnsResourceRecord *rr;
- unsigned added = 0;
- int ifindex, r;
-
- assert(q);
-
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
-
- r = dns_query_process_cname(q);
- if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
- goto finish;
- }
- if (r < 0)
- goto finish;
- if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
- return;
-
- r = sd_bus_message_new_method_return(q->request, &reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(is)");
- if (r < 0)
- goto finish;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- _cleanup_free_ char *normalized = NULL;
-
- r = dns_question_matches_rr(question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = dns_name_normalize(rr->ptr.name, &normalized);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(reply, "(is)", ifindex, normalized);
- if (r < 0)
- goto finish;
-
- added++;
- }
-
- if (added <= 0) {
- _cleanup_free_ char *ip = NULL;
-
- (void) in_addr_to_string(q->request_family, &q->request_address, &ip);
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR,
- "Address '%s' does not have any RR of requested type", strnull(ip));
- goto finish;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
- if (r < 0)
- goto finish;
-
- r = sd_bus_send(q->manager->bus, reply, NULL);
-
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send address reply: %m");
- sd_bus_reply_method_errno(q->request, r, NULL);
- }
-
- dns_query_free(q);
-}
-
-static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- Manager *m = userdata;
- int family, ifindex;
- uint64_t flags;
- const void *d;
- DnsQuery *q;
- size_t sz;
- int r;
-
- assert(message);
- assert(m);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(message, "ii", &ifindex, &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
-
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- r = sd_bus_message_read(message, "t", &flags);
- if (r < 0)
- return r;
-
- r = check_ifindex_flags(ifindex, &flags, 0, error);
- if (r < 0)
- return r;
-
- r = dns_question_new_reverse(&question, family, d);
- if (r < 0)
- return r;
-
- r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
- if (r < 0)
- return r;
-
- q->request = sd_bus_message_ref(message);
- q->request_family = family;
- memcpy(&q->request_address, d, sz);
- q->complete = bus_method_resolve_address_complete;
-
- r = dns_query_bus_track(q, message);
- if (r < 0)
- goto fail;
-
- r = dns_query_go(q);
- if (r < 0)
- goto fail;
-
- return 1;
-
-fail:
- dns_query_free(q);
- return r;
-}
-
-static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) {
- int r;
-
- assert(m);
- assert(rr);
-
- r = sd_bus_message_open_container(m, 'r', "iqqay");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "iqq",
- ifindex,
- rr->key->class,
- rr->key->type);
- if (r < 0)
- return r;
-
- r = dns_resource_record_to_wire_format(rr, false);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_array(m, 'y', rr->wire_format, rr->wire_format_size);
- if (r < 0)
- return r;
-
- return sd_bus_message_close_container(m);
-}
-
-static void bus_method_resolve_record_complete(DnsQuery *q) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- DnsResourceRecord *rr;
- DnsQuestion *question;
- unsigned added = 0;
- int ifindex;
- int r;
-
- assert(q);
-
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
-
- r = dns_query_process_cname(q);
- if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
- goto finish;
- }
- if (r < 0)
- goto finish;
- if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
- return;
-
- r = sd_bus_message_new_method_return(q->request, &reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(iqqay)");
- if (r < 0)
- goto finish;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = bus_message_append_rr(reply, rr, ifindex);
- if (r < 0)
- goto finish;
-
- added++;
- }
-
- if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q));
- goto finish;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
- if (r < 0)
- goto finish;
-
- r = sd_bus_send(q->manager->bus, reply, NULL);
-
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send record reply: %m");
- sd_bus_reply_method_errno(q->request, r, NULL);
- }
-
- dns_query_free(q);
-}
-
-static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- Manager *m = userdata;
- uint16_t class, type;
- const char *name;
- int r, ifindex;
- uint64_t flags;
- DnsQuery *q;
-
- assert(message);
- assert(m);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags);
- if (r < 0)
- return r;
-
- r = dns_name_is_valid(name);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
-
- if (!dns_type_is_valid_query(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
- if (dns_type_is_zone_transer(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
- if (dns_type_is_obsolete(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
-
- r = check_ifindex_flags(ifindex, &flags, 0, error);
- if (r < 0)
- return r;
-
- question = dns_question_new(1);
- if (!question)
- return -ENOMEM;
-
- key = dns_resource_key_new(class, type, name);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
-
- r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
- if (r < 0)
- return r;
-
- /* Let's request that the TTL is fixed up for locally cached entries, after all we return it in the wire format
- * blob */
- q->clamp_ttl = true;
-
- q->request = sd_bus_message_ref(message);
- q->complete = bus_method_resolve_record_complete;
-
- r = dns_query_bus_track(q, message);
- if (r < 0)
- goto fail;
-
- r = dns_query_go(q);
- if (r < 0)
- goto fail;
-
- return 1;
-
-fail:
- dns_query_free(q);
- return r;
-}
-
-static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
- _cleanup_free_ char *normalized = NULL;
- DnsQuery *aux;
- int r;
-
- assert(q);
- assert(reply);
- assert(rr);
- assert(rr->key);
-
- if (rr->key->type != DNS_TYPE_SRV)
- return 0;
-
- if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
- /* First, let's see if we could find an appropriate A or AAAA
- * record for the SRV record */
- LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
- DnsResourceRecord *zz;
- DnsQuestion *question;
-
- if (aux->state != DNS_TRANSACTION_SUCCESS)
- continue;
- if (aux->auxiliary_result != 0)
- continue;
-
- question = dns_query_question_for_protocol(aux, aux->answer_protocol);
-
- r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- DNS_ANSWER_FOREACH(zz, aux->answer) {
-
- r = dns_question_matches_rr(question, zz, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- canonical = dns_resource_record_ref(zz);
- break;
- }
-
- if (canonical)
- break;
- }
-
- /* Is there are successful A/AAAA lookup for this SRV RR? If not, don't add it */
- if (!canonical)
- return 0;
- }
-
- r = sd_bus_message_open_container(reply, 'r', "qqqsa(iiay)s");
- if (r < 0)
- return r;
-
- r = dns_name_normalize(rr->srv.name, &normalized);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(
- reply,
- "qqqs",
- rr->srv.priority, rr->srv.weight, rr->srv.port, normalized);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
- if (r < 0)
- return r;
-
- if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
- LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
- DnsResourceRecord *zz;
- DnsQuestion *question;
- int ifindex;
-
- if (aux->state != DNS_TRANSACTION_SUCCESS)
- continue;
- if (aux->auxiliary_result != 0)
- continue;
-
- question = dns_query_question_for_protocol(aux, aux->answer_protocol);
-
- r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
-
- r = dns_question_matches_rr(question, zz, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = append_address(reply, zz, ifindex);
- if (r < 0)
- return r;
- }
- }
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- if (canonical) {
- normalized = mfree(normalized);
-
- r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized);
- if (r < 0)
- return r;
- }
-
- /* Note that above we appended the hostname as encoded in the
- * SRV, and here the canonical hostname this maps to. */
- r = sd_bus_message_append(reply, "s", normalized);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) {
- DnsTxtItem *i;
- int r;
-
- assert(reply);
- assert(rr);
- assert(rr->key);
-
- if (rr->key->type != DNS_TYPE_TXT)
- return 0;
-
- LIST_FOREACH(items, i, rr->txt.items) {
-
- if (i->length <= 0)
- continue;
-
- r = sd_bus_message_append_array(reply, 'y', i->data, i->length);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-static void resolve_service_all_complete(DnsQuery *q) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
- DnsQuestion *question;
- DnsResourceRecord *rr;
- unsigned added = 0;
- DnsQuery *aux;
- int r;
-
- assert(q);
-
- if (q->block_all_complete > 0)
- return;
-
- if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
- DnsQuery *bad = NULL;
- bool have_success = false;
-
- LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
-
- switch (aux->state) {
-
- case DNS_TRANSACTION_PENDING:
- /* If an auxiliary query is still pending, let's wait */
- return;
-
- case DNS_TRANSACTION_SUCCESS:
- if (aux->auxiliary_result == 0)
- have_success = true;
- else
- bad = aux;
- break;
-
- default:
- bad = aux;
- break;
- }
- }
-
- if (!have_success) {
- /* We can only return one error, hence pick the last error we encountered */
-
- assert(bad);
-
- if (bad->state == DNS_TRANSACTION_SUCCESS) {
- assert(bad->auxiliary_result != 0);
-
- if (bad->auxiliary_result == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad));
- goto finish;
- }
-
- r = bad->auxiliary_result;
- goto finish;
- }
-
- r = reply_query_state(bad);
- goto finish;
- }
- }
-
- r = sd_bus_message_new_method_return(q->request, &reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "(qqqsa(iiay)s)");
- if (r < 0)
- goto finish;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
- DNS_ANSWER_FOREACH(rr, q->answer) {
- r = dns_question_matches_rr(question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = append_srv(q, reply, rr);
- if (r < 0)
- goto finish;
- if (r == 0) /* not an SRV record */
- continue;
-
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
-
- added++;
- }
-
- if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
- goto finish;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'a', "ay");
- if (r < 0)
- goto finish;
-
- DNS_ANSWER_FOREACH(rr, q->answer) {
- r = dns_question_matches_rr(question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = append_txt(reply, rr);
- if (r < 0)
- goto finish;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- goto finish;
-
- assert(canonical);
- r = dns_service_split(dns_resource_key_name(canonical->key), &name, &type, &domain);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(
- reply,
- "ssst",
- name, type, domain,
- SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
- if (r < 0)
- goto finish;
-
- r = sd_bus_send(q->manager->bus, reply, NULL);
-
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send service reply: %m");
- sd_bus_reply_method_errno(q->request, r, NULL);
- }
-
- dns_query_free(q);
-}
-
-static void resolve_service_hostname_complete(DnsQuery *q) {
- int r;
-
- assert(q);
- assert(q->auxiliary_for);
-
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- resolve_service_all_complete(q->auxiliary_for);
- return;
- }
-
- r = dns_query_process_cname(q);
- if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
- return;
-
- /* This auxiliary lookup is finished or failed, let's see if all are finished now. */
- q->auxiliary_result = r;
- resolve_service_all_complete(q->auxiliary_for);
-}
-
-static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifindex) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- DnsQuery *aux;
- int r;
-
- assert(q);
- assert(rr);
- assert(rr->key);
- assert(rr->key->type == DNS_TYPE_SRV);
-
- /* OK, we found an SRV record for the service. Let's resolve
- * the hostname included in it */
-
- r = dns_question_new_address(&question, q->request_family, rr->srv.name, false);
- if (r < 0)
- return r;
-
- r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
- if (r < 0)
- return r;
-
- aux->request_family = q->request_family;
- aux->complete = resolve_service_hostname_complete;
-
- r = dns_query_make_auxiliary(aux, q);
- if (r == -EAGAIN) {
- /* Too many auxiliary lookups? If so, don't complain,
- * let's just not add this one, we already have more
- * than enough */
-
- dns_query_free(aux);
- return 0;
- }
- if (r < 0)
- goto fail;
-
- /* Note that auxiliary queries do not track the original bus
- * client, only the primary request does that. */
-
- r = dns_query_go(aux);
- if (r < 0)
- goto fail;
-
- return 1;
-
-fail:
- dns_query_free(aux);
- return r;
-}
-
-static void bus_method_resolve_service_complete(DnsQuery *q) {
- bool has_root_domain = false;
- DnsResourceRecord *rr;
- DnsQuestion *question;
- unsigned found = 0;
- int ifindex, r;
-
- assert(q);
-
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
-
- r = dns_query_process_cname(q);
- if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
- goto finish;
- }
- if (r < 0)
- goto finish;
- if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
- return;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- if (rr->key->type != DNS_TYPE_SRV)
- continue;
-
- if (dns_name_is_root(rr->srv.name)) {
- has_root_domain = true;
- continue;
- }
-
- if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
- q->block_all_complete++;
- r = resolve_service_hostname(q, rr, ifindex);
- q->block_all_complete--;
-
- if (r < 0)
- goto finish;
- }
-
- found++;
- }
-
- if (has_root_domain && found <= 0) {
- /* If there's exactly one SRV RR and it uses
- * the root domain as host name, then the
- * service is explicitly not offered on the
- * domain. Report this as a recognizable
- * error. See RFC 2782, Section "Usage
- * Rules". */
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q));
- goto finish;
- }
-
- if (found <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
- goto finish;
- }
-
- /* Maybe we are already finished? check now... */
- resolve_service_all_complete(q);
- return;
-
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send service reply: %m");
- sd_bus_reply_method_errno(q->request, r, NULL);
- }
-
- dns_query_free(q);
-}
-
-static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
- const char *name, *type, *domain;
- Manager *m = userdata;
- int family, ifindex;
- uint64_t flags;
- DnsQuery *q;
- int r;
-
- assert(message);
- assert(m);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_read(message, "isssit", &ifindex, &name, &type, &domain, &family, &flags);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- if (isempty(name))
- name = NULL;
- else if (!dns_service_name_is_valid(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
-
- if (isempty(type))
- type = NULL;
- else if (!dns_srv_type_is_valid(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
-
- r = dns_name_is_valid(domain);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
-
- if (name && !type)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
-
- r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
- if (r < 0)
- return r;
-
- r = dns_question_new_service(&question_utf8, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), false);
- if (r < 0)
- return r;
-
- r = dns_question_new_service(&question_idna, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), true);
- if (r < 0)
- return r;
-
- r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH);
- if (r < 0)
- return r;
-
- q->request = sd_bus_message_ref(message);
- q->request_family = family;
- q->complete = bus_method_resolve_service_complete;
-
- r = dns_query_bus_track(q, message);
- if (r < 0)
- goto fail;
-
- r = dns_query_go(q);
- if (r < 0)
- goto fail;
-
- return 1;
-
-fail:
- dns_query_free(q);
- return r;
-}
-
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
- int r;
-
- assert(reply);
- assert(s);
-
- r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
- if (r < 0)
- return r;
-
- if (with_ifindex) {
- r = sd_bus_message_append(reply, "i", dns_server_ifindex(s));
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_append(reply, "i", s->family);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family));
- if (r < 0)
- return r;
-
- return sd_bus_message_close_container(reply);
-}
-
-static int bus_property_get_dns_servers(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
- unsigned c = 0;
- DnsServer *s;
- Iterator i;
- Link *l;
- int r;
-
- assert(reply);
- assert(m);
-
- r = sd_bus_message_open_container(reply, 'a', "(iiay)");
- if (r < 0)
- return r;
-
- LIST_FOREACH(servers, s, m->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
- if (r < 0)
- return r;
-
- c++;
- }
-
- HASHMAP_FOREACH(l, m->links, i) {
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, true);
- if (r < 0)
- return r;
- c++;
- }
- }
-
- if (c == 0) {
- LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = bus_dns_server_append(reply, s, true);
- if (r < 0)
- return r;
- }
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int bus_property_get_domains(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
- DnsSearchDomain *d;
- Iterator i;
- Link *l;
- int r;
-
- assert(reply);
- assert(m);
-
- r = sd_bus_message_open_container(reply, 'a', "(isb)");
- if (r < 0)
- return r;
-
- LIST_FOREACH(domains, d, m->search_domains) {
- r = sd_bus_message_append(reply, "(isb)", 0, d->name, d->route_only);
- if (r < 0)
- return r;
- }
-
- HASHMAP_FOREACH(l, m->links, i) {
- LIST_FOREACH(domains, d, l->search_domains) {
- r = sd_bus_message_append(reply, "(isb)", l->ifindex, d->name, d->route_only);
- if (r < 0)
- return r;
- }
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int bus_property_get_transaction_statistics(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "(tt)",
- (uint64_t) hashmap_size(m->dns_transactions),
- (uint64_t) m->n_transactions_total);
-}
-
-static int bus_property_get_cache_statistics(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- uint64_t size = 0, hit = 0, miss = 0;
- Manager *m = userdata;
- DnsScope *s;
-
- assert(reply);
- assert(m);
-
- LIST_FOREACH(scopes, s, m->dns_scopes) {
- size += dns_cache_size(&s->cache);
- hit += s->cache.n_hit;
- miss += s->cache.n_miss;
- }
-
- return sd_bus_message_append(reply, "(ttt)", size, hit, miss);
-}
-
-static int bus_property_get_dnssec_statistics(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "(tttt)",
- (uint64_t) m->n_dnssec_verdict[DNSSEC_SECURE],
- (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE],
- (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS],
- (uint64_t) m->n_dnssec_verdict[DNSSEC_INDETERMINATE]);
-}
-
-static int bus_property_get_dnssec_supported(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "b", manager_dnssec_supported(m));
-}
-
-static int bus_property_get_ntas(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
- const char *domain;
- Iterator i;
- int r;
-
- assert(reply);
- assert(m);
-
- r = sd_bus_message_open_container(reply, 'a', "s");
- if (r < 0)
- return r;
-
- SET_FOREACH(domain, m->trust_anchor.negative_by_name, i) {
- r = sd_bus_message_append(reply, "s", domain);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- DnsScope *s;
-
- assert(message);
- assert(m);
-
- LIST_FOREACH(scopes, s, m->dns_scopes)
- s->cache.n_hit = s->cache.n_miss = 0;
-
- m->n_transactions_total = 0;
- zero(m->n_dnssec_verdict);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
- Link *l;
-
- assert(m);
- assert(ret);
-
- if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
- l = hashmap_get(m->links, INT_TO_PTR(ifindex));
- if (!l)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
-
- *ret = l;
- return 0;
-}
-
-static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
- int ifindex, r;
- Link *l;
-
- assert(m);
- assert(message);
- assert(handler);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
- if (r < 0)
- return r;
-
- r = get_any_link(m, ifindex, &l, error);
- if (r < 0)
- return r;
-
- return handler(message, l, error);
-}
-
-static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
-}
-
-static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_domains, error);
-}
-
-static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
-}
-
-static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_mdns, error);
-}
-
-static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_dnssec, error);
-}
-
-static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error);
-}
-
-static int bus_method_revert_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return call_link_method(userdata, message, bus_link_method_revert, error);
-}
-
-static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *p = NULL;
- Manager *m = userdata;
- int r, ifindex;
- Link *l;
-
- assert(message);
- assert(m);
-
- assert_cc(sizeof(int) == sizeof(int32_t));
- r = sd_bus_message_read(message, "i", &ifindex);
- if (r < 0)
- return r;
-
- r = get_any_link(m, ifindex, &l, error);
- if (r < 0)
- return r;
-
- p = link_bus_path(l);
- if (!p)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", p);
-}
-
-static int bus_method_flush_caches(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
-
- assert(message);
- assert(m);
-
- manager_flush_caches(m);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-static const sd_bus_vtable resolve_vtable[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
- SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0),
- SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0),
- SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
- SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
- SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
- SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
- SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
-
- SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
- SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, 0),
- SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
- SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0),
- SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
- SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
- SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0),
- SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0),
- SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0),
-
- SD_BUS_VTABLE_END,
-};
-
-static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
- Manager *m = userdata;
-
- assert(s);
- assert(m);
-
- m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
-
- manager_connect_bus(m);
- return 0;
-}
-
-static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
- Manager *m = userdata;
- int b, r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "b", &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
- return 0;
- }
-
- if (b)
- return 0;
-
- log_debug("Coming back from suspend, verifying all RRs...");
-
- manager_verify_all(m);
- return 0;
-}
-
-int manager_connect_bus(Manager *m) {
- int r;
-
- assert(m);
-
- if (m->bus)
- return 0;
-
- r = sd_bus_default_system(&m->bus);
- if (r < 0) {
- /* We failed to connect? Yuck, we must be in early
- * boot. Let's try in 5s again. As soon as we have
- * kdbus we can stop doing this... */
-
- log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
-
- r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
- if (r < 0)
- return log_error_errno(r, "Failed to install bus reconnect time event: %m");
-
- (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry");
- return 0;
- }
-
- r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
- if (r < 0)
- return log_error_errno(r, "Failed to register object: %m");
-
- r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m);
- if (r < 0)
- return log_error_errno(r, "Failed to register link objects: %m");
-
- r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m);
- if (r < 0)
- return log_error_errno(r, "Failed to register link enumerator: %m");
-
- r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
- if (r < 0)
- return log_error_errno(r, "Failed to register name: %m");
-
- r = sd_bus_attach_event(m->bus, m->event, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop: %m");
-
- r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
- "type='signal',"
- "sender='org.freedesktop.login1',"
- "interface='org.freedesktop.login1.Manager',"
- "member='PrepareForSleep',"
- "path='/org/freedesktop/login1'",
- match_prepare_for_sleep,
- m);
- if (r < 0)
- log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
-
- return 0;
-}
diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h
deleted file mode 100644
index f49e1337d2..0000000000
--- a/src/resolve/resolved-bus.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-
-int manager_connect_bus(Manager *m);
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
deleted file mode 100644
index abf3263178..0000000000
--- a/src/resolve/resolved-conf.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include "alloc-util.h"
-#include "conf-parser.h"
-#include "def.h"
-#include "extract-word.h"
-#include "parse-util.h"
-#include "resolved-conf.h"
-#include "string-table.h"
-#include "string-util.h"
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode, "Failed to parse DNS stub listener mode setting");
-
-static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MAX] = {
- [DNS_STUB_LISTENER_NO] = "no",
- [DNS_STUB_LISTENER_UDP] = "udp",
- [DNS_STUB_LISTENER_TCP] = "tcp",
- [DNS_STUB_LISTENER_YES] = "yes",
-};
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
-
-int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
- union in_addr_union address;
- int family, r, ifindex = 0;
- DnsServer *s;
-
- assert(m);
- assert(word);
-
- r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex);
- if (r < 0)
- return r;
-
- /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
- if (!dns_server_address_valid(family, &address))
- return 0;
-
- /* Filter out duplicates */
- s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
- if (s) {
- /*
- * Drop the marker. This is used to find the servers
- * that ceased to exist, see
- * manager_mark_dns_servers() and
- * manager_flush_marked_dns_servers().
- */
- dns_server_move_back_and_unmark(s);
- return 0;
- }
-
- return dns_server_new(m, NULL, type, NULL, family, &address, ifindex);
-}
-
-int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
- int r;
-
- assert(m);
- assert(string);
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&string, &word, NULL, 0);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = manager_add_dns_server_by_string(m, type, word);
- if (r < 0)
- log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word);
- }
-
- return 0;
-}
-
-int manager_add_search_domain_by_string(Manager *m, const char *domain) {
- DnsSearchDomain *d;
- bool route_only;
- int r;
-
- assert(m);
- assert(domain);
-
- route_only = *domain == '~';
- if (route_only)
- domain++;
-
- if (dns_name_is_root(domain) || streq(domain, "*")) {
- route_only = true;
- domain = ".";
- }
-
- r = dns_search_domain_find(m->search_domains, domain, &d);
- if (r < 0)
- return r;
- if (r > 0)
- dns_search_domain_move_back_and_unmark(d);
- else {
- r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
- if (r < 0)
- return r;
- }
-
- d->route_only = route_only;
- return 0;
-}
-
-int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
- int r;
-
- assert(m);
- assert(string);
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = manager_add_search_domain_by_string(m, word);
- if (r < 0)
- log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word);
- }
-
- return 0;
-}
-
-int config_parse_dns_servers(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Manager *m = userdata;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(m);
-
- if (isempty(rvalue))
- /* Empty assignment means clear the list */
- dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
- else {
- /* Otherwise, add to the list */
- r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
- return 0;
- }
- }
-
- /* If we have a manual setting, then we stop reading
- * /etc/resolv.conf */
- if (ltype == DNS_SERVER_SYSTEM)
- m->read_resolv_conf = false;
- if (ltype == DNS_SERVER_FALLBACK)
- m->need_builtin_fallbacks = false;
-
- return 0;
-}
-
-int config_parse_search_domains(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Manager *m = userdata;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(m);
-
- if (isempty(rvalue))
- /* Empty assignment means clear the list */
- dns_search_domain_unlink_all(m->search_domains);
- else {
- /* Otherwise, add to the list */
- r = manager_parse_search_domains_and_warn(m, rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
- return 0;
- }
- }
-
- /* If we have a manual setting, then we stop reading
- * /etc/resolv.conf */
- m->read_resolv_conf = false;
-
- return 0;
-}
-
-int manager_parse_config_file(Manager *m) {
- int r;
-
- assert(m);
-
- r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf",
- CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
- "Resolve\0",
- config_item_perf_lookup, resolved_gperf_lookup,
- false, m);
- if (r < 0)
- return r;
-
- if (m->need_builtin_fallbacks) {
- r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
- if (r < 0)
- return r;
- }
-
- return 0;
-
-}
diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h
deleted file mode 100644
index 8184d6cadf..0000000000
--- a/src/resolve/resolved-conf.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef enum DnsStubListenerMode DnsStubListenerMode;
-
-enum DnsStubListenerMode {
- DNS_STUB_LISTENER_NO,
- DNS_STUB_LISTENER_UDP,
- DNS_STUB_LISTENER_TCP,
- DNS_STUB_LISTENER_YES,
- _DNS_STUB_LISTENER_MODE_MAX,
- _DNS_STUB_LISTENER_MODE_INVALID = -1
-};
-
-#include "resolved-manager.h"
-#include "resolved-dns-server.h"
-
-int manager_parse_config_file(Manager *m);
-
-int manager_add_search_domain_by_string(Manager *m, const char *domain);
-int manager_parse_search_domains_and_warn(Manager *m, const char *string);
-
-int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
-int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
-
-const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
-
-int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_dns_stub_listener_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-const char* dns_stub_listener_mode_to_string(DnsStubListenerMode p) _const_;
-DnsStubListenerMode dns_stub_listener_mode_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h
deleted file mode 100644
index c4c1915b18..0000000000
--- a/src/resolve/resolved-def.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <inttypes.h>
-
-#define SD_RESOLVED_DNS (UINT64_C(1) << 0)
-#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1)
-#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2)
-#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3)
-#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4)
-#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5)
-#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6)
-#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7)
-#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8)
-#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
-
-#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
-#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
-
-#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
deleted file mode 100644
index ab85754bf7..0000000000
--- a/src/resolve/resolved-dns-answer.c
+++ /dev/null
@@ -1,858 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "resolved-dns-answer.h"
-#include "resolved-dns-dnssec.h"
-#include "string-util.h"
-
-DnsAnswer *dns_answer_new(unsigned n) {
- DnsAnswer *a;
-
- a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
- if (!a)
- return NULL;
-
- a->n_ref = 1;
- a->n_allocated = n;
-
- return a;
-}
-
-DnsAnswer *dns_answer_ref(DnsAnswer *a) {
- if (!a)
- return NULL;
-
- assert(a->n_ref > 0);
- a->n_ref++;
- return a;
-}
-
-static void dns_answer_flush(DnsAnswer *a) {
- DnsResourceRecord *rr;
-
- if (!a)
- return;
-
- DNS_ANSWER_FOREACH(rr, a)
- dns_resource_record_unref(rr);
-
- a->n_rrs = 0;
-}
-
-DnsAnswer *dns_answer_unref(DnsAnswer *a) {
- if (!a)
- return NULL;
-
- assert(a->n_ref > 0);
-
- if (a->n_ref == 1) {
- dns_answer_flush(a);
- free(a);
- } else
- a->n_ref--;
-
- return NULL;
-}
-
-static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
- assert(rr);
-
- if (!a)
- return -ENOSPC;
-
- if (a->n_rrs >= a->n_allocated)
- return -ENOSPC;
-
- a->items[a->n_rrs++] = (DnsAnswerItem) {
- .rr = dns_resource_record_ref(rr),
- .ifindex = ifindex,
- .flags = flags,
- };
-
- return 1;
-}
-
-static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
- DnsResourceRecord *rr;
- DnsAnswerFlags flags;
- int ifindex, r;
-
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
- r = dns_answer_add_raw(a, rr, ifindex, flags);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
- unsigned i;
- int r;
-
- assert(rr);
-
- if (!a)
- return -ENOSPC;
- if (a->n_ref > 1)
- return -EBUSY;
-
- for (i = 0; i < a->n_rrs; i++) {
- if (a->items[i].ifindex != ifindex)
- continue;
-
- r = dns_resource_record_equal(a->items[i].rr, rr);
- if (r < 0)
- return r;
- if (r > 0) {
- /* Don't mix contradicting TTLs (see below) */
- if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
- return -EINVAL;
-
- /* Entry already exists, keep the entry with
- * the higher RR. */
- if (rr->ttl > a->items[i].rr->ttl) {
- dns_resource_record_ref(rr);
- dns_resource_record_unref(a->items[i].rr);
- a->items[i].rr = rr;
- }
-
- a->items[i].flags |= flags;
- return 0;
- }
-
- r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
- if (r < 0)
- return r;
- if (r > 0) {
- /* There's already an RR of the same RRset in
- * place! Let's see if the TTLs more or less
- * match. We don't really care if they match
- * precisely, but we do care whether one is 0
- * and the other is not. See RFC 2181, Section
- * 5.2.*/
-
- if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
- return -EINVAL;
- }
- }
-
- return dns_answer_add_raw(a, rr, ifindex, flags);
-}
-
-static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
- DnsResourceRecord *rr;
- DnsAnswerFlags flags;
- int ifindex, r;
-
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
- r = dns_answer_add(a, rr, ifindex, flags);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
- int r;
-
- assert(a);
- assert(rr);
-
- r = dns_answer_reserve_or_clone(a, 1);
- if (r < 0)
- return r;
-
- return dns_answer_add(*a, rr, ifindex, flags);
-}
-
-int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
-
- soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
- if (!soa)
- return -ENOMEM;
-
- soa->ttl = ttl;
-
- soa->soa.mname = strdup(name);
- if (!soa->soa.mname)
- return -ENOMEM;
-
- soa->soa.rname = strappend("root.", name);
- if (!soa->soa.rname)
- return -ENOMEM;
-
- soa->soa.serial = 1;
- soa->soa.refresh = 1;
- soa->soa.retry = 1;
- soa->soa.expire = 1;
- soa->soa.minimum = ttl;
-
- return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
-}
-
-int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
- DnsAnswerFlags flags = 0, i_flags;
- DnsResourceRecord *i;
- bool found = false;
- int r;
-
- assert(key);
-
- DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
- r = dns_resource_key_match_rr(key, i, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (!ret_flags)
- return 1;
-
- if (found)
- flags &= i_flags;
- else {
- flags = i_flags;
- found = true;
- }
- }
-
- if (ret_flags)
- *ret_flags = flags;
-
- return found;
-}
-
-int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
- DnsAnswerFlags flags = 0, i_flags;
- DnsResourceRecord *i;
- bool found = false;
- int r;
-
- assert(rr);
-
- DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
- r = dns_resource_record_equal(i, rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (!ret_flags)
- return 1;
-
- if (found)
- flags &= i_flags;
- else {
- flags = i_flags;
- found = true;
- }
- }
-
- if (ret_flags)
- *ret_flags = flags;
-
- return found;
-}
-
-int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
- DnsAnswerFlags flags = 0, i_flags;
- DnsResourceRecord *i;
- bool found = false;
- int r;
-
- assert(key);
-
- DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
- r = dns_resource_key_equal(i->key, key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (!ret_flags)
- return true;
-
- if (found)
- flags &= i_flags;
- else {
- flags = i_flags;
- found = true;
- }
- }
-
- if (ret_flags)
- *ret_flags = flags;
-
- return found;
-}
-
-int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
- DnsResourceRecord *i;
-
- DNS_ANSWER_FOREACH(i, a) {
- if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
- return true;
- }
-
- return false;
-}
-
-int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
- DnsResourceRecord *rr;
- int r;
-
- /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
-
- DNS_ANSWER_FOREACH(rr, answer) {
- const char *p;
-
- if (rr->key->type != DNS_TYPE_NSEC3)
- continue;
-
- p = dns_resource_key_name(rr->key);
- r = dns_name_parent(&p);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_name_equal(p, zone);
- if (r != 0)
- return r;
- }
-
- return false;
-}
-
-int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
- DnsResourceRecord *rr, *soa = NULL;
- DnsAnswerFlags rr_flags, soa_flags = 0;
- int r;
-
- assert(key);
-
- /* For a SOA record we can never find a matching SOA record */
- if (key->type == DNS_TYPE_SOA)
- return 0;
-
- DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
- r = dns_resource_key_match_soa(key, rr->key);
- if (r < 0)
- return r;
- if (r > 0) {
-
- if (soa) {
- r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
- if (r < 0)
- return r;
- if (r > 0)
- continue;
- }
-
- soa = rr;
- soa_flags = rr_flags;
- }
- }
-
- if (!soa)
- return 0;
-
- if (ret)
- *ret = soa;
- if (flags)
- *flags = soa_flags;
-
- return 1;
-}
-
-int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
- DnsResourceRecord *rr;
- DnsAnswerFlags rr_flags;
- int r;
-
- assert(key);
-
- /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
- if (!dns_type_may_redirect(key->type))
- return 0;
-
- DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
- r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
- if (r < 0)
- return r;
- if (r > 0) {
- if (ret)
- *ret = rr;
- if (flags)
- *flags = rr_flags;
- return 1;
- }
- }
-
- return 0;
-}
-
-int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
- int r;
-
- assert(ret);
-
- if (dns_answer_size(a) <= 0) {
- *ret = dns_answer_ref(b);
- return 0;
- }
-
- if (dns_answer_size(b) <= 0) {
- *ret = dns_answer_ref(a);
- return 0;
- }
-
- k = dns_answer_new(a->n_rrs + b->n_rrs);
- if (!k)
- return -ENOMEM;
-
- r = dns_answer_add_raw_all(k, a);
- if (r < 0)
- return r;
-
- r = dns_answer_add_all(k, b);
- if (r < 0)
- return r;
-
- *ret = k;
- k = NULL;
-
- return 0;
-}
-
-int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
- DnsAnswer *merged;
- int r;
-
- assert(a);
-
- r = dns_answer_merge(*a, b, &merged);
- if (r < 0)
- return r;
-
- dns_answer_unref(*a);
- *a = merged;
-
- return 0;
-}
-
-int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
- bool found = false, other = false;
- DnsResourceRecord *rr;
- unsigned i;
- int r;
-
- assert(a);
- assert(key);
-
- /* Remove all entries matching the specified key from *a */
-
- DNS_ANSWER_FOREACH(rr, *a) {
- r = dns_resource_key_equal(rr->key, key);
- if (r < 0)
- return r;
- if (r > 0)
- found = true;
- else
- other = true;
-
- if (found && other)
- break;
- }
-
- if (!found)
- return 0;
-
- if (!other) {
- *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
- return 1;
- }
-
- if ((*a)->n_ref > 1) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
- DnsAnswerFlags flags;
- int ifindex;
-
- copy = dns_answer_new((*a)->n_rrs);
- if (!copy)
- return -ENOMEM;
-
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
- r = dns_resource_key_equal(rr->key, key);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- r = dns_answer_add_raw(copy, rr, ifindex, flags);
- if (r < 0)
- return r;
- }
-
- dns_answer_unref(*a);
- *a = copy;
- copy = NULL;
-
- return 1;
- }
-
- /* Only a single reference, edit in-place */
-
- i = 0;
- for (;;) {
- if (i >= (*a)->n_rrs)
- break;
-
- r = dns_resource_key_equal((*a)->items[i].rr->key, key);
- if (r < 0)
- return r;
- if (r > 0) {
- /* Kill this entry */
-
- dns_resource_record_unref((*a)->items[i].rr);
- memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
- (*a)->n_rrs--;
- continue;
-
- } else
- /* Keep this entry */
- i++;
- }
-
- return 1;
-}
-
-int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
- bool found = false, other = false;
- DnsResourceRecord *rr;
- unsigned i;
- int r;
-
- assert(a);
- assert(rm);
-
- /* Remove all entries matching the specified RR from *a */
-
- DNS_ANSWER_FOREACH(rr, *a) {
- r = dns_resource_record_equal(rr, rm);
- if (r < 0)
- return r;
- if (r > 0)
- found = true;
- else
- other = true;
-
- if (found && other)
- break;
- }
-
- if (!found)
- return 0;
-
- if (!other) {
- *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
- return 1;
- }
-
- if ((*a)->n_ref > 1) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
- DnsAnswerFlags flags;
- int ifindex;
-
- copy = dns_answer_new((*a)->n_rrs);
- if (!copy)
- return -ENOMEM;
-
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
- r = dns_resource_record_equal(rr, rm);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- r = dns_answer_add_raw(copy, rr, ifindex, flags);
- if (r < 0)
- return r;
- }
-
- dns_answer_unref(*a);
- *a = copy;
- copy = NULL;
-
- return 1;
- }
-
- /* Only a single reference, edit in-place */
-
- i = 0;
- for (;;) {
- if (i >= (*a)->n_rrs)
- break;
-
- r = dns_resource_record_equal((*a)->items[i].rr, rm);
- if (r < 0)
- return r;
- if (r > 0) {
- /* Kill this entry */
-
- dns_resource_record_unref((*a)->items[i].rr);
- memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
- (*a)->n_rrs--;
- continue;
-
- } else
- /* Keep this entry */
- i++;
- }
-
- return 1;
-}
-
-int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
- DnsResourceRecord *rr_source;
- int ifindex_source, r;
- DnsAnswerFlags flags_source;
-
- assert(a);
- assert(key);
-
- /* Copy all RRs matching the specified key from source into *a */
-
- DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
-
- r = dns_resource_key_equal(rr_source->key, key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* Make space for at least one entry */
- r = dns_answer_reserve_or_clone(a, 1);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
- int r;
-
- assert(to);
- assert(from);
- assert(key);
-
- r = dns_answer_copy_by_key(to, *from, key, or_flags);
- if (r < 0)
- return r;
-
- return dns_answer_remove_by_key(from, key);
-}
-
-void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
- DnsAnswerItem *items;
- unsigned i, start, end;
-
- if (!a)
- return;
-
- if (a->n_rrs <= 1)
- return;
-
- start = 0;
- end = a->n_rrs-1;
-
- /* RFC 4795, Section 2.6 suggests we should order entries
- * depending on whether the sender is a link-local address. */
-
- items = newa(DnsAnswerItem, a->n_rrs);
- for (i = 0; i < a->n_rrs; i++) {
-
- if (a->items[i].rr->key->class == DNS_CLASS_IN &&
- ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
- (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
- /* Order address records that are not preferred to the end of the array */
- items[end--] = a->items[i];
- else
- /* Order all other records to the beginning of the array */
- items[start++] = a->items[i];
- }
-
- assert(start == end+1);
- memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
-}
-
-int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
- DnsAnswer *n;
-
- assert(a);
-
- if (n_free <= 0)
- return 0;
-
- if (*a) {
- unsigned ns;
-
- if ((*a)->n_ref > 1)
- return -EBUSY;
-
- ns = (*a)->n_rrs + n_free;
-
- if ((*a)->n_allocated >= ns)
- return 0;
-
- /* Allocate more than we need */
- ns *= 2;
-
- n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
- if (!n)
- return -ENOMEM;
-
- n->n_allocated = ns;
- } else {
- n = dns_answer_new(n_free);
- if (!n)
- return -ENOMEM;
- }
-
- *a = n;
- return 0;
-}
-
-int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
- int r;
-
- assert(a);
-
- /* Tries to extend the DnsAnswer object. And if that's not
- * possible, since we are not the sole owner, then allocate a
- * new, appropriately sized one. Either way, after this call
- * the object will only have a single reference, and has room
- * for at least the specified number of RRs. */
-
- r = dns_answer_reserve(a, n_free);
- if (r != -EBUSY)
- return r;
-
- assert(*a);
-
- n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
- if (!n)
- return -ENOMEM;
-
- r = dns_answer_add_raw_all(n, *a);
- if (r < 0)
- return r;
-
- dns_answer_unref(*a);
- *a = n;
- n = NULL;
-
- return 0;
-}
-
-void dns_answer_dump(DnsAnswer *answer, FILE *f) {
- DnsResourceRecord *rr;
- DnsAnswerFlags flags;
- int ifindex;
-
- if (!f)
- f = stdout;
-
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
- const char *t;
-
- fputc('\t', f);
-
- t = dns_resource_record_to_string(rr);
- if (!t) {
- log_oom();
- continue;
- }
-
- fputs(t, f);
-
- if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
- fputs("\t;", f);
-
- if (ifindex != 0)
- printf(" ifindex=%i", ifindex);
- if (flags & DNS_ANSWER_AUTHENTICATED)
- fputs(" authenticated", f);
- if (flags & DNS_ANSWER_CACHEABLE)
- fputs(" cachable", f);
- if (flags & DNS_ANSWER_SHARED_OWNER)
- fputs(" shared-owner", f);
-
- fputc('\n', f);
- }
-}
-
-bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
- DnsResourceRecord *rr;
- int r;
-
- assert(cname);
-
- /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
- * synthesized from it */
-
- if (cname->key->type != DNS_TYPE_CNAME)
- return 0;
-
- DNS_ANSWER_FOREACH(rr, a) {
- _cleanup_free_ char *n = NULL;
-
- if (rr->key->type != DNS_TYPE_DNAME)
- continue;
- if (rr->key->class != cname->key->class)
- continue;
-
- r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_name_equal(n, dns_resource_key_name(cname->key));
- if (r < 0)
- return r;
- if (r > 0)
- return 1;
-
- }
-
- return 0;
-}
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
deleted file mode 100644
index 4a92bd1150..0000000000
--- a/src/resolve/resolved-dns-answer.h
+++ /dev/null
@@ -1,147 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct DnsAnswer DnsAnswer;
-typedef struct DnsAnswerItem DnsAnswerItem;
-
-#include "macro.h"
-#include "resolved-dns-rr.h"
-
-/* A simple array of resource records. We keep track of the
- * originating ifindex for each RR where that makes sense, so that we
- * can qualify A and AAAA RRs referring to a local link with the
- * right ifindex.
- *
- * Note that we usually encode the empty DnsAnswer object as a simple NULL. */
-
-typedef enum DnsAnswerFlags {
- DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
- DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */
- DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */
-} DnsAnswerFlags;
-
-struct DnsAnswerItem {
- DnsResourceRecord *rr;
- int ifindex;
- DnsAnswerFlags flags;
-};
-
-struct DnsAnswer {
- unsigned n_ref;
- unsigned n_rrs, n_allocated;
- DnsAnswerItem items[0];
-};
-
-DnsAnswer *dns_answer_new(unsigned n);
-DnsAnswer *dns_answer_ref(DnsAnswer *a);
-DnsAnswer *dns_answer_unref(DnsAnswer *a);
-
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
-int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
-int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex);
-
-int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
-int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
-int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
-int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
-int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
-
-int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
-int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
-
-int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret);
-int dns_answer_extend(DnsAnswer **a, DnsAnswer *b);
-
-void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
-
-int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
-int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free);
-
-int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key);
-int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
-
-int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
-int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
-
-bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
-
-static inline unsigned dns_answer_size(DnsAnswer *a) {
- return a ? a->n_rrs : 0;
-}
-
-static inline bool dns_answer_isempty(DnsAnswer *a) {
- return dns_answer_size(a) <= 0;
-}
-
-void dns_answer_dump(DnsAnswer *answer, FILE *f);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
-
-#define _DNS_ANSWER_FOREACH(q, kk, a) \
- for (unsigned UNIQ_T(i, q) = ({ \
- (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
- 0; \
- }); \
- (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
- UNIQ_T(i, q)++, (kk) = (UNIQ_T(i, q) < (a)->n_rrs ? (a)->items[UNIQ_T(i, q)].rr : NULL))
-
-#define DNS_ANSWER_FOREACH(kk, a) _DNS_ANSWER_FOREACH(UNIQ, kk, a)
-
-#define _DNS_ANSWER_FOREACH_IFINDEX(q, kk, ifi, a) \
- for (unsigned UNIQ_T(i, q) = ({ \
- (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
- (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
- 0; \
- }); \
- (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
- UNIQ_T(i, q)++, \
- (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
- (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0))
-
-#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) _DNS_ANSWER_FOREACH_IFINDEX(UNIQ, kk, ifindex, a)
-
-#define _DNS_ANSWER_FOREACH_FLAGS(q, kk, fl, a) \
- for (unsigned UNIQ_T(i, q) = ({ \
- (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
- (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
- 0; \
- }); \
- (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
- UNIQ_T(i, q)++, \
- (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
- (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
-
-#define DNS_ANSWER_FOREACH_FLAGS(kk, flags, a) _DNS_ANSWER_FOREACH_FLAGS(UNIQ, kk, flags, a)
-
-#define _DNS_ANSWER_FOREACH_FULL(q, kk, ifi, fl, a) \
- for (unsigned UNIQ_T(i, q) = ({ \
- (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
- (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
- (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
- 0; \
- }); \
- (a) && (UNIQ_T(i, q) < (a)->n_rrs); \
- UNIQ_T(i, q)++, \
- (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
- (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0), \
- (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
-
-#define DNS_ANSWER_FOREACH_FULL(kk, ifindex, flags, a) _DNS_ANSWER_FOREACH_FULL(UNIQ, kk, ifindex, flags, a)
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
deleted file mode 100644
index 9233fb0ac1..0000000000
--- a/src/resolve/resolved-dns-cache.c
+++ /dev/null
@@ -1,1064 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/if.h>
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "resolved-dns-answer.h"
-#include "resolved-dns-cache.h"
-#include "resolved-dns-packet.h"
-#include "string-util.h"
-
-/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
- * leave DNS caches unbounded, but that's crazy. */
-#define CACHE_MAX 4096
-
-/* We never keep any item longer than 2h in our cache */
-#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
-
-typedef enum DnsCacheItemType DnsCacheItemType;
-typedef struct DnsCacheItem DnsCacheItem;
-
-enum DnsCacheItemType {
- DNS_CACHE_POSITIVE,
- DNS_CACHE_NODATA,
- DNS_CACHE_NXDOMAIN,
-};
-
-struct DnsCacheItem {
- DnsCacheItemType type;
- DnsResourceKey *key;
- DnsResourceRecord *rr;
-
- usec_t until;
- bool authenticated:1;
- bool shared_owner:1;
-
- int ifindex;
- int owner_family;
- union in_addr_union owner_address;
-
- unsigned prioq_idx;
- LIST_FIELDS(DnsCacheItem, by_key);
-};
-
-static void dns_cache_item_free(DnsCacheItem *i) {
- if (!i)
- return;
-
- dns_resource_record_unref(i->rr);
- dns_resource_key_unref(i->key);
- free(i);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
-
-static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
- DnsCacheItem *first;
-
- assert(c);
-
- if (!i)
- return;
-
- first = hashmap_get(c->by_key, i->key);
- LIST_REMOVE(by_key, first, i);
-
- if (first)
- assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
- else
- hashmap_remove(c->by_key, i->key);
-
- prioq_remove(c->by_expiry, i, &i->prioq_idx);
-
- dns_cache_item_free(i);
-}
-
-static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
- DnsCacheItem *first, *i;
- int r;
-
- first = hashmap_get(c->by_key, rr->key);
- LIST_FOREACH(by_key, i, first) {
- r = dns_resource_record_equal(i->rr, rr);
- if (r < 0)
- return r;
- if (r > 0) {
- dns_cache_item_unlink_and_free(c, i);
- return true;
- }
- }
-
- return false;
-}
-
-static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
- DnsCacheItem *first, *i, *n;
-
- assert(c);
- assert(key);
-
- first = hashmap_remove(c->by_key, key);
- if (!first)
- return false;
-
- LIST_FOREACH_SAFE(by_key, i, n, first) {
- prioq_remove(c->by_expiry, i, &i->prioq_idx);
- dns_cache_item_free(i);
- }
-
- return true;
-}
-
-void dns_cache_flush(DnsCache *c) {
- DnsResourceKey *key;
-
- assert(c);
-
- while ((key = hashmap_first_key(c->by_key)))
- dns_cache_remove_by_key(c, key);
-
- assert(hashmap_size(c->by_key) == 0);
- assert(prioq_size(c->by_expiry) == 0);
-
- c->by_key = hashmap_free(c->by_key);
- c->by_expiry = prioq_free(c->by_expiry);
-}
-
-static void dns_cache_make_space(DnsCache *c, unsigned add) {
- assert(c);
-
- if (add <= 0)
- return;
-
- /* Makes space for n new entries. Note that we actually allow
- * the cache to grow beyond CACHE_MAX, but only when we shall
- * add more RRs to the cache than CACHE_MAX at once. In that
- * case the cache will be emptied completely otherwise. */
-
- for (;;) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- DnsCacheItem *i;
-
- if (prioq_size(c->by_expiry) <= 0)
- break;
-
- if (prioq_size(c->by_expiry) + add < CACHE_MAX)
- break;
-
- i = prioq_peek(c->by_expiry);
- assert(i);
-
- /* Take an extra reference to the key so that it
- * doesn't go away in the middle of the remove call */
- key = dns_resource_key_ref(i->key);
- dns_cache_remove_by_key(c, key);
- }
-}
-
-void dns_cache_prune(DnsCache *c) {
- usec_t t = 0;
-
- assert(c);
-
- /* Remove all entries that are past their TTL */
-
- for (;;) {
- DnsCacheItem *i;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- i = prioq_peek(c->by_expiry);
- if (!i)
- break;
-
- if (t <= 0)
- t = now(clock_boottime_or_monotonic());
-
- if (i->until > t)
- break;
-
- /* Depending whether this is an mDNS shared entry
- * either remove only this one RR or the whole RRset */
- log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
- i->shared_owner ? "shared " : "",
- dns_resource_key_to_string(i->key, key_str, sizeof key_str),
- (t - i->until) / USEC_PER_SEC);
-
- if (i->shared_owner)
- dns_cache_item_unlink_and_free(c, i);
- else {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- /* Take an extra reference to the key so that it
- * doesn't go away in the middle of the remove call */
- key = dns_resource_key_ref(i->key);
- dns_cache_remove_by_key(c, key);
- }
- }
-}
-
-static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
- const DnsCacheItem *x = a, *y = b;
-
- if (x->until < y->until)
- return -1;
- if (x->until > y->until)
- return 1;
- return 0;
-}
-
-static int dns_cache_init(DnsCache *c) {
- int r;
-
- assert(c);
-
- r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
- if (r < 0)
- return r;
-
- return r;
-}
-
-static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
- DnsCacheItem *first;
- int r;
-
- assert(c);
- assert(i);
-
- r = prioq_put(c->by_expiry, i, &i->prioq_idx);
- if (r < 0)
- return r;
-
- first = hashmap_get(c->by_key, i->key);
- if (first) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
-
- /* Keep a reference to the original key, while we manipulate the list. */
- k = dns_resource_key_ref(first->key);
-
- /* Now, try to reduce the number of keys we keep */
- dns_resource_key_reduce(&first->key, &i->key);
-
- if (first->rr)
- dns_resource_key_reduce(&first->rr->key, &i->key);
- if (i->rr)
- dns_resource_key_reduce(&i->rr->key, &i->key);
-
- LIST_PREPEND(by_key, first, i);
- assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
- } else {
- r = hashmap_put(c->by_key, i->key, i);
- if (r < 0) {
- prioq_remove(c->by_expiry, i, &i->prioq_idx);
- return r;
- }
- }
-
- return 0;
-}
-
-static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
- DnsCacheItem *i;
-
- assert(c);
- assert(rr);
-
- LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
- if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
- return i;
-
- return NULL;
-}
-
-static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
- uint32_t ttl;
- usec_t u;
-
- assert(rr);
-
- ttl = MIN(rr->ttl, nsec_ttl);
- if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
- /* If this is a SOA RR, and it is requested, clamp to
- * the SOA's minimum field. This is used when we do
- * negative caching, to determine the TTL for the
- * negative caching entry. See RFC 2308, Section
- * 5. */
-
- if (ttl > rr->soa.minimum)
- ttl = rr->soa.minimum;
- }
-
- u = ttl * USEC_PER_SEC;
- if (u > CACHE_TTL_MAX_USEC)
- u = CACHE_TTL_MAX_USEC;
-
- if (rr->expiry != USEC_INFINITY) {
- usec_t left;
-
- /* Make use of the DNSSEC RRSIG expiry info, if we
- * have it */
-
- left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
- if (u > left)
- u = left;
- }
-
- return timestamp + u;
-}
-
-static void dns_cache_item_update_positive(
- DnsCache *c,
- DnsCacheItem *i,
- DnsResourceRecord *rr,
- bool authenticated,
- bool shared_owner,
- usec_t timestamp,
- int ifindex,
- int owner_family,
- const union in_addr_union *owner_address) {
-
- assert(c);
- assert(i);
- assert(rr);
- assert(owner_address);
-
- i->type = DNS_CACHE_POSITIVE;
-
- if (!i->by_key_prev)
- /* We are the first item in the list, we need to
- * update the key used in the hashmap */
-
- assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
-
- dns_resource_record_ref(rr);
- dns_resource_record_unref(i->rr);
- i->rr = rr;
-
- dns_resource_key_unref(i->key);
- i->key = dns_resource_key_ref(rr->key);
-
- i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
- i->authenticated = authenticated;
- i->shared_owner = shared_owner;
-
- i->ifindex = ifindex;
-
- i->owner_family = owner_family;
- i->owner_address = *owner_address;
-
- prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
-}
-
-static int dns_cache_put_positive(
- DnsCache *c,
- DnsResourceRecord *rr,
- bool authenticated,
- bool shared_owner,
- usec_t timestamp,
- int ifindex,
- int owner_family,
- const union in_addr_union *owner_address) {
-
- _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
- DnsCacheItem *existing;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE];
- int r, k;
-
- assert(c);
- assert(rr);
- assert(owner_address);
-
- /* Never cache pseudo RRs */
- if (dns_class_is_pseudo(rr->key->class))
- return 0;
- if (dns_type_is_pseudo(rr->key->type))
- return 0;
-
- /* New TTL is 0? Delete this specific entry... */
- if (rr->ttl <= 0) {
- k = dns_cache_remove_by_rr(c, rr);
- log_debug("%s: %s",
- k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
- dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
- return 0;
- }
-
- /* Entry exists already? Update TTL, timestamp and owner*/
- existing = dns_cache_get(c, rr);
- if (existing) {
- dns_cache_item_update_positive(
- c,
- existing,
- rr,
- authenticated,
- shared_owner,
- timestamp,
- ifindex,
- owner_family,
- owner_address);
- return 0;
- }
-
- /* Otherwise, add the new RR */
- r = dns_cache_init(c);
- if (r < 0)
- return r;
-
- dns_cache_make_space(c, 1);
-
- i = new0(DnsCacheItem, 1);
- if (!i)
- return -ENOMEM;
-
- i->type = DNS_CACHE_POSITIVE;
- i->key = dns_resource_key_ref(rr->key);
- i->rr = dns_resource_record_ref(rr);
- i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
- i->authenticated = authenticated;
- i->shared_owner = shared_owner;
- i->ifindex = ifindex;
- i->owner_family = owner_family;
- i->owner_address = *owner_address;
- i->prioq_idx = PRIOQ_IDX_NULL;
-
- r = dns_cache_link_item(c, i);
- if (r < 0)
- return r;
-
- if (log_get_max_level() >= LOG_DEBUG) {
- _cleanup_free_ char *t = NULL;
-
- (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
-
- log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
- i->authenticated ? "authenticated" : "unauthenticated",
- i->shared_owner ? " shared" : "",
- dns_resource_key_to_string(i->key, key_str, sizeof key_str),
- (i->until - timestamp) / USEC_PER_SEC,
- i->ifindex == 0 ? "*" : strna(if_indextoname(i->ifindex, ifname)),
- af_to_name_short(i->owner_family),
- strna(t));
- }
-
- i = NULL;
- return 0;
-}
-
-static int dns_cache_put_negative(
- DnsCache *c,
- DnsResourceKey *key,
- int rcode,
- bool authenticated,
- uint32_t nsec_ttl,
- usec_t timestamp,
- DnsResourceRecord *soa,
- int owner_family,
- const union in_addr_union *owner_address) {
-
- _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
- int r;
-
- assert(c);
- assert(key);
- assert(soa);
- assert(owner_address);
-
- /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
- * important to filter out as we use this as a pseudo-type for
- * NXDOMAIN entries */
- if (dns_class_is_pseudo(key->class))
- return 0;
- if (dns_type_is_pseudo(key->type))
- return 0;
-
- if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
- log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
- }
-
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
- return 0;
-
- r = dns_cache_init(c);
- if (r < 0)
- return r;
-
- dns_cache_make_space(c, 1);
-
- i = new0(DnsCacheItem, 1);
- if (!i)
- return -ENOMEM;
-
- i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
- i->until = calculate_until(soa, nsec_ttl, timestamp, true);
- i->authenticated = authenticated;
- i->owner_family = owner_family;
- i->owner_address = *owner_address;
- i->prioq_idx = PRIOQ_IDX_NULL;
-
- if (i->type == DNS_CACHE_NXDOMAIN) {
- /* NXDOMAIN entries should apply equally to all types, so we use ANY as
- * a pseudo type for this purpose here. */
- i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
- if (!i->key)
- return -ENOMEM;
-
- /* Make sure to remove any previous entry for this
- * specific ANY key. (For non-ANY keys the cache data
- * is already cleared by the caller.) Note that we
- * don't bother removing positive or NODATA cache
- * items in this case, because it would either be slow
- * or require explicit indexing by name */
- dns_cache_remove_by_key(c, key);
- } else
- i->key = dns_resource_key_ref(key);
-
- r = dns_cache_link_item(c, i);
- if (r < 0)
- return r;
-
- log_debug("Added %s cache entry for %s "USEC_FMT"s",
- i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN",
- dns_resource_key_to_string(i->key, key_str, sizeof key_str),
- (i->until - timestamp) / USEC_PER_SEC);
-
- i = NULL;
- return 0;
-}
-
-static void dns_cache_remove_previous(
- DnsCache *c,
- DnsResourceKey *key,
- DnsAnswer *answer) {
-
- DnsResourceRecord *rr;
- DnsAnswerFlags flags;
-
- assert(c);
-
- /* First, if we were passed a key (i.e. on LLMNR/DNS, but
- * not on mDNS), delete all matching old RRs, so that we only
- * keep complete by_key in place. */
- if (key)
- dns_cache_remove_by_key(c, key);
-
- /* Second, flush all entries matching the answer, unless this
- * is an RR that is explicitly marked to be "shared" between
- * peers (i.e. mDNS RRs without the flush-cache bit set). */
- DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
- if ((flags & DNS_ANSWER_CACHEABLE) == 0)
- continue;
-
- if (flags & DNS_ANSWER_SHARED_OWNER)
- continue;
-
- dns_cache_remove_by_key(c, rr->key);
- }
-}
-
-static bool rr_eligible(DnsResourceRecord *rr) {
- assert(rr);
-
- /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
- * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
- * existence from any cached NSEC/NSEC3, but that should be fine. */
-
- switch (rr->key->type) {
-
- case DNS_TYPE_NSEC:
- return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
- bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
-
- case DNS_TYPE_NSEC3:
- return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
- bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
-
- default:
- return true;
- }
-}
-
-int dns_cache_put(
- DnsCache *c,
- DnsResourceKey *key,
- int rcode,
- DnsAnswer *answer,
- bool authenticated,
- uint32_t nsec_ttl,
- usec_t timestamp,
- int owner_family,
- const union in_addr_union *owner_address) {
-
- DnsResourceRecord *soa = NULL, *rr;
- DnsAnswerFlags flags;
- unsigned cache_keys;
- int r, ifindex;
-
- assert(c);
- assert(owner_address);
-
- dns_cache_remove_previous(c, key, answer);
-
- /* We only care for positive replies and NXDOMAINs, on all
- * other replies we will simply flush the respective entries,
- * and that's it */
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
- return 0;
-
- if (dns_answer_size(answer) <= 0) {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- log_debug("Not caching negative entry without a SOA record: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
- }
-
- cache_keys = dns_answer_size(answer);
- if (key)
- cache_keys++;
-
- /* Make some space for our new entries */
- dns_cache_make_space(c, cache_keys);
-
- if (timestamp <= 0)
- timestamp = now(clock_boottime_or_monotonic());
-
- /* Second, add in positive entries for all contained RRs */
- DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
- if ((flags & DNS_ANSWER_CACHEABLE) == 0)
- continue;
-
- r = rr_eligible(rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_cache_put_positive(
- c,
- rr,
- flags & DNS_ANSWER_AUTHENTICATED,
- flags & DNS_ANSWER_SHARED_OWNER,
- timestamp,
- ifindex,
- owner_family, owner_address);
- if (r < 0)
- goto fail;
- }
-
- if (!key) /* mDNS doesn't know negative caching, really */
- return 0;
-
- /* Third, add in negative entries if the key has no RR */
- r = dns_answer_match_key(answer, key, NULL);
- if (r < 0)
- goto fail;
- if (r > 0)
- return 0;
-
- /* But not if it has a matching CNAME/DNAME (the negative
- * caching will be done on the canonical name, not on the
- * alias) */
- r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
- if (r < 0)
- goto fail;
- if (r > 0)
- return 0;
-
- /* See https://tools.ietf.org/html/rfc2308, which say that a
- * matching SOA record in the packet is used to enable
- * negative caching. */
- r = dns_answer_find_soa(answer, key, &soa, &flags);
- if (r < 0)
- goto fail;
- if (r == 0)
- return 0;
-
- /* Refuse using the SOA data if it is unsigned, but the key is
- * signed */
- if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
- return 0;
-
- r = dns_cache_put_negative(
- c,
- key,
- rcode,
- authenticated,
- nsec_ttl,
- timestamp,
- soa,
- owner_family, owner_address);
- if (r < 0)
- goto fail;
-
- return 0;
-
-fail:
- /* Adding all RRs failed. Let's clean up what we already
- * added, just in case */
-
- if (key)
- dns_cache_remove_by_key(c, key);
-
- DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
- if ((flags & DNS_ANSWER_CACHEABLE) == 0)
- continue;
-
- dns_cache_remove_by_key(c, rr->key);
- }
-
- return r;
-}
-
-static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
- DnsCacheItem *i;
- const char *n;
- int r;
-
- assert(c);
- assert(k);
-
- /* If we hit some OOM error, or suchlike, we don't care too
- * much, after all this is just a cache */
-
- i = hashmap_get(c->by_key, k);
- if (i)
- return i;
-
- n = dns_resource_key_name(k);
-
- /* Check if we have an NXDOMAIN cache item for the name, notice that we use
- * the pseudo-type ANY for NXDOMAIN cache items. */
- i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
- if (i && i->type == DNS_CACHE_NXDOMAIN)
- return i;
-
- if (dns_type_may_redirect(k->type)) {
- /* Check if we have a CNAME record instead */
- i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
- if (i)
- return i;
-
- /* OK, let's look for cached DNAME records. */
- for (;;) {
- if (isempty(n))
- return NULL;
-
- i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
- if (i)
- return i;
-
- /* Jump one label ahead */
- r = dns_name_parent(&n);
- if (r <= 0)
- return NULL;
- }
- }
-
- if (k->type != DNS_TYPE_NSEC) {
- /* Check if we have an NSEC record instead for the name. */
- i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
- if (i)
- return i;
- }
-
- return NULL;
-}
-
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
- unsigned n = 0;
- int r;
- bool nxdomain = false;
- DnsCacheItem *j, *first, *nsec = NULL;
- bool have_authenticated = false, have_non_authenticated = false;
- usec_t current;
-
- assert(c);
- assert(key);
- assert(rcode);
- assert(ret);
- assert(authenticated);
-
- if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
- /* If we have ANY lookups we don't use the cache, so
- * that the caller refreshes via the network. */
-
- log_debug("Ignoring cache for ANY lookup: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
-
- c->n_miss++;
-
- *ret = NULL;
- *rcode = DNS_RCODE_SUCCESS;
- return 0;
- }
-
- first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
- if (!first) {
- /* If one question cannot be answered we need to refresh */
-
- log_debug("Cache miss for %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
-
- c->n_miss++;
-
- *ret = NULL;
- *rcode = DNS_RCODE_SUCCESS;
- return 0;
- }
-
- LIST_FOREACH(by_key, j, first) {
- if (j->rr) {
- if (j->rr->key->type == DNS_TYPE_NSEC)
- nsec = j;
-
- n++;
- } else if (j->type == DNS_CACHE_NXDOMAIN)
- nxdomain = true;
-
- if (j->authenticated)
- have_authenticated = true;
- else
- have_non_authenticated = true;
- }
-
- if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
- /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
- * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
-
- log_debug("NSEC NODATA cache hit for %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
-
- /* We only found an NSEC record that matches our name.
- * If it says the type doesn't exist report
- * NODATA. Otherwise report a cache miss. */
-
- *ret = NULL;
- *rcode = DNS_RCODE_SUCCESS;
- *authenticated = nsec->authenticated;
-
- if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
- !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
- !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
- c->n_hit++;
- return 1;
- }
-
- c->n_miss++;
- return 0;
- }
-
- log_debug("%s cache hit for %s",
- n > 0 ? "Positive" :
- nxdomain ? "NXDOMAIN" : "NODATA",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
-
- if (n <= 0) {
- c->n_hit++;
-
- *ret = NULL;
- *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
- *authenticated = have_authenticated && !have_non_authenticated;
- return 1;
- }
-
- answer = dns_answer_new(n);
- if (!answer)
- return -ENOMEM;
-
- if (clamp_ttl)
- current = now(clock_boottime_or_monotonic());
-
- LIST_FOREACH(by_key, j, first) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- if (!j->rr)
- continue;
-
- if (clamp_ttl) {
- rr = dns_resource_record_ref(j->rr);
-
- r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
- if (r < 0)
- return r;
- }
-
- r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
- if (r < 0)
- return r;
- }
-
- c->n_hit++;
-
- *ret = answer;
- *rcode = DNS_RCODE_SUCCESS;
- *authenticated = have_authenticated && !have_non_authenticated;
- answer = NULL;
-
- return n;
-}
-
-int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
- DnsCacheItem *i, *first;
- bool same_owner = true;
-
- assert(cache);
- assert(rr);
-
- dns_cache_prune(cache);
-
- /* See if there's a cache entry for the same key. If there
- * isn't there's no conflict */
- first = hashmap_get(cache->by_key, rr->key);
- if (!first)
- return 0;
-
- /* See if the RR key is owned by the same owner, if so, there
- * isn't a conflict either */
- LIST_FOREACH(by_key, i, first) {
- if (i->owner_family != owner_family ||
- !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
- same_owner = false;
- break;
- }
- }
- if (same_owner)
- return 0;
-
- /* See if there's the exact same RR in the cache. If yes, then
- * there's no conflict. */
- if (dns_cache_get(cache, rr))
- return 0;
-
- /* There's a conflict */
- return 1;
-}
-
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
- unsigned ancount = 0;
- Iterator iterator;
- DnsCacheItem *i;
- int r;
-
- assert(cache);
- assert(p);
-
- HASHMAP_FOREACH(i, cache->by_key, iterator) {
- DnsCacheItem *j;
-
- LIST_FOREACH(by_key, j, i) {
- if (!j->rr)
- continue;
-
- if (!j->shared_owner)
- continue;
-
- r = dns_packet_append_rr(p, j->rr, NULL, NULL);
- if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
- /* For mDNS, if we're unable to stuff all known answers into the given packet,
- * allocate a new one, push the RR into that one and link it to the current one.
- */
-
- DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
- ancount = 0;
-
- r = dns_packet_new_query(&p->more, p->protocol, 0, true);
- if (r < 0)
- return r;
-
- /* continue with new packet */
- p = p->more;
- r = dns_packet_append_rr(p, j->rr, NULL, NULL);
- }
-
- if (r < 0)
- return r;
-
- ancount++;
- }
- }
-
- DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
-
- return 0;
-}
-
-void dns_cache_dump(DnsCache *cache, FILE *f) {
- Iterator iterator;
- DnsCacheItem *i;
-
- if (!cache)
- return;
-
- if (!f)
- f = stdout;
-
- HASHMAP_FOREACH(i, cache->by_key, iterator) {
- DnsCacheItem *j;
-
- LIST_FOREACH(by_key, j, i) {
-
- fputc('\t', f);
-
- if (j->rr) {
- const char *t;
- t = dns_resource_record_to_string(j->rr);
- if (!t) {
- log_oom();
- continue;
- }
-
- fputs(t, f);
- fputc('\n', f);
- } else {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
- fputs(" -- ", f);
- fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
- fputc('\n', f);
- }
- }
- }
-}
-
-bool dns_cache_is_empty(DnsCache *cache) {
- if (!cache)
- return true;
-
- return hashmap_isempty(cache->by_key);
-}
-
-unsigned dns_cache_size(DnsCache *cache) {
- if (!cache)
- return 0;
-
- return hashmap_size(cache->by_key);
-}
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
deleted file mode 100644
index 22a7c17377..0000000000
--- a/src/resolve/resolved-dns-cache.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "hashmap.h"
-#include "list.h"
-#include "prioq.h"
-#include "time-util.h"
-
-typedef struct DnsCache {
- Hashmap *by_key;
- Prioq *by_expiry;
- unsigned n_hit;
- unsigned n_miss;
-} DnsCache;
-
-#include "resolved-dns-answer.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-rr.h"
-
-void dns_cache_flush(DnsCache *c);
-void dns_cache_prune(DnsCache *c);
-
-int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **answer, bool *authenticated);
-
-int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
-
-void dns_cache_dump(DnsCache *cache, FILE *f);
-bool dns_cache_is_empty(DnsCache *cache);
-
-unsigned dns_cache_size(DnsCache *cache);
-
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
deleted file mode 100644
index d4a267c89f..0000000000
--- a/src/resolve/resolved-dns-dnssec.c
+++ /dev/null
@@ -1,2199 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_GCRYPT
-#include <gcrypt.h>
-#endif
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "gcrypt-util.h"
-#include "hexdecoct.h"
-#include "resolved-dns-dnssec.h"
-#include "resolved-dns-packet.h"
-#include "string-table.h"
-
-#define VERIFY_RRS_MAX 256
-#define MAX_KEY_SIZE (32*1024)
-
-/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
-#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
-
-/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
-#define NSEC3_ITERATIONS_MAX 2500
-
-/*
- * The DNSSEC Chain of trust:
- *
- * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
- * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
- * DS RRs are protected like normal RRs
- *
- * Example chain:
- * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
- */
-
-uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
- const uint8_t *p;
- uint32_t sum, f;
- size_t i;
-
- /* The algorithm from RFC 4034, Appendix B. */
-
- assert(dnskey);
- assert(dnskey->key->type == DNS_TYPE_DNSKEY);
-
- f = (uint32_t) dnskey->dnskey.flags;
-
- if (mask_revoke)
- f &= ~DNSKEY_FLAG_REVOKE;
-
- sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
-
- p = dnskey->dnskey.key;
-
- for (i = 0; i < dnskey->dnskey.key_size; i++)
- sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
-
- sum += (sum >> 16) & UINT32_C(0xFFFF);
-
- return sum & UINT32_C(0xFFFF);
-}
-
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
- size_t c = 0;
- int r;
-
- /* Converts the specified hostname into DNSSEC canonicalized
- * form. */
-
- if (buffer_max < 2)
- return -ENOBUFS;
-
- for (;;) {
- r = dns_label_unescape(&n, buffer, buffer_max);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (buffer_max < (size_t) r + 2)
- return -ENOBUFS;
-
- /* The DNSSEC canonical form is not clear on what to
- * do with dots appearing in labels, the way DNS-SD
- * does it. Refuse it for now. */
-
- if (memchr(buffer, '.', r))
- return -EINVAL;
-
- ascii_strlower_n(buffer, (size_t) r);
- buffer[r] = '.';
-
- buffer += r + 1;
- c += r + 1;
-
- buffer_max -= r + 1;
- }
-
- if (c <= 0) {
- /* Not even a single label: this is the root domain name */
-
- assert(buffer_max > 2);
- buffer[0] = '.';
- buffer[1] = 0;
-
- return 1;
- }
-
- return (int) c;
-}
-
-#ifdef HAVE_GCRYPT
-
-static int rr_compare(const void *a, const void *b) {
- DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
- size_t m;
- int r;
-
- /* Let's order the RRs according to RFC 4034, Section 6.3 */
-
- assert(x);
- assert(*x);
- assert((*x)->wire_format);
- assert(y);
- assert(*y);
- assert((*y)->wire_format);
-
- m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
-
- r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
- if (r != 0)
- return r;
-
- if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return -1;
- else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
- return 1;
-
- return 0;
-}
-
-static int dnssec_rsa_verify_raw(
- const char *hash_algorithm,
- const void *signature, size_t signature_size,
- const void *data, size_t data_size,
- const void *exponent, size_t exponent_size,
- const void *modulus, size_t modulus_size) {
-
- gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
- gcry_mpi_t n = NULL, e = NULL, s = NULL;
- gcry_error_t ge;
- int r;
-
- assert(hash_algorithm);
-
- ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&signature_sexp,
- NULL,
- "(sig-val (rsa (s %m)))",
- s);
-
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&data_sexp,
- NULL,
- "(data (flags pkcs1) (hash %s %b))",
- hash_algorithm,
- (int) data_size,
- data);
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&public_key_sexp,
- NULL,
- "(public-key (rsa (n %m) (e %m)))",
- n,
- e);
- if (ge != 0) {
- r = -EIO;
- goto finish;
- }
-
- ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
- if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
- r = 0;
- else if (ge != 0) {
- log_debug("RSA signature check failed: %s", gpg_strerror(ge));
- r = -EIO;
- } else
- r = 1;
-
-finish:
- if (e)
- gcry_mpi_release(e);
- if (n)
- gcry_mpi_release(n);
- if (s)
- gcry_mpi_release(s);
-
- if (public_key_sexp)
- gcry_sexp_release(public_key_sexp);
- if (signature_sexp)
- gcry_sexp_release(signature_sexp);
- if (data_sexp)
- gcry_sexp_release(data_sexp);
-
- return r;
-}
-
-static int dnssec_rsa_verify(
- const char *hash_algorithm,
- const void *hash, size_t hash_size,
- DnsResourceRecord *rrsig,
- DnsResourceRecord *dnskey) {
-
- size_t exponent_size, modulus_size;
- void *exponent, *modulus;
-
- assert(hash_algorithm);
- assert(hash);
- assert(hash_size > 0);
- assert(rrsig);
- assert(dnskey);
-
- if (*(uint8_t*) dnskey->dnskey.key == 0) {
- /* exponent is > 255 bytes long */
-
- exponent = (uint8_t*) dnskey->dnskey.key + 3;
- exponent_size =
- ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
- ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
-
- if (exponent_size < 256)
- return -EINVAL;
-
- if (3 + exponent_size >= dnskey->dnskey.key_size)
- return -EINVAL;
-
- modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
- modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
-
- } else {
- /* exponent is <= 255 bytes long */
-
- exponent = (uint8_t*) dnskey->dnskey.key + 1;
- exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
-
- if (exponent_size <= 0)
- return -EINVAL;
-
- if (1 + exponent_size >= dnskey->dnskey.key_size)
- return -EINVAL;
-
- modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
- modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
- }
-
- return dnssec_rsa_verify_raw(
- hash_algorithm,
- rrsig->rrsig.signature, rrsig->rrsig.signature_size,
- hash, hash_size,
- exponent, exponent_size,
- modulus, modulus_size);
-}
-
-static int dnssec_ecdsa_verify_raw(
- const char *hash_algorithm,
- const char *curve,
- const void *signature_r, size_t signature_r_size,
- const void *signature_s, size_t signature_s_size,
- const void *data, size_t data_size,
- const void *key, size_t key_size) {
-
- gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
- gcry_mpi_t q = NULL, r = NULL, s = NULL;
- gcry_error_t ge;
- int k;
-
- assert(hash_algorithm);
-
- ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&signature_sexp,
- NULL,
- "(sig-val (ecdsa (r %m) (s %m)))",
- r,
- s);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&data_sexp,
- NULL,
- "(data (flags rfc6979) (hash %s %b))",
- hash_algorithm,
- (int) data_size,
- data);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_sexp_build(&public_key_sexp,
- NULL,
- "(public-key (ecc (curve %s) (q %m)))",
- curve,
- q);
- if (ge != 0) {
- k = -EIO;
- goto finish;
- }
-
- ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
- if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
- k = 0;
- else if (ge != 0) {
- log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
- k = -EIO;
- } else
- k = 1;
-finish:
- if (r)
- gcry_mpi_release(r);
- if (s)
- gcry_mpi_release(s);
- if (q)
- gcry_mpi_release(q);
-
- if (public_key_sexp)
- gcry_sexp_release(public_key_sexp);
- if (signature_sexp)
- gcry_sexp_release(signature_sexp);
- if (data_sexp)
- gcry_sexp_release(data_sexp);
-
- return k;
-}
-
-static int dnssec_ecdsa_verify(
- const char *hash_algorithm,
- int algorithm,
- const void *hash, size_t hash_size,
- DnsResourceRecord *rrsig,
- DnsResourceRecord *dnskey) {
-
- const char *curve;
- size_t key_size;
- uint8_t *q;
-
- assert(hash);
- assert(hash_size);
- assert(rrsig);
- assert(dnskey);
-
- if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
- key_size = 32;
- curve = "NIST P-256";
- } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
- key_size = 48;
- curve = "NIST P-384";
- } else
- return -EOPNOTSUPP;
-
- if (dnskey->dnskey.key_size != key_size * 2)
- return -EINVAL;
-
- if (rrsig->rrsig.signature_size != key_size * 2)
- return -EINVAL;
-
- q = alloca(key_size*2 + 1);
- q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
- memcpy(q+1, dnskey->dnskey.key, key_size*2);
-
- return dnssec_ecdsa_verify_raw(
- hash_algorithm,
- curve,
- rrsig->rrsig.signature, key_size,
- (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
- hash, hash_size,
- q, key_size*2+1);
-}
-
-static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
- gcry_md_write(md, &v, sizeof(v));
-}
-
-static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
- v = htobe16(v);
- gcry_md_write(md, &v, sizeof(v));
-}
-
-static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
- v = htobe32(v);
- gcry_md_write(md, &v, sizeof(v));
-}
-
-static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
- int n_key_labels, n_signer_labels;
- const char *name;
- int r;
-
- /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
- * .n_skip_labels_signer fields so that we can use them later on. */
-
- assert(rrsig);
- assert(rrsig->key->type == DNS_TYPE_RRSIG);
-
- /* Check if this RRSIG RR is already prepared */
- if (rrsig->n_skip_labels_source != (unsigned) -1)
- return 0;
-
- if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
- return -EINVAL;
-
- name = dns_resource_key_name(rrsig->key);
-
- n_key_labels = dns_name_count_labels(name);
- if (n_key_labels < 0)
- return n_key_labels;
- if (rrsig->rrsig.labels > n_key_labels)
- return -EINVAL;
-
- n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
- if (n_signer_labels < 0)
- return n_signer_labels;
- if (n_signer_labels > rrsig->rrsig.labels)
- return -EINVAL;
-
- r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- /* Check if the signer is really a suffix of us */
- r = dns_name_equal(name, rrsig->rrsig.signer);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
- rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
-
- return 0;
-}
-
-static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
- usec_t expiration, inception, skew;
-
- assert(rrsig);
- assert(rrsig->key->type == DNS_TYPE_RRSIG);
-
- if (realtime == USEC_INFINITY)
- realtime = now(CLOCK_REALTIME);
-
- expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
- inception = rrsig->rrsig.inception * USEC_PER_SEC;
-
- /* Consider inverted validity intervals as expired */
- if (inception > expiration)
- return true;
-
- /* Permit a certain amount of clock skew of 10% of the valid
- * time range. This takes inspiration from unbound's
- * resolver. */
- skew = (expiration - inception) / 10;
- if (skew > SKEW_MAX)
- skew = SKEW_MAX;
-
- if (inception < skew)
- inception = 0;
- else
- inception -= skew;
-
- if (expiration + skew < expiration)
- expiration = USEC_INFINITY;
- else
- expiration += skew;
-
- return realtime < inception || realtime > expiration;
-}
-
-static int algorithm_to_gcrypt_md(uint8_t algorithm) {
-
- /* Translates a DNSSEC signature algorithm into a gcrypt
- * digest identifier.
- *
- * Note that we implement all algorithms listed as "Must
- * implement" and "Recommended to Implement" in RFC6944. We
- * don't implement any algorithms that are listed as
- * "Optional" or "Must Not Implement". Specifically, we do not
- * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
- * GOST-ECC. */
-
- switch (algorithm) {
-
- case DNSSEC_ALGORITHM_RSASHA1:
- case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
- return GCRY_MD_SHA1;
-
- case DNSSEC_ALGORITHM_RSASHA256:
- case DNSSEC_ALGORITHM_ECDSAP256SHA256:
- return GCRY_MD_SHA256;
-
- case DNSSEC_ALGORITHM_ECDSAP384SHA384:
- return GCRY_MD_SHA384;
-
- case DNSSEC_ALGORITHM_RSASHA512:
- return GCRY_MD_SHA512;
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static void dnssec_fix_rrset_ttl(
- DnsResourceRecord *list[],
- unsigned n,
- DnsResourceRecord *rrsig,
- usec_t realtime) {
-
- unsigned k;
-
- assert(list);
- assert(n > 0);
- assert(rrsig);
-
- for (k = 0; k < n; k++) {
- DnsResourceRecord *rr = list[k];
-
- /* Pick the TTL as the minimum of the RR's TTL, the
- * RR's original TTL according to the RRSIG and the
- * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
- rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
- rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
-
- /* Copy over information about the signer and wildcard source of synthesis */
- rr->n_skip_labels_source = rrsig->n_skip_labels_source;
- rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
- }
-
- rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
-}
-
-int dnssec_verify_rrset(
- DnsAnswer *a,
- const DnsResourceKey *key,
- DnsResourceRecord *rrsig,
- DnsResourceRecord *dnskey,
- usec_t realtime,
- DnssecResult *result) {
-
- uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
- DnsResourceRecord **list, *rr;
- const char *source, *name;
- gcry_md_hd_t md = NULL;
- int r, md_algorithm;
- size_t k, n = 0;
- size_t hash_size;
- void *hash;
- bool wildcard;
-
- assert(key);
- assert(rrsig);
- assert(dnskey);
- assert(result);
- assert(rrsig->key->type == DNS_TYPE_RRSIG);
- assert(dnskey->key->type == DNS_TYPE_DNSKEY);
-
- /* Verifies that the RRSet matches the specified "key" in "a",
- * using the signature "rrsig" and the key "dnskey". It's
- * assumed that RRSIG and DNSKEY match. */
-
- md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
- if (md_algorithm == -EOPNOTSUPP) {
- *result = DNSSEC_UNSUPPORTED_ALGORITHM;
- return 0;
- }
- if (md_algorithm < 0)
- return md_algorithm;
-
- r = dnssec_rrsig_prepare(rrsig);
- if (r == -EINVAL) {
- *result = DNSSEC_INVALID;
- return r;
- }
- if (r < 0)
- return r;
-
- r = dnssec_rrsig_expired(rrsig, realtime);
- if (r < 0)
- return r;
- if (r > 0) {
- *result = DNSSEC_SIGNATURE_EXPIRED;
- return 0;
- }
-
- name = dns_resource_key_name(key);
-
- /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
- if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
- r = dns_name_equal(rrsig->rrsig.signer, name);
- if (r < 0)
- return r;
- if (r == 0) {
- *result = DNSSEC_INVALID;
- return 0;
- }
- }
-
- /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
- if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
- r = dns_name_equal(rrsig->rrsig.signer, name);
- if (r < 0)
- return r;
- if (r > 0) {
- *result = DNSSEC_INVALID;
- return 0;
- }
- }
-
- /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
- r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
- if (r < 0)
- return r;
- if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
- /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
- *result = DNSSEC_INVALID;
- return 0;
- }
- if (r == 1) {
- /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
- * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
- r = dns_name_startswith(name, "*");
- if (r < 0)
- return r;
- if (r > 0)
- source = name;
-
- wildcard = r == 0;
- } else
- wildcard = r > 0;
-
- /* Collect all relevant RRs in a single array, so that we can look at the RRset */
- list = newa(DnsResourceRecord *, dns_answer_size(a));
-
- DNS_ANSWER_FOREACH(rr, a) {
- r = dns_resource_key_equal(key, rr->key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* We need the wire format for ordering, and digest calculation */
- r = dns_resource_record_to_wire_format(rr, true);
- if (r < 0)
- return r;
-
- list[n++] = rr;
-
- if (n > VERIFY_RRS_MAX)
- return -E2BIG;
- }
-
- if (n <= 0)
- return -ENODATA;
-
- /* Bring the RRs into canonical order */
- qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
-
- /* OK, the RRs are now in canonical order. Let's calculate the digest */
- initialize_libgcrypt(false);
-
- hash_size = gcry_md_get_algo_dlen(md_algorithm);
- assert(hash_size > 0);
-
- gcry_md_open(&md, md_algorithm, 0);
- if (!md)
- return -EIO;
-
- md_add_uint16(md, rrsig->rrsig.type_covered);
- md_add_uint8(md, rrsig->rrsig.algorithm);
- md_add_uint8(md, rrsig->rrsig.labels);
- md_add_uint32(md, rrsig->rrsig.original_ttl);
- md_add_uint32(md, rrsig->rrsig.expiration);
- md_add_uint32(md, rrsig->rrsig.inception);
- md_add_uint16(md, rrsig->rrsig.key_tag);
-
- r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
- if (r < 0)
- goto finish;
- gcry_md_write(md, wire_format_name, r);
-
- /* Convert the source of synthesis into wire format */
- r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
- if (r < 0)
- goto finish;
-
- for (k = 0; k < n; k++) {
- size_t l;
-
- rr = list[k];
-
- /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
- if (wildcard)
- gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
- gcry_md_write(md, wire_format_name, r);
-
- md_add_uint16(md, rr->key->type);
- md_add_uint16(md, rr->key->class);
- md_add_uint32(md, rrsig->rrsig.original_ttl);
-
- l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
- assert(l <= 0xFFFF);
-
- md_add_uint16(md, (uint16_t) l);
- gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
- }
-
- hash = gcry_md_read(md, 0);
- if (!hash) {
- r = -EIO;
- goto finish;
- }
-
- switch (rrsig->rrsig.algorithm) {
-
- case DNSSEC_ALGORITHM_RSASHA1:
- case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
- case DNSSEC_ALGORITHM_RSASHA256:
- case DNSSEC_ALGORITHM_RSASHA512:
- r = dnssec_rsa_verify(
- gcry_md_algo_name(md_algorithm),
- hash, hash_size,
- rrsig,
- dnskey);
- break;
-
- case DNSSEC_ALGORITHM_ECDSAP256SHA256:
- case DNSSEC_ALGORITHM_ECDSAP384SHA384:
- r = dnssec_ecdsa_verify(
- gcry_md_algo_name(md_algorithm),
- rrsig->rrsig.algorithm,
- hash, hash_size,
- rrsig,
- dnskey);
- break;
- }
-
- if (r < 0)
- goto finish;
-
- /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
- if (r > 0)
- dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
-
- if (r == 0)
- *result = DNSSEC_INVALID;
- else if (wildcard)
- *result = DNSSEC_VALIDATED_WILDCARD;
- else
- *result = DNSSEC_VALIDATED;
-
- r = 0;
-
-finish:
- gcry_md_close(md);
- return r;
-}
-
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
-
- assert(rrsig);
- assert(dnskey);
-
- /* Checks if the specified DNSKEY RR matches the key used for
- * the signature in the specified RRSIG RR */
-
- if (rrsig->key->type != DNS_TYPE_RRSIG)
- return -EINVAL;
-
- if (dnskey->key->type != DNS_TYPE_DNSKEY)
- return 0;
- if (dnskey->key->class != rrsig->key->class)
- return 0;
- if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
- return 0;
- if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
- return 0;
- if (dnskey->dnskey.protocol != 3)
- return 0;
- if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
- return 0;
-
- if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
- return 0;
-
- return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer);
-}
-
-int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
- assert(key);
- assert(rrsig);
-
- /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
-
- if (rrsig->key->type != DNS_TYPE_RRSIG)
- return 0;
- if (rrsig->key->class != key->class)
- return 0;
- if (rrsig->rrsig.type_covered != key->type)
- return 0;
-
- return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key));
-}
-
-int dnssec_verify_rrset_search(
- DnsAnswer *a,
- const DnsResourceKey *key,
- DnsAnswer *validated_dnskeys,
- usec_t realtime,
- DnssecResult *result,
- DnsResourceRecord **ret_rrsig) {
-
- bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
- DnsResourceRecord *rrsig;
- int r;
-
- assert(key);
- assert(result);
-
- /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
-
- if (!a || a->n_rrs <= 0)
- return -ENODATA;
-
- /* Iterate through each RRSIG RR. */
- DNS_ANSWER_FOREACH(rrsig, a) {
- DnsResourceRecord *dnskey;
- DnsAnswerFlags flags;
-
- /* Is this an RRSIG RR that applies to RRs matching our key? */
- r = dnssec_key_match_rrsig(key, rrsig);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- found_rrsig = true;
-
- /* Look for a matching key */
- DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
- DnssecResult one_result;
-
- if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
- continue;
-
- /* Is this a DNSKEY RR that matches they key of our RRSIG? */
- r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* Take the time here, if it isn't set yet, so
- * that we do all validations with the same
- * time. */
- if (realtime == USEC_INFINITY)
- realtime = now(CLOCK_REALTIME);
-
- /* Yay, we found a matching RRSIG with a matching
- * DNSKEY, awesome. Now let's verify all entries of
- * the RRSet against the RRSIG and DNSKEY
- * combination. */
-
- r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
- if (r < 0)
- return r;
-
- switch (one_result) {
-
- case DNSSEC_VALIDATED:
- case DNSSEC_VALIDATED_WILDCARD:
- /* Yay, the RR has been validated,
- * return immediately, but fix up the expiry */
- if (ret_rrsig)
- *ret_rrsig = rrsig;
-
- *result = one_result;
- return 0;
-
- case DNSSEC_INVALID:
- /* If the signature is invalid, let's try another
- key and/or signature. After all they
- key_tags and stuff are not unique, and
- might be shared by multiple keys. */
- found_invalid = true;
- continue;
-
- case DNSSEC_UNSUPPORTED_ALGORITHM:
- /* If the key algorithm is
- unsupported, try another
- RRSIG/DNSKEY pair, but remember we
- encountered this, so that we can
- return a proper error when we
- encounter nothing better. */
- found_unsupported_algorithm = true;
- continue;
-
- case DNSSEC_SIGNATURE_EXPIRED:
- /* If the signature is expired, try
- another one, but remember it, so
- that we can return this */
- found_expired_rrsig = true;
- continue;
-
- default:
- assert_not_reached("Unexpected DNSSEC validation result");
- }
- }
- }
-
- if (found_expired_rrsig)
- *result = DNSSEC_SIGNATURE_EXPIRED;
- else if (found_unsupported_algorithm)
- *result = DNSSEC_UNSUPPORTED_ALGORITHM;
- else if (found_invalid)
- *result = DNSSEC_INVALID;
- else if (found_rrsig)
- *result = DNSSEC_MISSING_KEY;
- else
- *result = DNSSEC_NO_SIGNATURE;
-
- if (ret_rrsig)
- *ret_rrsig = NULL;
-
- return 0;
-}
-
-int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
- DnsResourceRecord *rr;
- int r;
-
- /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
-
- DNS_ANSWER_FOREACH(rr, a) {
- r = dnssec_key_match_rrsig(key, rr);
- if (r < 0)
- return r;
- if (r > 0)
- return 1;
- }
-
- return 0;
-}
-
-static int digest_to_gcrypt_md(uint8_t algorithm) {
-
- /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
-
- switch (algorithm) {
-
- case DNSSEC_DIGEST_SHA1:
- return GCRY_MD_SHA1;
-
- case DNSSEC_DIGEST_SHA256:
- return GCRY_MD_SHA256;
-
- case DNSSEC_DIGEST_SHA384:
- return GCRY_MD_SHA384;
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
- char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
- gcry_md_hd_t md = NULL;
- size_t hash_size;
- int md_algorithm, r;
- void *result;
-
- assert(dnskey);
- assert(ds);
-
- /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
-
- if (dnskey->key->type != DNS_TYPE_DNSKEY)
- return -EINVAL;
- if (ds->key->type != DNS_TYPE_DS)
- return -EINVAL;
- if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
- return -EKEYREJECTED;
- if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
- return -EKEYREJECTED;
- if (dnskey->dnskey.protocol != 3)
- return -EKEYREJECTED;
-
- if (dnskey->dnskey.algorithm != ds->ds.algorithm)
- return 0;
- if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
- return 0;
-
- initialize_libgcrypt(false);
-
- md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
- if (md_algorithm < 0)
- return md_algorithm;
-
- hash_size = gcry_md_get_algo_dlen(md_algorithm);
- assert(hash_size > 0);
-
- if (ds->ds.digest_size != hash_size)
- return 0;
-
- r = dnssec_canonicalize(dns_resource_key_name(dnskey->key), owner_name, sizeof(owner_name));
- if (r < 0)
- return r;
-
- gcry_md_open(&md, md_algorithm, 0);
- if (!md)
- return -EIO;
-
- gcry_md_write(md, owner_name, r);
- if (mask_revoke)
- md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
- else
- md_add_uint16(md, dnskey->dnskey.flags);
- md_add_uint8(md, dnskey->dnskey.protocol);
- md_add_uint8(md, dnskey->dnskey.algorithm);
- gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
-
- result = gcry_md_read(md, 0);
- if (!result) {
- r = -EIO;
- goto finish;
- }
-
- r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
-
-finish:
- gcry_md_close(md);
- return r;
-}
-
-int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
- DnsResourceRecord *ds;
- DnsAnswerFlags flags;
- int r;
-
- assert(dnskey);
-
- if (dnskey->key->type != DNS_TYPE_DNSKEY)
- return 0;
-
- DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
-
- if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
- continue;
-
- if (ds->key->type != DNS_TYPE_DS)
- continue;
- if (ds->key->class != dnskey->key->class)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key));
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
- if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
- return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
- if (r < 0)
- return r;
- if (r > 0)
- return 1;
- }
-
- return 0;
-}
-
-static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
-
- /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
-
- switch (algorithm) {
-
- case NSEC3_ALGORITHM_SHA1:
- return GCRY_MD_SHA1;
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
- uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
- gcry_md_hd_t md = NULL;
- size_t hash_size;
- int algorithm;
- void *result;
- unsigned k;
- int r;
-
- assert(nsec3);
- assert(name);
- assert(ret);
-
- if (nsec3->key->type != DNS_TYPE_NSEC3)
- return -EINVAL;
-
- if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
- log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
- return -EOPNOTSUPP;
- }
-
- algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
- if (algorithm < 0)
- return algorithm;
-
- initialize_libgcrypt(false);
-
- hash_size = gcry_md_get_algo_dlen(algorithm);
- assert(hash_size > 0);
-
- if (nsec3->nsec3.next_hashed_name_size != hash_size)
- return -EINVAL;
-
- r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
- if (r < 0)
- return r;
-
- gcry_md_open(&md, algorithm, 0);
- if (!md)
- return -EIO;
-
- gcry_md_write(md, wire_format, r);
- gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
-
- result = gcry_md_read(md, 0);
- if (!result) {
- r = -EIO;
- goto finish;
- }
-
- for (k = 0; k < nsec3->nsec3.iterations; k++) {
- uint8_t tmp[hash_size];
- memcpy(tmp, result, hash_size);
-
- gcry_md_reset(md);
- gcry_md_write(md, tmp, hash_size);
- gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
-
- result = gcry_md_read(md, 0);
- if (!result) {
- r = -EIO;
- goto finish;
- }
- }
-
- memcpy(ret, result, hash_size);
- r = (int) hash_size;
-
-finish:
- gcry_md_close(md);
- return r;
-}
-
-static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
- const char *a, *b;
- int r;
-
- assert(rr);
-
- if (rr->key->type != DNS_TYPE_NSEC3)
- return 0;
-
- /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
- if (!IN_SET(rr->nsec3.flags, 0, 1))
- return 0;
-
- /* Ignore NSEC3 RRs whose algorithm we don't know */
- if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
- return 0;
- /* Ignore NSEC3 RRs with an excessive number of required iterations */
- if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
- return 0;
-
- /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
- * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
- if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1)
- return 0;
- /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
- if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1)
- return 0;
-
- if (!nsec3)
- return 1;
-
- /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
-
- if (nsec3 == rr) /* Shortcut */
- return 1;
-
- if (rr->key->class != nsec3->key->class)
- return 0;
- if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
- return 0;
- if (rr->nsec3.iterations != nsec3->nsec3.iterations)
- return 0;
- if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
- return 0;
- if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
- return 0;
-
- a = dns_resource_key_name(rr->key);
- r = dns_name_parent(&a); /* strip off hash */
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- b = dns_resource_key_name(nsec3->key);
- r = dns_name_parent(&b); /* strip off hash */
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- /* Make sure both have the same parent */
- return dns_name_equal(a, b);
-}
-
-static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
- _cleanup_free_ char *l = NULL;
- char *j;
-
- assert(hashed);
- assert(hashed_size > 0);
- assert(zone);
- assert(ret);
-
- l = base32hexmem(hashed, hashed_size, false);
- if (!l)
- return -ENOMEM;
-
- j = strjoin(l, ".", zone, NULL);
- if (!j)
- return -ENOMEM;
-
- *ret = j;
- return (int) hashed_size;
-}
-
-static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
- uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
- int hashed_size;
-
- assert(nsec3);
- assert(domain);
- assert(zone);
- assert(ret);
-
- hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
- if (hashed_size < 0)
- return hashed_size;
-
- return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
-}
-
-/* See RFC 5155, Section 8
- * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
- * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
- * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
- * matches the wildcard domain.
- *
- * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
- * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
- * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
- * to conclude anything we indicate this by returning NO_RR. */
-static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
- _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
- const char *zone, *p, *pp = NULL, *wildcard;
- DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
- DnsAnswerFlags flags;
- int hashed_size, r;
- bool a, no_closer = false, no_wildcard = false, optout = false;
-
- assert(key);
- assert(result);
-
- /* First step, find the zone name and the NSEC3 parameters of the zone.
- * it is sufficient to look for the longest common suffix we find with
- * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
- * records from a given zone in a response must use the same
- * parameters. */
- zone = dns_resource_key_name(key);
- for (;;) {
- DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
- r = nsec3_is_good(zone_rr, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone);
- if (r < 0)
- return r;
- if (r > 0)
- goto found_zone;
- }
-
- /* Strip one label from the front */
- r = dns_name_parent(&zone);
- if (r < 0)
- return r;
- if (r == 0)
- break;
- }
-
- *result = DNSSEC_NSEC_NO_RR;
- return 0;
-
-found_zone:
- /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
- p = dns_resource_key_name(key);
- for (;;) {
- _cleanup_free_ char *hashed_domain = NULL;
-
- hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
- if (hashed_size == -EOPNOTSUPP) {
- *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
- return 0;
- }
- if (hashed_size < 0)
- return hashed_size;
-
- DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
-
- r = nsec3_is_good(enclosure_rr, zone_rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain);
- if (r < 0)
- return r;
- if (r > 0) {
- a = flags & DNS_ANSWER_AUTHENTICATED;
- goto found_closest_encloser;
- }
- }
-
- /* We didn't find the closest encloser with this name,
- * but let's remember this domain name, it might be
- * the next closer name */
-
- pp = p;
-
- /* Strip one label from the front */
- r = dns_name_parent(&p);
- if (r < 0)
- return r;
- if (r == 0)
- break;
- }
-
- *result = DNSSEC_NSEC_NO_RR;
- return 0;
-
-found_closest_encloser:
- /* We found a closest encloser in 'p'; next closer is 'pp' */
-
- if (!pp) {
- /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
- * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
- * appropriately set. */
-
- if (key->type == DNS_TYPE_DS) {
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
- } else {
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
- !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
- }
-
- /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
- if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
- *result = DNSSEC_NSEC_FOUND;
- else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
- *result = DNSSEC_NSEC_CNAME;
- else
- *result = DNSSEC_NSEC_NODATA;
-
- if (authenticated)
- *authenticated = a;
- if (ttl)
- *ttl = enclosure_rr->ttl;
-
- return 0;
- }
-
- /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
- return -EBADMSG;
-
- /* Ensure that this data is from the delegated domain
- * (i.e. originates from the "lower" DNS server), and isn't
- * just glue records (i.e. doesn't originate from the "upper"
- * DNS server). */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
- !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
-
- /* Prove that there is no next closer and whether or not there is a wildcard domain. */
-
- wildcard = strjoina("*.", p);
- r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
- if (r < 0)
- return r;
- if (r != hashed_size)
- return -EBADMSG;
-
- r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
- if (r < 0)
- return r;
- if (r != hashed_size)
- return -EBADMSG;
-
- DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
- _cleanup_free_ char *next_hashed_domain = NULL;
-
- r = nsec3_is_good(rr, zone_rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
- if (r < 0)
- return r;
-
- r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain);
- if (r < 0)
- return r;
- if (r > 0) {
- if (rr->nsec3.flags & 1)
- optout = true;
-
- a = a && (flags & DNS_ANSWER_AUTHENTICATED);
-
- no_closer = true;
- }
-
- r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain);
- if (r < 0)
- return r;
- if (r > 0) {
- a = a && (flags & DNS_ANSWER_AUTHENTICATED);
-
- wildcard_rr = rr;
- }
-
- r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain);
- if (r < 0)
- return r;
- if (r > 0) {
- if (rr->nsec3.flags & 1)
- /* This only makes sense if we have a wildcard delegation, which is
- * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
- * this not happening, so hence cannot simply conclude NXDOMAIN as
- * we would wish */
- optout = true;
-
- a = a && (flags & DNS_ANSWER_AUTHENTICATED);
-
- no_wildcard = true;
- }
- }
-
- if (wildcard_rr && no_wildcard)
- return -EBADMSG;
-
- if (!no_closer) {
- *result = DNSSEC_NSEC_NO_RR;
- return 0;
- }
-
- if (wildcard_rr) {
- /* A wildcard exists that matches our query. */
- if (optout)
- /* This is not specified in any RFC to the best of my knowledge, but
- * if the next closer enclosure is covered by an opt-out NSEC3 RR
- * it means that we cannot prove that the source of synthesis is
- * correct, as there may be a closer match. */
- *result = DNSSEC_NSEC_OPTOUT;
- else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
- *result = DNSSEC_NSEC_FOUND;
- else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
- *result = DNSSEC_NSEC_CNAME;
- else
- *result = DNSSEC_NSEC_NODATA;
- } else {
- if (optout)
- /* The RFC only specifies that we have to care for optout for NODATA for
- * DS records. However, children of an insecure opt-out delegation should
- * also be considered opt-out, rather than verified NXDOMAIN.
- * Note that we do not require a proof of wildcard non-existence if the
- * next closer domain is covered by an opt-out, as that would not provide
- * any additional information. */
- *result = DNSSEC_NSEC_OPTOUT;
- else if (no_wildcard)
- *result = DNSSEC_NSEC_NXDOMAIN;
- else {
- *result = DNSSEC_NSEC_NO_RR;
-
- return 0;
- }
- }
-
- if (authenticated)
- *authenticated = a;
-
- if (ttl)
- *ttl = enclosure_rr->ttl;
-
- return 0;
-}
-
-static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
- char label[DNS_LABEL_MAX];
- const char *n;
- int r;
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_NSEC);
-
- /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */
-
- if (rr->n_skip_labels_source != 1)
- return 0;
-
- n = dns_resource_key_name(rr->key);
- r = dns_label_unescape(&n, label, sizeof(label));
- if (r <= 0)
- return r;
- if (r != 1 || label[0] != '*')
- return 0;
-
- return dns_name_endswith(name, n);
-}
-
-static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
- const char *nn, *common_suffix;
- int r;
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_NSEC);
-
- /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
- *
- * A couple of examples:
- *
- * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
- * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
- * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
- */
-
- /* First, determine parent of next domain. */
- nn = rr->nsec.next_domain_name;
- r = dns_name_parent(&nn);
- if (r <= 0)
- return r;
-
- /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
- * anything at all. */
- r = dns_name_endswith(nn, name);
- if (r <= 0)
- return r;
-
- /* If the name we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
- r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
- if (r < 0)
- return r;
-
- return dns_name_endswith(name, common_suffix);
-}
-
-static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) {
- int r;
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_NSEC);
-
- /* Checks whether this NSEC originates to the parent zone or the child zone. */
-
- r = dns_name_parent(&name);
- if (r <= 0)
- return r;
-
- r = dns_name_equal(name, dns_resource_key_name(rr->key));
- if (r <= 0)
- return r;
-
- /* DNAME, and NS without SOA is an indication for a delegation. */
- if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME))
- return 1;
-
- if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
- return 1;
-
- return 0;
-}
-
-static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
- const char *common_suffix, *p;
- int r;
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_NSEC);
-
- /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */
-
- r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
- if (r < 0)
- return r;
-
- for (;;) {
- p = name;
- r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- r = dns_name_equal(name, common_suffix);
- if (r < 0)
- return r;
- if (r > 0)
- break;
- }
-
- /* p is now the "Next Closer". */
-
- return dns_name_between(dns_resource_key_name(rr->key), p, rr->nsec.next_domain_name);
-}
-
-static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
- const char *common_suffix, *wc;
- int r;
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_NSEC);
-
- /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
- * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
- * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
- *
- * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
- * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
- * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either...
- */
-
- r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
- if (r < 0)
- return r;
-
- /* If the common suffix is not shared by the name we are interested in, it has nothing to say for us. */
- r = dns_name_endswith(name, common_suffix);
- if (r <= 0)
- return r;
-
- wc = strjoina("*.", common_suffix);
- return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
-}
-
-int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
- bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
- DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
- DnsAnswerFlags flags;
- const char *name;
- int r;
-
- assert(key);
- assert(result);
-
- /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
-
- name = dns_resource_key_name(key);
-
- DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
-
- if (rr->key->class != key->class)
- continue;
-
- have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
-
- if (rr->key->type != DNS_TYPE_NSEC)
- continue;
-
- /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */
- r = dns_resource_record_is_synthetic(rr);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- /* Check if this is a direct match. If so, we have encountered a NODATA case */
- r = dns_name_equal(dns_resource_key_name(rr->key), name);
- if (r < 0)
- return r;
- if (r == 0) {
- /* If it's not a direct match, maybe it's a wild card match? */
- r = dnssec_nsec_wildcard_equal(rr, name);
- if (r < 0)
- return r;
- }
- if (r > 0) {
- if (key->type == DNS_TYPE_DS) {
- /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
- * we have a problem. For DS RRs we want the NSEC RR from the parent */
- if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
- continue;
- } else {
- /* For all RR types, ensure that if NS is set SOA is set too, so that we know
- * we got the child's NSEC. */
- if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
- !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
- continue;
- }
-
- if (bitmap_isset(rr->nsec.types, key->type))
- *result = DNSSEC_NSEC_FOUND;
- else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
- *result = DNSSEC_NSEC_CNAME;
- else
- *result = DNSSEC_NSEC_NODATA;
-
- if (authenticated)
- *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- if (ttl)
- *ttl = rr->ttl;
-
- return 0;
- }
-
- /* Check if the name we are looking for is an empty non-terminal within the owner or next name
- * of the NSEC RR. */
- r = dnssec_nsec_in_path(rr, name);
- if (r < 0)
- return r;
- if (r > 0) {
- *result = DNSSEC_NSEC_NODATA;
-
- if (authenticated)
- *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- if (ttl)
- *ttl = rr->ttl;
-
- return 0;
- }
-
- /* The following two "covering" checks, are not useful if the NSEC is from the parent */
- r = dnssec_nsec_from_parent_zone(rr, name);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- /* Check if this NSEC RR proves the absence of an explicit RR under this name */
- r = dnssec_nsec_covers(rr, name);
- if (r < 0)
- return r;
- if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
- covering_rr = rr;
- covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- }
-
- /* Check if this NSEC RR proves the absence of a wildcard RR under this name */
- r = dnssec_nsec_covers_wildcard(rr, name);
- if (r < 0)
- return r;
- if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
- wildcard_rr = rr;
- wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- }
- }
-
- if (covering_rr && wildcard_rr) {
- /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
- * proved the NXDOMAIN case. */
- *result = DNSSEC_NSEC_NXDOMAIN;
-
- if (authenticated)
- *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
- if (ttl)
- *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
-
- return 0;
- }
-
- /* OK, this was not sufficient. Let's see if NSEC3 can help. */
- if (have_nsec3)
- return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
-
- /* No approproate NSEC RR found, report this. */
- *result = DNSSEC_NSEC_NO_RR;
- return 0;
-}
-
-static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
- DnsResourceRecord *rr;
- DnsAnswerFlags flags;
- int r;
-
- assert(name);
- assert(zone);
-
- /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
- * 'zone'. The 'zone' must be a suffix of the 'name'. */
-
- DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
- bool found = false;
-
- if (rr->key->type != type && type != DNS_TYPE_ANY)
- continue;
-
- switch (rr->key->type) {
-
- case DNS_TYPE_NSEC:
-
- /* We only care for NSEC RRs from the indicated zone */
- r = dns_resource_record_is_signer(rr, zone);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
- if (r < 0)
- return r;
-
- found = r > 0;
- break;
-
- case DNS_TYPE_NSEC3: {
- _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
-
- /* We only care for NSEC3 RRs from the indicated zone */
- r = dns_resource_record_is_signer(rr, zone);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = nsec3_is_good(rr, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- /* Format the domain we are testing with the NSEC3 RR's hash function */
- r = nsec3_hashed_domain_make(
- rr,
- name,
- zone,
- &hashed_domain);
- if (r < 0)
- return r;
- if ((size_t) r != rr->nsec3.next_hashed_name_size)
- break;
-
- /* Format the NSEC3's next hashed name as proper domain name */
- r = nsec3_hashed_domain_format(
- rr->nsec3.next_hashed_name,
- rr->nsec3.next_hashed_name_size,
- zone,
- &next_hashed_domain);
- if (r < 0)
- return r;
-
- r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain);
- if (r < 0)
- return r;
-
- found = r > 0;
- break;
- }
-
- default:
- continue;
- }
-
- if (found) {
- if (authenticated)
- *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- return 1;
- }
- }
-
- return 0;
-}
-
-static int dnssec_test_positive_wildcard_nsec3(
- DnsAnswer *answer,
- const char *name,
- const char *source,
- const char *zone,
- bool *authenticated) {
-
- const char *next_closer = NULL;
- int r;
-
- /* Run a positive NSEC3 wildcard proof. Specifically:
- *
- * A proof that the "next closer" of the generating wildcard does not exist.
- *
- * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
- * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
- * exists for the NSEC3 RR and we are done.
- *
- * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
- * c.d.e.f does not exist. */
-
- for (;;) {
- next_closer = name;
- r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r == 0)
- return 0;
-
- r = dns_name_equal(name, source);
- if (r < 0)
- return r;
- if (r > 0)
- break;
- }
-
- return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
-}
-
-static int dnssec_test_positive_wildcard_nsec(
- DnsAnswer *answer,
- const char *name,
- const char *source,
- const char *zone,
- bool *_authenticated) {
-
- bool authenticated = true;
- int r;
-
- /* Run a positive NSEC wildcard proof. Specifically:
- *
- * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
- * a prefix of the synthesizing source "source" in the zone "zone".
- *
- * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
- *
- * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
- * have to prove that none of the following exist:
- *
- * 1) a.b.c.d.e.f
- * 2) *.b.c.d.e.f
- * 3) b.c.d.e.f
- * 4) *.c.d.e.f
- * 5) c.d.e.f
- *
- */
-
- for (;;) {
- _cleanup_free_ char *wc = NULL;
- bool a = false;
-
- /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
- * i.e between the owner name and the next name of an NSEC RR. */
- r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
- if (r <= 0)
- return r;
-
- authenticated = authenticated && a;
-
- /* Strip one label off */
- r = dns_name_parent(&name);
- if (r <= 0)
- return r;
-
- /* Did we reach the source of synthesis? */
- r = dns_name_equal(name, source);
- if (r < 0)
- return r;
- if (r > 0) {
- /* Successful exit */
- *_authenticated = authenticated;
- return 1;
- }
-
- /* Safety check, that the source of synthesis is still our suffix */
- r = dns_name_endswith(name, source);
- if (r < 0)
- return r;
- if (r == 0)
- return -EBADMSG;
-
- /* Replace the label we stripped off with an asterisk */
- wc = strappend("*.", name);
- if (!wc)
- return -ENOMEM;
-
- /* And check if the proof holds for the asterisk name, too */
- r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
- if (r <= 0)
- return r;
-
- authenticated = authenticated && a;
- /* In the next iteration we'll check the non-asterisk-prefixed version */
- }
-}
-
-int dnssec_test_positive_wildcard(
- DnsAnswer *answer,
- const char *name,
- const char *source,
- const char *zone,
- bool *authenticated) {
-
- int r;
-
- assert(name);
- assert(source);
- assert(zone);
- assert(authenticated);
-
- r = dns_answer_contains_zone_nsec3(answer, zone);
- if (r < 0)
- return r;
- if (r > 0)
- return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
- else
- return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
-}
-
-#else
-
-int dnssec_verify_rrset(
- DnsAnswer *a,
- const DnsResourceKey *key,
- DnsResourceRecord *rrsig,
- DnsResourceRecord *dnskey,
- usec_t realtime,
- DnssecResult *result) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_verify_rrset_search(
- DnsAnswer *a,
- const DnsResourceKey *key,
- DnsAnswer *validated_dnskeys,
- usec_t realtime,
- DnssecResult *result,
- DnsResourceRecord **ret_rrsig) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
-
- return -EOPNOTSUPP;
-}
-
-int dnssec_test_positive_wildcard(
- DnsAnswer *answer,
- const char *name,
- const char *source,
- const char *zone,
- bool *authenticated) {
-
- return -EOPNOTSUPP;
-}
-
-#endif
-
-static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
- [DNSSEC_VALIDATED] = "validated",
- [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
- [DNSSEC_INVALID] = "invalid",
- [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
- [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
- [DNSSEC_NO_SIGNATURE] = "no-signature",
- [DNSSEC_MISSING_KEY] = "missing-key",
- [DNSSEC_UNSIGNED] = "unsigned",
- [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
- [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
- [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
-};
-DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
-
-static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
- [DNSSEC_SECURE] = "secure",
- [DNSSEC_INSECURE] = "insecure",
- [DNSSEC_BOGUS] = "bogus",
- [DNSSEC_INDETERMINATE] = "indeterminate",
-};
-DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
deleted file mode 100644
index 77bd4d71bf..0000000000
--- a/src/resolve/resolved-dns-dnssec.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef enum DnssecResult DnssecResult;
-typedef enum DnssecVerdict DnssecVerdict;
-
-#include "dns-domain.h"
-#include "resolved-dns-answer.h"
-#include "resolved-dns-rr.h"
-
-enum DnssecResult {
- /* These five are returned by dnssec_verify_rrset() */
- DNSSEC_VALIDATED,
- DNSSEC_VALIDATED_WILDCARD, /* Validated via a wildcard RRSIG, further NSEC/NSEC3 checks necessary */
- DNSSEC_INVALID,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_UNSUPPORTED_ALGORITHM,
-
- /* These two are added by dnssec_verify_rrset_search() */
- DNSSEC_NO_SIGNATURE,
- DNSSEC_MISSING_KEY,
-
- /* These two are added by the DnsTransaction logic */
- DNSSEC_UNSIGNED,
- DNSSEC_FAILED_AUXILIARY,
- DNSSEC_NSEC_MISMATCH,
- DNSSEC_INCOMPATIBLE_SERVER,
-
- _DNSSEC_RESULT_MAX,
- _DNSSEC_RESULT_INVALID = -1
-};
-
-enum DnssecVerdict {
- DNSSEC_SECURE,
- DNSSEC_INSECURE,
- DNSSEC_BOGUS,
- DNSSEC_INDETERMINATE,
-
- _DNSSEC_VERDICT_MAX,
- _DNSSEC_VERDICT_INVALID = -1
-};
-
-#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
-
-/* The longest digest we'll ever generate, of all digest algorithms we support */
-#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
-
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok);
-int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
-
-int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
-int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result, DnsResourceRecord **rrsig);
-
-int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke);
-int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
-
-int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key);
-
-uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke);
-
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
-
-int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret);
-
-typedef enum DnssecNsecResult {
- DNSSEC_NSEC_NO_RR, /* No suitable NSEC/NSEC3 RR found */
- DNSSEC_NSEC_CNAME, /* Didn't find what was asked for, but did find CNAME */
- DNSSEC_NSEC_UNSUPPORTED_ALGORITHM,
- DNSSEC_NSEC_NXDOMAIN,
- DNSSEC_NSEC_NODATA,
- DNSSEC_NSEC_FOUND,
- DNSSEC_NSEC_OPTOUT,
-} DnssecNsecResult;
-
-int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
-
-
-int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated);
-
-const char* dnssec_result_to_string(DnssecResult m) _const_;
-DnssecResult dnssec_result_from_string(const char *s) _pure_;
-
-const char* dnssec_verdict_to_string(DnssecVerdict m) _const_;
-DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
deleted file mode 100644
index 337a8c473f..0000000000
--- a/src/resolve/resolved-dns-packet.c
+++ /dev/null
@@ -1,2301 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "resolved-dns-packet.h"
-#include "string-table.h"
-#include "strv.h"
-#include "unaligned.h"
-#include "utf8.h"
-#include "util.h"
-
-#define EDNS0_OPT_DO (1<<15)
-
-typedef struct DnsPacketRewinder {
- DnsPacket *packet;
- size_t saved_rindex;
-} DnsPacketRewinder;
-
-static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
- if (rewinder->packet)
- dns_packet_rewind(rewinder->packet, rewinder->saved_rindex);
-}
-
-#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0)
-#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0)
-
-int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
- DnsPacket *p;
- size_t a;
-
- assert(ret);
-
- if (mtu <= UDP_PACKET_HEADER_SIZE)
- a = DNS_PACKET_SIZE_START;
- else
- a = mtu - UDP_PACKET_HEADER_SIZE;
-
- if (a < DNS_PACKET_HEADER_SIZE)
- a = DNS_PACKET_HEADER_SIZE;
-
- /* round up to next page size */
- a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket));
-
- /* make sure we never allocate more than useful */
- if (a > DNS_PACKET_SIZE_MAX)
- a = DNS_PACKET_SIZE_MAX;
-
- p = malloc0(ALIGN(sizeof(DnsPacket)) + a);
- if (!p)
- return -ENOMEM;
-
- p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
- p->allocated = a;
- p->protocol = protocol;
- p->opt_start = p->opt_size = (size_t) -1;
- p->n_ref = 1;
-
- *ret = p;
-
- return 0;
-}
-
-void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) {
-
- DnsPacketHeader *h;
-
- assert(p);
-
- h = DNS_PACKET_HEADER(p);
-
- switch(p->protocol) {
- case DNS_PROTOCOL_LLMNR:
- assert(!truncated);
-
- h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
- 0 /* opcode */,
- 0 /* c */,
- 0 /* tc */,
- 0 /* t */,
- 0 /* ra */,
- 0 /* ad */,
- 0 /* cd */,
- 0 /* rcode */));
- break;
-
- case DNS_PROTOCOL_MDNS:
- h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
- 0 /* opcode */,
- 0 /* aa */,
- truncated /* tc */,
- 0 /* rd (ask for recursion) */,
- 0 /* ra */,
- 0 /* ad */,
- 0 /* cd */,
- 0 /* rcode */));
- break;
-
- default:
- assert(!truncated);
-
- h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
- 0 /* opcode */,
- 0 /* aa */,
- 0 /* tc */,
- 1 /* rd (ask for recursion) */,
- 0 /* ra */,
- 0 /* ad */,
- dnssec_checking_disabled /* cd */,
- 0 /* rcode */));
- }
-}
-
-int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) {
- DnsPacket *p;
- int r;
-
- assert(ret);
-
- r = dns_packet_new(&p, protocol, mtu);
- if (r < 0)
- return r;
-
- /* Always set the TC bit to 0 initially.
- * If there are multiple packets later, we'll update the bit shortly before sending.
- */
- dns_packet_set_flags(p, dnssec_checking_disabled, false);
-
- *ret = p;
- return 0;
-}
-
-DnsPacket *dns_packet_ref(DnsPacket *p) {
-
- if (!p)
- return NULL;
-
- assert(!p->on_stack);
-
- assert(p->n_ref > 0);
- p->n_ref++;
- return p;
-}
-
-static void dns_packet_free(DnsPacket *p) {
- char *s;
-
- assert(p);
-
- dns_question_unref(p->question);
- dns_answer_unref(p->answer);
- dns_resource_record_unref(p->opt);
-
- while ((s = hashmap_steal_first_key(p->names)))
- free(s);
- hashmap_free(p->names);
-
- free(p->_data);
-
- if (!p->on_stack)
- free(p);
-}
-
-DnsPacket *dns_packet_unref(DnsPacket *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
-
- dns_packet_unref(p->more);
-
- if (p->n_ref == 1)
- dns_packet_free(p);
- else
- p->n_ref--;
-
- return NULL;
-}
-
-int dns_packet_validate(DnsPacket *p) {
- assert(p);
-
- if (p->size < DNS_PACKET_HEADER_SIZE)
- return -EBADMSG;
-
- if (p->size > DNS_PACKET_SIZE_MAX)
- return -EBADMSG;
-
- return 1;
-}
-
-int dns_packet_validate_reply(DnsPacket *p) {
- int r;
-
- assert(p);
-
- r = dns_packet_validate(p);
- if (r < 0)
- return r;
-
- if (DNS_PACKET_QR(p) != 1)
- return 0;
-
- if (DNS_PACKET_OPCODE(p) != 0)
- return -EBADMSG;
-
- switch (p->protocol) {
-
- case DNS_PROTOCOL_LLMNR:
- /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
- if (DNS_PACKET_QDCOUNT(p) != 1)
- return -EBADMSG;
-
- break;
-
- case DNS_PROTOCOL_MDNS:
- /* RFC 6762, Section 18 */
- if (DNS_PACKET_RCODE(p) != 0)
- return -EBADMSG;
-
- break;
-
- default:
- break;
- }
-
- return 1;
-}
-
-int dns_packet_validate_query(DnsPacket *p) {
- int r;
-
- assert(p);
-
- r = dns_packet_validate(p);
- if (r < 0)
- return r;
-
- if (DNS_PACKET_QR(p) != 0)
- return 0;
-
- if (DNS_PACKET_OPCODE(p) != 0)
- return -EBADMSG;
-
- if (DNS_PACKET_TC(p))
- return -EBADMSG;
-
- switch (p->protocol) {
-
- case DNS_PROTOCOL_LLMNR:
- case DNS_PROTOCOL_DNS:
- /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
- if (DNS_PACKET_QDCOUNT(p) != 1)
- return -EBADMSG;
-
- /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */
- if (DNS_PACKET_ANCOUNT(p) > 0)
- return -EBADMSG;
-
- /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */
- if (DNS_PACKET_NSCOUNT(p) > 0)
- return -EBADMSG;
-
- break;
-
- case DNS_PROTOCOL_MDNS:
- /* RFC 6762, Section 18 */
- if (DNS_PACKET_AA(p) != 0 ||
- DNS_PACKET_RD(p) != 0 ||
- DNS_PACKET_RA(p) != 0 ||
- DNS_PACKET_AD(p) != 0 ||
- DNS_PACKET_CD(p) != 0 ||
- DNS_PACKET_RCODE(p) != 0)
- return -EBADMSG;
-
- break;
-
- default:
- break;
- }
-
- return 1;
-}
-
-static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) {
- assert(p);
-
- if (p->size + add > p->allocated) {
- size_t a;
-
- a = PAGE_ALIGN((p->size + add) * 2);
- if (a > DNS_PACKET_SIZE_MAX)
- a = DNS_PACKET_SIZE_MAX;
-
- if (p->size + add > a)
- return -EMSGSIZE;
-
- if (p->_data) {
- void *d;
-
- d = realloc(p->_data, a);
- if (!d)
- return -ENOMEM;
-
- p->_data = d;
- } else {
- p->_data = malloc(a);
- if (!p->_data)
- return -ENOMEM;
-
- memcpy(p->_data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size);
- memzero((uint8_t*) p->_data + p->size, a - p->size);
- }
-
- p->allocated = a;
- }
-
- if (start)
- *start = p->size;
-
- if (ret)
- *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size;
-
- p->size += add;
- return 0;
-}
-
-void dns_packet_truncate(DnsPacket *p, size_t sz) {
- Iterator i;
- char *s;
- void *n;
-
- assert(p);
-
- if (p->size <= sz)
- return;
-
- HASHMAP_FOREACH_KEY(n, s, p->names, i) {
-
- if (PTR_TO_SIZE(n) < sz)
- continue;
-
- hashmap_remove(p->names, s);
- free(s);
- }
-
- p->size = sz;
-}
-
-int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) {
- void *q;
- int r;
-
- assert(p);
-
- r = dns_packet_extend(p, l, &q, start);
- if (r < 0)
- return r;
-
- memcpy(q, d, l);
- return 0;
-}
-
-int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) {
- void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_extend(p, sizeof(uint8_t), &d, start);
- if (r < 0)
- return r;
-
- ((uint8_t*) d)[0] = v;
-
- return 0;
-}
-
-int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) {
- void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_extend(p, sizeof(uint16_t), &d, start);
- if (r < 0)
- return r;
-
- unaligned_write_be16(d, v);
-
- return 0;
-}
-
-int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) {
- void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_extend(p, sizeof(uint32_t), &d, start);
- if (r < 0)
- return r;
-
- unaligned_write_be32(d, v);
-
- return 0;
-}
-
-int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) {
- assert(p);
- assert(s);
-
- return dns_packet_append_raw_string(p, s, strlen(s), start);
-}
-
-int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start) {
- void *d;
- int r;
-
- assert(p);
- assert(s || size == 0);
-
- if (size > 255)
- return -E2BIG;
-
- r = dns_packet_extend(p, 1 + size, &d, start);
- if (r < 0)
- return r;
-
- ((uint8_t*) d)[0] = (uint8_t) size;
-
- memcpy_safe(((uint8_t*) d) + 1, s, size);
-
- return 0;
-}
-
-int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonical_candidate, size_t *start) {
- uint8_t *w;
- int r;
-
- /* Append a label to a packet. Optionally, does this in DNSSEC
- * canonical form, if this label is marked as a candidate for
- * it, and the canonical form logic is enabled for the
- * packet */
-
- assert(p);
- assert(d);
-
- if (l > DNS_LABEL_MAX)
- return -E2BIG;
-
- r = dns_packet_extend(p, 1 + l, (void**) &w, start);
- if (r < 0)
- return r;
-
- *(w++) = (uint8_t) l;
-
- if (p->canonical_form && canonical_candidate) {
- size_t i;
-
- /* Generate in canonical form, as defined by DNSSEC
- * RFC 4034, Section 6.2, i.e. all lower-case. */
-
- for (i = 0; i < l; i++)
- w[i] = (uint8_t) ascii_tolower(d[i]);
- } else
- /* Otherwise, just copy the string unaltered. This is
- * essential for DNS-SD, where the casing of labels
- * matters and needs to be retained. */
- memcpy(w, d, l);
-
- return 0;
-}
-
-int dns_packet_append_name(
- DnsPacket *p,
- const char *name,
- bool allow_compression,
- bool canonical_candidate,
- size_t *start) {
-
- size_t saved_size;
- int r;
-
- assert(p);
- assert(name);
-
- if (p->refuse_compression)
- allow_compression = false;
-
- saved_size = p->size;
-
- while (!dns_name_is_root(name)) {
- const char *z = name;
- char label[DNS_LABEL_MAX];
- size_t n = 0;
-
- if (allow_compression)
- n = PTR_TO_SIZE(hashmap_get(p->names, name));
- if (n > 0) {
- assert(n < p->size);
-
- if (n < 0x4000) {
- r = dns_packet_append_uint16(p, 0xC000 | n, NULL);
- if (r < 0)
- goto fail;
-
- goto done;
- }
- }
-
- r = dns_label_unescape(&name, label, sizeof(label));
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_label(p, label, r, canonical_candidate, &n);
- if (r < 0)
- goto fail;
-
- if (allow_compression) {
- _cleanup_free_ char *s = NULL;
-
- s = strdup(z);
- if (!s) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops);
- if (r < 0)
- goto fail;
-
- r = hashmap_put(p->names, s, SIZE_TO_PTR(n));
- if (r < 0)
- goto fail;
-
- s = NULL;
- }
- }
-
- r = dns_packet_append_uint8(p, 0, NULL);
- if (r < 0)
- return r;
-
-done:
- if (start)
- *start = saved_size;
-
- return 0;
-
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) {
- size_t saved_size;
- int r;
-
- assert(p);
- assert(k);
-
- saved_size = p->size;
-
- r = dns_packet_append_name(p, dns_resource_key_name(k), true, true, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, k->type, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, k->class, NULL);
- if (r < 0)
- goto fail;
-
- if (start)
- *start = saved_size;
-
- return 0;
-
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, const uint8_t *types, size_t *start) {
- size_t saved_size;
- int r;
-
- assert(p);
- assert(types);
- assert(length > 0);
-
- saved_size = p->size;
-
- r = dns_packet_append_uint8(p, window, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, length, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, types, length, NULL);
- if (r < 0)
- goto fail;
-
- if (start)
- *start = saved_size;
-
- return 0;
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
- Iterator i;
- uint8_t window = 0;
- uint8_t entry = 0;
- uint8_t bitmaps[32] = {};
- unsigned n;
- size_t saved_size;
- int r;
-
- assert(p);
-
- saved_size = p->size;
-
- BITMAP_FOREACH(n, types, i) {
- assert(n <= 0xffff);
-
- if ((n >> 8) != window && bitmaps[entry / 8] != 0) {
- r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
- if (r < 0)
- goto fail;
-
- zero(bitmaps);
- }
-
- window = n >> 8;
- entry = n & 255;
-
- bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
- }
-
- if (bitmaps[entry / 8] != 0) {
- r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
- if (r < 0)
- goto fail;
- }
-
- if (start)
- *start = saved_size;
-
- return 0;
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-/* Append the OPT pseudo-RR described in RFC6891 */
-int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start) {
- size_t saved_size;
- int r;
-
- assert(p);
- /* we must never advertise supported packet size smaller than the legacy max */
- assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
- assert(rcode >= 0);
- assert(rcode <= _DNS_RCODE_MAX);
-
- if (p->opt_start != (size_t) -1)
- return -EBUSY;
-
- assert(p->opt_size == (size_t) -1);
-
- saved_size = p->size;
-
- /* empty name */
- r = dns_packet_append_uint8(p, 0, NULL);
- if (r < 0)
- return r;
-
- /* type */
- r = dns_packet_append_uint16(p, DNS_TYPE_OPT, NULL);
- if (r < 0)
- goto fail;
-
- /* class: maximum udp packet that can be received */
- r = dns_packet_append_uint16(p, max_udp_size, NULL);
- if (r < 0)
- goto fail;
-
- /* extended RCODE and VERSION */
- r = dns_packet_append_uint16(p, ((uint16_t) rcode & 0x0FF0) << 4, NULL);
- if (r < 0)
- goto fail;
-
- /* flags: DNSSEC OK (DO), see RFC3225 */
- r = dns_packet_append_uint16(p, edns0_do ? EDNS0_OPT_DO : 0, NULL);
- if (r < 0)
- goto fail;
-
- /* RDLENGTH */
- if (edns0_do && !DNS_PACKET_QR(p)) {
- /* If DO is on and this is not a reply, also append RFC6975 Algorithm data */
-
- static const uint8_t rfc6975[] = {
-
- 0, 5, /* OPTION_CODE: DAU */
- 0, 6, /* LIST_LENGTH */
- DNSSEC_ALGORITHM_RSASHA1,
- DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
- DNSSEC_ALGORITHM_RSASHA256,
- DNSSEC_ALGORITHM_RSASHA512,
- DNSSEC_ALGORITHM_ECDSAP256SHA256,
- DNSSEC_ALGORITHM_ECDSAP384SHA384,
-
- 0, 6, /* OPTION_CODE: DHU */
- 0, 3, /* LIST_LENGTH */
- DNSSEC_DIGEST_SHA1,
- DNSSEC_DIGEST_SHA256,
- DNSSEC_DIGEST_SHA384,
-
- 0, 7, /* OPTION_CODE: N3U */
- 0, 1, /* LIST_LENGTH */
- NSEC3_ALGORITHM_SHA1,
- };
-
- r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
- } else
- r = dns_packet_append_uint16(p, 0, NULL);
- if (r < 0)
- goto fail;
-
- DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) + 1);
-
- p->opt_start = saved_size;
- p->opt_size = p->size - saved_size;
-
- if (start)
- *start = saved_size;
-
- return 0;
-
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-int dns_packet_truncate_opt(DnsPacket *p) {
- assert(p);
-
- if (p->opt_start == (size_t) -1) {
- assert(p->opt_size == (size_t) -1);
- return 0;
- }
-
- assert(p->opt_size != (size_t) -1);
- assert(DNS_PACKET_ARCOUNT(p) > 0);
-
- if (p->opt_start + p->opt_size != p->size)
- return -EBUSY;
-
- dns_packet_truncate(p, p->opt_start);
- DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) - 1);
- p->opt_start = p->opt_size = (size_t) -1;
-
- return 1;
-}
-
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
-
- size_t saved_size, rdlength_offset, end, rdlength, rds;
- int r;
-
- assert(p);
- assert(rr);
-
- saved_size = p->size;
-
- r = dns_packet_append_key(p, rr->key, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->ttl, NULL);
- if (r < 0)
- goto fail;
-
- /* Initially we write 0 here */
- r = dns_packet_append_uint16(p, 0, &rdlength_offset);
- if (r < 0)
- goto fail;
-
- rds = p->size - saved_size;
-
- switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
-
- case DNS_TYPE_SRV:
- r = dns_packet_append_uint16(p, rr->srv.priority, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, rr->srv.weight, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, rr->srv.port, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);
- break;
-
- case DNS_TYPE_HINFO:
- r = dns_packet_append_string(p, rr->hinfo.cpu, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_string(p, rr->hinfo.os, NULL);
- break;
-
- case DNS_TYPE_SPF: /* exactly the same as TXT */
- case DNS_TYPE_TXT:
-
- if (!rr->txt.items) {
- /* RFC 6763, section 6.1 suggests to generate
- * single empty string for an empty array. */
-
- r = dns_packet_append_raw_string(p, NULL, 0, NULL);
- if (r < 0)
- goto fail;
- } else {
- DnsTxtItem *i;
-
- LIST_FOREACH(items, i, rr->txt.items) {
- r = dns_packet_append_raw_string(p, i->data, i->length, NULL);
- if (r < 0)
- goto fail;
- }
- }
-
- r = 0;
- break;
-
- case DNS_TYPE_A:
- r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
- break;
-
- case DNS_TYPE_AAAA:
- r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
- break;
-
- case DNS_TYPE_SOA:
- r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->soa.serial, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->soa.refresh, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->soa.retry, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->soa.expire, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->soa.minimum, NULL);
- break;
-
- case DNS_TYPE_MX:
- r = dns_packet_append_uint16(p, rr->mx.priority, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);
- break;
-
- case DNS_TYPE_LOC:
- r = dns_packet_append_uint8(p, rr->loc.version, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->loc.size, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->loc.horiz_pre, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->loc.vert_pre, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->loc.latitude, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->loc.longitude, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->loc.altitude, NULL);
- break;
-
- case DNS_TYPE_DS:
- r = dns_packet_append_uint16(p, rr->ds.key_tag, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->ds.algorithm, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->ds.digest_type, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->ds.digest, rr->ds.digest_size, NULL);
- break;
-
- case DNS_TYPE_SSHFP:
- r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, NULL);
- break;
-
- case DNS_TYPE_DNSKEY:
- r = dns_packet_append_uint16(p, rr->dnskey.flags, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->dnskey.protocol, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->dnskey.algorithm, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->dnskey.key, rr->dnskey.key_size, NULL);
- break;
-
- case DNS_TYPE_RRSIG:
- r = dns_packet_append_uint16(p, rr->rrsig.type_covered, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->rrsig.algorithm, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->rrsig.labels, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->rrsig.original_ttl, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->rrsig.expiration, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint32(p, rr->rrsig.inception, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, rr->rrsig.key_tag, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL);
- break;
-
- case DNS_TYPE_NSEC:
- r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_types(p, rr->nsec.types, NULL);
- if (r < 0)
- goto fail;
-
- break;
-
- case DNS_TYPE_NSEC3:
- r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_types(p, rr->nsec3.types, NULL);
- if (r < 0)
- goto fail;
-
- break;
-
- case DNS_TYPE_TLSA:
- r = dns_packet_append_uint8(p, rr->tlsa.cert_usage, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->tlsa.selector, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_uint8(p, rr->tlsa.matching_type, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL);
- break;
-
- case DNS_TYPE_CAA:
- r = dns_packet_append_uint8(p, rr->caa.flags, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_string(p, rr->caa.tag, NULL);
- if (r < 0)
- goto fail;
-
- r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL);
- break;
-
- case DNS_TYPE_OPT:
- case DNS_TYPE_OPENPGPKEY:
- case _DNS_TYPE_INVALID: /* unparseable */
- default:
-
- r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL);
- break;
- }
- if (r < 0)
- goto fail;
-
- /* Let's calculate the actual data size and update the field */
- rdlength = p->size - rdlength_offset - sizeof(uint16_t);
- if (rdlength > 0xFFFF) {
- r = -ENOSPC;
- goto fail;
- }
-
- end = p->size;
- p->size = rdlength_offset;
- r = dns_packet_append_uint16(p, rdlength, NULL);
- if (r < 0)
- goto fail;
- p->size = end;
-
- if (start)
- *start = saved_size;
-
- if (rdata_start)
- *rdata_start = rds;
-
- return 0;
-
-fail:
- dns_packet_truncate(p, saved_size);
- return r;
-}
-
-int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
- DnsResourceKey *key;
- int r;
-
- assert(p);
-
- DNS_QUESTION_FOREACH(key, q) {
- r = dns_packet_append_key(p, key, NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
- DnsResourceRecord *rr;
- int r;
-
- assert(p);
-
- DNS_ANSWER_FOREACH(rr, a) {
- r = dns_packet_append_rr(p, rr, NULL, NULL);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
- assert(p);
-
- if (p->rindex + sz > p->size)
- return -EMSGSIZE;
-
- if (ret)
- *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex;
-
- if (start)
- *start = p->rindex;
-
- p->rindex += sz;
- return 0;
-}
-
-void dns_packet_rewind(DnsPacket *p, size_t idx) {
- assert(p);
- assert(idx <= p->size);
- assert(idx >= DNS_PACKET_HEADER_SIZE);
-
- p->rindex = idx;
-}
-
-int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) {
- const void *q;
- int r;
-
- assert(p);
- assert(d);
-
- r = dns_packet_read(p, sz, &q, start);
- if (r < 0)
- return r;
-
- memcpy(d, q, sz);
- return 0;
-}
-
-static int dns_packet_read_memdup(
- DnsPacket *p, size_t size,
- void **ret, size_t *ret_size,
- size_t *ret_start) {
-
- const void *src;
- size_t start;
- int r;
-
- assert(p);
- assert(ret);
-
- r = dns_packet_read(p, size, &src, &start);
- if (r < 0)
- return r;
-
- if (size <= 0)
- *ret = NULL;
- else {
- void *copy;
-
- copy = memdup(src, size);
- if (!copy)
- return -ENOMEM;
-
- *ret = copy;
- }
-
- if (ret_size)
- *ret_size = size;
- if (ret_start)
- *ret_start = start;
-
- return 0;
-}
-
-int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) {
- const void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_read(p, sizeof(uint8_t), &d, start);
- if (r < 0)
- return r;
-
- *ret = ((uint8_t*) d)[0];
- return 0;
-}
-
-int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) {
- const void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_read(p, sizeof(uint16_t), &d, start);
- if (r < 0)
- return r;
-
- *ret = unaligned_read_be16(d);
-
- return 0;
-}
-
-int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
- const void *d;
- int r;
-
- assert(p);
-
- r = dns_packet_read(p, sizeof(uint32_t), &d, start);
- if (r < 0)
- return r;
-
- *ret = unaligned_read_be32(d);
-
- return 0;
-}
-
-int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- const void *d;
- char *t;
- uint8_t c;
- int r;
-
- assert(p);
- INIT_REWINDER(rewinder, p);
-
- r = dns_packet_read_uint8(p, &c, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read(p, c, &d, NULL);
- if (r < 0)
- return r;
-
- if (memchr(d, 0, c))
- return -EBADMSG;
-
- t = strndup(d, c);
- if (!t)
- return -ENOMEM;
-
- if (!utf8_is_valid(t)) {
- free(t);
- return -EBADMSG;
- }
-
- *ret = t;
-
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- uint8_t c;
- int r;
-
- assert(p);
- INIT_REWINDER(rewinder, p);
-
- r = dns_packet_read_uint8(p, &c, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read(p, c, ret, NULL);
- if (r < 0)
- return r;
-
- if (size)
- *size = c;
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-int dns_packet_read_name(
- DnsPacket *p,
- char **_ret,
- bool allow_compression,
- size_t *start) {
-
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- size_t after_rindex = 0, jump_barrier;
- _cleanup_free_ char *ret = NULL;
- size_t n = 0, allocated = 0;
- bool first = true;
- int r;
-
- assert(p);
- assert(_ret);
- INIT_REWINDER(rewinder, p);
- jump_barrier = p->rindex;
-
- if (p->refuse_compression)
- allow_compression = false;
-
- for (;;) {
- uint8_t c, d;
-
- r = dns_packet_read_uint8(p, &c, NULL);
- if (r < 0)
- return r;
-
- if (c == 0)
- /* End of name */
- break;
- else if (c <= 63) {
- const char *label;
-
- /* Literal label */
- r = dns_packet_read(p, c, (const void**) &label, NULL);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
- return -ENOMEM;
-
- if (first)
- first = false;
- else
- ret[n++] = '.';
-
- r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
- if (r < 0)
- return r;
-
- n += r;
- continue;
- } else if (allow_compression && (c & 0xc0) == 0xc0) {
- uint16_t ptr;
-
- /* Pointer */
- r = dns_packet_read_uint8(p, &d, NULL);
- if (r < 0)
- return r;
-
- ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
- if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier)
- return -EBADMSG;
-
- if (after_rindex == 0)
- after_rindex = p->rindex;
-
- /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
- jump_barrier = ptr;
- p->rindex = ptr;
- } else
- return -EBADMSG;
- }
-
- if (!GREEDY_REALLOC(ret, allocated, n + 1))
- return -ENOMEM;
-
- ret[n] = 0;
-
- if (after_rindex != 0)
- p->rindex= after_rindex;
-
- *_ret = ret;
- ret = NULL;
-
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
- uint8_t window;
- uint8_t length;
- const uint8_t *bitmap;
- uint8_t bit = 0;
- unsigned i;
- bool found = false;
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- int r;
-
- assert(p);
- assert(types);
- INIT_REWINDER(rewinder, p);
-
- r = bitmap_ensure_allocated(types);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &window, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &length, NULL);
- if (r < 0)
- return r;
-
- if (length == 0 || length > 32)
- return -EBADMSG;
-
- r = dns_packet_read(p, length, (const void **)&bitmap, NULL);
- if (r < 0)
- return r;
-
- for (i = 0; i < length; i++) {
- uint8_t bitmask = 1 << 7;
-
- if (!bitmap[i]) {
- found = false;
- bit += 8;
- continue;
- }
-
- found = true;
-
- while (bitmask) {
- if (bitmap[i] & bitmask) {
- uint16_t n;
-
- n = (uint16_t) window << 8 | (uint16_t) bit;
-
- /* Ignore pseudo-types. see RFC4034 section 4.1.2 */
- if (dns_type_is_pseudo(n))
- continue;
-
- r = bitmap_set(*types, n);
- if (r < 0)
- return r;
- }
-
- bit++;
- bitmask >>= 1;
- }
- }
-
- if (!found)
- return -EBADMSG;
-
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- int r;
-
- INIT_REWINDER(rewinder, p);
-
- while (p->rindex < rewinder.saved_rindex + size) {
- r = dns_packet_read_type_window(p, types, NULL);
- if (r < 0)
- return r;
-
- /* don't read past end of current RR */
- if (p->rindex > rewinder.saved_rindex + size)
- return -EBADMSG;
- }
-
- if (p->rindex != rewinder.saved_rindex + size)
- return -EBADMSG;
-
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- _cleanup_free_ char *name = NULL;
- bool cache_flush = false;
- uint16_t class, type;
- DnsResourceKey *key;
- int r;
-
- assert(p);
- assert(ret);
- INIT_REWINDER(rewinder, p);
-
- r = dns_packet_read_name(p, &name, true, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint16(p, &type, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint16(p, &class, NULL);
- if (r < 0)
- return r;
-
- if (p->protocol == DNS_PROTOCOL_MDNS) {
- /* See RFC6762, Section 10.2 */
-
- if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
- class &= ~MDNS_RR_CACHE_FLUSH;
- cache_flush = true;
- }
- }
-
- key = dns_resource_key_new_consume(class, type, name);
- if (!key)
- return -ENOMEM;
-
- name = NULL;
- *ret = key;
-
- if (ret_cache_flush)
- *ret_cache_flush = cache_flush;
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-static bool loc_size_ok(uint8_t size) {
- uint8_t m = size >> 4, e = size & 0xF;
-
- return m <= 9 && e <= 9 && (m > 0 || e == 0);
-}
-
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
- size_t offset;
- uint16_t rdlength;
- bool cache_flush;
- int r;
-
- assert(p);
- assert(ret);
-
- INIT_REWINDER(rewinder, p);
-
- r = dns_packet_read_key(p, &key, &cache_flush, NULL);
- if (r < 0)
- return r;
-
- if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type))
- return -EBADMSG;
-
- rr = dns_resource_record_new(key);
- if (!rr)
- return -ENOMEM;
-
- r = dns_packet_read_uint32(p, &rr->ttl, NULL);
- if (r < 0)
- return r;
-
- /* RFC 2181, Section 8, suggests to
- * treat a TTL with the MSB set as a zero TTL. */
- if (rr->ttl & UINT32_C(0x80000000))
- rr->ttl = 0;
-
- r = dns_packet_read_uint16(p, &rdlength, NULL);
- if (r < 0)
- return r;
-
- if (p->rindex + rdlength > p->size)
- return -EBADMSG;
-
- offset = p->rindex;
-
- switch (rr->key->type) {
-
- case DNS_TYPE_SRV:
- r = dns_packet_read_uint16(p, &rr->srv.priority, NULL);
- if (r < 0)
- return r;
- r = dns_packet_read_uint16(p, &rr->srv.weight, NULL);
- if (r < 0)
- return r;
- r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
- if (r < 0)
- return r;
- r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- r = dns_packet_read_name(p, &rr->ptr.name, true, NULL);
- break;
-
- case DNS_TYPE_HINFO:
- r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
- break;
-
- case DNS_TYPE_SPF: /* exactly the same as TXT */
- case DNS_TYPE_TXT:
- if (rdlength <= 0) {
- DnsTxtItem *i;
- /* RFC 6763, section 6.1 suggests to treat
- * empty TXT RRs as equivalent to a TXT record
- * with a single empty string. */
-
- i = malloc0(offsetof(DnsTxtItem, data) + 1); /* for safety reasons we add an extra NUL byte */
- if (!i)
- return -ENOMEM;
-
- rr->txt.items = i;
- } else {
- DnsTxtItem *last = NULL;
-
- while (p->rindex < offset + rdlength) {
- DnsTxtItem *i;
- const void *data;
- size_t sz;
-
- r = dns_packet_read_raw_string(p, &data, &sz, NULL);
- if (r < 0)
- return r;
-
- i = malloc0(offsetof(DnsTxtItem, data) + sz + 1); /* extra NUL byte at the end */
- if (!i)
- return -ENOMEM;
-
- memcpy(i->data, data, sz);
- i->length = sz;
-
- LIST_INSERT_AFTER(items, rr->txt.items, last, i);
- last = i;
- }
- }
-
- r = 0;
- break;
-
- case DNS_TYPE_A:
- r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL);
- break;
-
- case DNS_TYPE_AAAA:
- r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL);
- break;
-
- case DNS_TYPE_SOA:
- r = dns_packet_read_name(p, &rr->soa.mname, true, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_name(p, &rr->soa.rname, true, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
- break;
-
- case DNS_TYPE_MX:
- r = dns_packet_read_uint16(p, &rr->mx.priority, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL);
- break;
-
- case DNS_TYPE_LOC: {
- uint8_t t;
- size_t pos;
-
- r = dns_packet_read_uint8(p, &t, &pos);
- if (r < 0)
- return r;
-
- if (t == 0) {
- rr->loc.version = t;
-
- r = dns_packet_read_uint8(p, &rr->loc.size, NULL);
- if (r < 0)
- return r;
-
- if (!loc_size_ok(rr->loc.size))
- return -EBADMSG;
-
- r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL);
- if (r < 0)
- return r;
-
- if (!loc_size_ok(rr->loc.horiz_pre))
- return -EBADMSG;
-
- r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL);
- if (r < 0)
- return r;
-
- if (!loc_size_ok(rr->loc.vert_pre))
- return -EBADMSG;
-
- r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL);
- if (r < 0)
- return r;
-
- break;
- } else {
- dns_packet_rewind(p, pos);
- rr->unparseable = true;
- goto unparseable;
- }
- }
-
- case DNS_TYPE_DS:
- r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, rdlength - 4,
- &rr->ds.digest, &rr->ds.digest_size,
- NULL);
- if (r < 0)
- return r;
-
- if (rr->ds.digest_size <= 0)
- /* the accepted size depends on the algorithm, but for now
- just ensure that the value is greater than zero */
- return -EBADMSG;
-
- break;
-
- case DNS_TYPE_SSHFP:
- r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, rdlength - 2,
- &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
- NULL);
-
- if (rr->sshfp.fingerprint_size <= 0)
- /* the accepted size depends on the algorithm, but for now
- just ensure that the value is greater than zero */
- return -EBADMSG;
-
- break;
-
- case DNS_TYPE_DNSKEY:
- r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, rdlength - 4,
- &rr->dnskey.key, &rr->dnskey.key_size,
- NULL);
-
- if (rr->dnskey.key_size <= 0)
- /* the accepted size depends on the algorithm, but for now
- just ensure that the value is greater than zero */
- return -EBADMSG;
-
- break;
-
- case DNS_TYPE_RRSIG:
- r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
- &rr->rrsig.signature, &rr->rrsig.signature_size,
- NULL);
-
- if (rr->rrsig.signature_size <= 0)
- /* the accepted size depends on the algorithm, but for now
- just ensure that the value is greater than zero */
- return -EBADMSG;
-
- break;
-
- case DNS_TYPE_NSEC: {
-
- /*
- * RFC6762, section 18.14 explictly states mDNS should use name compression.
- * This contradicts RFC3845, section 2.1.1
- */
-
- bool allow_compressed = p->protocol == DNS_PROTOCOL_MDNS;
-
- r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
-
- /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
- * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
- * without the NSEC bit set. */
-
- break;
- }
- case DNS_TYPE_NSEC3: {
- uint8_t size;
-
- r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL);
- if (r < 0)
- return r;
-
- /* this may be zero */
- r = dns_packet_read_uint8(p, &size, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &size, NULL);
- if (r < 0)
- return r;
-
- if (size <= 0)
- return -EBADMSG;
-
- r = dns_packet_read_memdup(p, size,
- &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size,
- NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
-
- /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
-
- break;
- }
-
- case DNS_TYPE_TLSA:
- r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p, rdlength - 3,
- &rr->tlsa.data, &rr->tlsa.data_size,
- NULL);
-
- if (rr->tlsa.data_size <= 0)
- /* the accepted size depends on the algorithm, but for now
- just ensure that the value is greater than zero */
- return -EBADMSG;
-
- break;
-
- case DNS_TYPE_CAA:
- r = dns_packet_read_uint8(p, &rr->caa.flags, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_string(p, &rr->caa.tag, NULL);
- if (r < 0)
- return r;
-
- r = dns_packet_read_memdup(p,
- rdlength + offset - p->rindex,
- &rr->caa.value, &rr->caa.value_size, NULL);
-
- break;
-
- case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
- case DNS_TYPE_OPENPGPKEY:
- default:
- unparseable:
- r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
-
- break;
- }
- if (r < 0)
- return r;
- if (p->rindex != offset + rdlength)
- return -EBADMSG;
-
- *ret = rr;
- rr = NULL;
-
- if (ret_cache_flush)
- *ret_cache_flush = cache_flush;
- if (start)
- *start = rewinder.saved_rindex;
- CANCEL_REWINDER(rewinder);
-
- return 0;
-}
-
-static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
- const uint8_t* p;
- bool found_dau_dhu_n3u = false;
- size_t l;
-
- /* Checks whether the specified OPT RR is well-formed and whether it contains RFC6975 data (which is not OK in
- * a reply). */
-
- assert(rr);
- assert(rr->key->type == DNS_TYPE_OPT);
-
- /* Check that the version is 0 */
- if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0) {
- *rfc6975 = false;
- return true; /* if it's not version 0, it's OK, but we will ignore the OPT field contents */
- }
-
- p = rr->opt.data;
- l = rr->opt.data_size;
- while (l > 0) {
- uint16_t option_code, option_length;
-
- /* At least four bytes for OPTION-CODE and OPTION-LENGTH are required */
- if (l < 4U)
- return false;
-
- option_code = unaligned_read_be16(p);
- option_length = unaligned_read_be16(p + 2);
-
- if (l < option_length + 4U)
- return false;
-
- /* RFC 6975 DAU, DHU or N3U fields found. */
- if (IN_SET(option_code, 5, 6, 7))
- found_dau_dhu_n3u = true;
-
- p += option_length + 4U;
- l -= option_length + 4U;
- }
-
- *rfc6975 = found_dau_dhu_n3u;
- return true;
-}
-
-int dns_packet_extract(DnsPacket *p) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
- unsigned n, i;
- int r;
-
- if (p->extracted)
- return 0;
-
- INIT_REWINDER(rewinder, p);
- dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
-
- n = DNS_PACKET_QDCOUNT(p);
- if (n > 0) {
- question = dns_question_new(n);
- if (!question)
- return -ENOMEM;
-
- for (i = 0; i < n; i++) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- bool cache_flush;
-
- r = dns_packet_read_key(p, &key, &cache_flush, NULL);
- if (r < 0)
- return r;
-
- if (cache_flush)
- return -EBADMSG;
-
- if (!dns_type_is_valid_query(key->type))
- return -EBADMSG;
-
- r = dns_question_add(question, key);
- if (r < 0)
- return r;
- }
- }
-
- n = DNS_PACKET_RRCOUNT(p);
- if (n > 0) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
- bool bad_opt = false;
-
- answer = dns_answer_new(n);
- if (!answer)
- return -ENOMEM;
-
- for (i = 0; i < n; i++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- bool cache_flush = false;
-
- r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
- if (r < 0)
- return r;
-
- /* Try to reduce memory usage a bit */
- if (previous)
- dns_resource_key_reduce(&rr->key, &previous->key);
-
- if (rr->key->type == DNS_TYPE_OPT) {
- bool has_rfc6975;
-
- if (p->opt || bad_opt) {
- /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong
- * with the server, and if one is valid we wouldn't know which one. */
- log_debug("Multiple OPT RRs detected, ignoring all.");
- bad_opt = true;
- continue;
- }
-
- if (!dns_name_is_root(dns_resource_key_name(rr->key))) {
- /* If the OPT RR is not owned by the root domain, then it is bad, let's ignore
- * it. */
- log_debug("OPT RR is not owned by root domain, ignoring.");
- bad_opt = true;
- continue;
- }
-
- if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
- /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint
- * the EDNS implementation is borked, like the Belkin one is, hence ignore
- * it. */
- log_debug("OPT RR in wrong section, ignoring.");
- bad_opt = true;
- continue;
- }
-
- if (!opt_is_good(rr, &has_rfc6975)) {
- log_debug("Malformed OPT RR, ignoring.");
- bad_opt = true;
- continue;
- }
-
- if (DNS_PACKET_QR(p)) {
- /* Additional checks for responses */
-
- if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) {
- /* If this is a reply and we don't know the EDNS version then something
- * is weird... */
- log_debug("EDNS version newer that our request, bad server.");
- return -EBADMSG;
- }
-
- if (has_rfc6975) {
- /* If the OPT RR contains RFC6975 algorithm data, then this is indication that
- * the server just copied the OPT it got from us (which contained that data)
- * back into the reply. If so, then it doesn't properly support EDNS, as
- * RFC6975 makes it very clear that the algorithm data should only be contained
- * in questions, never in replies. Crappy Belkin routers copy the OPT data for
- * example, hence let's detect this so that we downgrade early. */
- log_debug("OPT RR contained RFC6975 data, ignoring.");
- bad_opt = true;
- continue;
- }
- }
-
- p->opt = dns_resource_record_ref(rr);
- } else {
-
- /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
- * cached. Hence mark only those RRs as cacheable by default, but not the ones from the
- * Additional or Authority sections. */
-
- r = dns_answer_add(answer, rr, p->ifindex,
- (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
- (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
- if (r < 0)
- return r;
- }
-
- /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
- * that we only do this if we actually decided to keep the RR around. */
- dns_resource_record_unref(previous);
- previous = dns_resource_record_ref(rr);
- }
-
- if (bad_opt)
- p->opt = dns_resource_record_unref(p->opt);
- }
-
- p->question = question;
- question = NULL;
-
- p->answer = answer;
- answer = NULL;
-
- p->extracted = true;
-
- /* no CANCEL, always rewind */
- return 0;
-}
-
-int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
- int r;
-
- assert(p);
- assert(key);
-
- /* Checks if the specified packet is a reply for the specified
- * key and the specified key is the only one in the question
- * section. */
-
- if (DNS_PACKET_QR(p) != 1)
- return 0;
-
- /* Let's unpack the packet, if that hasn't happened yet. */
- r = dns_packet_extract(p);
- if (r < 0)
- return r;
-
- if (p->question->n_keys != 1)
- return 0;
-
- return dns_resource_key_equal(p->question->keys[0], key);
-}
-
-static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
- [DNS_RCODE_SUCCESS] = "SUCCESS",
- [DNS_RCODE_FORMERR] = "FORMERR",
- [DNS_RCODE_SERVFAIL] = "SERVFAIL",
- [DNS_RCODE_NXDOMAIN] = "NXDOMAIN",
- [DNS_RCODE_NOTIMP] = "NOTIMP",
- [DNS_RCODE_REFUSED] = "REFUSED",
- [DNS_RCODE_YXDOMAIN] = "YXDOMAIN",
- [DNS_RCODE_YXRRSET] = "YRRSET",
- [DNS_RCODE_NXRRSET] = "NXRRSET",
- [DNS_RCODE_NOTAUTH] = "NOTAUTH",
- [DNS_RCODE_NOTZONE] = "NOTZONE",
- [DNS_RCODE_BADVERS] = "BADVERS",
- [DNS_RCODE_BADKEY] = "BADKEY",
- [DNS_RCODE_BADTIME] = "BADTIME",
- [DNS_RCODE_BADMODE] = "BADMODE",
- [DNS_RCODE_BADNAME] = "BADNAME",
- [DNS_RCODE_BADALG] = "BADALG",
- [DNS_RCODE_BADTRUNC] = "BADTRUNC",
- [DNS_RCODE_BADCOOKIE] = "BADCOOKIE",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int);
-
-static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
- [DNS_PROTOCOL_DNS] = "dns",
- [DNS_PROTOCOL_MDNS] = "mdns",
- [DNS_PROTOCOL_LLMNR] = "llmnr",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol);
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
deleted file mode 100644
index 054dc88a85..0000000000
--- a/src/resolve/resolved-dns-packet.h
+++ /dev/null
@@ -1,303 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-
-#include "hashmap.h"
-#include "in-addr-util.h"
-#include "macro.h"
-#include "sparse-endian.h"
-
-typedef struct DnsPacketHeader DnsPacketHeader;
-typedef struct DnsPacket DnsPacket;
-
-#include "resolved-def.h"
-#include "resolved-dns-answer.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-rr.h"
-
-typedef enum DnsProtocol {
- DNS_PROTOCOL_DNS,
- DNS_PROTOCOL_MDNS,
- DNS_PROTOCOL_LLMNR,
- _DNS_PROTOCOL_MAX,
- _DNS_PROTOCOL_INVALID = -1
-} DnsProtocol;
-
-struct DnsPacketHeader {
- uint16_t id;
- be16_t flags;
- be16_t qdcount;
- be16_t ancount;
- be16_t nscount;
- be16_t arcount;
-};
-
-#define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader)
-#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))
-
-/* The various DNS protocols deviate in how large a packet can grow,
- but the TCP transport has a 16bit size field, hence that appears to
- be the absolute maximum. */
-#define DNS_PACKET_SIZE_MAX 0xFFFF
-
-/* RFC 1035 say 512 is the maximum, for classic unicast DNS */
-#define DNS_PACKET_UNICAST_SIZE_MAX 512
-
-/* With EDNS0 we can use larger packets, default to 4096, which is what is commonly used */
-#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096
-
-#define DNS_PACKET_SIZE_START 512
-
-struct DnsPacket {
- int n_ref;
- DnsProtocol protocol;
- size_t size, allocated, rindex;
- void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
- Hashmap *names; /* For name compression */
- size_t opt_start, opt_size;
-
- /* Parsed data */
- DnsQuestion *question;
- DnsAnswer *answer;
- DnsResourceRecord *opt;
-
- /* Packet reception metadata */
- int ifindex;
- int family, ipproto;
- union in_addr_union sender, destination;
- uint16_t sender_port, destination_port;
- uint32_t ttl;
-
- /* For support of truncated packets */
- DnsPacket *more;
-
- bool on_stack:1;
- bool extracted:1;
- bool refuse_compression:1;
- bool canonical_form:1;
-};
-
-static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
- if (_unlikely_(!p))
- return NULL;
-
- if (p->_data)
- return p->_data;
-
- return ((uint8_t*) p) + ALIGN(sizeof(DnsPacket));
-}
-
-#define DNS_PACKET_HEADER(p) ((DnsPacketHeader*) DNS_PACKET_DATA(p))
-#define DNS_PACKET_ID(p) DNS_PACKET_HEADER(p)->id
-#define DNS_PACKET_QR(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 15) & 1)
-#define DNS_PACKET_OPCODE(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 11) & 15)
-#define DNS_PACKET_AA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1)
-#define DNS_PACKET_TC(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 9) & 1)
-#define DNS_PACKET_RD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1)
-#define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1)
-#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
-#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
-
-#define DNS_PACKET_FLAG_TC (UINT16_C(1) << 9)
-
-static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
- uint16_t rcode;
-
- if (p->opt)
- rcode = (uint16_t) (p->opt->ttl >> 24);
- else
- rcode = 0;
-
- return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 0xF);
-}
-
-static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) {
-
- /* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */
-
- if (p->opt)
- return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class);
-
- return DNS_PACKET_UNICAST_SIZE_MAX;
-}
-
-static inline bool DNS_PACKET_DO(DnsPacket *p) {
- if (!p->opt)
- return false;
-
- return !!(p->opt->ttl & (1U << 15));
-}
-
-static inline bool DNS_PACKET_VERSION_SUPPORTED(DnsPacket *p) {
- /* Returns true if this packet is in a version we support. Which means either non-EDNS or EDNS(0), but not EDNS
- * of any newer versions */
-
- if (!p->opt)
- return true;
-
- return DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(p->opt);
-}
-
-/* LLMNR defines some bits differently */
-#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p)
-#define DNS_PACKET_LLMNR_T(p) DNS_PACKET_RD(p)
-
-#define DNS_PACKET_QDCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->qdcount)
-#define DNS_PACKET_ANCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->ancount)
-#define DNS_PACKET_NSCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->nscount)
-#define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount)
-
-#define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \
- (((uint16_t) !!(qr) << 15) | \
- ((uint16_t) ((opcode) & 15) << 11) | \
- ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \
- ((uint16_t) !!(tc) << 9) | \
- ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \
- ((uint16_t) !!(ra) << 7) | \
- ((uint16_t) !!(ad) << 5) | \
- ((uint16_t) !!(cd) << 4) | \
- ((uint16_t) ((rcode) & 15)))
-
-static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
- return
- (unsigned) DNS_PACKET_ANCOUNT(p) +
- (unsigned) DNS_PACKET_NSCOUNT(p) +
- (unsigned) DNS_PACKET_ARCOUNT(p);
-}
-
-int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu);
-int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled);
-
-void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated);
-
-DnsPacket *dns_packet_ref(DnsPacket *p);
-DnsPacket *dns_packet_unref(DnsPacket *p);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref);
-
-int dns_packet_validate(DnsPacket *p);
-int dns_packet_validate_reply(DnsPacket *p);
-int dns_packet_validate_query(DnsPacket *p);
-
-int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key);
-
-int dns_packet_append_blob(DnsPacket *p, const void *d, size_t sz, size_t *start);
-int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
-int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
-int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);
-int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
-int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
-int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start);
-int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);
-int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
-int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start);
-int dns_packet_append_question(DnsPacket *p, DnsQuestion *q);
-int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a);
-
-void dns_packet_truncate(DnsPacket *p, size_t sz);
-int dns_packet_truncate_opt(DnsPacket *p);
-
-int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start);
-int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start);
-int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start);
-int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start);
-int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
-int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
-int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
-int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
-
-void dns_packet_rewind(DnsPacket *p, size_t idx);
-
-int dns_packet_skip_question(DnsPacket *p);
-int dns_packet_extract(DnsPacket *p);
-
-static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) {
- /* Never cache data originating from localhost, under the
- * assumption, that it's coming from a locally DNS forwarder
- * or server, that is caching on its own. */
-
- return in_addr_is_localhost(p->family, &p->sender) == 0;
-}
-
-/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
-enum {
- DNS_RCODE_SUCCESS = 0,
- DNS_RCODE_FORMERR = 1,
- DNS_RCODE_SERVFAIL = 2,
- DNS_RCODE_NXDOMAIN = 3,
- DNS_RCODE_NOTIMP = 4,
- DNS_RCODE_REFUSED = 5,
- DNS_RCODE_YXDOMAIN = 6,
- DNS_RCODE_YXRRSET = 7,
- DNS_RCODE_NXRRSET = 8,
- DNS_RCODE_NOTAUTH = 9,
- DNS_RCODE_NOTZONE = 10,
- DNS_RCODE_BADVERS = 16,
- DNS_RCODE_BADSIG = 16, /* duplicate value! */
- DNS_RCODE_BADKEY = 17,
- DNS_RCODE_BADTIME = 18,
- DNS_RCODE_BADMODE = 19,
- DNS_RCODE_BADNAME = 20,
- DNS_RCODE_BADALG = 21,
- DNS_RCODE_BADTRUNC = 22,
- DNS_RCODE_BADCOOKIE = 23,
- _DNS_RCODE_MAX_DEFINED,
- _DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */
-};
-
-const char* dns_rcode_to_string(int i) _const_;
-int dns_rcode_from_string(const char *s) _pure_;
-
-const char* dns_protocol_to_string(DnsProtocol p) _const_;
-DnsProtocol dns_protocol_from_string(const char *s) _pure_;
-
-#define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
-#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
-
-#define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) })
-#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } })
-
-static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
- uint64_t f;
-
- /* Converts a protocol + family into a flags field as used in queries and responses */
-
- f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0;
-
- switch (protocol) {
- case DNS_PROTOCOL_DNS:
- return f|SD_RESOLVED_DNS;
-
- case DNS_PROTOCOL_LLMNR:
- return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4);
-
- case DNS_PROTOCOL_MDNS:
- return f|(family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4);
-
- default:
- return f;
- }
-}
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
deleted file mode 100644
index e03db4d003..0000000000
--- a/src/resolve/resolved-dns-query.c
+++ /dev/null
@@ -1,1117 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "dns-type.h"
-#include "hostname-util.h"
-#include "local-addresses.h"
-#include "resolved-dns-query.h"
-#include "resolved-dns-synthesize.h"
-#include "resolved-etc-hosts.h"
-#include "string-util.h"
-
-/* How long to wait for the query in total */
-#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
-
-#define CNAME_MAX 8
-#define QUERIES_MAX 2048
-#define AUXILIARY_QUERIES_MAX 64
-
-static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
- DnsQueryCandidate *c;
-
- assert(ret);
- assert(q);
- assert(s);
-
- c = new0(DnsQueryCandidate, 1);
- if (!c)
- return -ENOMEM;
-
- c->query = q;
- c->scope = s;
-
- LIST_PREPEND(candidates_by_query, q->candidates, c);
- LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
-
- *ret = c;
- return 0;
-}
-
-static void dns_query_candidate_stop(DnsQueryCandidate *c) {
- DnsTransaction *t;
-
- assert(c);
-
- while ((t = set_steal_first(c->transactions))) {
- set_remove(t->notify_query_candidates, c);
- set_remove(t->notify_query_candidates_done, c);
- dns_transaction_gc(t);
- }
-}
-
-DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
-
- if (!c)
- return NULL;
-
- dns_query_candidate_stop(c);
-
- set_free(c->transactions);
- dns_search_domain_unref(c->search_domain);
-
- if (c->query)
- LIST_REMOVE(candidates_by_query, c->query->candidates, c);
-
- if (c->scope)
- LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
-
- return mfree(c);
-}
-
-static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
- DnsSearchDomain *next = NULL;
-
- assert(c);
-
- if (c->search_domain && c->search_domain->linked)
- next = c->search_domain->domains_next;
- else
- next = dns_scope_get_search_domains(c->scope);
-
- for (;;) {
- if (!next) /* We hit the end of the list */
- return 0;
-
- if (!next->route_only)
- break;
-
- /* Skip over route-only domains */
- next = next->domains_next;
- }
-
- dns_search_domain_unref(c->search_domain);
- c->search_domain = dns_search_domain_ref(next);
-
- return 1;
-}
-
-static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
- DnsTransaction *t;
- int r;
-
- assert(c);
- assert(key);
-
- t = dns_scope_find_transaction(c->scope, key, true);
- if (!t) {
- r = dns_transaction_new(&t, c->scope, key);
- if (r < 0)
- return r;
- } else {
- if (set_contains(c->transactions, t))
- return 0;
- }
-
- r = set_ensure_allocated(&c->transactions, NULL);
- if (r < 0)
- goto gc;
-
- r = set_ensure_allocated(&t->notify_query_candidates, NULL);
- if (r < 0)
- goto gc;
-
- r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
- if (r < 0)
- goto gc;
-
- r = set_put(t->notify_query_candidates, c);
- if (r < 0)
- goto gc;
-
- r = set_put(c->transactions, t);
- if (r < 0) {
- (void) set_remove(t->notify_query_candidates, c);
- goto gc;
- }
-
- t->clamp_ttl = c->query->clamp_ttl;
- return 1;
-
-gc:
- dns_transaction_gc(t);
- return r;
-}
-
-static int dns_query_candidate_go(DnsQueryCandidate *c) {
- DnsTransaction *t;
- Iterator i;
- int r;
- unsigned n = 0;
-
- assert(c);
-
- /* Start the transactions that are not started yet */
- SET_FOREACH(t, c->transactions, i) {
- if (t->state != DNS_TRANSACTION_NULL)
- continue;
-
- r = dns_transaction_go(t);
- if (r < 0)
- return r;
-
- n++;
- }
-
- /* If there was nothing to start, then let's proceed immediately */
- if (n == 0)
- dns_query_candidate_notify(c);
-
- return 0;
-}
-
-static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
- DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- DnsTransaction *t;
- Iterator i;
-
- assert(c);
-
- if (c->error_code != 0)
- return DNS_TRANSACTION_ERRNO;
-
- SET_FOREACH(t, c->transactions, i) {
-
- switch (t->state) {
-
- case DNS_TRANSACTION_NULL:
- /* If there's a NULL transaction pending, then
- * this means not all transactions where
- * started yet, and we were called from within
- * the stackframe that is supposed to start
- * remaining transactions. In this case,
- * simply claim the candidate is pending. */
-
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- /* If there's one transaction currently in
- * VALIDATING state, then this means there's
- * also one in PENDING state, hence we can
- * return PENDING immediately. */
- return DNS_TRANSACTION_PENDING;
-
- case DNS_TRANSACTION_SUCCESS:
- state = t->state;
- break;
-
- default:
- if (state != DNS_TRANSACTION_SUCCESS)
- state = t->state;
-
- break;
- }
- }
-
- return state;
-}
-
-static bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) {
- int family;
-
- assert(c);
-
- /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of
- * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR,
- * or a routable IPv6 address if we query an AAAA RR. */
-
- if (!c->query->suppress_unroutable_family)
- return true;
-
- if (c->scope->protocol != DNS_PROTOCOL_DNS)
- return true;
-
- family = dns_type_to_af(type);
- if (family < 0)
- return true;
-
- if (c->scope->link)
- return link_relevant(c->scope->link, family, false);
- else
- return manager_routable(c->scope->manager, family);
-}
-
-static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
- DnsQuestion *question;
- DnsResourceKey *key;
- int n = 0, r;
-
- assert(c);
-
- dns_query_candidate_stop(c);
-
- question = dns_query_question_for_protocol(c->query, c->scope->protocol);
-
- /* Create one transaction per question key */
- DNS_QUESTION_FOREACH(key, question) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
- DnsResourceKey *qkey;
-
- if (!dns_query_candidate_is_routable(c, key->type))
- continue;
-
- if (c->search_domain) {
- r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
- if (r < 0)
- goto fail;
-
- qkey = new_key;
- } else
- qkey = key;
-
- if (!dns_scope_good_key(c->scope, qkey))
- continue;
-
- r = dns_query_candidate_add_transaction(c, qkey);
- if (r < 0)
- goto fail;
-
- n++;
- }
-
- return n;
-
-fail:
- dns_query_candidate_stop(c);
- return r;
-}
-
-void dns_query_candidate_notify(DnsQueryCandidate *c) {
- DnsTransactionState state;
- int r;
-
- assert(c);
-
- state = dns_query_candidate_state(c);
-
- if (DNS_TRANSACTION_IS_LIVE(state))
- return;
-
- if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
-
- r = dns_query_candidate_next_search_domain(c);
- if (r < 0)
- goto fail;
-
- if (r > 0) {
- /* OK, there's another search domain to try, let's do so. */
-
- r = dns_query_candidate_setup_transactions(c);
- if (r < 0)
- goto fail;
-
- if (r > 0) {
- /* New transactions where queued. Start them and wait */
-
- r = dns_query_candidate_go(c);
- if (r < 0)
- goto fail;
-
- return;
- }
- }
-
- }
-
- dns_query_ready(c->query);
- return;
-
-fail:
- log_warning_errno(r, "Failed to follow search domains: %m");
- c->error_code = r;
- dns_query_ready(c->query);
-}
-
-static void dns_query_stop(DnsQuery *q) {
- DnsQueryCandidate *c;
-
- assert(q);
-
- q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
-
- LIST_FOREACH(candidates_by_query, c, q->candidates)
- dns_query_candidate_stop(c);
-}
-
-static void dns_query_free_candidates(DnsQuery *q) {
- assert(q);
-
- while (q->candidates)
- dns_query_candidate_free(q->candidates);
-}
-
-static void dns_query_reset_answer(DnsQuery *q) {
- assert(q);
-
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = 0;
- q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- q->answer_errno = 0;
- q->answer_authenticated = false;
- q->answer_protocol = _DNS_PROTOCOL_INVALID;
- q->answer_family = AF_UNSPEC;
- q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
-}
-
-DnsQuery *dns_query_free(DnsQuery *q) {
- if (!q)
- return NULL;
-
- while (q->auxiliary_queries)
- dns_query_free(q->auxiliary_queries);
-
- if (q->auxiliary_for) {
- assert(q->auxiliary_for->n_auxiliary_queries > 0);
- q->auxiliary_for->n_auxiliary_queries--;
- LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
- }
-
- dns_query_free_candidates(q);
-
- dns_question_unref(q->question_idna);
- dns_question_unref(q->question_utf8);
-
- dns_query_reset_answer(q);
-
- sd_bus_message_unref(q->request);
- sd_bus_track_unref(q->bus_track);
-
- dns_packet_unref(q->request_dns_packet);
-
- if (q->request_dns_stream) {
- /* Detach the stream from our query, in case something else keeps a reference to it. */
- q->request_dns_stream->complete = NULL;
- q->request_dns_stream->on_packet = NULL;
- q->request_dns_stream->query = NULL;
- dns_stream_unref(q->request_dns_stream);
- }
-
- free(q->request_address_string);
-
- if (q->manager) {
- LIST_REMOVE(queries, q->manager->dns_queries, q);
- q->manager->n_dns_queries--;
- }
-
- return mfree(q);
-}
-
-int dns_query_new(
- Manager *m,
- DnsQuery **ret,
- DnsQuestion *question_utf8,
- DnsQuestion *question_idna,
- int ifindex,
- uint64_t flags) {
-
- _cleanup_(dns_query_freep) DnsQuery *q = NULL;
- DnsResourceKey *key;
- bool good = false;
- int r;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- assert(m);
-
- if (dns_question_size(question_utf8) > 0) {
- r = dns_question_is_valid_for_query(question_utf8);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- good = true;
- }
-
- /* If the IDNA and UTF8 questions are the same, merge their references */
- r = dns_question_is_equal(question_idna, question_utf8);
- if (r < 0)
- return r;
- if (r > 0)
- question_idna = question_utf8;
- else {
- if (dns_question_size(question_idna) > 0) {
- r = dns_question_is_valid_for_query(question_idna);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- good = true;
- }
- }
-
- if (!good) /* don't allow empty queries */
- return -EINVAL;
-
- if (m->n_dns_queries >= QUERIES_MAX)
- return -EBUSY;
-
- q = new0(DnsQuery, 1);
- if (!q)
- return -ENOMEM;
-
- q->question_utf8 = dns_question_ref(question_utf8);
- q->question_idna = dns_question_ref(question_idna);
- q->ifindex = ifindex;
- q->flags = flags;
- q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- q->answer_protocol = _DNS_PROTOCOL_INVALID;
- q->answer_family = AF_UNSPEC;
-
- /* First dump UTF8 question */
- DNS_QUESTION_FOREACH(key, question_utf8)
- log_debug("Looking up RR for %s.",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
-
- /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
- DNS_QUESTION_FOREACH(key, question_idna) {
- r = dns_question_contains(question_utf8, key);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- log_debug("Looking up IDNA RR for %s.",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- }
-
- LIST_PREPEND(queries, m->dns_queries, q);
- m->n_dns_queries++;
- q->manager = m;
-
- if (ret)
- *ret = q;
- q = NULL;
-
- return 0;
-}
-
-int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
- assert(q);
- assert(auxiliary_for);
-
- /* Ensure that the query is not auxiliary yet, and
- * nothing else is auxiliary to it either */
- assert(!q->auxiliary_for);
- assert(!q->auxiliary_queries);
-
- /* Ensure that the unit we shall be made auxiliary for isn't
- * auxiliary itself */
- assert(!auxiliary_for->auxiliary_for);
-
- if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
- return -EAGAIN;
-
- LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
- q->auxiliary_for = auxiliary_for;
-
- auxiliary_for->n_auxiliary_queries++;
- return 0;
-}
-
-static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
- assert(q);
- assert(!DNS_TRANSACTION_IS_LIVE(state));
- assert(DNS_TRANSACTION_IS_LIVE(q->state));
-
- /* Note that this call might invalidate the query. Callers
- * should hence not attempt to access the query or transaction
- * after calling this function. */
-
- q->state = state;
-
- dns_query_stop(q);
- if (q->complete)
- q->complete(q);
-}
-
-static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
- DnsQuery *q = userdata;
-
- assert(s);
- assert(q);
-
- dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
- return 0;
-}
-
-static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
- DnsQueryCandidate *c;
- int r;
-
- assert(q);
- assert(s);
-
- r = dns_query_candidate_new(&c, q, s);
- if (r < 0)
- return r;
-
- /* If this a single-label domain on DNS, we might append a suitable search domain first. */
- if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
- r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
- if (r < 0)
- goto fail;
- if (r > 0) {
- /* OK, we need a search domain now. Let's find one for this scope */
-
- r = dns_query_candidate_next_search_domain(c);
- if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
- goto fail;
- }
- }
-
- r = dns_query_candidate_setup_transactions(c);
- if (r < 0)
- goto fail;
-
- return 0;
-
-fail:
- dns_query_candidate_free(c);
- return r;
-}
-
-static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- int r;
-
- assert(q);
- assert(state);
-
- /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
- * the normal lookup finished. The data from the network hence takes precedence over the data we
- * synthesize. (But note that many scopes refuse to resolve certain domain names) */
-
- if (!IN_SET(*state,
- DNS_TRANSACTION_RCODE_FAILURE,
- DNS_TRANSACTION_NO_SERVERS,
- DNS_TRANSACTION_TIMEOUT,
- DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
- DNS_TRANSACTION_NETWORK_DOWN,
- DNS_TRANSACTION_NOT_FOUND))
- return 0;
-
- r = dns_synthesize_answer(
- q->manager,
- q->question_utf8,
- q->ifindex,
- &answer);
-
- if (r <= 0)
- return r;
-
- dns_query_reset_answer(q);
-
- q->answer = answer;
- answer = NULL;
- q->answer_rcode = DNS_RCODE_SUCCESS;
- q->answer_protocol = dns_synthesize_protocol(q->flags);
- q->answer_family = dns_synthesize_family(q->flags);
- q->answer_authenticated = true;
-
- *state = DNS_TRANSACTION_SUCCESS;
-
- return 1;
-}
-
-static int dns_query_try_etc_hosts(DnsQuery *q) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- int r;
-
- assert(q);
-
- /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The
- * data from /etc/hosts hence takes precedence over the network. */
-
- r = manager_etc_hosts_lookup(
- q->manager,
- q->question_utf8,
- &answer);
- if (r <= 0)
- return r;
-
- dns_query_reset_answer(q);
-
- q->answer = answer;
- answer = NULL;
- q->answer_rcode = DNS_RCODE_SUCCESS;
- q->answer_protocol = dns_synthesize_protocol(q->flags);
- q->answer_family = dns_synthesize_family(q->flags);
- q->answer_authenticated = true;
-
- return 1;
-}
-
-int dns_query_go(DnsQuery *q) {
- DnsScopeMatch found = DNS_SCOPE_NO;
- DnsScope *s, *first = NULL;
- DnsQueryCandidate *c;
- int r;
-
- assert(q);
-
- if (q->state != DNS_TRANSACTION_NULL)
- return 0;
-
- r = dns_query_try_etc_hosts(q);
- if (r < 0)
- return r;
- if (r > 0) {
- dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
- return 1;
- }
-
- LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
- DnsScopeMatch match;
- const char *name;
-
- name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
- if (!name)
- continue;
-
- match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
- if (match < 0)
- return match;
-
- if (match == DNS_SCOPE_NO)
- continue;
-
- found = match;
-
- if (match == DNS_SCOPE_YES) {
- first = s;
- break;
- } else {
- assert(match == DNS_SCOPE_MAYBE);
-
- if (!first)
- first = s;
- }
- }
-
- if (found == DNS_SCOPE_NO) {
- DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
-
- r = dns_query_synthesize_reply(q, &state);
- if (r < 0)
- return r;
-
- dns_query_complete(q, state);
- return 1;
- }
-
- r = dns_query_add_candidate(q, first);
- if (r < 0)
- goto fail;
-
- LIST_FOREACH(scopes, s, first->scopes_next) {
- DnsScopeMatch match;
- const char *name;
-
- name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
- if (!name)
- continue;
-
- match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
- if (match < 0)
- goto fail;
-
- if (match != found)
- continue;
-
- r = dns_query_add_candidate(q, s);
- if (r < 0)
- goto fail;
- }
-
- dns_query_reset_answer(q);
-
- r = sd_event_add_time(
- q->manager->event,
- &q->timeout_event_source,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
- on_query_timeout, q);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
-
- q->state = DNS_TRANSACTION_PENDING;
- q->block_ready++;
-
- /* Start the transactions */
- LIST_FOREACH(candidates_by_query, c, q->candidates) {
- r = dns_query_candidate_go(c);
- if (r < 0) {
- q->block_ready--;
- goto fail;
- }
- }
-
- q->block_ready--;
- dns_query_ready(q);
-
- return 1;
-
-fail:
- dns_query_stop(q);
- return r;
-}
-
-static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
- DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- bool has_authenticated = false, has_non_authenticated = false;
- DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
- DnsTransaction *t;
- Iterator i;
- int r;
-
- assert(q);
-
- if (!c) {
- r = dns_query_synthesize_reply(q, &state);
- if (r < 0)
- goto fail;
-
- dns_query_complete(q, state);
- return;
- }
-
- if (c->error_code != 0) {
- /* If the candidate had an error condition of its own, start with that. */
- state = DNS_TRANSACTION_ERRNO;
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = 0;
- q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- q->answer_errno = c->error_code;
- }
-
- SET_FOREACH(t, c->transactions, i) {
-
- switch (t->state) {
-
- case DNS_TRANSACTION_SUCCESS: {
- /* We found a successfully reply, merge it into the answer */
- r = dns_answer_extend(&q->answer, t->answer);
- if (r < 0)
- goto fail;
-
- q->answer_rcode = t->answer_rcode;
- q->answer_errno = 0;
-
- if (t->answer_authenticated) {
- has_authenticated = true;
- dnssec_result_authenticated = t->answer_dnssec_result;
- } else {
- has_non_authenticated = true;
- dnssec_result_non_authenticated = t->answer_dnssec_result;
- }
-
- state = DNS_TRANSACTION_SUCCESS;
- break;
- }
-
- case DNS_TRANSACTION_NULL:
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- case DNS_TRANSACTION_ABORTED:
- /* Ignore transactions that didn't complete */
- continue;
-
- default:
- /* Any kind of failure? Store the data away,
- * if there's nothing stored yet. */
-
- if (state == DNS_TRANSACTION_SUCCESS)
- continue;
-
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = t->answer_rcode;
- q->answer_dnssec_result = t->answer_dnssec_result;
- q->answer_errno = t->answer_errno;
-
- state = t->state;
- break;
- }
- }
-
- if (state == DNS_TRANSACTION_SUCCESS) {
- q->answer_authenticated = has_authenticated && !has_non_authenticated;
- q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
- }
-
- q->answer_protocol = c->scope->protocol;
- q->answer_family = c->scope->family;
-
- dns_search_domain_unref(q->answer_search_domain);
- q->answer_search_domain = dns_search_domain_ref(c->search_domain);
-
- r = dns_query_synthesize_reply(q, &state);
- if (r < 0)
- goto fail;
-
- dns_query_complete(q, state);
- return;
-
-fail:
- q->answer_errno = -r;
- dns_query_complete(q, DNS_TRANSACTION_ERRNO);
-}
-
-void dns_query_ready(DnsQuery *q) {
-
- DnsQueryCandidate *bad = NULL, *c;
- bool pending = false;
-
- assert(q);
- assert(DNS_TRANSACTION_IS_LIVE(q->state));
-
- /* Note that this call might invalidate the query. Callers
- * should hence not attempt to access the query or transaction
- * after calling this function, unless the block_ready
- * counter was explicitly bumped before doing so. */
-
- if (q->block_ready > 0)
- return;
-
- LIST_FOREACH(candidates_by_query, c, q->candidates) {
- DnsTransactionState state;
-
- state = dns_query_candidate_state(c);
- switch (state) {
-
- case DNS_TRANSACTION_SUCCESS:
- /* One of the candidates is successful,
- * let's use it, and copy its data out */
- dns_query_accept(q, c);
- return;
-
- case DNS_TRANSACTION_NULL:
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- /* One of the candidates is still going on,
- * let's maybe wait for it */
- pending = true;
- break;
-
- default:
- /* Any kind of failure */
- bad = c;
- break;
- }
- }
-
- if (pending)
- return;
-
- dns_query_accept(q, bad);
-}
-
-static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
- _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
- int r, k;
-
- assert(q);
-
- q->n_cname_redirects++;
- if (q->n_cname_redirects > CNAME_MAX)
- return -ELOOP;
-
- r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
- if (r < 0)
- return r;
- else if (r > 0)
- log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
-
- k = dns_question_is_equal(q->question_idna, q->question_utf8);
- if (k < 0)
- return r;
- if (k > 0) {
- /* Same question? Shortcut new question generation */
- nq_utf8 = dns_question_ref(nq_idna);
- k = r;
- } else {
- k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
- if (k < 0)
- return k;
- else if (k > 0)
- log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
- }
-
- if (r == 0 && k == 0) /* No actual cname happened? */
- return -ELOOP;
-
- if (q->answer_protocol == DNS_PROTOCOL_DNS) {
- /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources
- * cannot invade the local namespace. The opposite way we permit: local names may redirect to global
- * ones. */
-
- q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */
- }
-
- /* Turn off searching for the new name */
- q->flags |= SD_RESOLVED_NO_SEARCH;
-
- dns_question_unref(q->question_idna);
- q->question_idna = nq_idna;
- nq_idna = NULL;
-
- dns_question_unref(q->question_utf8);
- q->question_utf8 = nq_utf8;
- nq_utf8 = NULL;
-
- dns_query_free_candidates(q);
- dns_query_reset_answer(q);
-
- q->state = DNS_TRANSACTION_NULL;
-
- return 0;
-}
-
-int dns_query_process_cname(DnsQuery *q) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
- DnsQuestion *question;
- DnsResourceRecord *rr;
- int r;
-
- assert(q);
-
- if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
- return DNS_QUERY_NOMATCH;
-
- question = dns_query_question_for_protocol(q, q->answer_protocol);
-
- DNS_ANSWER_FOREACH(rr, q->answer) {
- r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- return r;
- if (r > 0)
- return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
-
- r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- return r;
- if (r > 0 && !cname)
- cname = dns_resource_record_ref(rr);
- }
-
- if (!cname)
- return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
-
- if (q->flags & SD_RESOLVED_NO_CNAME)
- return -ELOOP;
-
- /* OK, let's actually follow the CNAME */
- r = dns_query_cname_redirect(q, cname);
- if (r < 0)
- return r;
-
- /* Let's see if the answer can already answer the new
- * redirected question */
- r = dns_query_process_cname(q);
- if (r != DNS_QUERY_NOMATCH)
- return r;
-
- /* OK, it cannot, let's begin with the new query */
- r = dns_query_go(q);
- if (r < 0)
- return r;
-
- return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
-}
-
-static int on_bus_track(sd_bus_track *t, void *userdata) {
- DnsQuery *q = userdata;
-
- assert(t);
- assert(q);
-
- log_debug("Client of active query vanished, aborting query.");
- dns_query_complete(q, DNS_TRANSACTION_ABORTED);
- return 0;
-}
-
-int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
- int r;
-
- assert(q);
- assert(m);
-
- if (!q->bus_track) {
- r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_track_add_sender(q->bus_track, m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
- assert(q);
-
- switch (protocol) {
-
- case DNS_PROTOCOL_DNS:
- return q->question_idna;
-
- case DNS_PROTOCOL_MDNS:
- case DNS_PROTOCOL_LLMNR:
- return q->question_utf8;
-
- default:
- return NULL;
- }
-}
-
-const char *dns_query_string(DnsQuery *q) {
- const char *name;
- int r;
-
- /* Returns a somewhat useful human-readable lookup key string for this query */
-
- if (q->request_address_string)
- return q->request_address_string;
-
- if (q->request_address_valid) {
- r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
- if (r >= 0)
- return q->request_address_string;
- }
-
- name = dns_question_first_name(q->question_utf8);
- if (name)
- return name;
-
- return dns_question_first_name(q->question_idna);
-}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
deleted file mode 100644
index 49a35b846b..0000000000
--- a/src/resolve/resolved-dns-query.h
+++ /dev/null
@@ -1,141 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-
-#include "sd-bus.h"
-
-#include "set.h"
-
-typedef struct DnsQueryCandidate DnsQueryCandidate;
-typedef struct DnsQuery DnsQuery;
-
-#include "resolved-dns-answer.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-stream.h"
-#include "resolved-dns-search-domain.h"
-
-struct DnsQueryCandidate {
- DnsQuery *query;
- DnsScope *scope;
-
- DnsSearchDomain *search_domain;
-
- int error_code;
- Set *transactions;
-
- LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
- LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
-};
-
-struct DnsQuery {
- Manager *manager;
-
- /* When resolving a service, we first create a TXT+SRV query,
- * and then for the hostnames we discover auxiliary A+AAAA
- * queries. This pointer always points from the auxiliary
- * queries back to the TXT+SRV query. */
- DnsQuery *auxiliary_for;
- LIST_HEAD(DnsQuery, auxiliary_queries);
- unsigned n_auxiliary_queries;
- int auxiliary_result;
-
- /* The question, formatted in IDNA for use on classic DNS, and as UTF8 for use in LLMNR or mDNS. Note that even
- * on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names (in contrast to their
- * domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference between these two fields is mostly
- * relevant only for explicit *hostname* lookups as well as the domain suffixes of service lookups. */
- DnsQuestion *question_idna;
- DnsQuestion *question_utf8;
-
- uint64_t flags;
- int ifindex;
-
- /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address
- * family */
- bool suppress_unroutable_family;
-
-
- /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
- bool clamp_ttl;
-
- DnsTransactionState state;
- unsigned n_cname_redirects;
-
- LIST_HEAD(DnsQueryCandidate, candidates);
- sd_event_source *timeout_event_source;
-
- /* Discovered data */
- DnsAnswer *answer;
- int answer_rcode;
- DnssecResult answer_dnssec_result;
- bool answer_authenticated;
- DnsProtocol answer_protocol;
- int answer_family;
- DnsSearchDomain *answer_search_domain;
- int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
-
- /* Bus client information */
- sd_bus_message *request;
- int request_family;
- bool request_address_valid;
- union in_addr_union request_address;
- unsigned block_all_complete;
- char *request_address_string;
-
- /* DNS stub information */
- DnsPacket *request_dns_packet;
- DnsStream *request_dns_stream;
-
- /* Completion callback */
- void (*complete)(DnsQuery* q);
- unsigned block_ready;
-
- sd_bus_track *bus_track;
-
- LIST_FIELDS(DnsQuery, queries);
- LIST_FIELDS(DnsQuery, auxiliary_queries);
-};
-
-enum {
- DNS_QUERY_MATCH,
- DNS_QUERY_NOMATCH,
- DNS_QUERY_RESTARTED,
-};
-
-DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
-void dns_query_candidate_notify(DnsQueryCandidate *c);
-
-int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);
-DnsQuery *dns_query_free(DnsQuery *q);
-
-int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
-
-int dns_query_go(DnsQuery *q);
-void dns_query_ready(DnsQuery *q);
-
-int dns_query_process_cname(DnsQuery *q);
-
-int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
-
-DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol);
-
-const char *dns_query_string(DnsQuery *q);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
deleted file mode 100644
index c8b502d1cd..0000000000
--- a/src/resolve/resolved-dns-question.c
+++ /dev/null
@@ -1,468 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "dns-type.h"
-#include "resolved-dns-question.h"
-
-DnsQuestion *dns_question_new(unsigned n) {
- DnsQuestion *q;
-
- assert(n > 0);
-
- q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n);
- if (!q)
- return NULL;
-
- q->n_ref = 1;
- q->n_allocated = n;
-
- return q;
-}
-
-DnsQuestion *dns_question_ref(DnsQuestion *q) {
- if (!q)
- return NULL;
-
- assert(q->n_ref > 0);
- q->n_ref++;
- return q;
-}
-
-DnsQuestion *dns_question_unref(DnsQuestion *q) {
- if (!q)
- return NULL;
-
- assert(q->n_ref > 0);
-
- if (q->n_ref == 1) {
- unsigned i;
-
- for (i = 0; i < q->n_keys; i++)
- dns_resource_key_unref(q->keys[i]);
- free(q);
- } else
- q->n_ref--;
-
- return NULL;
-}
-
-int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
- unsigned i;
- int r;
-
- assert(key);
-
- if (!q)
- return -ENOSPC;
-
- for (i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_equal(q->keys[i], key);
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
- }
-
- if (q->n_keys >= q->n_allocated)
- return -ENOSPC;
-
- q->keys[q->n_keys++] = dns_resource_key_ref(key);
- return 0;
-}
-
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
- unsigned i;
- int r;
-
- assert(rr);
-
- if (!q)
- return 0;
-
- for (i = 0; i < q->n_keys; i++) {
- r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
- unsigned i;
- int r;
-
- assert(rr);
-
- if (!q)
- return 0;
-
- if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
- return 0;
-
- for (i = 0; i < q->n_keys; i++) {
- /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
- if (!dns_type_may_redirect(q->keys[i]->type))
- return 0;
-
- r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_question_is_valid_for_query(DnsQuestion *q) {
- const char *name;
- unsigned i;
- int r;
-
- if (!q)
- return 0;
-
- if (q->n_keys <= 0)
- return 0;
-
- if (q->n_keys > 65535)
- return 0;
-
- name = dns_resource_key_name(q->keys[0]);
- if (!name)
- return 0;
-
- /* Check that all keys in this question bear the same name */
- for (i = 0; i < q->n_keys; i++) {
- assert(q->keys[i]);
-
- if (i > 0) {
- r = dns_name_equal(dns_resource_key_name(q->keys[i]), name);
- if (r <= 0)
- return r;
- }
-
- if (!dns_type_is_valid_query(q->keys[i]->type))
- return 0;
- }
-
- return 1;
-}
-
-int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
- unsigned j;
- int r;
-
- assert(k);
-
- if (!a)
- return 0;
-
- for (j = 0; j < a->n_keys; j++) {
- r = dns_resource_key_equal(a->keys[j], k);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
- unsigned j;
- int r;
-
- if (a == b)
- return 1;
-
- if (!a)
- return !b || b->n_keys == 0;
- if (!b)
- return a->n_keys == 0;
-
- /* Checks if all keys in a are also contained b, and vice versa */
-
- for (j = 0; j < a->n_keys; j++) {
- r = dns_question_contains(b, a->keys[j]);
- if (r <= 0)
- return r;
- }
-
- for (j = 0; j < b->n_keys; j++) {
- r = dns_question_contains(a, b->keys[j]);
- if (r <= 0)
- return r;
- }
-
- return 1;
-}
-
-int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
- _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
- DnsResourceKey *key;
- bool same = true;
- int r;
-
- assert(cname);
- assert(ret);
- assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
-
- if (dns_question_size(q) <= 0) {
- *ret = NULL;
- return 0;
- }
-
- DNS_QUESTION_FOREACH(key, q) {
- _cleanup_free_ char *destination = NULL;
- const char *d;
-
- if (cname->key->type == DNS_TYPE_CNAME)
- d = cname->cname.name;
- else {
- r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- d = destination;
- }
-
- r = dns_name_equal(dns_resource_key_name(key), d);
- if (r < 0)
- return r;
-
- if (r == 0) {
- same = false;
- break;
- }
- }
-
- /* Fully the same, indicate we didn't do a thing */
- if (same) {
- *ret = NULL;
- return 0;
- }
-
- n = dns_question_new(q->n_keys);
- if (!n)
- return -ENOMEM;
-
- /* Create a new question, and patch in the new name */
- DNS_QUESTION_FOREACH(key, q) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
-
- k = dns_resource_key_new_redirect(key, cname);
- if (!k)
- return -ENOMEM;
-
- r = dns_question_add(n, k);
- if (r < 0)
- return r;
- }
-
- *ret = n;
- n = NULL;
-
- return 1;
-}
-
-const char *dns_question_first_name(DnsQuestion *q) {
-
- if (!q)
- return NULL;
-
- if (q->n_keys < 1)
- return NULL;
-
- return dns_resource_key_name(q->keys[0]);
-}
-
-int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
- _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
- _cleanup_free_ char *buf = NULL;
- int r;
-
- assert(ret);
- assert(name);
-
- if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
- return -EAFNOSUPPORT;
-
- if (convert_idna) {
- r = dns_name_apply_idna(name, &buf);
- if (r < 0)
- return r;
-
- name = buf;
- }
-
- q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
- if (!q)
- return -ENOMEM;
-
- if (family != AF_INET6) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(q, key);
- if (r < 0)
- return r;
- }
-
- if (family != AF_INET) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(q, key);
- if (r < 0)
- return r;
- }
-
- *ret = q;
- q = NULL;
-
- return 0;
-}
-
-int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
- _cleanup_free_ char *reverse = NULL;
- int r;
-
- assert(ret);
- assert(a);
-
- if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
- return -EAFNOSUPPORT;
-
- r = dns_name_reverse(family, a, &reverse);
- if (r < 0)
- return r;
-
- q = dns_question_new(1);
- if (!q)
- return -ENOMEM;
-
- key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
- if (!key)
- return -ENOMEM;
-
- reverse = NULL;
-
- r = dns_question_add(q, key);
- if (r < 0)
- return r;
-
- *ret = q;
- q = NULL;
-
- return 0;
-}
-
-int dns_question_new_service(
- DnsQuestion **ret,
- const char *service,
- const char *type,
- const char *domain,
- bool with_txt,
- bool convert_idna) {
-
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
- _cleanup_free_ char *buf = NULL, *joined = NULL;
- const char *name;
- int r;
-
- assert(ret);
-
- /* We support three modes of invocation:
- *
- * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
- * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
- * that's necessary.
- *
- * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
- * style prefix. In this case we'll IDNA convert the domain, if that's requested.
- *
- * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
- * together. The service name is never IDNA converted, and the domain is if requested.
- *
- * It's not supported to specify a service name without a type, or no domain name.
- */
-
- if (!domain)
- return -EINVAL;
-
- if (type) {
- if (convert_idna) {
- r = dns_name_apply_idna(domain, &buf);
- if (r < 0)
- return r;
-
- domain = buf;
- }
-
- r = dns_service_join(service, type, domain, &joined);
- if (r < 0)
- return r;
-
- name = joined;
- } else {
- if (service)
- return -EINVAL;
-
- name = domain;
- }
-
- q = dns_question_new(1 + with_txt);
- if (!q)
- return -ENOMEM;
-
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(q, key);
- if (r < 0)
- return r;
-
- if (with_txt) {
- dns_resource_key_unref(key);
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
- if (!key)
- return -ENOMEM;
-
- r = dns_question_add(q, key);
- if (r < 0)
- return r;
- }
-
- *ret = q;
- q = NULL;
-
- return 0;
-}
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
deleted file mode 100644
index a9a1863b1e..0000000000
--- a/src/resolve/resolved-dns-question.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct DnsQuestion DnsQuestion;
-
-#include "macro.h"
-#include "resolved-dns-rr.h"
-
-/* A simple array of resource keys */
-
-struct DnsQuestion {
- unsigned n_ref;
- unsigned n_keys, n_allocated;
- DnsResourceKey* keys[0];
-};
-
-DnsQuestion *dns_question_new(unsigned n);
-DnsQuestion *dns_question_ref(DnsQuestion *q);
-DnsQuestion *dns_question_unref(DnsQuestion *q);
-
-int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna);
-int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
-int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
-
-int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
-
-int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
-int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
-int dns_question_is_valid_for_query(DnsQuestion *q);
-int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k);
-int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
-
-int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
-
-const char *dns_question_first_name(DnsQuestion *q);
-
-static inline unsigned dns_question_size(DnsQuestion *q) {
- return q ? q->n_keys : 0;
-}
-
-static inline bool dns_question_isempty(DnsQuestion *q) {
- return dns_question_size(q) <= 0;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
-
-#define _DNS_QUESTION_FOREACH(u, key, q) \
- for (unsigned UNIQ_T(i, u) = ({ \
- (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \
- 0; \
- }); \
- (q) && (UNIQ_T(i, u) < (q)->n_keys); \
- UNIQ_T(i, u)++, (key) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->keys[UNIQ_T(i, u)] : NULL))
-
-#define DNS_QUESTION_FOREACH(key, q) _DNS_QUESTION_FOREACH(UNIQ, key, q)
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
deleted file mode 100644
index 87e4abec6e..0000000000
--- a/src/resolve/resolved-dns-rr.c
+++ /dev/null
@@ -1,1835 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <math.h>
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "dns-type.h"
-#include "escape.h"
-#include "hexdecoct.h"
-#include "resolved-dns-dnssec.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-rr.h"
-#include "string-table.h"
-#include "string-util.h"
-#include "strv.h"
-#include "terminal-util.h"
-
-DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
- DnsResourceKey *k;
- size_t l;
-
- assert(name);
-
- l = strlen(name);
- k = malloc0(sizeof(DnsResourceKey) + l + 1);
- if (!k)
- return NULL;
-
- k->n_ref = 1;
- k->class = class;
- k->type = type;
-
- strcpy((char*) k + sizeof(DnsResourceKey), name);
-
- return k;
-}
-
-DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
- int r;
-
- assert(key);
- assert(cname);
-
- assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
-
- if (cname->key->type == DNS_TYPE_CNAME)
- return dns_resource_key_new(key->class, key->type, cname->cname.name);
- else {
- DnsResourceKey *k;
- char *destination = NULL;
-
- r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
- if (r < 0)
- return NULL;
- if (r == 0)
- return dns_resource_key_ref((DnsResourceKey*) key);
-
- k = dns_resource_key_new_consume(key->class, key->type, destination);
- if (!k)
- return mfree(destination);
-
- return k;
- }
-}
-
-int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
- DnsResourceKey *new_key;
- char *joined;
- int r;
-
- assert(ret);
- assert(key);
- assert(name);
-
- if (dns_name_is_root(name)) {
- *ret = dns_resource_key_ref(key);
- return 0;
- }
-
- r = dns_name_concat(dns_resource_key_name(key), name, &joined);
- if (r < 0)
- return r;
-
- new_key = dns_resource_key_new_consume(key->class, key->type, joined);
- if (!new_key) {
- free(joined);
- return -ENOMEM;
- }
-
- *ret = new_key;
- return 0;
-}
-
-DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
- DnsResourceKey *k;
-
- assert(name);
-
- k = new0(DnsResourceKey, 1);
- if (!k)
- return NULL;
-
- k->n_ref = 1;
- k->class = class;
- k->type = type;
- k->_name = name;
-
- return k;
-}
-
-DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
-
- if (!k)
- return NULL;
-
- /* Static/const keys created with DNS_RESOURCE_KEY_CONST will
- * set this to -1, they should not be reffed/unreffed */
- assert(k->n_ref != (unsigned) -1);
-
- assert(k->n_ref > 0);
- k->n_ref++;
-
- return k;
-}
-
-DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
- if (!k)
- return NULL;
-
- assert(k->n_ref != (unsigned) -1);
- assert(k->n_ref > 0);
-
- if (k->n_ref == 1) {
- free(k->_name);
- free(k);
- } else
- k->n_ref--;
-
- return NULL;
-}
-
-const char* dns_resource_key_name(const DnsResourceKey *key) {
- const char *name;
-
- if (!key)
- return NULL;
-
- if (key->_name)
- name = key->_name;
- else
- name = (char*) key + sizeof(DnsResourceKey);
-
- if (dns_name_is_root(name))
- return ".";
- else
- return name;
-}
-
-bool dns_resource_key_is_address(const DnsResourceKey *key) {
- assert(key);
-
- /* Check if this is an A or AAAA resource key */
-
- return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA);
-}
-
-int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
- int r;
-
- if (a == b)
- return 1;
-
- r = dns_name_equal(dns_resource_key_name(a), dns_resource_key_name(b));
- if (r <= 0)
- return r;
-
- if (a->class != b->class)
- return 0;
-
- if (a->type != b->type)
- return 0;
-
- return 1;
-}
-
-int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
- int r;
-
- assert(key);
- assert(rr);
-
- if (key == rr->key)
- return 1;
-
- /* Checks if an rr matches the specified key. If a search
- * domain is specified, it will also be checked if the key
- * with the search domain suffixed might match the RR. */
-
- if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
- return 0;
-
- if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
- return 0;
-
- r = dns_name_equal(dns_resource_key_name(rr->key), dns_resource_key_name(key));
- if (r != 0)
- return r;
-
- if (search_domain) {
- _cleanup_free_ char *joined = NULL;
-
- r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined);
- if (r < 0)
- return r;
-
- return dns_name_equal(dns_resource_key_name(rr->key), joined);
- }
-
- return 0;
-}
-
-int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
- int r;
-
- assert(key);
- assert(cname);
-
- if (cname->class != key->class && key->class != DNS_CLASS_ANY)
- return 0;
-
- if (cname->type == DNS_TYPE_CNAME)
- r = dns_name_equal(dns_resource_key_name(key), dns_resource_key_name(cname));
- else if (cname->type == DNS_TYPE_DNAME)
- r = dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(cname));
- else
- return 0;
-
- if (r != 0)
- return r;
-
- if (search_domain) {
- _cleanup_free_ char *joined = NULL;
-
- r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined);
- if (r < 0)
- return r;
-
- if (cname->type == DNS_TYPE_CNAME)
- return dns_name_equal(joined, dns_resource_key_name(cname));
- else if (cname->type == DNS_TYPE_DNAME)
- return dns_name_endswith(joined, dns_resource_key_name(cname));
- }
-
- return 0;
-}
-
-int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa) {
- assert(soa);
- assert(key);
-
- /* Checks whether 'soa' is a SOA record for the specified key. */
-
- if (soa->class != key->class)
- return 0;
-
- if (soa->type != DNS_TYPE_SOA)
- return 0;
-
- return dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(soa));
-}
-
-static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
- const DnsResourceKey *k = i;
-
- assert(k);
-
- dns_name_hash_func(dns_resource_key_name(k), state);
- siphash24_compress(&k->class, sizeof(k->class), state);
- siphash24_compress(&k->type, sizeof(k->type), state);
-}
-
-static int dns_resource_key_compare_func(const void *a, const void *b) {
- const DnsResourceKey *x = a, *y = b;
- int ret;
-
- ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y));
- if (ret != 0)
- return ret;
-
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
-
- if (x->class < y->class)
- return -1;
- if (x->class > y->class)
- return 1;
-
- return 0;
-}
-
-const struct hash_ops dns_resource_key_hash_ops = {
- .hash = dns_resource_key_hash_func,
- .compare = dns_resource_key_compare_func
-};
-
-char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) {
- const char *c, *t;
- char *ans = buf;
-
- /* If we cannot convert the CLASS/TYPE into a known string,
- use the format recommended by RFC 3597, Section 5. */
-
- c = dns_class_to_string(key->class);
- t = dns_type_to_string(key->type);
-
- snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
- dns_resource_key_name(key),
- c ?: "", c ? "" : "CLASS", c ? 0 : key->class,
- t ?: "", t ? "" : "TYPE", t ? 0 : key->class);
-
- return ans;
-}
-
-bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) {
- assert(a);
- assert(b);
-
- /* Try to replace one RR key by another if they are identical, thus saving a bit of memory. Note that we do
- * this only for RR keys, not for RRs themselves, as they carry a lot of additional metadata (where they come
- * from, validity data, and suchlike), and cannot be replaced so easily by other RRs that have the same
- * superficial data. */
-
- if (!*a)
- return false;
- if (!*b)
- return false;
-
- /* We refuse merging const keys */
- if ((*a)->n_ref == (unsigned) -1)
- return false;
- if ((*b)->n_ref == (unsigned) -1)
- return false;
-
- /* Already the same? */
- if (*a == *b)
- return true;
-
- /* Are they really identical? */
- if (dns_resource_key_equal(*a, *b) <= 0)
- return false;
-
- /* Keep the one which already has more references. */
- if ((*a)->n_ref > (*b)->n_ref) {
- dns_resource_key_unref(*b);
- *b = dns_resource_key_ref(*a);
- } else {
- dns_resource_key_unref(*a);
- *a = dns_resource_key_ref(*b);
- }
-
- return true;
-}
-
-DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
- DnsResourceRecord *rr;
-
- rr = new0(DnsResourceRecord, 1);
- if (!rr)
- return NULL;
-
- rr->n_ref = 1;
- rr->key = dns_resource_key_ref(key);
- rr->expiry = USEC_INFINITY;
- rr->n_skip_labels_signer = rr->n_skip_labels_source = (unsigned) -1;
-
- return rr;
-}
-
-DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(class, type, name);
- if (!key)
- return NULL;
-
- return dns_resource_record_new(key);
-}
-
-DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
- if (!rr)
- return NULL;
-
- assert(rr->n_ref > 0);
- rr->n_ref++;
-
- return rr;
-}
-
-DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
- if (!rr)
- return NULL;
-
- assert(rr->n_ref > 0);
-
- if (rr->n_ref > 1) {
- rr->n_ref--;
- return NULL;
- }
-
- if (rr->key) {
- switch(rr->key->type) {
-
- case DNS_TYPE_SRV:
- free(rr->srv.name);
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- free(rr->ptr.name);
- break;
-
- case DNS_TYPE_HINFO:
- free(rr->hinfo.cpu);
- free(rr->hinfo.os);
- break;
-
- case DNS_TYPE_TXT:
- case DNS_TYPE_SPF:
- dns_txt_item_free_all(rr->txt.items);
- break;
-
- case DNS_TYPE_SOA:
- free(rr->soa.mname);
- free(rr->soa.rname);
- break;
-
- case DNS_TYPE_MX:
- free(rr->mx.exchange);
- break;
-
- case DNS_TYPE_DS:
- free(rr->ds.digest);
- break;
-
- case DNS_TYPE_SSHFP:
- free(rr->sshfp.fingerprint);
- break;
-
- case DNS_TYPE_DNSKEY:
- free(rr->dnskey.key);
- break;
-
- case DNS_TYPE_RRSIG:
- free(rr->rrsig.signer);
- free(rr->rrsig.signature);
- break;
-
- case DNS_TYPE_NSEC:
- free(rr->nsec.next_domain_name);
- bitmap_free(rr->nsec.types);
- break;
-
- case DNS_TYPE_NSEC3:
- free(rr->nsec3.next_hashed_name);
- free(rr->nsec3.salt);
- bitmap_free(rr->nsec3.types);
- break;
-
- case DNS_TYPE_LOC:
- case DNS_TYPE_A:
- case DNS_TYPE_AAAA:
- break;
-
- case DNS_TYPE_TLSA:
- free(rr->tlsa.data);
- break;
-
- case DNS_TYPE_CAA:
- free(rr->caa.tag);
- free(rr->caa.value);
- break;
-
- case DNS_TYPE_OPENPGPKEY:
- default:
- free(rr->generic.data);
- }
-
- free(rr->wire_format);
- dns_resource_key_unref(rr->key);
- }
-
- free(rr->to_string);
- return mfree(rr);
-}
-
-int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_free_ char *ptr = NULL;
- int r;
-
- assert(ret);
- assert(address);
- assert(hostname);
-
- r = dns_name_reverse(family, address, &ptr);
- if (r < 0)
- return r;
-
- key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
- if (!key)
- return -ENOMEM;
-
- ptr = NULL;
-
- rr = dns_resource_record_new(key);
- if (!rr)
- return -ENOMEM;
-
- rr->ptr.name = strdup(hostname);
- if (!rr->ptr.name)
- return -ENOMEM;
-
- *ret = rr;
- rr = NULL;
-
- return 0;
-}
-
-int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) {
- DnsResourceRecord *rr;
-
- assert(ret);
- assert(address);
- assert(family);
-
- if (family == AF_INET) {
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
- if (!rr)
- return -ENOMEM;
-
- rr->a.in_addr = address->in;
-
- } else if (family == AF_INET6) {
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
- if (!rr)
- return -ENOMEM;
-
- rr->aaaa.in6_addr = address->in6;
- } else
- return -EAFNOSUPPORT;
-
- *ret = rr;
-
- return 0;
-}
-
-#define FIELD_EQUAL(a, b, field) \
- ((a).field ## _size == (b).field ## _size && \
- memcmp((a).field, (b).field, (a).field ## _size) == 0)
-
-int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
- int r;
-
- assert(a);
- assert(b);
-
- if (a == b)
- return 1;
-
- r = dns_resource_key_equal(a->key, b->key);
- if (r <= 0)
- return r;
-
- if (a->unparseable != b->unparseable)
- return 0;
-
- switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
-
- case DNS_TYPE_SRV:
- r = dns_name_equal(a->srv.name, b->srv.name);
- if (r <= 0)
- return r;
-
- return a->srv.priority == b->srv.priority &&
- a->srv.weight == b->srv.weight &&
- a->srv.port == b->srv.port;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- return dns_name_equal(a->ptr.name, b->ptr.name);
-
- case DNS_TYPE_HINFO:
- return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
- strcaseeq(a->hinfo.os, b->hinfo.os);
-
- case DNS_TYPE_SPF: /* exactly the same as TXT */
- case DNS_TYPE_TXT:
- return dns_txt_item_equal(a->txt.items, b->txt.items);
-
- case DNS_TYPE_A:
- return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
-
- case DNS_TYPE_AAAA:
- return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
-
- case DNS_TYPE_SOA:
- r = dns_name_equal(a->soa.mname, b->soa.mname);
- if (r <= 0)
- return r;
- r = dns_name_equal(a->soa.rname, b->soa.rname);
- if (r <= 0)
- return r;
-
- return a->soa.serial == b->soa.serial &&
- a->soa.refresh == b->soa.refresh &&
- a->soa.retry == b->soa.retry &&
- a->soa.expire == b->soa.expire &&
- a->soa.minimum == b->soa.minimum;
-
- case DNS_TYPE_MX:
- if (a->mx.priority != b->mx.priority)
- return 0;
-
- return dns_name_equal(a->mx.exchange, b->mx.exchange);
-
- case DNS_TYPE_LOC:
- assert(a->loc.version == b->loc.version);
-
- return a->loc.size == b->loc.size &&
- a->loc.horiz_pre == b->loc.horiz_pre &&
- a->loc.vert_pre == b->loc.vert_pre &&
- a->loc.latitude == b->loc.latitude &&
- a->loc.longitude == b->loc.longitude &&
- a->loc.altitude == b->loc.altitude;
-
- case DNS_TYPE_DS:
- return a->ds.key_tag == b->ds.key_tag &&
- a->ds.algorithm == b->ds.algorithm &&
- a->ds.digest_type == b->ds.digest_type &&
- FIELD_EQUAL(a->ds, b->ds, digest);
-
- case DNS_TYPE_SSHFP:
- return a->sshfp.algorithm == b->sshfp.algorithm &&
- a->sshfp.fptype == b->sshfp.fptype &&
- FIELD_EQUAL(a->sshfp, b->sshfp, fingerprint);
-
- case DNS_TYPE_DNSKEY:
- return a->dnskey.flags == b->dnskey.flags &&
- a->dnskey.protocol == b->dnskey.protocol &&
- a->dnskey.algorithm == b->dnskey.algorithm &&
- FIELD_EQUAL(a->dnskey, b->dnskey, key);
-
- case DNS_TYPE_RRSIG:
- /* do the fast comparisons first */
- return a->rrsig.type_covered == b->rrsig.type_covered &&
- a->rrsig.algorithm == b->rrsig.algorithm &&
- a->rrsig.labels == b->rrsig.labels &&
- a->rrsig.original_ttl == b->rrsig.original_ttl &&
- a->rrsig.expiration == b->rrsig.expiration &&
- a->rrsig.inception == b->rrsig.inception &&
- a->rrsig.key_tag == b->rrsig.key_tag &&
- FIELD_EQUAL(a->rrsig, b->rrsig, signature) &&
- dns_name_equal(a->rrsig.signer, b->rrsig.signer);
-
- case DNS_TYPE_NSEC:
- return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
- bitmap_equal(a->nsec.types, b->nsec.types);
-
- case DNS_TYPE_NSEC3:
- return a->nsec3.algorithm == b->nsec3.algorithm &&
- a->nsec3.flags == b->nsec3.flags &&
- a->nsec3.iterations == b->nsec3.iterations &&
- FIELD_EQUAL(a->nsec3, b->nsec3, salt) &&
- FIELD_EQUAL(a->nsec3, b->nsec3, next_hashed_name) &&
- bitmap_equal(a->nsec3.types, b->nsec3.types);
-
- case DNS_TYPE_TLSA:
- return a->tlsa.cert_usage == b->tlsa.cert_usage &&
- a->tlsa.selector == b->tlsa.selector &&
- a->tlsa.matching_type == b->tlsa.matching_type &&
- FIELD_EQUAL(a->tlsa, b->tlsa, data);
-
- case DNS_TYPE_CAA:
- return a->caa.flags == b->caa.flags &&
- streq(a->caa.tag, b->caa.tag) &&
- FIELD_EQUAL(a->caa, b->caa, value);
-
- case DNS_TYPE_OPENPGPKEY:
- default:
- return FIELD_EQUAL(a->generic, b->generic, data);
- }
-}
-
-static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
- uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
- char *s;
- char NS = latitude >= 1U<<31 ? 'N' : 'S';
- char EW = longitude >= 1U<<31 ? 'E' : 'W';
-
- int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
- int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
- double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
- double siz = (size >> 4) * exp10((double) (size & 0xF));
- double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
- double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
-
- if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
- (lat / 60000 / 60),
- (lat / 60000) % 60,
- (lat % 60000) / 1000.,
- NS,
- (lon / 60000 / 60),
- (lon / 60000) % 60,
- (lon % 60000) / 1000.,
- EW,
- alt / 100.,
- siz / 100.,
- hor / 100.,
- ver / 100.) < 0)
- return NULL;
-
- return s;
-}
-
-static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
- struct tm tm;
-
- assert(buf);
- assert(l > strlen("YYYYMMDDHHmmSS"));
-
- if (!gmtime_r(&sec, &tm))
- return -EINVAL;
-
- if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
- return -EINVAL;
-
- return 0;
-}
-
-static char *format_types(Bitmap *types) {
- _cleanup_strv_free_ char **strv = NULL;
- _cleanup_free_ char *str = NULL;
- Iterator i;
- unsigned type;
- int r;
-
- BITMAP_FOREACH(type, types, i) {
- if (dns_type_to_string(type)) {
- r = strv_extend(&strv, dns_type_to_string(type));
- if (r < 0)
- return NULL;
- } else {
- char *t;
-
- r = asprintf(&t, "TYPE%u", type);
- if (r < 0)
- return NULL;
-
- r = strv_consume(&strv, t);
- if (r < 0)
- return NULL;
- }
- }
-
- str = strv_join(strv, " ");
- if (!str)
- return NULL;
-
- return strjoin("( ", str, " )", NULL);
-}
-
-static char *format_txt(DnsTxtItem *first) {
- DnsTxtItem *i;
- size_t c = 1;
- char *p, *s;
-
- LIST_FOREACH(items, i, first)
- c += i->length * 4 + 3;
-
- p = s = new(char, c);
- if (!s)
- return NULL;
-
- LIST_FOREACH(items, i, first) {
- size_t j;
-
- if (i != first)
- *(p++) = ' ';
-
- *(p++) = '"';
-
- for (j = 0; j < i->length; j++) {
- if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
- *(p++) = '\\';
- *(p++) = '0' + (i->data[j] / 100);
- *(p++) = '0' + ((i->data[j] / 10) % 10);
- *(p++) = '0' + (i->data[j] % 10);
- } else
- *(p++) = i->data[j];
- }
-
- *(p++) = '"';
- }
-
- *p = 0;
- return s;
-}
-
-const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
- _cleanup_free_ char *t = NULL;
- char *s, k[DNS_RESOURCE_KEY_STRING_MAX];
- int r;
-
- assert(rr);
-
- if (rr->to_string)
- return rr->to_string;
-
- dns_resource_key_to_string(rr->key, k, sizeof(k));
-
- switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
-
- case DNS_TYPE_SRV:
- r = asprintf(&s, "%s %u %u %u %s",
- k,
- rr->srv.priority,
- rr->srv.weight,
- rr->srv.port,
- strna(rr->srv.name));
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- s = strjoin(k, " ", rr->ptr.name, NULL);
- if (!s)
- return NULL;
-
- break;
-
- case DNS_TYPE_HINFO:
- s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
- if (!s)
- return NULL;
- break;
-
- case DNS_TYPE_SPF: /* exactly the same as TXT */
- case DNS_TYPE_TXT:
- t = format_txt(rr->txt.items);
- if (!t)
- return NULL;
-
- s = strjoin(k, " ", t, NULL);
- if (!s)
- return NULL;
- break;
-
- case DNS_TYPE_A: {
- _cleanup_free_ char *x = NULL;
-
- r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
- if (r < 0)
- return NULL;
-
- s = strjoin(k, " ", x, NULL);
- if (!s)
- return NULL;
- break;
- }
-
- case DNS_TYPE_AAAA:
- r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
- if (r < 0)
- return NULL;
-
- s = strjoin(k, " ", t, NULL);
- if (!s)
- return NULL;
- break;
-
- case DNS_TYPE_SOA:
- r = asprintf(&s, "%s %s %s %u %u %u %u %u",
- k,
- strna(rr->soa.mname),
- strna(rr->soa.rname),
- rr->soa.serial,
- rr->soa.refresh,
- rr->soa.retry,
- rr->soa.expire,
- rr->soa.minimum);
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_MX:
- r = asprintf(&s, "%s %u %s",
- k,
- rr->mx.priority,
- rr->mx.exchange);
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_LOC:
- assert(rr->loc.version == 0);
-
- t = format_location(rr->loc.latitude,
- rr->loc.longitude,
- rr->loc.altitude,
- rr->loc.size,
- rr->loc.horiz_pre,
- rr->loc.vert_pre);
- if (!t)
- return NULL;
-
- s = strjoin(k, " ", t, NULL);
- if (!s)
- return NULL;
- break;
-
- case DNS_TYPE_DS:
- t = hexmem(rr->ds.digest, rr->ds.digest_size);
- if (!t)
- return NULL;
-
- r = asprintf(&s, "%s %u %u %u %s",
- k,
- rr->ds.key_tag,
- rr->ds.algorithm,
- rr->ds.digest_type,
- t);
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_SSHFP:
- t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
- if (!t)
- return NULL;
-
- r = asprintf(&s, "%s %u %u %s",
- k,
- rr->sshfp.algorithm,
- rr->sshfp.fptype,
- t);
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_DNSKEY: {
- _cleanup_free_ char *alg = NULL;
- char *ss;
- int n;
- uint16_t key_tag;
-
- key_tag = dnssec_keytag(rr, true);
-
- r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
- if (r < 0)
- return NULL;
-
- r = asprintf(&s, "%s %u %u %s %n",
- k,
- rr->dnskey.flags,
- rr->dnskey.protocol,
- alg,
- &n);
- if (r < 0)
- return NULL;
-
- r = base64_append(&s, n,
- rr->dnskey.key, rr->dnskey.key_size,
- 8, columns());
- if (r < 0)
- return NULL;
-
- r = asprintf(&ss, "%s\n"
- " -- Flags:%s%s%s\n"
- " -- Key tag: %u",
- s,
- rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "",
- rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "",
- rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "",
- key_tag);
- if (r < 0)
- return NULL;
- free(s);
- s = ss;
-
- break;
- }
-
- case DNS_TYPE_RRSIG: {
- _cleanup_free_ char *alg = NULL;
- char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
- const char *type;
- int n;
-
- type = dns_type_to_string(rr->rrsig.type_covered);
-
- r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg);
- if (r < 0)
- return NULL;
-
- r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
- if (r < 0)
- return NULL;
-
- r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
- if (r < 0)
- return NULL;
-
- /* TYPE?? follows
- * http://tools.ietf.org/html/rfc3597#section-5 */
-
- r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %n",
- k,
- type ?: "TYPE",
- type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
- alg,
- rr->rrsig.labels,
- rr->rrsig.original_ttl,
- expiration,
- inception,
- rr->rrsig.key_tag,
- rr->rrsig.signer,
- &n);
- if (r < 0)
- return NULL;
-
- r = base64_append(&s, n,
- rr->rrsig.signature, rr->rrsig.signature_size,
- 8, columns());
- if (r < 0)
- return NULL;
-
- break;
- }
-
- case DNS_TYPE_NSEC:
- t = format_types(rr->nsec.types);
- if (!t)
- return NULL;
-
- r = asprintf(&s, "%s %s %s",
- k,
- rr->nsec.next_domain_name,
- t);
- if (r < 0)
- return NULL;
- break;
-
- case DNS_TYPE_NSEC3: {
- _cleanup_free_ char *salt = NULL, *hash = NULL;
-
- if (rr->nsec3.salt_size > 0) {
- salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
- if (!salt)
- return NULL;
- }
-
- hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
- if (!hash)
- return NULL;
-
- t = format_types(rr->nsec3.types);
- if (!t)
- return NULL;
-
- r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
- k,
- rr->nsec3.algorithm,
- rr->nsec3.flags,
- rr->nsec3.iterations,
- rr->nsec3.salt_size > 0 ? salt : "-",
- hash,
- t);
- if (r < 0)
- return NULL;
-
- break;
- }
-
- case DNS_TYPE_TLSA: {
- const char *cert_usage, *selector, *matching_type;
-
- cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage);
- selector = tlsa_selector_to_string(rr->tlsa.selector);
- matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type);
-
- t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
- if (!t)
- return NULL;
-
- r = asprintf(&s,
- "%s %u %u %u %s\n"
- " -- Cert. usage: %s\n"
- " -- Selector: %s\n"
- " -- Matching type: %s",
- k,
- rr->tlsa.cert_usage,
- rr->tlsa.selector,
- rr->tlsa.matching_type,
- t,
- cert_usage,
- selector,
- matching_type);
- if (r < 0)
- return NULL;
-
- break;
- }
-
- case DNS_TYPE_CAA: {
- _cleanup_free_ char *value;
-
- value = octescape(rr->caa.value, rr->caa.value_size);
- if (!value)
- return NULL;
-
- r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u",
- k,
- rr->caa.flags,
- rr->caa.tag,
- value,
- rr->caa.flags ? "\n -- Flags:" : "",
- rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "",
- rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "",
- rr->caa.flags & ~CAA_FLAG_CRITICAL);
- if (r < 0)
- return NULL;
-
- break;
- }
-
- case DNS_TYPE_OPENPGPKEY: {
- int n;
-
- r = asprintf(&s, "%s %n",
- k,
- &n);
- if (r < 0)
- return NULL;
-
- r = base64_append(&s, n,
- rr->generic.data, rr->generic.data_size,
- 8, columns());
- if (r < 0)
- return NULL;
- break;
- }
-
- default:
- t = hexmem(rr->generic.data, rr->generic.data_size);
- if (!t)
- return NULL;
-
- /* Format as documented in RFC 3597, Section 5 */
- r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
- if (r < 0)
- return NULL;
- break;
- }
-
- rr->to_string = s;
- return s;
-}
-
-ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) {
- assert(rr);
- assert(out);
-
- switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
- case DNS_TYPE_SRV:
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- case DNS_TYPE_HINFO:
- case DNS_TYPE_SPF:
- case DNS_TYPE_TXT:
- case DNS_TYPE_A:
- case DNS_TYPE_AAAA:
- case DNS_TYPE_SOA:
- case DNS_TYPE_MX:
- case DNS_TYPE_LOC:
- case DNS_TYPE_DS:
- case DNS_TYPE_DNSKEY:
- case DNS_TYPE_RRSIG:
- case DNS_TYPE_NSEC:
- case DNS_TYPE_NSEC3:
- return -EINVAL;
-
- case DNS_TYPE_SSHFP:
- *out = rr->sshfp.fingerprint;
- return rr->sshfp.fingerprint_size;
-
- case DNS_TYPE_TLSA:
- *out = rr->tlsa.data;
- return rr->tlsa.data_size;
-
-
- case DNS_TYPE_OPENPGPKEY:
- default:
- *out = rr->generic.data;
- return rr->generic.data_size;
- }
-}
-
-int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
-
- DnsPacket packet = {
- .n_ref = 1,
- .protocol = DNS_PROTOCOL_DNS,
- .on_stack = true,
- .refuse_compression = true,
- .canonical_form = canonical,
- };
-
- size_t start, rds;
- int r;
-
- assert(rr);
-
- /* Generates the RR in wire-format, optionally in the
- * canonical form as discussed in the DNSSEC RFC 4034, Section
- * 6.2. We allocate a throw-away DnsPacket object on the stack
- * here, because we need some book-keeping for memory
- * management, and can reuse the DnsPacket serializer, that
- * can generate the canonical form, too, but also knows label
- * compression and suchlike. */
-
- if (rr->wire_format && rr->wire_format_canonical == canonical)
- return 0;
-
- r = dns_packet_append_rr(&packet, rr, &start, &rds);
- if (r < 0)
- return r;
-
- assert(start == 0);
- assert(packet._data);
-
- free(rr->wire_format);
- rr->wire_format = packet._data;
- rr->wire_format_size = packet.size;
- rr->wire_format_rdata_offset = rds;
- rr->wire_format_canonical = canonical;
-
- packet._data = NULL;
- dns_packet_unref(&packet);
-
- return 0;
-}
-
-int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) {
- const char *n;
- int r;
-
- assert(rr);
- assert(ret);
-
- /* Returns the RRset's signer, if it is known. */
-
- if (rr->n_skip_labels_signer == (unsigned) -1)
- return -ENODATA;
-
- n = dns_resource_key_name(rr->key);
- r = dns_name_skip(n, rr->n_skip_labels_signer, &n);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- *ret = n;
- return 0;
-}
-
-int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) {
- const char *n;
- int r;
-
- assert(rr);
- assert(ret);
-
- /* Returns the RRset's synthesizing source, if it is known. */
-
- if (rr->n_skip_labels_source == (unsigned) -1)
- return -ENODATA;
-
- n = dns_resource_key_name(rr->key);
- r = dns_name_skip(n, rr->n_skip_labels_source, &n);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
-
- *ret = n;
- return 0;
-}
-
-int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) {
- const char *signer;
- int r;
-
- assert(rr);
-
- r = dns_resource_record_signer(rr, &signer);
- if (r < 0)
- return r;
-
- return dns_name_equal(zone, signer);
-}
-
-int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
- int r;
-
- assert(rr);
-
- /* Returns > 0 if the RR is generated from a wildcard, and is not the asterisk name itself */
-
- if (rr->n_skip_labels_source == (unsigned) -1)
- return -ENODATA;
-
- if (rr->n_skip_labels_source == 0)
- return 0;
-
- if (rr->n_skip_labels_source > 1)
- return 1;
-
- r = dns_name_startswith(dns_resource_key_name(rr->key), "*");
- if (r < 0)
- return r;
-
- return !r;
-}
-
-void dns_resource_record_hash_func(const void *i, struct siphash *state) {
- const DnsResourceRecord *rr = i;
-
- assert(rr);
-
- dns_resource_key_hash_func(rr->key, state);
-
- switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
-
- case DNS_TYPE_SRV:
- siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
- siphash24_compress(&rr->srv.weight, sizeof(rr->srv.weight), state);
- siphash24_compress(&rr->srv.port, sizeof(rr->srv.port), state);
- dns_name_hash_func(rr->srv.name, state);
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- dns_name_hash_func(rr->ptr.name, state);
- break;
-
- case DNS_TYPE_HINFO:
- string_hash_func(rr->hinfo.cpu, state);
- string_hash_func(rr->hinfo.os, state);
- break;
-
- case DNS_TYPE_TXT:
- case DNS_TYPE_SPF: {
- DnsTxtItem *j;
-
- LIST_FOREACH(items, j, rr->txt.items) {
- siphash24_compress(j->data, j->length, state);
-
- /* Add an extra NUL byte, so that "a" followed by "b" doesn't result in the same hash as "ab"
- * followed by "". */
- siphash24_compress_byte(0, state);
- }
- break;
- }
-
- case DNS_TYPE_A:
- siphash24_compress(&rr->a.in_addr, sizeof(rr->a.in_addr), state);
- break;
-
- case DNS_TYPE_AAAA:
- siphash24_compress(&rr->aaaa.in6_addr, sizeof(rr->aaaa.in6_addr), state);
- break;
-
- case DNS_TYPE_SOA:
- dns_name_hash_func(rr->soa.mname, state);
- dns_name_hash_func(rr->soa.rname, state);
- siphash24_compress(&rr->soa.serial, sizeof(rr->soa.serial), state);
- siphash24_compress(&rr->soa.refresh, sizeof(rr->soa.refresh), state);
- siphash24_compress(&rr->soa.retry, sizeof(rr->soa.retry), state);
- siphash24_compress(&rr->soa.expire, sizeof(rr->soa.expire), state);
- siphash24_compress(&rr->soa.minimum, sizeof(rr->soa.minimum), state);
- break;
-
- case DNS_TYPE_MX:
- siphash24_compress(&rr->mx.priority, sizeof(rr->mx.priority), state);
- dns_name_hash_func(rr->mx.exchange, state);
- break;
-
- case DNS_TYPE_LOC:
- siphash24_compress(&rr->loc.version, sizeof(rr->loc.version), state);
- siphash24_compress(&rr->loc.size, sizeof(rr->loc.size), state);
- siphash24_compress(&rr->loc.horiz_pre, sizeof(rr->loc.horiz_pre), state);
- siphash24_compress(&rr->loc.vert_pre, sizeof(rr->loc.vert_pre), state);
- siphash24_compress(&rr->loc.latitude, sizeof(rr->loc.latitude), state);
- siphash24_compress(&rr->loc.longitude, sizeof(rr->loc.longitude), state);
- siphash24_compress(&rr->loc.altitude, sizeof(rr->loc.altitude), state);
- break;
-
- case DNS_TYPE_SSHFP:
- siphash24_compress(&rr->sshfp.algorithm, sizeof(rr->sshfp.algorithm), state);
- siphash24_compress(&rr->sshfp.fptype, sizeof(rr->sshfp.fptype), state);
- siphash24_compress(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, state);
- break;
-
- case DNS_TYPE_DNSKEY:
- siphash24_compress(&rr->dnskey.flags, sizeof(rr->dnskey.flags), state);
- siphash24_compress(&rr->dnskey.protocol, sizeof(rr->dnskey.protocol), state);
- siphash24_compress(&rr->dnskey.algorithm, sizeof(rr->dnskey.algorithm), state);
- siphash24_compress(rr->dnskey.key, rr->dnskey.key_size, state);
- break;
-
- case DNS_TYPE_RRSIG:
- siphash24_compress(&rr->rrsig.type_covered, sizeof(rr->rrsig.type_covered), state);
- siphash24_compress(&rr->rrsig.algorithm, sizeof(rr->rrsig.algorithm), state);
- siphash24_compress(&rr->rrsig.labels, sizeof(rr->rrsig.labels), state);
- siphash24_compress(&rr->rrsig.original_ttl, sizeof(rr->rrsig.original_ttl), state);
- siphash24_compress(&rr->rrsig.expiration, sizeof(rr->rrsig.expiration), state);
- siphash24_compress(&rr->rrsig.inception, sizeof(rr->rrsig.inception), state);
- siphash24_compress(&rr->rrsig.key_tag, sizeof(rr->rrsig.key_tag), state);
- dns_name_hash_func(rr->rrsig.signer, state);
- siphash24_compress(rr->rrsig.signature, rr->rrsig.signature_size, state);
- break;
-
- case DNS_TYPE_NSEC:
- dns_name_hash_func(rr->nsec.next_domain_name, state);
- /* FIXME: we leave out the type bitmap here. Hash
- * would be better if we'd take it into account
- * too. */
- break;
-
- case DNS_TYPE_DS:
- siphash24_compress(&rr->ds.key_tag, sizeof(rr->ds.key_tag), state);
- siphash24_compress(&rr->ds.algorithm, sizeof(rr->ds.algorithm), state);
- siphash24_compress(&rr->ds.digest_type, sizeof(rr->ds.digest_type), state);
- siphash24_compress(rr->ds.digest, rr->ds.digest_size, state);
- break;
-
- case DNS_TYPE_NSEC3:
- siphash24_compress(&rr->nsec3.algorithm, sizeof(rr->nsec3.algorithm), state);
- siphash24_compress(&rr->nsec3.flags, sizeof(rr->nsec3.flags), state);
- siphash24_compress(&rr->nsec3.iterations, sizeof(rr->nsec3.iterations), state);
- siphash24_compress(rr->nsec3.salt, rr->nsec3.salt_size, state);
- siphash24_compress(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, state);
- /* FIXME: We leave the bitmaps out */
- break;
-
- case DNS_TYPE_TLSA:
- siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state);
- siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state);
- siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state);
- siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state);
- break;
-
- case DNS_TYPE_CAA:
- siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state);
- string_hash_func(rr->caa.tag, state);
- siphash24_compress(rr->caa.value, rr->caa.value_size, state);
- break;
-
- case DNS_TYPE_OPENPGPKEY:
- default:
- siphash24_compress(rr->generic.data, rr->generic.data_size, state);
- break;
- }
-}
-
-static int dns_resource_record_compare_func(const void *a, const void *b) {
- const DnsResourceRecord *x = a, *y = b;
- int ret;
-
- ret = dns_resource_key_compare_func(x->key, y->key);
- if (ret != 0)
- return ret;
-
- if (dns_resource_record_equal(x, y))
- return 0;
-
- /* This is a bit dirty, we don't implement proper ordering, but
- * the hashtable doesn't need ordering anyway, hence we don't
- * care. */
- return x < y ? -1 : 1;
-}
-
-const struct hash_ops dns_resource_record_hash_ops = {
- .hash = dns_resource_record_hash_func,
- .compare = dns_resource_record_compare_func,
-};
-
-DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
- DnsResourceRecord *t;
-
- assert(rr);
-
- copy = dns_resource_record_new(rr->key);
- if (!copy)
- return NULL;
-
- copy->ttl = rr->ttl;
- copy->expiry = rr->expiry;
- copy->n_skip_labels_signer = rr->n_skip_labels_signer;
- copy->n_skip_labels_source = rr->n_skip_labels_source;
- copy->unparseable = rr->unparseable;
-
- switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
-
- case DNS_TYPE_SRV:
- copy->srv.priority = rr->srv.priority;
- copy->srv.weight = rr->srv.weight;
- copy->srv.port = rr->srv.port;
- copy->srv.name = strdup(rr->srv.name);
- if (!copy->srv.name)
- return NULL;
- break;
-
- case DNS_TYPE_PTR:
- case DNS_TYPE_NS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME:
- copy->ptr.name = strdup(rr->ptr.name);
- if (!copy->ptr.name)
- return NULL;
- break;
-
- case DNS_TYPE_HINFO:
- copy->hinfo.cpu = strdup(rr->hinfo.cpu);
- if (!copy->hinfo.cpu)
- return NULL;
-
- copy->hinfo.os = strdup(rr->hinfo.os);
- if(!copy->hinfo.os)
- return NULL;
- break;
-
- case DNS_TYPE_TXT:
- case DNS_TYPE_SPF:
- copy->txt.items = dns_txt_item_copy(rr->txt.items);
- if (!copy->txt.items)
- return NULL;
- break;
-
- case DNS_TYPE_A:
- copy->a = rr->a;
- break;
-
- case DNS_TYPE_AAAA:
- copy->aaaa = rr->aaaa;
- break;
-
- case DNS_TYPE_SOA:
- copy->soa.mname = strdup(rr->soa.mname);
- if (!copy->soa.mname)
- return NULL;
- copy->soa.rname = strdup(rr->soa.rname);
- if (!copy->soa.rname)
- return NULL;
- copy->soa.serial = rr->soa.serial;
- copy->soa.refresh = rr->soa.refresh;
- copy->soa.retry = rr->soa.retry;
- copy->soa.expire = rr->soa.expire;
- copy->soa.minimum = rr->soa.minimum;
- break;
-
- case DNS_TYPE_MX:
- copy->mx.priority = rr->mx.priority;
- copy->mx.exchange = strdup(rr->mx.exchange);
- if (!copy->mx.exchange)
- return NULL;
- break;
-
- case DNS_TYPE_LOC:
- copy->loc = rr->loc;
- break;
-
- case DNS_TYPE_SSHFP:
- copy->sshfp.algorithm = rr->sshfp.algorithm;
- copy->sshfp.fptype = rr->sshfp.fptype;
- copy->sshfp.fingerprint = memdup(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
- if (!copy->sshfp.fingerprint)
- return NULL;
- copy->sshfp.fingerprint_size = rr->sshfp.fingerprint_size;
- break;
-
- case DNS_TYPE_DNSKEY:
- copy->dnskey.flags = rr->dnskey.flags;
- copy->dnskey.protocol = rr->dnskey.protocol;
- copy->dnskey.algorithm = rr->dnskey.algorithm;
- copy->dnskey.key = memdup(rr->dnskey.key, rr->dnskey.key_size);
- if (!copy->dnskey.key)
- return NULL;
- copy->dnskey.key_size = rr->dnskey.key_size;
- break;
-
- case DNS_TYPE_RRSIG:
- copy->rrsig.type_covered = rr->rrsig.type_covered;
- copy->rrsig.algorithm = rr->rrsig.algorithm;
- copy->rrsig.labels = rr->rrsig.labels;
- copy->rrsig.original_ttl = rr->rrsig.original_ttl;
- copy->rrsig.expiration = rr->rrsig.expiration;
- copy->rrsig.inception = rr->rrsig.inception;
- copy->rrsig.key_tag = rr->rrsig.key_tag;
- copy->rrsig.signer = strdup(rr->rrsig.signer);
- if (!copy->rrsig.signer)
- return NULL;
- copy->rrsig.signature = memdup(rr->rrsig.signature, rr->rrsig.signature_size);
- if (!copy->rrsig.signature)
- return NULL;
- copy->rrsig.signature_size = rr->rrsig.signature_size;
- break;
-
- case DNS_TYPE_NSEC:
- copy->nsec.next_domain_name = strdup(rr->nsec.next_domain_name);
- if (!copy->nsec.next_domain_name)
- return NULL;
- copy->nsec.types = bitmap_copy(rr->nsec.types);
- if (!copy->nsec.types)
- return NULL;
- break;
-
- case DNS_TYPE_DS:
- copy->ds.key_tag = rr->ds.key_tag;
- copy->ds.algorithm = rr->ds.algorithm;
- copy->ds.digest_type = rr->ds.digest_type;
- copy->ds.digest = memdup(rr->ds.digest, rr->ds.digest_size);
- if (!copy->ds.digest)
- return NULL;
- copy->ds.digest_size = rr->ds.digest_size;
- break;
-
- case DNS_TYPE_NSEC3:
- copy->nsec3.algorithm = rr->nsec3.algorithm;
- copy->nsec3.flags = rr->nsec3.flags;
- copy->nsec3.iterations = rr->nsec3.iterations;
- copy->nsec3.salt = memdup(rr->nsec3.salt, rr->nsec3.salt_size);
- if (!copy->nsec3.salt)
- return NULL;
- copy->nsec3.salt_size = rr->nsec3.salt_size;
- copy->nsec3.next_hashed_name = memdup(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size);
- if (!copy->nsec3.next_hashed_name_size)
- return NULL;
- copy->nsec3.next_hashed_name_size = rr->nsec3.next_hashed_name_size;
- copy->nsec3.types = bitmap_copy(rr->nsec3.types);
- if (!copy->nsec3.types)
- return NULL;
- break;
-
- case DNS_TYPE_TLSA:
- copy->tlsa.cert_usage = rr->tlsa.cert_usage;
- copy->tlsa.selector = rr->tlsa.selector;
- copy->tlsa.matching_type = rr->tlsa.matching_type;
- copy->tlsa.data = memdup(rr->tlsa.data, rr->tlsa.data_size);
- if (!copy->tlsa.data)
- return NULL;
- copy->tlsa.data_size = rr->tlsa.data_size;
- break;
-
- case DNS_TYPE_CAA:
- copy->caa.flags = rr->caa.flags;
- copy->caa.tag = strdup(rr->caa.tag);
- if (!copy->caa.tag)
- return NULL;
- copy->caa.value = memdup(rr->caa.value, rr->caa.value_size);
- if (!copy->caa.value)
- return NULL;
- copy->caa.value_size = rr->caa.value_size;
- break;
-
- case DNS_TYPE_OPT:
- default:
- copy->generic.data = memdup(rr->generic.data, rr->generic.data_size);
- if (!copy->generic.data)
- return NULL;
- copy->generic.data_size = rr->generic.data_size;
- break;
- }
-
- t = copy;
- copy = NULL;
-
- return t;
-}
-
-int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) {
- DnsResourceRecord *old_rr, *new_rr;
- uint32_t new_ttl;
-
- assert(rr);
- old_rr = *rr;
-
- if (old_rr->key->type == DNS_TYPE_OPT)
- return -EINVAL;
-
- new_ttl = MIN(old_rr->ttl, max_ttl);
- if (new_ttl == old_rr->ttl)
- return 0;
-
- if (old_rr->n_ref == 1) {
- /* Patch in place */
- old_rr->ttl = new_ttl;
- return 1;
- }
-
- new_rr = dns_resource_record_copy(old_rr);
- if (!new_rr)
- return -ENOMEM;
-
- new_rr->ttl = new_ttl;
-
- dns_resource_record_unref(*rr);
- *rr = new_rr;
-
- return 1;
-}
-
-DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
- DnsTxtItem *n;
-
- if (!i)
- return NULL;
-
- n = i->items_next;
-
- free(i);
- return dns_txt_item_free_all(n);
-}
-
-bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
-
- if (a == b)
- return true;
-
- if (!a != !b)
- return false;
-
- if (!a)
- return true;
-
- if (a->length != b->length)
- return false;
-
- if (memcmp(a->data, b->data, a->length) != 0)
- return false;
-
- return dns_txt_item_equal(a->items_next, b->items_next);
-}
-
-DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
- DnsTxtItem *i, *copy = NULL, *end = NULL;
-
- LIST_FOREACH(items, i, first) {
- DnsTxtItem *j;
-
- j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1);
- if (!j) {
- dns_txt_item_free_all(copy);
- return NULL;
- }
-
- LIST_INSERT_AFTER(items, copy, end, j);
- end = j;
- }
-
- return copy;
-}
-
-static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
- /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
- [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
- [DNSSEC_ALGORITHM_DH] = "DH",
- [DNSSEC_ALGORITHM_DSA] = "DSA",
- [DNSSEC_ALGORITHM_ECC] = "ECC",
- [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
- [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
- [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
- [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
- [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
- [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
- [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
- [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
- [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
- [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
- [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
-};
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255);
-
-static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
- /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
- [DNSSEC_DIGEST_SHA1] = "SHA-1",
- [DNSSEC_DIGEST_SHA256] = "SHA-256",
- [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
- [DNSSEC_DIGEST_SHA384] = "SHA-384",
-};
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255);
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
deleted file mode 100644
index 42d39a1251..0000000000
--- a/src/resolve/resolved-dns-rr.h
+++ /dev/null
@@ -1,353 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <netinet/in.h>
-
-#include "bitmap.h"
-#include "dns-type.h"
-#include "hashmap.h"
-#include "in-addr-util.h"
-#include "list.h"
-#include "string-util.h"
-
-typedef struct DnsResourceKey DnsResourceKey;
-typedef struct DnsResourceRecord DnsResourceRecord;
-typedef struct DnsTxtItem DnsTxtItem;
-
-/* DNSKEY RR flags */
-#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0)
-#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7)
-#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
-
-/* mDNS RR flags */
-#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15)
-
-/* DNSSEC algorithm identifiers, see
- * http://tools.ietf.org/html/rfc4034#appendix-A.1 and
- * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
-enum {
- DNSSEC_ALGORITHM_RSAMD5 = 1,
- DNSSEC_ALGORITHM_DH,
- DNSSEC_ALGORITHM_DSA,
- DNSSEC_ALGORITHM_ECC,
- DNSSEC_ALGORITHM_RSASHA1,
- DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
- DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
- DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */
- DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
- DNSSEC_ALGORITHM_ECC_GOST = 12, /* RFC 5933 */
- DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */
- DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
- DNSSEC_ALGORITHM_INDIRECT = 252,
- DNSSEC_ALGORITHM_PRIVATEDNS,
- DNSSEC_ALGORITHM_PRIVATEOID,
- _DNSSEC_ALGORITHM_MAX_DEFINED
-};
-
-/* DNSSEC digest identifiers, see
- * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
-enum {
- DNSSEC_DIGEST_SHA1 = 1,
- DNSSEC_DIGEST_SHA256 = 2, /* RFC 4509 */
- DNSSEC_DIGEST_GOST_R_34_11_94 = 3, /* RFC 5933 */
- DNSSEC_DIGEST_SHA384 = 4, /* RFC 6605 */
- _DNSSEC_DIGEST_MAX_DEFINED
-};
-
-/* DNSSEC NSEC3 hash algorithms, see
- * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
-enum {
- NSEC3_ALGORITHM_SHA1 = 1,
- _NSEC3_ALGORITHM_MAX_DEFINED
-};
-
-struct DnsResourceKey {
- unsigned n_ref; /* (unsigned -1) for const keys, see below */
- uint16_t class, type;
- char *_name; /* don't access directly, use dns_resource_key_name()! */
-};
-
-/* Creates a temporary resource key. This is only useful to quickly
- * look up something, without allocating a full DnsResourceKey object
- * for it. Note that it is not OK to take references to this kind of
- * resource key object. */
-#define DNS_RESOURCE_KEY_CONST(c, t, n) \
- ((DnsResourceKey) { \
- .n_ref = (unsigned) -1, \
- .class = c, \
- .type = t, \
- ._name = (char*) n, \
- })
-
-
-struct DnsTxtItem {
- size_t length;
- LIST_FIELDS(DnsTxtItem, items);
- uint8_t data[];
-};
-
-struct DnsResourceRecord {
- unsigned n_ref;
- DnsResourceKey *key;
-
- char *to_string;
-
- uint32_t ttl;
- usec_t expiry; /* RRSIG signature expiry */
-
- /* How many labels to strip to determine "signer" of the RRSIG (aka, the zone). -1 if not signed. */
- unsigned n_skip_labels_signer;
- /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
- unsigned n_skip_labels_source;
-
- bool unparseable:1;
-
- bool wire_format_canonical:1;
- void *wire_format;
- size_t wire_format_size;
- size_t wire_format_rdata_offset;
-
- union {
- struct {
- void *data;
- size_t data_size;
- } generic, opt;
-
- struct {
- uint16_t priority;
- uint16_t weight;
- uint16_t port;
- char *name;
- } srv;
-
- struct {
- char *name;
- } ptr, ns, cname, dname;
-
- struct {
- char *cpu;
- char *os;
- } hinfo;
-
- struct {
- DnsTxtItem *items;
- } txt, spf;
-
- struct {
- struct in_addr in_addr;
- } a;
-
- struct {
- struct in6_addr in6_addr;
- } aaaa;
-
- struct {
- char *mname;
- char *rname;
- uint32_t serial;
- uint32_t refresh;
- uint32_t retry;
- uint32_t expire;
- uint32_t minimum;
- } soa;
-
- struct {
- uint16_t priority;
- char *exchange;
- } mx;
-
- /* https://tools.ietf.org/html/rfc1876 */
- struct {
- uint8_t version;
- uint8_t size;
- uint8_t horiz_pre;
- uint8_t vert_pre;
- uint32_t latitude;
- uint32_t longitude;
- uint32_t altitude;
- } loc;
-
- /* https://tools.ietf.org/html/rfc4255#section-3.1 */
- struct {
- uint8_t algorithm;
- uint8_t fptype;
- void *fingerprint;
- size_t fingerprint_size;
- } sshfp;
-
- /* http://tools.ietf.org/html/rfc4034#section-2.1 */
- struct {
- uint16_t flags;
- uint8_t protocol;
- uint8_t algorithm;
- void* key;
- size_t key_size;
- } dnskey;
-
- /* http://tools.ietf.org/html/rfc4034#section-3.1 */
- struct {
- uint16_t type_covered;
- uint8_t algorithm;
- uint8_t labels;
- uint32_t original_ttl;
- uint32_t expiration;
- uint32_t inception;
- uint16_t key_tag;
- char *signer;
- void *signature;
- size_t signature_size;
- } rrsig;
-
- /* https://tools.ietf.org/html/rfc4034#section-4.1 */
- struct {
- char *next_domain_name;
- Bitmap *types;
- } nsec;
-
- /* https://tools.ietf.org/html/rfc4034#section-5.1 */
- struct {
- uint16_t key_tag;
- uint8_t algorithm;
- uint8_t digest_type;
- void *digest;
- size_t digest_size;
- } ds;
-
- struct {
- uint8_t algorithm;
- uint8_t flags;
- uint16_t iterations;
- void *salt;
- size_t salt_size;
- void *next_hashed_name;
- size_t next_hashed_name_size;
- Bitmap *types;
- } nsec3;
-
- /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23 */
- struct {
- uint8_t cert_usage;
- uint8_t selector;
- uint8_t matching_type;
- void *data;
- size_t data_size;
- } tlsa;
-
- /* https://tools.ietf.org/html/rfc6844 */
- struct {
- uint8_t flags;
- char *tag;
- void *value;
- size_t value_size;
- } caa;
- };
-};
-
-static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) {
- if (!rr)
- return NULL;
-
- if (!rr->wire_format)
- return NULL;
-
- assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
- return (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset;
-}
-
-static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
- if (!rr)
- return 0;
- if (!rr->wire_format)
- return 0;
-
- assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
- return rr->wire_format_size - rr->wire_format_rdata_offset;
-}
-
-static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) {
- assert(rr);
- assert(rr->key->type == DNS_TYPE_OPT);
-
- return ((rr->ttl >> 16) & 0xFF) == 0;
-}
-
-DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
-DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
-int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
-DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
-DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
-DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
-const char* dns_resource_key_name(const DnsResourceKey *key);
-bool dns_resource_key_is_address(const DnsResourceKey *key);
-int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
-int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
-int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
-int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa);
-
-/* _DNS_{CLASS,TYPE}_STRING_MAX include one byte for NUL, which we use for space instead below.
- * DNS_HOSTNAME_MAX does not include the NUL byte, so we need to add 1. */
-#define DNS_RESOURCE_KEY_STRING_MAX (_DNS_CLASS_STRING_MAX + _DNS_TYPE_STRING_MAX + DNS_HOSTNAME_MAX + 1)
-
-char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size);
-ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
-
-static inline bool dns_key_is_shared(const DnsResourceKey *key) {
- return IN_SET(key->type, DNS_TYPE_PTR);
-}
-
-bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b);
-
-DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
-DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
-DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
-DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
-int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
-int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
-int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
-const char* dns_resource_record_to_string(DnsResourceRecord *rr);
-DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
-
-int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
-
-int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret);
-int dns_resource_record_source(DnsResourceRecord *rr, const char **ret);
-int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone);
-int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
-
-int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl);
-
-DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
-bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
-DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
-
-void dns_resource_record_hash_func(const void *i, struct siphash *state);
-
-extern const struct hash_ops dns_resource_key_hash_ops;
-extern const struct hash_ops dns_resource_record_hash_ops;
-
-int dnssec_algorithm_to_string_alloc(int i, char **ret);
-int dnssec_algorithm_from_string(const char *s) _pure_;
-
-int dnssec_digest_to_string_alloc(int i, char **ret);
-int dnssec_digest_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
deleted file mode 100644
index 8dbc7f623b..0000000000
--- a/src/resolve/resolved-dns-scope.c
+++ /dev/null
@@ -1,1043 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <netinet/tcp.h>
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "fd-util.h"
-#include "hostname-util.h"
-#include "missing.h"
-#include "random-util.h"
-#include "resolved-dns-scope.h"
-#include "resolved-llmnr.h"
-#include "resolved-mdns.h"
-#include "socket-util.h"
-#include "strv.h"
-
-#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
-#define MULTICAST_RATELIMIT_BURST 1000
-
-/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */
-#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC)
-#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC)
-
-int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
- DnsScope *s;
-
- assert(m);
- assert(ret);
-
- s = new0(DnsScope, 1);
- if (!s)
- return -ENOMEM;
-
- s->manager = m;
- s->link = l;
- s->protocol = protocol;
- s->family = family;
- s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
-
- if (protocol == DNS_PROTOCOL_DNS) {
- /* Copy DNSSEC mode from the link if it is set there,
- * otherwise take the manager's DNSSEC mode. Note that
- * we copy this only at scope creation time, and do
- * not update it from the on, even if the setting
- * changes. */
-
- if (l)
- s->dnssec_mode = link_get_dnssec_mode(l);
- else
- s->dnssec_mode = manager_get_dnssec_mode(m);
- } else
- s->dnssec_mode = DNSSEC_NO;
-
- LIST_PREPEND(scopes, m->dns_scopes, s);
-
- dns_scope_llmnr_membership(s, true);
- dns_scope_mdns_membership(s, true);
-
- 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));
-
- /* Enforce ratelimiting for the multicast protocols */
- RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST);
-
- *ret = s;
- return 0;
-}
-
-static void dns_scope_abort_transactions(DnsScope *s) {
- assert(s);
-
- while (s->transactions) {
- DnsTransaction *t = s->transactions;
-
- /* Abort the transaction, but make sure it is not
- * freed while we still look at it */
-
- t->block_gc++;
- if (DNS_TRANSACTION_IS_LIVE(t->state))
- dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
- t->block_gc--;
-
- dns_transaction_free(t);
- }
-}
-
-DnsScope* dns_scope_free(DnsScope *s) {
- DnsResourceRecord *rr;
-
- if (!s)
- return NULL;
-
- 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));
-
- dns_scope_llmnr_membership(s, false);
- dns_scope_mdns_membership(s, false);
- dns_scope_abort_transactions(s);
-
- while (s->query_candidates)
- dns_query_candidate_free(s->query_candidates);
-
- hashmap_free(s->transactions_by_key);
-
- while ((rr = ordered_hashmap_steal_first(s->conflict_queue)))
- dns_resource_record_unref(rr);
-
- ordered_hashmap_free(s->conflict_queue);
- sd_event_source_unref(s->conflict_event_source);
-
- dns_cache_flush(&s->cache);
- dns_zone_flush(&s->zone);
-
- LIST_REMOVE(scopes, s->manager->dns_scopes, s);
- return mfree(s);
-}
-
-DnsServer *dns_scope_get_dns_server(DnsScope *s) {
- assert(s);
-
- if (s->protocol != DNS_PROTOCOL_DNS)
- return NULL;
-
- if (s->link)
- return link_get_dns_server(s->link);
- else
- return manager_get_dns_server(s->manager);
-}
-
-void dns_scope_next_dns_server(DnsScope *s) {
- assert(s);
-
- if (s->protocol != DNS_PROTOCOL_DNS)
- return;
-
- if (s->link)
- link_next_dns_server(s->link);
- else
- manager_next_dns_server(s->manager);
-}
-
-void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
- assert(s);
-
- if (rtt <= s->max_rtt)
- return;
-
- s->max_rtt = rtt;
- s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC);
-}
-
-void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
- assert(s);
-
- if (s->resend_timeout <= usec)
- s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
-}
-
-static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
- union in_addr_union addr;
- int ifindex = 0, r;
- int family;
- uint32_t mtu;
-
- assert(s);
- assert(p);
- assert(p->protocol == s->protocol);
-
- if (s->link) {
- mtu = s->link->mtu;
- ifindex = s->link->ifindex;
- } else
- mtu = manager_find_mtu(s->manager);
-
- switch (s->protocol) {
-
- case DNS_PROTOCOL_DNS:
- assert(fd >= 0);
-
- if (DNS_PACKET_QDCOUNT(p) > 1)
- return -EOPNOTSUPP;
-
- if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
- return -EMSGSIZE;
-
- if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
- return -EMSGSIZE;
-
- r = manager_write(s->manager, fd, p);
- if (r < 0)
- return r;
-
- break;
-
- case DNS_PROTOCOL_LLMNR:
- assert(fd < 0);
-
- if (DNS_PACKET_QDCOUNT(p) > 1)
- return -EOPNOTSUPP;
-
- if (!ratelimit_test(&s->ratelimit))
- return -EBUSY;
-
- family = s->family;
-
- if (family == AF_INET) {
- addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
- fd = manager_llmnr_ipv4_udp_fd(s->manager);
- } else if (family == AF_INET6) {
- addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
- fd = manager_llmnr_ipv6_udp_fd(s->manager);
- } else
- return -EAFNOSUPPORT;
- if (fd < 0)
- return fd;
-
- r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, p);
- if (r < 0)
- return r;
-
- break;
-
- case DNS_PROTOCOL_MDNS:
- assert(fd < 0);
-
- if (!ratelimit_test(&s->ratelimit))
- return -EBUSY;
-
- family = s->family;
-
- if (family == AF_INET) {
- addr.in = MDNS_MULTICAST_IPV4_ADDRESS;
- fd = manager_mdns_ipv4_fd(s->manager);
- } else if (family == AF_INET6) {
- addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS;
- fd = manager_mdns_ipv6_fd(s->manager);
- } else
- return -EAFNOSUPPORT;
- if (fd < 0)
- return fd;
-
- r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, NULL, p);
- if (r < 0)
- return r;
-
- break;
-
- default:
- return -EAFNOSUPPORT;
- }
-
- return 1;
-}
-
-int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) {
- int r;
-
- assert(s);
- assert(p);
- assert(p->protocol == s->protocol);
- assert((s->protocol == DNS_PROTOCOL_DNS) == (fd >= 0));
-
- do {
- /* If there are multiple linked packets, set the TC bit in all but the last of them */
- if (p->more) {
- assert(p->protocol == DNS_PROTOCOL_MDNS);
- dns_packet_set_flags(p, true, true);
- }
-
- r = dns_scope_emit_one(s, fd, p);
- if (r < 0)
- return r;
-
- p = p->more;
- } while (p);
-
- return 0;
-}
-
-static int dns_scope_socket(
- DnsScope *s,
- int type,
- int family,
- const union in_addr_union *address,
- DnsServer *server,
- uint16_t port) {
-
- _cleanup_close_ int fd = -1;
- union sockaddr_union sa = {};
- socklen_t salen;
- static const int one = 1;
- int ret, r, ifindex;
-
- assert(s);
-
- if (server) {
- assert(family == AF_UNSPEC);
- assert(!address);
-
- ifindex = dns_server_ifindex(server);
-
- sa.sa.sa_family = server->family;
- if (server->family == AF_INET) {
- sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = server->address.in;
- salen = sizeof(sa.in);
- } else if (server->family == AF_INET6) {
- sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = server->address.in6;
- sa.in6.sin6_scope_id = ifindex;
- salen = sizeof(sa.in6);
- } else
- return -EAFNOSUPPORT;
- } else {
- assert(family != AF_UNSPEC);
- assert(address);
-
- sa.sa.sa_family = family;
- ifindex = s->link ? s->link->ifindex : 0;
-
- if (family == AF_INET) {
- sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = address->in;
- salen = sizeof(sa.in);
- } else if (family == AF_INET6) {
- sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = address->in6;
- sa.in6.sin6_scope_id = ifindex;
- salen = sizeof(sa.in6);
- } else
- return -EAFNOSUPPORT;
- }
-
- fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (fd < 0)
- return -errno;
-
- if (type == SOCK_STREAM) {
- r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
- if (r < 0)
- return -errno;
- }
-
- if (s->link) {
- be32_t ifindex_be = htobe32(ifindex);
-
- if (sa.sa.sa_family == AF_INET) {
- r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be));
- if (r < 0)
- return -errno;
- } else if (sa.sa.sa_family == AF_INET6) {
- r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be));
- if (r < 0)
- return -errno;
- }
- }
-
- if (s->protocol == DNS_PROTOCOL_LLMNR) {
- /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
-
- if (sa.sa.sa_family == AF_INET) {
- r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
- if (r < 0)
- return -errno;
- } else if (sa.sa.sa_family == AF_INET6) {
- r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
- if (r < 0)
- return -errno;
- }
- }
-
- r = connect(fd, &sa.sa, salen);
- if (r < 0 && errno != EINPROGRESS)
- return -errno;
-
- ret = fd;
- fd = -1;
-
- return ret;
-}
-
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
- return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
-}
-
-int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
- return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
-}
-
-DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
- DnsSearchDomain *d;
- DnsServer *dns_server;
-
- assert(s);
- assert(domain);
-
- /* Checks if the specified domain is something to look up on
- * this scope. Note that this accepts non-qualified hostnames,
- * i.e. those without any search path prefixed yet. */
-
- if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
- return DNS_SCOPE_NO;
-
- if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
- return DNS_SCOPE_NO;
-
- /* Never resolve any loopback hostname or IP address via DNS,
- * LLMNR or mDNS. Instead, always rely on synthesized RRs for
- * these. */
- if (is_localhost(domain) ||
- dns_name_endswith(domain, "127.in-addr.arpa") > 0 ||
- 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)
- return DNS_SCOPE_NO;
-
- /* Never respond to some of the domains listed in RFC6303 */
- if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 ||
- dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 ||
- 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)
- return DNS_SCOPE_NO;
-
- /* Never respond to some of the domains listed in RFC6761 */
- if (dns_name_endswith(domain, "invalid") > 0)
- return DNS_SCOPE_NO;
-
- /* Always honour search domains for routing queries. Note that
- * we return DNS_SCOPE_YES here, rather than just
- * DNS_SCOPE_MAYBE, which means wildcard scopes won't be
- * considered anymore. */
- LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
- if (dns_name_endswith(domain, d->name) > 0)
- return DNS_SCOPE_YES;
-
- /* If the DNS server has route-only domains, don't send other requests
- * to it. This would be a privacy violation, will most probably fail
- * anyway, and adds unnecessary load. */
- dns_server = dns_scope_get_dns_server(s);
- if (dns_server && dns_server_limited_domains(dns_server))
- return DNS_SCOPE_NO;
-
- switch (s->protocol) {
-
- case DNS_PROTOCOL_DNS:
-
- /* Exclude link-local IP ranges */
- if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
- dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 &&
- dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 &&
- dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 &&
- dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0 &&
- /* If networks use .local in their private setups, they are supposed to also add .local to their search
- * domains, which we already checked above. Otherwise, we consider .local specific to mDNS and won't
- * send such queries ordinary DNS servers. */
- dns_name_endswith(domain, "local") == 0)
- return DNS_SCOPE_MAYBE;
-
- return DNS_SCOPE_NO;
-
- case DNS_PROTOCOL_MDNS:
- if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
- (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
- (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
- dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */
- manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */
- return DNS_SCOPE_MAYBE;
-
- return DNS_SCOPE_NO;
-
- case DNS_PROTOCOL_LLMNR:
- if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
- (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
- (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
- !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
- manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
- return DNS_SCOPE_MAYBE;
-
- return DNS_SCOPE_NO;
-
- default:
- assert_not_reached("Unknown scope protocol");
- }
-}
-
-bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
- int key_family;
-
- assert(s);
- assert(key);
-
- /* Check if it makes sense to resolve the specified key on
- * this scope. Note that this call assumes as fully qualified
- * name, i.e. the search suffixes already appended. */
-
- if (key->class != DNS_CLASS_IN)
- return false;
-
- if (s->protocol == DNS_PROTOCOL_DNS) {
-
- /* On classic DNS, looking 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 */
-
- key_family = dns_type_to_af(key->type);
- if (key_family < 0)
- return true;
-
- return key_family == s->family;
-}
-
-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) {
- assert(s);
-
- 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) {
- assert(s);
-
- 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;
- int r;
-
- assert(s);
- assert(ret);
-
- if (dns_question_isempty(q) &&
- dns_answer_isempty(answer) &&
- dns_answer_isempty(soa))
- 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));
-
- r = dns_packet_append_question(p, q);
- if (r < 0)
- return r;
- DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
-
- r = dns_packet_append_answer(p, answer);
- if (r < 0)
- return r;
- DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer));
-
- r = dns_packet_append_answer(p, soa);
- if (r < 0)
- return r;
- DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa));
-
- *ret = p;
- p = NULL;
-
- return 0;
-}
-
-static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
- DnsResourceRecord *rr;
- DnsResourceKey *key;
-
- assert(s);
- assert(p);
-
- DNS_QUESTION_FOREACH(key, p->question)
- dns_zone_verify_conflicts(&s->zone, key);
-
- DNS_ANSWER_FOREACH(rr, p->answer)
- dns_zone_verify_conflicts(&s->zone, rr->key);
-}
-
-void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
- _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
- DnsResourceKey *key = NULL;
- bool tentative = false;
- int r;
-
- 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 resource records 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(dns_question_size(p->question) == 1);
- key = p->question->keys[0];
-
- r = dns_zone_lookup(&s->zone, key, 0, &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);
- if (r < 0) {
- log_debug_errno(r, "Failed to enqueue reply packet: %m");
- return;
- }
-
- /* Let's take an extra reference on this stream, so that it stays around after returning. The reference
- * will be dangling until the stream is disconnected, and the default completion handler of the stream
- * will then unref the stream and destroy it */
- if (DNS_STREAM_QUEUED(stream))
- dns_stream_ref(stream);
- } else {
- int fd;
-
- 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, NULL, 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_RCODE_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));
-
- /* For mDNS, the transaction ID should always be 0 */
- if (s->protocol != DNS_PROTOCOL_MDNS)
- 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_udp(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");
-
- (void) sd_event_source_set_description(scope->conflict_event_source, "scope-conflict");
-
- 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);
-}
-
-bool dns_scope_network_good(DnsScope *s) {
- /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
- * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
- * DNS scope we check whether there are any links that are up and have an address. */
-
- if (s->link)
- return true;
-
- return manager_routable(s->manager, AF_UNSPEC);
-}
-
-int dns_scope_ifindex(DnsScope *s) {
- assert(s);
-
- if (s->link)
- return s->link->ifindex;
-
- return 0;
-}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
deleted file mode 100644
index 01a83a76b2..0000000000
--- a/src/resolve/resolved-dns-scope.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "list.h"
-
-typedef struct DnsScope DnsScope;
-
-#include "resolved-dns-cache.h"
-#include "resolved-dns-dnssec.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-query.h"
-#include "resolved-dns-search-domain.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-stream.h"
-#include "resolved-dns-zone.h"
-#include "resolved-link.h"
-
-typedef enum DnsScopeMatch {
- DNS_SCOPE_NO,
- DNS_SCOPE_MAYBE,
- DNS_SCOPE_YES,
- _DNS_SCOPE_MATCH_MAX,
- _DNS_SCOPE_INVALID = -1
-} DnsScopeMatch;
-
-struct DnsScope {
- Manager *manager;
-
- DnsProtocol protocol;
- int family;
- DnssecMode dnssec_mode;
-
- Link *link;
-
- DnsCache cache;
- DnsZone zone;
-
- OrderedHashmap *conflict_queue;
- sd_event_source *conflict_event_source;
-
- RateLimit ratelimit;
-
- usec_t resend_timeout;
- usec_t max_rtt;
-
- LIST_HEAD(DnsQueryCandidate, query_candidates);
-
- /* Note that we keep track of ongoing transactions in two
- * ways: once in a hashmap, indexed by the rr key, and once in
- * a linked list. We use the hashmap to quickly find
- * transactions we can reuse for a key. But note that there
- * might be multiple transactions for the same key (because
- * the zone probing can't reuse a transaction answered from
- * the zone or the cache), and the hashmap only tracks the
- * most recent entry. */
- Hashmap *transactions_by_key;
- LIST_HEAD(DnsTransaction, transactions);
-
- LIST_FIELDS(DnsScope, scopes);
-};
-
-int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
-DnsScope* dns_scope_free(DnsScope *s);
-
-void dns_scope_packet_received(DnsScope *s, usec_t rtt);
-void dns_scope_packet_lost(DnsScope *s, usec_t usec);
-
-int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
-int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port);
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
-
-DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
-bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
-
-DnsServer *dns_scope_get_dns_server(DnsScope *s);
-void dns_scope_next_dns_server(DnsScope *s);
-
-int dns_scope_llmnr_membership(DnsScope *s, bool b);
-int dns_scope_mdns_membership(DnsScope *s, bool b);
-
-void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
-
-DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok);
-
-int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
-void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
-
-void dns_scope_dump(DnsScope *s, FILE *f);
-
-DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
-
-bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
-
-bool dns_scope_network_good(DnsScope *s);
-
-int dns_scope_ifindex(DnsScope *s);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
deleted file mode 100644
index 1386e6a17b..0000000000
--- a/src/resolve/resolved-dns-search-domain.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "resolved-dns-search-domain.h"
-
-int dns_search_domain_new(
- Manager *m,
- DnsSearchDomain **ret,
- DnsSearchDomainType type,
- Link *l,
- const char *name) {
-
- _cleanup_free_ char *normalized = NULL;
- DnsSearchDomain *d;
- int r;
-
- assert(m);
- assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
- assert(name);
-
- r = dns_name_normalize(name, &normalized);
- if (r < 0)
- return r;
-
- if (l) {
- if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
- return -E2BIG;
- } else {
- if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
- return -E2BIG;
- }
-
- d = new0(DnsSearchDomain, 1);
- if (!d)
- return -ENOMEM;
-
- d->n_ref = 1;
- d->manager = m;
- d->type = type;
- d->name = normalized;
- normalized = NULL;
-
- switch (type) {
-
- case DNS_SEARCH_DOMAIN_LINK:
- d->link = l;
- LIST_APPEND(domains, l->search_domains, d);
- l->n_search_domains++;
- break;
-
- case DNS_SERVER_SYSTEM:
- LIST_APPEND(domains, m->search_domains, d);
- m->n_search_domains++;
- break;
-
- default:
- assert_not_reached("Unknown search domain type");
- }
-
- d->linked = true;
-
- if (ret)
- *ret = d;
-
- return 0;
-}
-
-DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref++;
-
- return d;
-}
-
-DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
- if (!d)
- return NULL;
-
- assert(d->n_ref > 0);
- d->n_ref--;
-
- if (d->n_ref > 0)
- return NULL;
-
- free(d->name);
- return mfree(d);
-}
-
-void dns_search_domain_unlink(DnsSearchDomain *d) {
- assert(d);
- assert(d->manager);
-
- if (!d->linked)
- return;
-
- switch (d->type) {
-
- case DNS_SEARCH_DOMAIN_LINK:
- assert(d->link);
- assert(d->link->n_search_domains > 0);
- LIST_REMOVE(domains, d->link->search_domains, d);
- d->link->n_search_domains--;
- break;
-
- case DNS_SEARCH_DOMAIN_SYSTEM:
- assert(d->manager->n_search_domains > 0);
- LIST_REMOVE(domains, d->manager->search_domains, d);
- d->manager->n_search_domains--;
- break;
- }
-
- d->linked = false;
-
- dns_search_domain_unref(d);
-}
-
-void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
- DnsSearchDomain *tail;
-
- assert(d);
-
- if (!d->marked)
- return;
-
- d->marked = false;
-
- if (!d->linked || !d->domains_next)
- return;
-
- switch (d->type) {
-
- case DNS_SEARCH_DOMAIN_LINK:
- assert(d->link);
- LIST_FIND_TAIL(domains, d, tail);
- LIST_REMOVE(domains, d->link->search_domains, d);
- LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
- break;
-
- case DNS_SEARCH_DOMAIN_SYSTEM:
- LIST_FIND_TAIL(domains, d, tail);
- LIST_REMOVE(domains, d->manager->search_domains, d);
- LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
- break;
-
- default:
- assert_not_reached("Unknown search domain type");
- }
-}
-
-void dns_search_domain_unlink_all(DnsSearchDomain *first) {
- DnsSearchDomain *next;
-
- if (!first)
- return;
-
- next = first->domains_next;
- dns_search_domain_unlink(first);
-
- dns_search_domain_unlink_all(next);
-}
-
-void dns_search_domain_unlink_marked(DnsSearchDomain *first) {
- DnsSearchDomain *next;
-
- if (!first)
- return;
-
- next = first->domains_next;
-
- if (first->marked)
- dns_search_domain_unlink(first);
-
- dns_search_domain_unlink_marked(next);
-}
-
-void dns_search_domain_mark_all(DnsSearchDomain *first) {
- if (!first)
- return;
-
- first->marked = true;
- dns_search_domain_mark_all(first->domains_next);
-}
-
-int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
- DnsSearchDomain *d;
- int r;
-
- assert(name);
- assert(ret);
-
- LIST_FOREACH(domains, d, first) {
-
- r = dns_name_equal(name, d->name);
- if (r < 0)
- return r;
- if (r > 0) {
- *ret = d;
- return 1;
- }
- }
-
- *ret = NULL;
- return 0;
-}
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
deleted file mode 100644
index eaacef4edc..0000000000
--- a/src/resolve/resolved-dns-search-domain.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "macro.h"
-
-typedef struct DnsSearchDomain DnsSearchDomain;
-
-typedef enum DnsSearchDomainType {
- DNS_SEARCH_DOMAIN_SYSTEM,
- DNS_SEARCH_DOMAIN_LINK,
-} DnsSearchDomainType;
-
-#include "resolved-link.h"
-#include "resolved-manager.h"
-
-struct DnsSearchDomain {
- Manager *manager;
-
- unsigned n_ref;
-
- DnsSearchDomainType type;
- Link *link;
-
- char *name;
-
- bool marked:1;
- bool route_only:1;
-
- bool linked:1;
- LIST_FIELDS(DnsSearchDomain, domains);
-};
-
-int dns_search_domain_new(
- Manager *m,
- DnsSearchDomain **ret,
- DnsSearchDomainType type,
- Link *link,
- const char *name);
-
-DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
-DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d);
-
-void dns_search_domain_unlink(DnsSearchDomain *d);
-void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d);
-
-void dns_search_domain_unlink_all(DnsSearchDomain *first);
-void dns_search_domain_unlink_marked(DnsSearchDomain *first);
-void dns_search_domain_mark_all(DnsSearchDomain *first);
-
-int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret);
-
-static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
- return d ? d->name : NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
deleted file mode 100644
index 22c64e8491..0000000000
--- a/src/resolve/resolved-dns-server.c
+++ /dev/null
@@ -1,796 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sd-messages.h>
-
-#include "alloc-util.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-stub.h"
-#include "resolved-resolv-conf.h"
-#include "siphash24.h"
-#include "string-table.h"
-#include "string-util.h"
-
-/* After how much time to repeat classic DNS requests */
-#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
-#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
-
-/* The amount of time to wait before retrying with a full feature set */
-#define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR)
-#define DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC (5 * USEC_PER_MINUTE)
-
-/* The number of times we will attempt a certain feature set before degrading */
-#define DNS_SERVER_FEATURE_RETRY_ATTEMPTS 3
-
-int dns_server_new(
- Manager *m,
- DnsServer **ret,
- DnsServerType type,
- Link *l,
- int family,
- const union in_addr_union *in_addr,
- int ifindex) {
-
- DnsServer *s;
-
- assert(m);
- assert((type == DNS_SERVER_LINK) == !!l);
- assert(in_addr);
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
-
- if (l) {
- if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
- return -E2BIG;
- } else {
- if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
- return -E2BIG;
- }
-
- s = new0(DnsServer, 1);
- if (!s)
- return -ENOMEM;
-
- s->n_ref = 1;
- s->manager = m;
- s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
- s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
- s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
- s->type = type;
- s->family = family;
- s->address = *in_addr;
- s->ifindex = ifindex;
- s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
-
- switch (type) {
-
- case DNS_SERVER_LINK:
- s->link = l;
- LIST_APPEND(servers, l->dns_servers, s);
- l->n_dns_servers++;
- break;
-
- case DNS_SERVER_SYSTEM:
- LIST_APPEND(servers, m->dns_servers, s);
- m->n_dns_servers++;
- break;
-
- case DNS_SERVER_FALLBACK:
- LIST_APPEND(servers, m->fallback_dns_servers, s);
- m->n_dns_servers++;
- break;
-
- default:
- assert_not_reached("Unknown server type");
- }
-
- s->linked = true;
-
- /* A new DNS server that isn't fallback is added and the one
- * we used so far was a fallback one? Then let's try to pick
- * the new one */
- if (type != DNS_SERVER_FALLBACK &&
- m->current_dns_server &&
- m->current_dns_server->type == DNS_SERVER_FALLBACK)
- manager_set_dns_server(m, NULL);
-
- if (ret)
- *ret = s;
-
- return 0;
-}
-
-DnsServer* dns_server_ref(DnsServer *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref++;
-
- return s;
-}
-
-DnsServer* dns_server_unref(DnsServer *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref--;
-
- if (s->n_ref > 0)
- return NULL;
-
- free(s->server_string);
- return mfree(s);
-}
-
-void dns_server_unlink(DnsServer *s) {
- assert(s);
- assert(s->manager);
-
- /* This removes the specified server from the linked list of
- * servers, but any server might still stay around if it has
- * refs, for example from an ongoing transaction. */
-
- if (!s->linked)
- return;
-
- switch (s->type) {
-
- case DNS_SERVER_LINK:
- assert(s->link);
- assert(s->link->n_dns_servers > 0);
- LIST_REMOVE(servers, s->link->dns_servers, s);
- s->link->n_dns_servers--;
- break;
-
- case DNS_SERVER_SYSTEM:
- assert(s->manager->n_dns_servers > 0);
- LIST_REMOVE(servers, s->manager->dns_servers, s);
- s->manager->n_dns_servers--;
- break;
-
- case DNS_SERVER_FALLBACK:
- assert(s->manager->n_dns_servers > 0);
- LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
- s->manager->n_dns_servers--;
- break;
- }
-
- s->linked = false;
-
- if (s->link && s->link->current_dns_server == s)
- link_set_dns_server(s->link, NULL);
-
- if (s->manager->current_dns_server == s)
- manager_set_dns_server(s->manager, NULL);
-
- dns_server_unref(s);
-}
-
-void dns_server_move_back_and_unmark(DnsServer *s) {
- DnsServer *tail;
-
- assert(s);
-
- if (!s->marked)
- return;
-
- s->marked = false;
-
- if (!s->linked || !s->servers_next)
- return;
-
- /* Move us to the end of the list, so that the order is
- * strictly kept, if we are not at the end anyway. */
-
- switch (s->type) {
-
- case DNS_SERVER_LINK:
- assert(s->link);
- LIST_FIND_TAIL(servers, s, tail);
- LIST_REMOVE(servers, s->link->dns_servers, s);
- LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
- break;
-
- case DNS_SERVER_SYSTEM:
- LIST_FIND_TAIL(servers, s, tail);
- LIST_REMOVE(servers, s->manager->dns_servers, s);
- LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
- break;
-
- case DNS_SERVER_FALLBACK:
- LIST_FIND_TAIL(servers, s, tail);
- LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
- LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
- break;
-
- default:
- assert_not_reached("Unknown server type");
- }
-}
-
-static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
- assert(s);
-
- if (s->verified_feature_level > level)
- return;
-
- if (s->verified_feature_level != level) {
- log_debug("Verified we get a response at feature level %s from DNS server %s.",
- dns_server_feature_level_to_string(level),
- dns_server_string(s));
- s->verified_feature_level = level;
- }
-
- assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
-}
-
-static void dns_server_reset_counters(DnsServer *s) {
- assert(s);
-
- s->n_failed_udp = 0;
- s->n_failed_tcp = 0;
- s->packet_truncated = false;
- s->verified_usec = 0;
-
- /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the
- * grace period ends, but not when lowering the possible feature level, as a lower level feature level should
- * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's
- * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that
- * either.
- *
- * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A),
- * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not
- * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be
- * incomplete. */
-}
-
-void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
- assert(s);
-
- if (protocol == IPPROTO_UDP) {
- if (s->possible_feature_level == level)
- s->n_failed_udp = 0;
-
- /* If the RRSIG data is missing, then we can only validate EDNS0 at max */
- if (s->packet_rrsig_missing && level >= DNS_SERVER_FEATURE_LEVEL_DO)
- level = DNS_SERVER_FEATURE_LEVEL_DO - 1;
-
- /* If the OPT RR got lost, then we can only validate UDP at max */
- if (s->packet_bad_opt && level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
- level = DNS_SERVER_FEATURE_LEVEL_EDNS0 - 1;
-
- /* Even if we successfully receive a reply to a request announcing support for large packets,
- that does not mean we can necessarily receive large packets. */
- if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
- level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
-
- } else if (protocol == IPPROTO_TCP) {
-
- if (s->possible_feature_level == level)
- s->n_failed_tcp = 0;
-
- /* Successful TCP connections are only useful to verify the TCP feature level. */
- level = DNS_SERVER_FEATURE_LEVEL_TCP;
- }
-
- dns_server_verified(s, level);
-
- /* Remember the size of the largest UDP packet we received from a server,
- we know that we can always announce support for packets with at least
- this size. */
- if (protocol == IPPROTO_UDP && s->received_udp_packet_max < size)
- s->received_udp_packet_max = size;
-
- if (s->max_rtt < rtt) {
- s->max_rtt = rtt;
- s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC);
- }
-}
-
-void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) {
- assert(s);
- assert(s->manager);
-
- if (s->possible_feature_level == level) {
- if (protocol == IPPROTO_UDP)
- s->n_failed_udp++;
- else if (protocol == IPPROTO_TCP)
- s->n_failed_tcp++;
- }
-
- if (s->resend_timeout > usec)
- return;
-
- s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
-}
-
-void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
- assert(s);
-
- /* Invoked whenever we get a packet with TC bit set. */
-
- if (s->possible_feature_level != level)
- return;
-
- s->packet_truncated = true;
-}
-
-void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level) {
- assert(s);
-
- if (level < DNS_SERVER_FEATURE_LEVEL_DO)
- return;
-
- /* If the RRSIG RRs are missing, we have to downgrade what we previously verified */
- if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO)
- s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_DO-1;
-
- s->packet_rrsig_missing = true;
-}
-
-void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level) {
- assert(s);
-
- if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
- return;
-
- /* If the OPT RR got lost, we have to downgrade what we previously verified */
- if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
- s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0-1;
-
- s->packet_bad_opt = true;
-}
-
-void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level) {
- assert(s);
-
- /* Invoked whenever we got a FORMERR, SERVFAIL or NOTIMP rcode from a server and downgrading the feature level
- * for the transaction made it go away. In this case we immediately downgrade to the feature level that made
- * things work. */
-
- if (s->verified_feature_level > level)
- s->verified_feature_level = level;
-
- if (s->possible_feature_level > level) {
- s->possible_feature_level = level;
- dns_server_reset_counters(s);
- }
-
- log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s));
-}
-
-static bool dns_server_grace_period_expired(DnsServer *s) {
- usec_t ts;
-
- assert(s);
- assert(s->manager);
-
- if (s->verified_usec == 0)
- return false;
-
- assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
- if (s->verified_usec + s->features_grace_period_usec > ts)
- return false;
-
- s->features_grace_period_usec = MIN(s->features_grace_period_usec * 2, DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC);
-
- return true;
-}
-
-DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
- assert(s);
-
- if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
- dns_server_grace_period_expired(s)) {
-
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
-
- dns_server_reset_counters(s);
-
- s->packet_bad_opt = false;
- s->packet_rrsig_missing = false;
-
- log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
- dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_string(s));
-
- } else if (s->possible_feature_level <= s->verified_feature_level)
- s->possible_feature_level = s->verified_feature_level;
- else {
- DnsServerFeatureLevel p = s->possible_feature_level;
-
- if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP) {
-
- /* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't
- * work. Upgrade back to UDP again. */
- log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again...");
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
-
- } else if (s->packet_bad_opt &&
- s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
-
- /* A reply to one of our EDNS0 queries didn't carry a valid OPT RR, then downgrade to below
- * EDNS0 levels. After all, some records generate different responses with and without OPT RR
- * in the request. Example:
- * https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
-
- log_debug("Server doesn't support EDNS(0) properly, downgrading feature level...");
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
-
- } else if (s->packet_rrsig_missing &&
- s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO) {
-
- /* RRSIG data was missing on a EDNS0 packet with DO bit set. This means the server doesn't
- * augment responses with DNSSEC RRs. If so, let's better not ask the server for it anymore,
- * after all some servers generate different replies depending if an OPT RR is in the query or
- * not. */
-
- log_debug("Detected server responses lack RRSIG records, downgrading feature level...");
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0;
-
- } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) {
-
- /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the
- * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good
- * idea. We might downgrade all the way down to TCP this way. */
-
- log_debug("Lost too many UDP packets, downgrading feature level...");
- s->possible_feature_level--;
-
- } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->packet_truncated &&
- s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
-
- /* We got too many TCP connection failures in a row, we had at least one truncated packet, and
- * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0
- * data we hope to make the packet smaller, so that it still works via UDP given that TCP
- * appears not to be a fallback. Note that if we are already at the lowest UDP level, we don't
- * go further down, since that's TCP, and TCP failed too often after all. */
-
- log_debug("Got too many failed TCP connection failures and truncated UDP packets, downgrading feature level...");
- s->possible_feature_level--;
- }
-
- if (p != s->possible_feature_level) {
-
- /* We changed the feature level, reset the counting */
- dns_server_reset_counters(s);
-
- log_warning("Using degraded feature set (%s) for DNS server %s.",
- dns_server_feature_level_to_string(s->possible_feature_level),
- dns_server_string(s));
- }
- }
-
- return s->possible_feature_level;
-}
-
-int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
- size_t packet_size;
- bool edns_do;
- int r;
-
- assert(server);
- assert(packet);
- assert(packet->protocol == DNS_PROTOCOL_DNS);
-
- /* Fix the OPT field in the packet to match our current feature level. */
-
- r = dns_packet_truncate_opt(packet);
- if (r < 0)
- return r;
-
- if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
- return 0;
-
- edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
-
- if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
- packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
- else
- packet_size = server->received_udp_packet_max;
-
- return dns_packet_append_opt(packet, packet_size, edns_do, 0, NULL);
-}
-
-int dns_server_ifindex(const DnsServer *s) {
- assert(s);
-
- /* The link ifindex always takes precedence */
- if (s->link)
- return s->link->ifindex;
-
- if (s->ifindex > 0)
- return s->ifindex;
-
- return 0;
-}
-
-const char *dns_server_string(DnsServer *server) {
- assert(server);
-
- if (!server->server_string)
- (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string);
-
- return strna(server->server_string);
-}
-
-bool dns_server_dnssec_supported(DnsServer *server) {
- assert(server);
-
- /* Returns whether the server supports DNSSEC according to what we know about it */
-
- if (server->possible_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
- return false;
-
- if (server->packet_bad_opt)
- return false;
-
- if (server->packet_rrsig_missing)
- return false;
-
- /* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */
- if (server->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS)
- return false;
-
- return true;
-}
-
-void dns_server_warn_downgrade(DnsServer *server) {
- assert(server);
-
- if (server->warned_downgrade)
- return;
-
- log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE),
- LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
- "DNS_SERVER=%s", dns_server_string(server),
- "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level),
- NULL);
-
- server->warned_downgrade = true;
-}
-
-bool dns_server_limited_domains(DnsServer *server) {
- DnsSearchDomain *domain;
- bool domain_restricted = false;
-
- /* Check if the server has route-only domains without ~., i. e. whether
- * it should only be used for particular domains */
- if (!server->link)
- return false;
-
- LIST_FOREACH(domains, domain, server->link->search_domains)
- if (domain->route_only) {
- domain_restricted = true;
- /* ~. means "any domain", thus it is a global server */
- if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
- return false;
- }
-
- return domain_restricted;
-}
-
-static void dns_server_hash_func(const void *p, struct siphash *state) {
- const DnsServer *s = p;
-
- assert(s);
-
- siphash24_compress(&s->family, sizeof(s->family), state);
- siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
- siphash24_compress(&s->ifindex, sizeof(s->ifindex), state);
-}
-
-static int dns_server_compare_func(const void *a, const void *b) {
- const DnsServer *x = a, *y = b;
- int r;
-
- if (x->family < y->family)
- return -1;
- if (x->family > y->family)
- return 1;
-
- r = memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
- if (r != 0)
- return r;
-
- if (x->ifindex < y->ifindex)
- return -1;
- if (x->ifindex > y->ifindex)
- return 1;
-
- return 0;
-}
-
-const struct hash_ops dns_server_hash_ops = {
- .hash = dns_server_hash_func,
- .compare = dns_server_compare_func
-};
-
-void dns_server_unlink_all(DnsServer *first) {
- DnsServer *next;
-
- if (!first)
- return;
-
- next = first->servers_next;
- dns_server_unlink(first);
-
- dns_server_unlink_all(next);
-}
-
-void dns_server_unlink_marked(DnsServer *first) {
- DnsServer *next;
-
- if (!first)
- return;
-
- next = first->servers_next;
-
- if (first->marked)
- dns_server_unlink(first);
-
- dns_server_unlink_marked(next);
-}
-
-void dns_server_mark_all(DnsServer *first) {
- if (!first)
- return;
-
- first->marked = true;
- dns_server_mark_all(first->servers_next);
-}
-
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) {
- DnsServer *s;
-
- LIST_FOREACH(servers, s, first)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex)
- return s;
-
- return NULL;
-}
-
-DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
- assert(m);
-
- switch (t) {
-
- case DNS_SERVER_SYSTEM:
- return m->dns_servers;
-
- case DNS_SERVER_FALLBACK:
- return m->fallback_dns_servers;
-
- default:
- return NULL;
- }
-}
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
- assert(m);
-
- if (m->current_dns_server == s)
- return s;
-
- if (s)
- log_info("Switching to %s DNS server %s.",
- dns_server_type_to_string(s->type),
- dns_server_string(s));
-
- dns_server_unref(m->current_dns_server);
- m->current_dns_server = dns_server_ref(s);
-
- if (m->unicast_scope)
- dns_cache_flush(&m->unicast_scope->cache);
-
- return s;
-}
-
-DnsServer *manager_get_dns_server(Manager *m) {
- Link *l;
- assert(m);
-
- /* Try to read updates resolv.conf */
- manager_read_resolv_conf(m);
-
- /* If no DNS server was chosen so far, pick the first one */
- if (!m->current_dns_server)
- manager_set_dns_server(m, m->dns_servers);
-
- if (!m->current_dns_server) {
- bool found = false;
- Iterator i;
-
- /* No DNS servers configured, let's see if there are
- * any on any links. If not, we use the fallback
- * servers */
-
- HASHMAP_FOREACH(l, m->links, i)
- if (l->dns_servers) {
- found = true;
- break;
- }
-
- if (!found)
- manager_set_dns_server(m, m->fallback_dns_servers);
- }
-
- return m->current_dns_server;
-}
-
-void manager_next_dns_server(Manager *m) {
- assert(m);
-
- /* If there's currently no DNS server set, then the next
- * manager_get_dns_server() will find one */
- if (!m->current_dns_server)
- return;
-
- /* Change to the next one, but make sure to follow the linked
- * list only if the server is still linked. */
- if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
- manager_set_dns_server(m, m->current_dns_server->servers_next);
- return;
- }
-
- /* If there was no next one, then start from the beginning of
- * the list */
- if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
- manager_set_dns_server(m, m->fallback_dns_servers);
- else
- manager_set_dns_server(m, m->dns_servers);
-}
-
-bool dns_server_address_valid(int family, const union in_addr_union *sa) {
-
- /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */
-
- if (in_addr_is_null(family, sa))
- return false;
-
- if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB))
- return false;
-
- return true;
-}
-
-static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
- [DNS_SERVER_SYSTEM] = "system",
- [DNS_SERVER_FALLBACK] = "fallback",
- [DNS_SERVER_LINK] = "link",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType);
-
-static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
- [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
- [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
- [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
- [DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
- [DNS_SERVER_FEATURE_LEVEL_LARGE] = "UDP+EDNS0+DO+LARGE",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
deleted file mode 100644
index 83e288a202..0000000000
--- a/src/resolve/resolved-dns-server.h
+++ /dev/null
@@ -1,149 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "in-addr-util.h"
-
-typedef struct DnsServer DnsServer;
-
-typedef enum DnsServerType {
- DNS_SERVER_SYSTEM,
- DNS_SERVER_FALLBACK,
- DNS_SERVER_LINK,
-} DnsServerType;
-#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1)
-
-const char* dns_server_type_to_string(DnsServerType i) _const_;
-DnsServerType dns_server_type_from_string(const char *s) _pure_;
-
-typedef enum DnsServerFeatureLevel {
- DNS_SERVER_FEATURE_LEVEL_TCP,
- DNS_SERVER_FEATURE_LEVEL_UDP,
- DNS_SERVER_FEATURE_LEVEL_EDNS0,
- DNS_SERVER_FEATURE_LEVEL_DO,
- DNS_SERVER_FEATURE_LEVEL_LARGE,
- _DNS_SERVER_FEATURE_LEVEL_MAX,
- _DNS_SERVER_FEATURE_LEVEL_INVALID = -1
-} DnsServerFeatureLevel;
-
-#define DNS_SERVER_FEATURE_LEVEL_WORST 0
-#define DNS_SERVER_FEATURE_LEVEL_BEST (_DNS_SERVER_FEATURE_LEVEL_MAX - 1)
-
-const char* dns_server_feature_level_to_string(int i) _const_;
-int dns_server_feature_level_from_string(const char *s) _pure_;
-
-#include "resolved-link.h"
-#include "resolved-manager.h"
-
-struct DnsServer {
- Manager *manager;
-
- unsigned n_ref;
-
- DnsServerType type;
- Link *link;
-
- int family;
- union in_addr_union address;
- int ifindex; /* for IPv6 link-local DNS servers */
-
- char *server_string;
-
- usec_t resend_timeout;
- usec_t max_rtt;
-
- DnsServerFeatureLevel verified_feature_level;
- DnsServerFeatureLevel possible_feature_level;
-
- size_t received_udp_packet_max;
-
- unsigned n_failed_udp;
- unsigned n_failed_tcp;
-
- bool packet_truncated:1;
- bool packet_bad_opt:1;
- bool packet_rrsig_missing:1;
-
- usec_t verified_usec;
- usec_t features_grace_period_usec;
-
- /* Whether we already warned about downgrading to non-DNSSEC mode for this server */
- bool warned_downgrade:1;
-
- /* Used when GC'ing old DNS servers when configuration changes. */
- bool marked:1;
-
- /* If linked is set, then this server appears in the servers linked list */
- bool linked:1;
- LIST_FIELDS(DnsServer, servers);
-};
-
-int dns_server_new(
- Manager *m,
- DnsServer **ret,
- DnsServerType type,
- Link *link,
- int family,
- const union in_addr_union *address,
- int ifindex);
-
-DnsServer* dns_server_ref(DnsServer *s);
-DnsServer* dns_server_unref(DnsServer *s);
-
-void dns_server_unlink(DnsServer *s);
-void dns_server_move_back_and_unmark(DnsServer *s);
-
-void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size);
-void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec);
-void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level);
-void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level);
-void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level);
-void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level);
-
-DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
-
-int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
-
-const char *dns_server_string(DnsServer *server);
-int dns_server_ifindex(const DnsServer *s);
-
-bool dns_server_dnssec_supported(DnsServer *server);
-
-void dns_server_warn_downgrade(DnsServer *server);
-
-bool dns_server_limited_domains(DnsServer *server);
-
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
-
-void dns_server_unlink_all(DnsServer *first);
-void dns_server_unlink_marked(DnsServer *first);
-void dns_server_mark_all(DnsServer *first);
-
-DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
-
-DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
-DnsServer *manager_get_dns_server(Manager *m);
-void manager_next_dns_server(Manager *m);
-
-bool dns_server_address_valid(int family, const union in_addr_union *sa);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
-
-extern const struct hash_ops dns_server_hash_ops;
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
deleted file mode 100644
index 878bae47dc..0000000000
--- a/src/resolve/resolved-dns-stream.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <netinet/tcp.h>
-
-#include "alloc-util.h"
-#include "fd-util.h"
-#include "io-util.h"
-#include "missing.h"
-#include "resolved-dns-stream.h"
-
-#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
-#define DNS_STREAMS_MAX 128
-
-static void dns_stream_stop(DnsStream *s) {
- assert(s);
-
- s->io_event_source = sd_event_source_unref(s->io_event_source);
- s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
- s->fd = safe_close(s->fd);
-}
-
-static int dns_stream_update_io(DnsStream *s) {
- int f = 0;
-
- assert(s);
-
- if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
- f |= EPOLLOUT;
- if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
- f |= EPOLLIN;
-
- return sd_event_source_set_io_events(s->io_event_source, f);
-}
-
-static int dns_stream_complete(DnsStream *s, int error) {
- assert(s);
-
- dns_stream_stop(s);
-
- if (s->complete)
- s->complete(s, error);
- else /* the default action if no completion function is set is to close the stream */
- dns_stream_unref(s);
-
- return 0;
-}
-
-static int dns_stream_identify(DnsStream *s) {
- union {
- struct cmsghdr header; /* For alignment */
- uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
- + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
- } control;
- struct msghdr mh = {};
- struct cmsghdr *cmsg;
- socklen_t sl;
- int r;
-
- assert(s);
-
- if (s->identified)
- return 0;
-
- /* Query the local side */
- s->local_salen = sizeof(s->local);
- r = getsockname(s->fd, &s->local.sa, &s->local_salen);
- if (r < 0)
- return -errno;
- if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
- s->ifindex = s->local.in6.sin6_scope_id;
-
- /* Query the remote side */
- s->peer_salen = sizeof(s->peer);
- r = getpeername(s->fd, &s->peer.sa, &s->peer_salen);
- if (r < 0)
- return -errno;
- if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0)
- s->ifindex = s->peer.in6.sin6_scope_id;
-
- /* Check consistency */
- assert(s->peer.sa.sa_family == s->local.sa.sa_family);
- assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
-
- /* Query connection meta information */
- sl = sizeof(control);
- if (s->peer.sa.sa_family == AF_INET) {
- r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
- if (r < 0)
- return -errno;
- } else if (s->peer.sa.sa_family == AF_INET6) {
-
- r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
- if (r < 0)
- return -errno;
- } else
- return -EAFNOSUPPORT;
-
- mh.msg_control = &control;
- mh.msg_controllen = sl;
-
- CMSG_FOREACH(cmsg, &mh) {
-
- if (cmsg->cmsg_level == IPPROTO_IPV6) {
- assert(s->peer.sa.sa_family == AF_INET6);
-
- switch (cmsg->cmsg_type) {
-
- case IPV6_PKTINFO: {
- struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
-
- if (s->ifindex <= 0)
- s->ifindex = i->ipi6_ifindex;
- break;
- }
-
- case IPV6_HOPLIMIT:
- s->ttl = *(int *) CMSG_DATA(cmsg);
- break;
- }
-
- } else if (cmsg->cmsg_level == IPPROTO_IP) {
- assert(s->peer.sa.sa_family == AF_INET);
-
- switch (cmsg->cmsg_type) {
-
- case IP_PKTINFO: {
- struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
-
- if (s->ifindex <= 0)
- s->ifindex = i->ipi_ifindex;
- break;
- }
-
- case IP_TTL:
- s->ttl = *(int *) CMSG_DATA(cmsg);
- break;
- }
- }
- }
-
- /* The Linux kernel sets the interface index to the loopback
- * device if the connection came from the local host since it
- * avoids the routing table in such a case. Let's unset the
- * interface index in such a case. */
- if (s->ifindex == LOOPBACK_IFINDEX)
- s->ifindex = 0;
-
- /* If we don't know the interface index still, we look for the
- * first local interface with a matching address. Yuck! */
- if (s->ifindex <= 0)
- s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr);
-
- if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
- uint32_t ifindex = htobe32(s->ifindex);
-
- /* Make sure all packets for this connection are sent on the same interface */
- if (s->local.sa.sa_family == AF_INET) {
- r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
- if (r < 0)
- log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m");
- } else if (s->local.sa.sa_family == AF_INET6) {
- r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
- if (r < 0)
- log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m");
- }
- }
-
- s->identified = true;
-
- return 0;
-}
-
-static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
- DnsStream *s = userdata;
-
- assert(s);
-
- return dns_stream_complete(s, ETIMEDOUT);
-}
-
-static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- DnsStream *s = userdata;
- int r;
-
- assert(s);
-
- r = dns_stream_identify(s);
- if (r < 0)
- return dns_stream_complete(s, -r);
-
- if ((revents & EPOLLOUT) &&
- s->write_packet &&
- s->n_written < sizeof(s->write_size) + s->write_packet->size) {
-
- struct iovec iov[2];
- ssize_t ss;
-
- iov[0].iov_base = &s->write_size;
- iov[0].iov_len = sizeof(s->write_size);
- iov[1].iov_base = DNS_PACKET_DATA(s->write_packet);
- iov[1].iov_len = s->write_packet->size;
-
- IOVEC_INCREMENT(iov, 2, s->n_written);
-
- ss = writev(fd, iov, 2);
- if (ss < 0) {
- if (errno != EINTR && errno != EAGAIN)
- return dns_stream_complete(s, errno);
- } else
- s->n_written += ss;
-
- /* Are we done? If so, disable the event source for EPOLLOUT */
- if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
- r = dns_stream_update_io(s);
- if (r < 0)
- return dns_stream_complete(s, -r);
- }
- }
-
- if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
- (!s->read_packet ||
- s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
-
- if (s->n_read < sizeof(s->read_size)) {
- ssize_t ss;
-
- ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
- if (ss < 0) {
- if (errno != EINTR && errno != EAGAIN)
- return dns_stream_complete(s, errno);
- } else if (ss == 0)
- return dns_stream_complete(s, ECONNRESET);
- else
- s->n_read += ss;
- }
-
- if (s->n_read >= sizeof(s->read_size)) {
-
- if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
- return dns_stream_complete(s, EBADMSG);
-
- if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
- ssize_t ss;
-
- if (!s->read_packet) {
- r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size));
- if (r < 0)
- return dns_stream_complete(s, -r);
-
- s->read_packet->size = be16toh(s->read_size);
- s->read_packet->ipproto = IPPROTO_TCP;
- s->read_packet->family = s->peer.sa.sa_family;
- s->read_packet->ttl = s->ttl;
- s->read_packet->ifindex = s->ifindex;
-
- if (s->read_packet->family == AF_INET) {
- s->read_packet->sender.in = s->peer.in.sin_addr;
- s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
- s->read_packet->destination.in = s->local.in.sin_addr;
- s->read_packet->destination_port = be16toh(s->local.in.sin_port);
- } else {
- assert(s->read_packet->family == AF_INET6);
- s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
- s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
- s->read_packet->destination.in6 = s->local.in6.sin6_addr;
- s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
-
- if (s->read_packet->ifindex == 0)
- s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
- if (s->read_packet->ifindex == 0)
- s->read_packet->ifindex = s->local.in6.sin6_scope_id;
- }
- }
-
- ss = read(fd,
- (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
- sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
- if (ss < 0) {
- if (errno != EINTR && errno != EAGAIN)
- return dns_stream_complete(s, errno);
- } else if (ss == 0)
- return dns_stream_complete(s, ECONNRESET);
- else
- s->n_read += ss;
- }
-
- /* Are we done? If so, disable the event source for EPOLLIN */
- if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
- r = dns_stream_update_io(s);
- if (r < 0)
- return dns_stream_complete(s, -r);
-
- /* If there's a packet handler
- * installed, call that. Note that
- * this is optional... */
- if (s->on_packet)
- return s->on_packet(s);
- }
- }
- }
-
- if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
- (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
- return dns_stream_complete(s, 0);
-
- return 0;
-}
-
-DnsStream *dns_stream_unref(DnsStream *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref--;
-
- if (s->n_ref > 0)
- return NULL;
-
- dns_stream_stop(s);
-
- if (s->manager) {
- LIST_REMOVE(streams, s->manager->dns_streams, s);
- s->manager->n_dns_streams--;
- }
-
- dns_packet_unref(s->write_packet);
- dns_packet_unref(s->read_packet);
-
- return mfree(s);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref);
-
-DnsStream *dns_stream_ref(DnsStream *s) {
- if (!s)
- return NULL;
-
- assert(s->n_ref > 0);
- s->n_ref++;
-
- return s;
-}
-
-int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
- _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
- int r;
-
- assert(m);
- assert(fd >= 0);
-
- if (m->n_dns_streams > DNS_STREAMS_MAX)
- return -EBUSY;
-
- s = new0(DnsStream, 1);
- if (!s)
- return -ENOMEM;
-
- s->n_ref = 1;
- s->fd = -1;
- s->protocol = protocol;
-
- r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
-
- r = sd_event_add_time(
- m->event,
- &s->timeout_event_source,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0,
- on_stream_timeout, s);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
-
- LIST_PREPEND(streams, m->dns_streams, s);
- s->manager = m;
- s->fd = fd;
- m->n_dns_streams++;
-
- *ret = s;
- s = NULL;
-
- return 0;
-}
-
-int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
- assert(s);
-
- if (s->write_packet)
- return -EBUSY;
-
- s->write_packet = dns_packet_ref(p);
- s->write_size = htobe16(p->size);
- s->n_written = 0;
-
- return dns_stream_update_io(s);
-}
diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h
deleted file mode 100644
index 4cdb4f6806..0000000000
--- a/src/resolve/resolved-dns-stream.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "socket-util.h"
-
-typedef struct DnsStream DnsStream;
-
-#include "resolved-dns-packet.h"
-#include "resolved-dns-transaction.h"
-#include "resolved-manager.h"
-
-/* Streams are used by three subsystems:
- *
- * 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP
- * 2. The LLMNR logic when accepting a TCP-based lookup
- * 3. The DNS stub logic when accepting a TCP-based lookup
- */
-
-struct DnsStream {
- Manager *manager;
- int n_ref;
-
- DnsProtocol protocol;
-
- int fd;
- union sockaddr_union peer;
- socklen_t peer_salen;
- union sockaddr_union local;
- socklen_t local_salen;
- int ifindex;
- uint32_t ttl;
- bool identified;
-
- sd_event_source *io_event_source;
- sd_event_source *timeout_event_source;
-
- be16_t write_size, read_size;
- DnsPacket *write_packet, *read_packet;
- size_t n_written, n_read;
-
- int (*on_packet)(DnsStream *s);
- int (*complete)(DnsStream *s, int error);
-
- DnsTransaction *transaction; /* when used by the transaction logic */
- DnsQuery *query; /* when used by the DNS stub logic */
-
- LIST_FIELDS(DnsStream, streams);
-};
-
-int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd);
-DnsStream *dns_stream_unref(DnsStream *s);
-DnsStream *dns_stream_ref(DnsStream *s);
-
-int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
-
-static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
- assert(s);
-
- if (s->fd < 0) /* already stopped? */
- return false;
-
- return !!s->write_packet;
-}
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
deleted file mode 100644
index e76de6c06a..0000000000
--- a/src/resolve/resolved-dns-stub.c
+++ /dev/null
@@ -1,538 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "fd-util.h"
-#include "resolved-dns-stub.h"
-#include "socket-util.h"
-
-/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
- * IP and UDP header sizes */
-#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
-
-static int manager_dns_stub_udp_fd(Manager *m);
-static int manager_dns_stub_tcp_fd(Manager *m);
-
-static int dns_stub_make_reply_packet(
- uint16_t id,
- int rcode,
- DnsQuestion *q,
- DnsAnswer *answer,
- bool add_opt, /* add an OPT RR to this packet */
- bool edns0_do, /* set the EDNS0 DNSSEC OK bit */
- bool ad, /* set the DNSSEC authenticated data bit */
- DnsPacket **ret) {
-
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsResourceRecord *rr;
- unsigned c = 0;
- int r;
-
- /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
- * roundtrips aren't expensive. */
-
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- return r;
-
- /* If the client didn't do EDNS, clamp the rcode to 4 bit */
- if (!add_opt && rcode > 0xF)
- rcode = DNS_RCODE_SERVFAIL;
-
- DNS_PACKET_HEADER(p)->id = id;
- DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
- 1 /* qr */,
- 0 /* opcode */,
- 0 /* aa */,
- 0 /* tc */,
- 1 /* rd */,
- 1 /* ra */,
- ad /* ad */,
- 0 /* cd */,
- rcode));
-
- r = dns_packet_append_question(p, q);
- if (r < 0)
- return r;
- DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
-
- DNS_ANSWER_FOREACH(rr, answer) {
- r = dns_question_matches_rr(q, rr, NULL);
- if (r < 0)
- return r;
- if (r > 0)
- goto add;
-
- r = dns_question_matches_cname_or_dname(q, rr, NULL);
- if (r < 0)
- return r;
- if (r > 0)
- goto add;
-
- continue;
- add:
- r = dns_packet_append_rr(p, rr, NULL, NULL);
- if (r < 0)
- return r;
-
- c++;
- }
- DNS_PACKET_HEADER(p)->ancount = htobe16(c);
-
- if (add_opt) {
- r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
- if (r < 0)
- return r;
- }
-
- *ret = p;
- p = NULL;
-
- return 0;
-}
-
-static void dns_stub_detach_stream(DnsStream *s) {
- assert(s);
-
- s->complete = NULL;
- s->on_packet = NULL;
- s->query = NULL;
-}
-
-static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
- int r;
-
- assert(m);
- assert(p);
- assert(reply);
-
- if (s)
- r = dns_stream_write_packet(s, reply);
- else {
- int fd;
-
- /* Truncate the message to the right size */
- if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) {
- dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX);
- DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC);
- }
-
- fd = manager_dns_stub_udp_fd(m);
- if (fd < 0)
- return log_debug_errno(fd, "Failed to get reply socket: %m");
-
- /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
- * is because otherwise the kernel will choose it automatically based on the routing table and will
- * thus pick 127.0.0.1 rather than 127.0.0.53. */
-
- r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
- }
- if (r < 0)
- return log_debug_errno(r, "Failed to send reply packet: %m");
-
- return 0;
-}
-
-static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
- _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
- int r;
-
- assert(m);
- assert(p);
-
- r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply);
- if (r < 0)
- return log_debug_errno(r, "Failed to build failure packet: %m");
-
- return dns_stub_send(m, s, p, reply);
-}
-
-static void dns_stub_query_complete(DnsQuery *q) {
- int r;
-
- assert(q);
- assert(q->request_dns_packet);
-
- switch (q->state) {
-
- case DNS_TRANSACTION_SUCCESS: {
- _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
-
- r = dns_stub_make_reply_packet(
- DNS_PACKET_ID(q->request_dns_packet),
- q->answer_rcode,
- q->question_idna,
- q->answer,
- !!q->request_dns_packet->opt,
- DNS_PACKET_DO(q->request_dns_packet),
- DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
- &reply);
- if (r < 0) {
- log_debug_errno(r, "Failed to build reply packet: %m");
- break;
- }
-
- (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply);
- break;
- }
-
- case DNS_TRANSACTION_RCODE_FAILURE:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
- break;
-
- case DNS_TRANSACTION_NOT_FOUND:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
- break;
-
- case DNS_TRANSACTION_TIMEOUT:
- case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
- /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
- break;
-
- case DNS_TRANSACTION_NO_SERVERS:
- case DNS_TRANSACTION_INVALID_REPLY:
- case DNS_TRANSACTION_ERRNO:
- case DNS_TRANSACTION_ABORTED:
- case DNS_TRANSACTION_DNSSEC_FAILED:
- case DNS_TRANSACTION_NO_TRUST_ANCHOR:
- case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
- case DNS_TRANSACTION_NETWORK_DOWN:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
- break;
-
- case DNS_TRANSACTION_NULL:
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- default:
- assert_not_reached("Impossible state");
- }
-
- /* If there's a packet to write set, let's leave the stream around */
- if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) {
-
- /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
- * default completion action of the stream will drop the reference. */
-
- dns_stub_detach_stream(q->request_dns_stream);
- q->request_dns_stream = NULL;
- }
-
- dns_query_free(q);
-}
-
-static int dns_stub_stream_complete(DnsStream *s, int error) {
- assert(s);
-
- log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m");
-
- assert(s->query);
- dns_query_free(s->query);
-
- return 0;
-}
-
-static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
- DnsQuery *q = NULL;
- int r;
-
- assert(m);
- assert(p);
- assert(p->protocol == DNS_PROTOCOL_DNS);
-
- /* Takes ownership of the *s stream object */
-
- if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
- in_addr_is_localhost(p->family, &p->destination) <= 0) {
- log_error("Got packet on unexpected IP range, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
- goto fail;
- }
-
- r = dns_packet_extract(p);
- if (r < 0) {
- log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
- goto fail;
- }
-
- if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
- log_debug("Got EDNS OPT field with unsupported version number.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
- goto fail;
- }
-
- if (dns_type_is_obsolete(p->question->keys[0]->type)) {
- log_debug("Got message with obsolete key type, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
- goto fail;
- }
-
- if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
- log_debug("Got request for zone transfer, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
- goto fail;
- }
-
- if (!DNS_PACKET_RD(p)) {
- /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
- log_debug("Got request with recursion disabled, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
- goto fail;
- }
-
- if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
- log_debug("Got request with DNSSEC CD bit set, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
- goto fail;
- }
-
- r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME);
- if (r < 0) {
- log_error_errno(r, "Failed to generate query object: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
- goto fail;
- }
-
- /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
- q->clamp_ttl = true;
-
- q->request_dns_packet = dns_packet_ref(p);
- q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
- q->complete = dns_stub_query_complete;
-
- if (s) {
- s->on_packet = NULL;
- s->complete = dns_stub_stream_complete;
- s->query = q;
- }
-
- r = dns_query_go(q);
- if (r < 0) {
- log_error_errno(r, "Failed to start query: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
- goto fail;
- }
-
- log_info("Processing query...");
- return;
-
-fail:
- if (s && DNS_STREAM_QUEUED(s))
- dns_stub_detach_stream(s);
-
- dns_query_free(q);
-}
-
-static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- Manager *m = userdata;
- int r;
-
- r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
- if (r <= 0)
- return r;
-
- if (dns_packet_validate_query(p) > 0) {
- log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
-
- dns_stub_process_query(m, NULL, p);
- } else
- log_debug("Invalid DNS stub UDP packet, ignoring.");
-
- return 0;
-}
-
-static int manager_dns_stub_udp_fd(Manager *m) {
- static const int one = 1;
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(53),
- .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
- };
- _cleanup_close_ int fd = -1;
- int r;
-
- if (m->dns_stub_udp_fd >= 0)
- return m->dns_stub_udp_fd;
-
- fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (fd < 0)
- return -errno;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
-
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
-
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
-
- /* Make sure no traffic from outside the local host can leak to onto this socket */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
- return -errno;
-
- if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
- return -errno;
-
- r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
- m->dns_stub_udp_fd = fd;
- fd = -1;
-
- return m->dns_stub_udp_fd;
-}
-
-static int on_dns_stub_stream_packet(DnsStream *s) {
- assert(s);
- assert(s->read_packet);
-
- if (dns_packet_validate_query(s->read_packet) > 0) {
- log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
-
- dns_stub_process_query(s->manager, s, s->read_packet);
- } else
- log_debug("Invalid DNS stub TCP packet, ignoring.");
-
- /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
- * or that didn't happen in which case we want to free the stream */
- dns_stream_unref(s);
-
- return 0;
-}
-
-static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- DnsStream *stream;
- Manager *m = userdata;
- int cfd, r;
-
- cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
- if (cfd < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- return -errno;
- }
-
- r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd);
- if (r < 0) {
- safe_close(cfd);
- return r;
- }
-
- stream->on_packet = on_dns_stub_stream_packet;
-
- /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
- * of the stream, or by our packet callback, or when the manager is shut down. */
-
- return 0;
-}
-
-static int manager_dns_stub_tcp_fd(Manager *m) {
- static const int one = 1;
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
- .in.sin_port = htobe16(53),
- };
- _cleanup_close_ int fd = -1;
- int r;
-
- if (m->dns_stub_tcp_fd >= 0)
- return m->dns_stub_tcp_fd;
-
- fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (fd < 0)
- return -errno;
-
- if (setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof one) < 0)
- return -errno;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one) < 0)
- return -errno;
-
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof one) < 0)
- return -errno;
-
- if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof one) < 0)
- return -errno;
-
- /* Make sure no traffic from outside the local host can leak to onto this socket */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3) < 0)
- return -errno;
-
- if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
- return -errno;
-
- if (listen(fd, SOMAXCONN) < 0)
- return -errno;
-
- r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
- m->dns_stub_tcp_fd = fd;
- fd = -1;
-
- return m->dns_stub_tcp_fd;
-}
-
-int manager_dns_stub_start(Manager *m) {
- const char *t = "UDP";
- int r = 0;
-
- assert(m);
-
- if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
- r = manager_dns_stub_udp_fd(m);
-
- if (r >= 0 &&
- IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
- t = "TCP";
- r = manager_dns_stub_tcp_fd(m);
- }
-
- if (IN_SET(r, -EADDRINUSE, -EPERM)) {
- if (r == -EADDRINUSE)
- log_warning_errno(r,
- "Another process is already listening on %s socket 127.0.0.53:53.\n"
- "Turning off local DNS stub support.", t);
- else
- log_warning_errno(r,
- "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
- "Turning off local DNS stub support.", t);
- manager_dns_stub_stop(m);
- } else if (r < 0)
- return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
-
- return 0;
-}
-
-void manager_dns_stub_stop(Manager *m) {
- assert(m);
-
- m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
- m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
-
- m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
- m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
-}
diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h
deleted file mode 100644
index 12b86f6753..0000000000
--- a/src/resolve/resolved-dns-stub.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-
-/* 127.0.0.53 in native endian */
-#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
-
-void manager_dns_stub_stop(Manager *m);
-int manager_dns_stub_start(Manager *m);
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
deleted file mode 100644
index e3003411f7..0000000000
--- a/src/resolve/resolved-dns-synthesize.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "hostname-util.h"
-#include "local-addresses.h"
-#include "resolved-dns-synthesize.h"
-
-int dns_synthesize_ifindex(int ifindex) {
-
- /* When the caller asked for resolving on a specific
- * interface, we synthesize the answer for that
- * interface. However, if nothing specific was claimed and we
- * only return localhost RRs, we synthesize the answer for
- * localhost. */
-
- if (ifindex > 0)
- return ifindex;
-
- return LOOPBACK_IFINDEX;
-}
-
-int dns_synthesize_family(uint64_t flags) {
-
- /* Picks an address family depending on set flags. This is
- * purely for synthesized answers, where the family we return
- * for the reply should match what was requested in the
- * question, even though we are synthesizing the answer
- * here. */
-
- if (!(flags & SD_RESOLVED_DNS)) {
- if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
- return AF_INET;
- if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
- return AF_INET6;
- }
-
- return AF_UNSPEC;
-}
-
-DnsProtocol dns_synthesize_protocol(uint64_t flags) {
-
- /* Similar as dns_synthesize_family() but does this for the
- * protocol. If resolving via DNS was requested, we claim it
- * was DNS. Similar, if nothing specific was
- * requested. However, if only resolving via LLMNR was
- * requested we return that. */
-
- if (flags & SD_RESOLVED_DNS)
- return DNS_PROTOCOL_DNS;
- if (flags & SD_RESOLVED_LLMNR)
- return DNS_PROTOCOL_LLMNR;
- if (flags & SD_RESOLVED_MDNS)
- return DNS_PROTOCOL_MDNS;
-
- return DNS_PROTOCOL_DNS;
-}
-
-static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
- int r;
-
- assert(m);
- assert(key);
- assert(answer);
-
- r = dns_answer_reserve(answer, 2);
- if (r < 0)
- return r;
-
- if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key));
- if (!rr)
- return -ENOMEM;
-
- rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
-
- r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key));
- if (!rr)
- return -ENOMEM;
-
- rr->aaaa.in6_addr = in6addr_loopback;
-
- r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
- if (!rr)
- return -ENOMEM;
-
- rr->ptr.name = strdup(to);
- if (!rr->ptr.name)
- return -ENOMEM;
-
- return dns_answer_add(*answer, rr, ifindex, flags);
-}
-
-static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
- int r;
-
- assert(m);
- assert(key);
- assert(answer);
-
- if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_rr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- r = dns_answer_reserve(answer, n_addresses);
- if (r < 0)
- return r;
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_ptr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses,
- int af, const union in_addr_union *match) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- if (af != AF_UNSPEC) {
-
- if (addresses[j].family != af)
- continue;
-
- if (match && !in_addr_equal(af, match, &addresses[j].address))
- continue;
- }
-
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(m);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_addresses(m->rtnl, ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- if (n == 0) {
- struct local_address buffer[2];
-
- /* If we have no local addresses then use ::1
- * and 127.0.0.2 as local ones. */
-
- if (af == AF_INET || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET,
- .ifindex = dns_synthesize_ifindex(ifindex),
- .address.in.s_addr = htobe32(0x7F000002),
- };
-
- if (af == AF_INET6 || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET6,
- .ifindex = dns_synthesize_ifindex(ifindex),
- .address.in6 = in6addr_loopback,
- };
-
- return answer_add_addresses_rr(answer, dns_resource_key_name(key), buffer, n);
- }
- }
-
- return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
-}
-
-static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n, r;
-
- assert(m);
- assert(address);
- assert(answer);
-
- if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
-
- /* Always map the IPv4 address 127.0.0.2 to the local
- * hostname, in addition to "localhost": */
-
- r = dns_answer_reserve(answer, 3);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- return 0;
- }
-
- n = local_addresses(m->rtnl, ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
- if (r < 0)
- return r;
-
- return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
-}
-
-static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(m);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_gateways(m->rtnl, ifindex, af, &addresses);
- if (n < 0)
- return n;
- }
-
- return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
-}
-
-static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n;
-
- assert(m);
- assert(address);
- assert(answer);
-
- n = local_gateways(m->rtnl, ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
-}
-
-int dns_synthesize_answer(
- Manager *m,
- DnsQuestion *q,
- int ifindex,
- DnsAnswer **ret) {
-
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnsResourceKey *key;
- bool found = false;
- int r;
-
- assert(m);
- assert(q);
-
- DNS_QUESTION_FOREACH(key, q) {
- union in_addr_union address;
- const char *name;
- int af;
-
- if (key->class != DNS_CLASS_IN &&
- key->class != DNS_CLASS_ANY)
- continue;
-
- name = dns_resource_key_name(key);
-
- if (is_localhost(name)) {
-
- r = synthesize_localhost_rr(m, key, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
-
- } else if (manager_is_own_hostname(m, name)) {
-
- r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
-
- } else if (is_gateway_hostname(name)) {
-
- r = synthesize_gateway_rr(m, key, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
-
- } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
- dns_name_equal(name, "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) {
-
- r = synthesize_localhost_ptr(m, key, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
-
- } else if (dns_name_address(name, &af, &address) > 0) {
-
- r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
-
- r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
- } else
- continue;
-
- found = true;
- }
-
- r = found;
-
- if (ret) {
- *ret = answer;
- answer = NULL;
- }
-
- return r;
-}
diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h
deleted file mode 100644
index 5d829bb2e7..0000000000
--- a/src/resolve/resolved-dns-synthesize.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-dns-answer.h"
-#include "resolved-dns-question.h"
-#include "resolved-manager.h"
-
-int dns_synthesize_ifindex(int ifindex);
-int dns_synthesize_family(uint64_t flags);
-DnsProtocol dns_synthesize_protocol(uint64_t flags);
-
-int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
deleted file mode 100644
index 2fce44ec8b..0000000000
--- a/src/resolve/resolved-dns-transaction.c
+++ /dev/null
@@ -1,3106 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sd-messages.h>
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "errno-list.h"
-#include "fd-util.h"
-#include "random-util.h"
-#include "resolved-dns-cache.h"
-#include "resolved-dns-transaction.h"
-#include "resolved-llmnr.h"
-#include "string-table.h"
-
-#define TRANSACTIONS_MAX 4096
-
-static void dns_transaction_reset_answer(DnsTransaction *t) {
- assert(t);
-
- t->received = dns_packet_unref(t->received);
- t->answer = dns_answer_unref(t->answer);
- t->answer_rcode = 0;
- t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
- t->answer_authenticated = false;
- t->answer_nsec_ttl = (uint32_t) -1;
- t->answer_errno = 0;
-}
-
-static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
- DnsTransaction *z;
-
- assert(t);
-
- while ((z = set_steal_first(t->dnssec_transactions))) {
- set_remove(z->notify_transactions, t);
- set_remove(z->notify_transactions_done, t);
- dns_transaction_gc(z);
- }
-}
-
-static void dns_transaction_close_connection(DnsTransaction *t) {
- assert(t);
-
- if (t->stream) {
- /* Let's detach the stream from our transaction, in case something else keeps a reference to it. */
- t->stream->complete = NULL;
- t->stream->on_packet = NULL;
- t->stream->transaction = NULL;
- t->stream = dns_stream_unref(t->stream);
- }
-
- t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
- t->dns_udp_fd = safe_close(t->dns_udp_fd);
-}
-
-static void dns_transaction_stop_timeout(DnsTransaction *t) {
- assert(t);
-
- t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
-}
-
-DnsTransaction* dns_transaction_free(DnsTransaction *t) {
- DnsQueryCandidate *c;
- DnsZoneItem *i;
- DnsTransaction *z;
-
- if (!t)
- return NULL;
-
- log_debug("Freeing transaction %" PRIu16 ".", t->id);
-
- dns_transaction_close_connection(t);
- dns_transaction_stop_timeout(t);
-
- dns_packet_unref(t->sent);
- dns_transaction_reset_answer(t);
-
- dns_server_unref(t->server);
-
- if (t->scope) {
- hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
- LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
-
- if (t->id != 0)
- hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
- }
-
- while ((c = set_steal_first(t->notify_query_candidates)))
- set_remove(c->transactions, t);
- set_free(t->notify_query_candidates);
-
- while ((c = set_steal_first(t->notify_query_candidates_done)))
- set_remove(c->transactions, t);
- set_free(t->notify_query_candidates_done);
-
- while ((i = set_steal_first(t->notify_zone_items)))
- i->probe_transaction = NULL;
- set_free(t->notify_zone_items);
-
- while ((i = set_steal_first(t->notify_zone_items_done)))
- i->probe_transaction = NULL;
- set_free(t->notify_zone_items_done);
-
- while ((z = set_steal_first(t->notify_transactions)))
- set_remove(z->dnssec_transactions, t);
- set_free(t->notify_transactions);
-
- while ((z = set_steal_first(t->notify_transactions_done)))
- set_remove(z->dnssec_transactions, t);
- set_free(t->notify_transactions_done);
-
- dns_transaction_flush_dnssec_transactions(t);
- set_free(t->dnssec_transactions);
-
- dns_answer_unref(t->validated_keys);
- dns_resource_key_unref(t->key);
-
- return mfree(t);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
-
-bool dns_transaction_gc(DnsTransaction *t) {
- assert(t);
-
- if (t->block_gc > 0)
- return true;
-
- if (set_isempty(t->notify_query_candidates) &&
- set_isempty(t->notify_query_candidates_done) &&
- set_isempty(t->notify_zone_items) &&
- set_isempty(t->notify_zone_items_done) &&
- set_isempty(t->notify_transactions) &&
- set_isempty(t->notify_transactions_done)) {
- dns_transaction_free(t);
- return false;
- }
-
- return true;
-}
-
-static uint16_t pick_new_id(Manager *m) {
- uint16_t new_id;
-
- /* Find a fresh, unused transaction id. Note that this loop is bounded because there's a limit on the number of
- * transactions, and it's much lower than the space of IDs. */
-
- assert_cc(TRANSACTIONS_MAX < 0xFFFF);
-
- do
- random_bytes(&new_id, sizeof(new_id));
- while (new_id == 0 ||
- hashmap_get(m->dns_transactions, UINT_TO_PTR(new_id)));
-
- return new_id;
-}
-
-int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
- _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
- int r;
-
- assert(ret);
- assert(s);
- assert(key);
-
- /* Don't allow looking up invalid or pseudo RRs */
- if (!dns_type_is_valid_query(key->type))
- return -EINVAL;
- if (dns_type_is_obsolete(key->type))
- return -EOPNOTSUPP;
-
- /* We only support the IN class */
- if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY)
- return -EOPNOTSUPP;
-
- if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX)
- return -EBUSY;
-
- r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&s->transactions_by_key, &dns_resource_key_hash_ops);
- if (r < 0)
- return r;
-
- t = new0(DnsTransaction, 1);
- if (!t)
- return -ENOMEM;
-
- t->dns_udp_fd = -1;
- t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
- t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- t->answer_nsec_ttl = (uint32_t) -1;
- t->key = dns_resource_key_ref(key);
- t->current_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
- t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
-
- t->id = pick_new_id(s->manager);
-
- r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
- if (r < 0) {
- t->id = 0;
- return r;
- }
-
- r = hashmap_replace(s->transactions_by_key, t->key, t);
- if (r < 0) {
- hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
- return r;
- }
-
- LIST_PREPEND(transactions_by_scope, s->transactions, t);
- t->scope = s;
-
- s->manager->n_transactions_total++;
-
- if (ret)
- *ret = t;
-
- t = NULL;
-
- return 0;
-}
-
-static void dns_transaction_shuffle_id(DnsTransaction *t) {
- uint16_t new_id;
- assert(t);
-
- /* Pick a new ID for this transaction. */
-
- new_id = pick_new_id(t->scope->manager);
- assert_se(hashmap_remove_and_put(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id), UINT_TO_PTR(new_id), t) >= 0);
-
- log_debug("Transaction %" PRIu16 " is now %" PRIu16 ".", t->id, new_id);
- t->id = new_id;
-
- /* Make sure we generate a new packet with the new ID */
- t->sent = dns_packet_unref(t->sent);
-}
-
-static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
- _cleanup_free_ char *pretty = NULL;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
- DnsZoneItem *z;
-
- assert(t);
- assert(p);
-
- if (manager_our_packet(t->scope->manager, p) != 0)
- return;
-
- (void) in_addr_to_string(p->family, &p->sender, &pretty);
-
- log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
- t->id,
- dns_resource_key_to_string(t->key, key_str, sizeof key_str),
- dns_protocol_to_string(t->scope->protocol),
- t->scope->link ? t->scope->link->name : "*",
- af_to_name_short(t->scope->family),
- strnull(pretty));
-
- /* RFC 4795, Section 4.1 says that the peer with the
- * lexicographically smaller IP address loses */
- if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
- log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
- return;
- }
-
- log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
-
- t->block_gc++;
-
- while ((z = set_first(t->notify_zone_items))) {
- /* First, make sure the zone item drops the reference
- * to us */
- dns_zone_item_probe_stop(z);
-
- /* Secondly, report this as conflict, so that we might
- * look for a different hostname */
- dns_zone_item_conflict(z);
- }
- t->block_gc--;
-
- dns_transaction_gc(t);
-}
-
-void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
- DnsQueryCandidate *c;
- DnsZoneItem *z;
- DnsTransaction *d;
- const char *st;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- assert(t);
- assert(!DNS_TRANSACTION_IS_LIVE(state));
-
- if (state == DNS_TRANSACTION_DNSSEC_FAILED) {
- dns_resource_key_to_string(t->key, key_str, sizeof key_str);
-
- log_struct(LOG_NOTICE,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE),
- LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)),
- "DNS_TRANSACTION=%" PRIu16, t->id,
- "DNS_QUESTION=%s", key_str,
- "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
- "DNS_SERVER=%s", dns_server_string(t->server),
- "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level),
- NULL);
- }
-
- /* Note that this call might invalidate the query. Callers
- * should hence not attempt to access the query or transaction
- * after calling this function. */
-
- if (state == DNS_TRANSACTION_ERRNO)
- st = errno_to_name(t->answer_errno);
- else
- st = dns_transaction_state_to_string(state);
-
- log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
- t->id,
- dns_resource_key_to_string(t->key, key_str, sizeof key_str),
- dns_protocol_to_string(t->scope->protocol),
- t->scope->link ? t->scope->link->name : "*",
- af_to_name_short(t->scope->family),
- st,
- t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
- t->answer_authenticated ? "authenticated" : "unsigned");
-
- t->state = state;
-
- dns_transaction_close_connection(t);
- dns_transaction_stop_timeout(t);
-
- /* Notify all queries that are interested, but make sure the
- * transaction isn't freed while we are still looking at it */
- t->block_gc++;
-
- SET_FOREACH_MOVE(c, t->notify_query_candidates_done, t->notify_query_candidates)
- dns_query_candidate_notify(c);
- SWAP_TWO(t->notify_query_candidates, t->notify_query_candidates_done);
-
- SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items)
- dns_zone_item_notify(z);
- SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done);
-
- SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions)
- dns_transaction_notify(d, t);
- SWAP_TWO(t->notify_transactions, t->notify_transactions_done);
-
- t->block_gc--;
- dns_transaction_gc(t);
-}
-
-static int dns_transaction_pick_server(DnsTransaction *t) {
- DnsServer *server;
-
- assert(t);
- assert(t->scope->protocol == DNS_PROTOCOL_DNS);
-
- /* Pick a DNS server and a feature level for it. */
-
- server = dns_scope_get_dns_server(t->scope);
- if (!server)
- return -ESRCH;
-
- /* If we changed the server invalidate the feature level clamping, as the new server might have completely
- * different properties. */
- if (server != t->server)
- t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
-
- t->current_feature_level = dns_server_possible_feature_level(server);
-
- /* Clamp the feature level if that is requested. */
- if (t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID &&
- t->current_feature_level > t->clamp_feature_level)
- t->current_feature_level = t->clamp_feature_level;
-
- log_debug("Using feature level %s for transaction %u.", dns_server_feature_level_to_string(t->current_feature_level), t->id);
-
- if (server == t->server)
- return 0;
-
- dns_server_unref(t->server);
- t->server = dns_server_ref(server);
-
- log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id);
-
- return 1;
-}
-
-static void dns_transaction_retry(DnsTransaction *t, bool next_server) {
- int r;
-
- assert(t);
-
- log_debug("Retrying transaction %" PRIu16 ".", t->id);
-
- /* Before we try again, switch to a new server. */
- if (next_server)
- dns_scope_next_dns_server(t->scope);
-
- r = dns_transaction_go(t);
- if (r < 0) {
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- }
-}
-
-static int dns_transaction_maybe_restart(DnsTransaction *t) {
- int r;
-
- assert(t);
-
- /* Returns > 0 if the transaction was restarted, 0 if not */
-
- if (!t->server)
- return 0;
-
- if (t->current_feature_level <= dns_server_possible_feature_level(t->server))
- return 0;
-
- /* The server's current feature level is lower than when we sent the original query. We learnt something from
- the response or possibly an auxiliary DNSSEC response that we didn't know before. We take that as reason to
- restart the whole transaction. This is a good idea to deal with servers that respond rubbish if we include
- OPT RR or DO bit. One of these cases is documented here, for example:
- https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
-
- log_debug("Server feature level is now lower than when we began our transaction. Restarting with new ID.");
- dns_transaction_shuffle_id(t);
-
- r = dns_transaction_go(t);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int on_stream_complete(DnsStream *s, int error) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsTransaction *t;
-
- assert(s);
- assert(s->transaction);
-
- /* Copy the data we care about out of the stream before we
- * destroy it. */
- t = s->transaction;
- p = dns_packet_ref(s->read_packet);
-
- dns_transaction_close_connection(t);
-
- if (ERRNO_IS_DISCONNECT(error)) {
- usec_t usec;
-
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
- /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the
- * question on this scope. */
- dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
- return 0;
- }
-
- log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
- dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level, usec - t->start_usec);
-
- dns_transaction_retry(t, true);
- return 0;
- }
- if (error != 0) {
- t->answer_errno = error;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- return 0;
- }
-
- if (dns_packet_validate_reply(p) <= 0) {
- log_debug("Invalid TCP reply packet.");
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return 0;
- }
-
- dns_scope_check_conflicts(t->scope, p);
-
- t->block_gc++;
- dns_transaction_process_reply(t, p);
- t->block_gc--;
-
- /* If the response wasn't useful, then complete the transition
- * now. After all, we are the worst feature set now with TCP
- * sockets, and there's really no point in retrying. */
- if (t->state == DNS_TRANSACTION_PENDING)
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- else
- dns_transaction_gc(t);
-
- return 0;
-}
-
-static int dns_transaction_open_tcp(DnsTransaction *t) {
- _cleanup_close_ int fd = -1;
- int r;
-
- assert(t);
-
- dns_transaction_close_connection(t);
-
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_DNS:
- r = dns_transaction_pick_server(t);
- if (r < 0)
- return r;
-
- if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
- return -EOPNOTSUPP;
-
- r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
- if (r < 0)
- return r;
-
- fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
- break;
-
- case DNS_PROTOCOL_LLMNR:
- /* When we already received a reply to this (but it was truncated), send to its sender address */
- if (t->received)
- fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
- else {
- union in_addr_union address;
- int family = AF_UNSPEC;
-
- /* Otherwise, try to talk to the owner of a
- * the IP address, in case this is a reverse
- * PTR lookup */
-
- r = dns_name_address(dns_resource_key_name(t->key), &family, &address);
- if (r < 0)
- return r;
- if (r == 0)
- return -EINVAL;
- if (family != t->scope->family)
- return -ESRCH;
-
- fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
- }
-
- break;
-
- default:
- return -EAFNOSUPPORT;
- }
-
- if (fd < 0)
- return fd;
-
- r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
- if (r < 0)
- return r;
- fd = -1;
-
- r = dns_stream_write_packet(t->stream, t->sent);
- if (r < 0) {
- t->stream = dns_stream_unref(t->stream);
- return r;
- }
-
- t->stream->complete = on_stream_complete;
- t->stream->transaction = t;
-
- /* The interface index is difficult to determine if we are
- * connecting to the local host, hence fill this in right away
- * instead of determining it from the socket */
- t->stream->ifindex = dns_scope_ifindex(t->scope);
-
- dns_transaction_reset_answer(t);
-
- t->tried_stream = true;
-
- return 0;
-}
-
-static void dns_transaction_cache_answer(DnsTransaction *t) {
- assert(t);
-
- /* For mDNS we cache whenever we get the packet, rather than
- * in each transaction. */
- if (!IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR))
- return;
-
- /* Caching disabled? */
- if (!t->scope->manager->enable_cache)
- return;
-
- /* We never cache if this packet is from the local host, under
- * the assumption that a locally running DNS server would
- * cache this anyway, and probably knows better when to flush
- * the cache then we could. */
- if (!DNS_PACKET_SHALL_CACHE(t->received))
- return;
-
- dns_cache_put(&t->scope->cache,
- t->key,
- t->answer_rcode,
- t->answer,
- t->answer_authenticated,
- t->answer_nsec_ttl,
- 0,
- t->received->family,
- &t->received->sender);
-}
-
-static bool dns_transaction_dnssec_is_live(DnsTransaction *t) {
- DnsTransaction *dt;
- Iterator i;
-
- assert(t);
-
- SET_FOREACH(dt, t->dnssec_transactions, i)
- if (DNS_TRANSACTION_IS_LIVE(dt->state))
- return true;
-
- return false;
-}
-
-static int dns_transaction_dnssec_ready(DnsTransaction *t) {
- DnsTransaction *dt;
- Iterator i;
-
- assert(t);
-
- /* Checks whether the auxiliary DNSSEC transactions of our transaction have completed, or are still
- * ongoing. Returns 0, if we aren't ready for the DNSSEC validation, positive if we are. */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- switch (dt->state) {
-
- case DNS_TRANSACTION_NULL:
- case DNS_TRANSACTION_PENDING:
- case DNS_TRANSACTION_VALIDATING:
- /* Still ongoing */
- return 0;
-
- case DNS_TRANSACTION_RCODE_FAILURE:
- if (!IN_SET(dt->answer_rcode, DNS_RCODE_NXDOMAIN, DNS_RCODE_SERVFAIL)) {
- log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode));
- goto fail;
- }
-
- /* Fall-through: NXDOMAIN/SERVFAIL is good enough for us. This is because some DNS servers
- * erronously return NXDOMAIN/SERVFAIL for empty non-terminals (Akamai...) or missing DS
- * records (Facebook), and we need to handle that nicely, when asking for parent SOA or similar
- * RRs to make unsigned proofs. */
-
- case DNS_TRANSACTION_SUCCESS:
- /* All good. */
- break;
-
- case DNS_TRANSACTION_DNSSEC_FAILED:
- /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
- * validationr result */
-
- log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result));
- t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
- return 0;
-
-
- default:
- log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
- goto fail;
- }
- }
-
- /* All is ready, we can go and validate */
- return 1;
-
-fail:
- t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY;
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
- return 0;
-}
-
-static void dns_transaction_process_dnssec(DnsTransaction *t) {
- int r;
-
- assert(t);
-
- /* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
- r = dns_transaction_dnssec_ready(t);
- if (r < 0)
- goto fail;
- if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */
- return;
-
- /* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better
- * restart the lookup immediately. */
- r = dns_transaction_maybe_restart(t);
- if (r < 0)
- goto fail;
- if (r > 0) /* Transaction got restarted... */
- return;
-
- /* All our auxiliary DNSSEC transactions are complete now. Try
- * to validate our RRset now. */
- r = dns_transaction_validate_dnssec(t);
- if (r == -EBADMSG) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
- if (r < 0)
- goto fail;
-
- if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
- t->scope->dnssec_mode == DNSSEC_YES) {
- /* We are not in automatic downgrade mode, and the
- * server is bad, refuse operation. */
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
- return;
- }
-
- if (!IN_SET(t->answer_dnssec_result,
- _DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */
- DNSSEC_VALIDATED, /* Answer is signed and validated successfully */
- DNSSEC_UNSIGNED, /* Answer is right-fully unsigned */
- DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
- return;
- }
-
- if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER)
- dns_server_warn_downgrade(t->server);
-
- dns_transaction_cache_answer(t);
-
- if (t->answer_rcode == DNS_RCODE_SUCCESS)
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- else
- dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
-
- return;
-
-fail:
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
-}
-
-static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
- int r;
-
- assert(t);
-
- /* Checks whether the answer is positive, i.e. either a direct
- * answer to the question, or a CNAME/DNAME for it */
-
- r = dns_answer_match_key(t->answer, t->key, flags);
- if (r != 0)
- return r;
-
- r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
- if (r != 0)
- return r;
-
- return false;
-}
-
-static int dns_transaction_fix_rcode(DnsTransaction *t) {
- int r;
-
- assert(t);
-
- /* Fix up the RCODE to SUCCESS if we get at least one matching RR in a response. Note that this contradicts the
- * DNS RFCs a bit. Specifically, RFC 6604 Section 3 clarifies that the RCODE shall say something about a
- * CNAME/DNAME chain element coming after the last chain element contained in the message, and not the first
- * one included. However, it also indicates that not all DNS servers implement this correctly. Moreover, when
- * using DNSSEC we usually only can prove the first element of a CNAME/DNAME chain anyway, hence let's settle
- * on always processing the RCODE as referring to the immediate look-up we do, i.e. the first element of a
- * CNAME/DNAME chain. This way, we uniformly handle CNAME/DNAME chains, regardless if the DNS server
- * incorrectly implements RCODE, whether DNSSEC is in use, or whether the DNS server only supplied us with an
- * incomplete CNAME/DNAME chain.
- *
- * Or in other words: if we get at least one positive reply in a message we patch NXDOMAIN to become SUCCESS,
- * and then rely on the CNAME chasing logic to figure out that there's actually a CNAME error with a new
- * lookup. */
-
- if (t->answer_rcode != DNS_RCODE_NXDOMAIN)
- return 0;
-
- r = dns_transaction_has_positive_answer(t, NULL);
- if (r <= 0)
- return r;
-
- t->answer_rcode = DNS_RCODE_SUCCESS;
- return 0;
-}
-
-void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
- usec_t ts;
- int r;
-
- assert(t);
- assert(p);
- assert(t->scope);
- assert(t->scope->manager);
-
- if (t->state != DNS_TRANSACTION_PENDING)
- return;
-
- /* Note that this call might invalidate the query. Callers
- * should hence not attempt to access the query or transaction
- * after calling this function. */
-
- log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
-
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_LLMNR:
- /* For LLMNR we will not accept any packets from other interfaces */
-
- if (p->ifindex != dns_scope_ifindex(t->scope))
- return;
-
- if (p->family != t->scope->family)
- return;
-
- /* Tentative packets are not full responses but still
- * useful for identifying uniqueness conflicts during
- * probing. */
- if (DNS_PACKET_LLMNR_T(p)) {
- dns_transaction_tentative(t, p);
- return;
- }
-
- break;
-
- case DNS_PROTOCOL_MDNS:
- /* For mDNS we will not accept any packets from other interfaces */
-
- if (p->ifindex != dns_scope_ifindex(t->scope))
- return;
-
- if (p->family != t->scope->family)
- return;
-
- break;
-
- case DNS_PROTOCOL_DNS:
- /* Note that we do not need to verify the
- * addresses/port numbers of incoming traffic, as we
- * invoked connect() on our UDP socket in which case
- * the kernel already does the needed verification for
- * us. */
- break;
-
- default:
- assert_not_reached("Invalid DNS protocol.");
- }
-
- if (t->received != p) {
- dns_packet_unref(t->received);
- t->received = dns_packet_ref(p);
- }
-
- t->answer_source = DNS_TRANSACTION_NETWORK;
-
- if (p->ipproto == IPPROTO_TCP) {
- if (DNS_PACKET_TC(p)) {
- /* Truncated via TCP? Somebody must be fucking with us */
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
-
- if (DNS_PACKET_ID(p) != t->id) {
- /* Not the reply to our query? Somebody must be fucking with us */
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
- }
-
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_DNS:
- assert(t->server);
-
- if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) {
-
- /* Request failed, immediately try again with reduced features */
-
- if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) {
- /* This was already at the lowest possible feature level? If so, we can't downgrade
- * this transaction anymore, hence let's process the response, and accept the rcode. */
- log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
- break;
- }
-
- /* Reduce this feature level by one and try again. */
- t->clamp_feature_level = t->current_feature_level - 1;
-
- log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
- dns_rcode_to_string(DNS_PACKET_RCODE(p)),
- dns_server_feature_level_to_string(t->clamp_feature_level));
-
- dns_transaction_retry(t, false /* use the same server */);
- return;
- } else if (DNS_PACKET_TC(p))
- dns_server_packet_truncated(t->server, t->current_feature_level);
-
- break;
-
- case DNS_PROTOCOL_LLMNR:
- case DNS_PROTOCOL_MDNS:
- dns_scope_packet_received(t->scope, ts - t->start_usec);
- break;
-
- default:
- assert_not_reached("Invalid DNS protocol.");
- }
-
- if (DNS_PACKET_TC(p)) {
-
- /* Truncated packets for mDNS are not allowed. Give up immediately. */
- if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
-
- log_debug("Reply truncated, retrying via TCP.");
-
- /* Response was truncated, let's try again with good old TCP */
- r = dns_transaction_open_tcp(t);
- if (r == -ESRCH) {
- /* No servers found? Damn! */
- dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
- return;
- }
- if (r == -EOPNOTSUPP) {
- /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC */
- dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
- return;
- }
- if (r < 0) {
- /* On LLMNR, if we cannot connect to the host,
- * we immediately give up */
- if (t->scope->protocol != DNS_PROTOCOL_DNS)
- goto fail;
-
- /* On DNS, couldn't send? Try immediately again, with a new server */
- dns_transaction_retry(t, true);
- }
-
- return;
- }
-
- /* After the superficial checks, actually parse the message. */
- r = dns_packet_extract(p);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
-
- if (t->server) {
- /* Report that we successfully received a valid packet with a good rcode after we initially got a bad
- * rcode and subsequently downgraded the protocol */
-
- if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN) &&
- t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID)
- dns_server_packet_rcode_downgrade(t->server, t->clamp_feature_level);
-
- /* Report that the OPT RR was missing */
- if (!p->opt)
- dns_server_packet_bad_opt(t->server, t->current_feature_level);
-
- /* Report that we successfully received a packet */
- dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size);
- }
-
- /* See if we know things we didn't know before that indicate we better restart the lookup immediately. */
- r = dns_transaction_maybe_restart(t);
- if (r < 0)
- goto fail;
- if (r > 0) /* Transaction got restarted... */
- return;
-
- if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
-
- /* Only consider responses with equivalent query section to the request */
- r = dns_packet_is_reply_for(p, t->key);
- if (r < 0)
- goto fail;
- if (r == 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
- return;
- }
-
- /* Install the answer as answer to the transaction */
- dns_answer_unref(t->answer);
- t->answer = dns_answer_ref(p->answer);
- t->answer_rcode = DNS_PACKET_RCODE(p);
- t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
- t->answer_authenticated = false;
-
- r = dns_transaction_fix_rcode(t);
- if (r < 0)
- goto fail;
-
- /* Block GC while starting requests for additional DNSSEC RRs */
- t->block_gc++;
- r = dns_transaction_request_dnssec_keys(t);
- t->block_gc--;
-
- /* Maybe the transaction is ready for GC'ing now? If so, free it and return. */
- if (!dns_transaction_gc(t))
- return;
-
- /* Requesting additional keys might have resulted in
- * this transaction to fail, since the auxiliary
- * request failed for some reason. If so, we are not
- * in pending state anymore, and we should exit
- * quickly. */
- if (t->state != DNS_TRANSACTION_PENDING)
- return;
- if (r < 0)
- goto fail;
- if (r > 0) {
- /* There are DNSSEC transactions pending now. Update the state accordingly. */
- t->state = DNS_TRANSACTION_VALIDATING;
- dns_transaction_close_connection(t);
- dns_transaction_stop_timeout(t);
- return;
- }
- }
-
- dns_transaction_process_dnssec(t);
- return;
-
-fail:
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
-}
-
-static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsTransaction *t = userdata;
- int r;
-
- assert(t);
- assert(t->scope);
-
- r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
- if (ERRNO_IS_DISCONNECT(-r)) {
- usec_t usec;
-
- /* UDP connection failure get reported via ICMP and then are possible delivered to us on the next
- * recvmsg(). Treat this like a lost packet. */
-
- log_debug_errno(r, "Connection failure for DNS UDP packet: %m");
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
- dns_server_packet_lost(t->server, IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
-
- dns_transaction_retry(t, true);
- return 0;
- }
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- t->answer_errno = -r;
- return 0;
- }
-
- r = dns_packet_validate_reply(p);
- if (r < 0) {
- log_debug_errno(r, "Received invalid DNS packet as response, ignoring: %m");
- return 0;
- }
- if (r == 0) {
- log_debug("Received inappropriate DNS packet as response, ignoring.");
- return 0;
- }
-
- if (DNS_PACKET_ID(p) != t->id) {
- log_debug("Received packet with incorrect transaction ID, ignoring.");
- return 0;
- }
-
- dns_transaction_process_reply(t, p);
- return 0;
-}
-
-static int dns_transaction_emit_udp(DnsTransaction *t) {
- int r;
-
- assert(t);
-
- if (t->scope->protocol == DNS_PROTOCOL_DNS) {
-
- r = dns_transaction_pick_server(t);
- if (r < 0)
- return r;
-
- if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
- return -EAGAIN;
-
- if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
- return -EOPNOTSUPP;
-
- if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */
- int fd;
-
- dns_transaction_close_connection(t);
-
- fd = dns_scope_socket_udp(t->scope, t->server, 53);
- if (fd < 0)
- return fd;
-
- r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
- if (r < 0) {
- safe_close(fd);
- return r;
- }
-
- (void) sd_event_source_set_description(t->dns_udp_event_source, "dns-transaction-udp");
- t->dns_udp_fd = fd;
- }
-
- r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
- if (r < 0)
- return r;
- } else
- dns_transaction_close_connection(t);
-
- r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->sent);
- if (r < 0)
- return r;
-
- dns_transaction_reset_answer(t);
-
- return 0;
-}
-
-static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
- DnsTransaction *t = userdata;
-
- assert(s);
- assert(t);
-
- if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) {
- /* Timeout reached? Increase the timeout for the server used */
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_DNS:
- assert(t->server);
- dns_server_packet_lost(t->server, t->stream ? IPPROTO_TCP : IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
- break;
-
- case DNS_PROTOCOL_LLMNR:
- case DNS_PROTOCOL_MDNS:
- dns_scope_packet_lost(t->scope, usec - t->start_usec);
- break;
-
- default:
- assert_not_reached("Invalid DNS protocol.");
- }
-
- if (t->initial_jitter_scheduled)
- t->initial_jitter_elapsed = true;
- }
-
- log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
-
- dns_transaction_retry(t, true);
- return 0;
-}
-
-static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
- assert(t);
- assert(t->scope);
-
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_DNS:
- assert(t->server);
- return t->server->resend_timeout;
-
- case DNS_PROTOCOL_MDNS:
- assert(t->n_attempts > 0);
- return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
-
- case DNS_PROTOCOL_LLMNR:
- return t->scope->resend_timeout;
-
- default:
- assert_not_reached("Invalid DNS protocol.");
- }
-}
-
-static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
- int r;
-
- assert(t);
-
- dns_transaction_stop_timeout(t);
-
- r = dns_scope_network_good(t->scope);
- if (r < 0)
- return r;
- if (r == 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN);
- return 0;
- }
-
- if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
- dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
- return 0;
- }
-
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR && t->tried_stream) {
- /* If we already tried via a stream, then we don't
- * retry on LLMNR. See RFC 4795, Section 2.7. */
- dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
- return 0;
- }
-
- t->n_attempts++;
- t->start_usec = ts;
-
- dns_transaction_reset_answer(t);
- dns_transaction_flush_dnssec_transactions(t);
-
- /* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */
- if (t->scope->protocol == DNS_PROTOCOL_DNS) {
- r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, t->key, &t->answer);
- if (r < 0)
- return r;
- if (r > 0) {
- t->answer_rcode = DNS_RCODE_SUCCESS;
- t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
- t->answer_authenticated = true;
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- return 0;
- }
-
- if (dns_name_is_root(dns_resource_key_name(t->key)) &&
- t->key->type == DNS_TYPE_DS) {
-
- /* Hmm, this is a request for the root DS? A
- * DS RR doesn't exist in the root zone, and
- * if our trust anchor didn't know it either,
- * this means we cannot do any DNSSEC logic
- * anymore. */
-
- if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
- /* We are in downgrade mode. In this
- * case, synthesize an unsigned empty
- * response, so that the any lookup
- * depending on this one can continue
- * assuming there was no DS, and hence
- * the root zone was unsigned. */
-
- t->answer_rcode = DNS_RCODE_SUCCESS;
- t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
- t->answer_authenticated = false;
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- } else
- /* If we are not in downgrade mode,
- * then fail the lookup, because we
- * cannot reasonably answer it. There
- * might be DS RRs, but we don't know
- * them, and the DNS server won't tell
- * them to us (and even if it would,
- * we couldn't validate and trust them. */
- dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR);
-
- return 0;
- }
- }
-
- /* Check the zone, but only if this transaction is not used
- * for probing or verifying a zone item. */
- if (set_isempty(t->notify_zone_items)) {
-
- r = dns_zone_lookup(&t->scope->zone, t->key, dns_scope_ifindex(t->scope), &t->answer, NULL, NULL);
- if (r < 0)
- return r;
- if (r > 0) {
- t->answer_rcode = DNS_RCODE_SUCCESS;
- t->answer_source = DNS_TRANSACTION_ZONE;
- t->answer_authenticated = true;
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- return 0;
- }
- }
-
- /* Check the cache, but only if this transaction is not used
- * for probing or verifying a zone item. */
- if (set_isempty(t->notify_zone_items)) {
-
- /* Before trying the cache, let's make sure we figured out a
- * server to use. Should this cause a change of server this
- * might flush the cache. */
- dns_scope_get_dns_server(t->scope);
-
- /* Let's then prune all outdated entries */
- dns_cache_prune(&t->scope->cache);
-
- r = dns_cache_lookup(&t->scope->cache, t->key, t->clamp_ttl, &t->answer_rcode, &t->answer, &t->answer_authenticated);
- if (r < 0)
- return r;
- if (r > 0) {
- t->answer_source = DNS_TRANSACTION_CACHE;
- if (t->answer_rcode == DNS_RCODE_SUCCESS)
- dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
- else
- dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
- return 0;
- }
- }
-
- return 1;
-}
-
-static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
-
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- bool add_known_answers = false;
- DnsTransaction *other;
- unsigned qdcount;
- usec_t ts;
- int r;
-
- assert(t);
- assert(t->scope->protocol == DNS_PROTOCOL_MDNS);
-
- /* Discard any previously prepared packet, so we can start over and coalesce again */
- t->sent = dns_packet_unref(t->sent);
-
- r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
- if (r < 0)
- return r;
-
- r = dns_packet_append_key(p, t->key, NULL);
- if (r < 0)
- return r;
-
- qdcount = 1;
-
- if (dns_key_is_shared(t->key))
- add_known_answers = true;
-
- /*
- * For mDNS, we want to coalesce as many open queries in pending transactions into one single
- * query packet on the wire as possible. To achieve that, we iterate through all pending transactions
- * in our current scope, and see whether their timing contraints allow them to be sent.
- */
-
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
- LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
-
- /* Skip ourselves */
- if (other == t)
- continue;
-
- if (other->state != DNS_TRANSACTION_PENDING)
- continue;
-
- if (other->next_attempt_after > ts)
- continue;
-
- if (qdcount >= UINT16_MAX)
- break;
-
- r = dns_packet_append_key(p, other->key, NULL);
-
- /*
- * If we can't stuff more questions into the packet, just give up.
- * One of the 'other' transactions will fire later and take care of the rest.
- */
- if (r == -EMSGSIZE)
- break;
-
- if (r < 0)
- return r;
-
- r = dns_transaction_prepare(other, ts);
- if (r <= 0)
- continue;
-
- ts += transaction_get_resend_timeout(other);
-
- r = sd_event_add_time(
- other->scope->manager->event,
- &other->timeout_event_source,
- clock_boottime_or_monotonic(),
- ts, 0,
- on_transaction_timeout, other);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
- other->state = DNS_TRANSACTION_PENDING;
- other->next_attempt_after = ts;
-
- qdcount++;
-
- if (dns_key_is_shared(other->key))
- add_known_answers = true;
- }
-
- DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
-
- /* Append known answer section if we're asking for any shared record */
- if (add_known_answers) {
- r = dns_cache_export_shared_to_packet(&t->scope->cache, p);
- if (r < 0)
- return r;
- }
-
- t->sent = p;
- p = NULL;
-
- return 0;
-}
-
-static int dns_transaction_make_packet(DnsTransaction *t) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- int r;
-
- assert(t);
-
- if (t->scope->protocol == DNS_PROTOCOL_MDNS)
- return dns_transaction_make_packet_mdns(t);
-
- if (t->sent)
- return 0;
-
- r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode != DNSSEC_NO);
- if (r < 0)
- return r;
-
- r = dns_packet_append_key(p, t->key, NULL);
- if (r < 0)
- return r;
-
- DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
- DNS_PACKET_HEADER(p)->id = t->id;
-
- t->sent = p;
- p = NULL;
-
- return 0;
-}
-
-int dns_transaction_go(DnsTransaction *t) {
- usec_t ts;
- int r;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- assert(t);
-
- /* Returns > 0 if the transaction is now pending, returns 0 if could be processed immediately and has finished
- * now. */
-
- assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
- r = dns_transaction_prepare(t, ts);
- if (r <= 0)
- return r;
-
- log_debug("Transaction %" PRIu16 " for <%s> scope %s on %s/%s.",
- t->id,
- dns_resource_key_to_string(t->key, key_str, sizeof key_str),
- dns_protocol_to_string(t->scope->protocol),
- t->scope->link ? t->scope->link->name : "*",
- af_to_name_short(t->scope->family));
-
- if (!t->initial_jitter_scheduled &&
- (t->scope->protocol == DNS_PROTOCOL_LLMNR ||
- t->scope->protocol == DNS_PROTOCOL_MDNS)) {
- usec_t jitter, accuracy;
-
- /* RFC 4795 Section 2.7 suggests all queries should be
- * delayed by a random time from 0 to JITTER_INTERVAL. */
-
- t->initial_jitter_scheduled = true;
-
- random_bytes(&jitter, sizeof(jitter));
-
- switch (t->scope->protocol) {
-
- case DNS_PROTOCOL_LLMNR:
- jitter %= LLMNR_JITTER_INTERVAL_USEC;
- accuracy = LLMNR_JITTER_INTERVAL_USEC;
- break;
-
- case DNS_PROTOCOL_MDNS:
- jitter %= MDNS_JITTER_RANGE_USEC;
- jitter += MDNS_JITTER_MIN_USEC;
- accuracy = MDNS_JITTER_RANGE_USEC;
- break;
- default:
- assert_not_reached("bad protocol");
- }
-
- r = sd_event_add_time(
- t->scope->manager->event,
- &t->timeout_event_source,
- clock_boottime_or_monotonic(),
- ts + jitter, accuracy,
- on_transaction_timeout, t);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
- t->n_attempts = 0;
- t->next_attempt_after = ts;
- t->state = DNS_TRANSACTION_PENDING;
-
- log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter);
- return 0;
- }
-
- /* Otherwise, we need to ask the network */
- r = dns_transaction_make_packet(t);
- if (r < 0)
- return r;
-
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
- (dns_name_endswith(dns_resource_key_name(t->key), "in-addr.arpa") > 0 ||
- dns_name_endswith(dns_resource_key_name(t->key), "ip6.arpa") > 0)) {
-
- /* RFC 4795, Section 2.4. says reverse lookups shall
- * always be made via TCP on LLMNR */
- r = dns_transaction_open_tcp(t);
- } else {
- /* Try via UDP, and if that fails due to large size or lack of
- * support try via TCP */
- r = dns_transaction_emit_udp(t);
- if (r == -EMSGSIZE)
- log_debug("Sending query via TCP since it is too large.");
- if (r == -EAGAIN)
- log_debug("Sending query via TCP since server doesn't support UDP.");
- if (r == -EMSGSIZE || r == -EAGAIN)
- r = dns_transaction_open_tcp(t);
- }
-
- if (r == -ESRCH) {
- /* No servers to send this to? */
- dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
- return 0;
- }
- if (r == -EOPNOTSUPP) {
- /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC */
- dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
- return 0;
- }
- if (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) {
- /* On LLMNR, if we cannot connect to a host via TCP when doing reverse lookups. This means we cannot
- * answer this request with this protocol. */
- dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
- return 0;
- }
- if (r < 0) {
- if (t->scope->protocol != DNS_PROTOCOL_DNS)
- return r;
-
- /* Couldn't send? Try immediately again, with a new server */
- dns_scope_next_dns_server(t->scope);
-
- return dns_transaction_go(t);
- }
-
- ts += transaction_get_resend_timeout(t);
-
- r = sd_event_add_time(
- t->scope->manager->event,
- &t->timeout_event_source,
- clock_boottime_or_monotonic(),
- ts, 0,
- on_transaction_timeout, t);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
- t->state = DNS_TRANSACTION_PENDING;
- t->next_attempt_after = ts;
-
- return 1;
-}
-
-static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) {
- DnsTransaction *n;
- Iterator i;
- int r;
-
- assert(t);
- assert(aux);
-
- /* Try to find cyclic dependencies between transaction objects */
-
- if (t == aux)
- return 1;
-
- SET_FOREACH(n, aux->dnssec_transactions, i) {
- r = dns_transaction_find_cyclic(t, n);
- if (r != 0)
- return r;
- }
-
- return 0;
-}
-
-static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
- DnsTransaction *aux;
- int r;
-
- assert(t);
- assert(ret);
- assert(key);
-
- aux = dns_scope_find_transaction(t->scope, key, true);
- if (!aux) {
- r = dns_transaction_new(&aux, t->scope, key);
- if (r < 0)
- return r;
- } else {
- if (set_contains(t->dnssec_transactions, aux)) {
- *ret = aux;
- return 0;
- }
-
- r = dns_transaction_find_cyclic(t, aux);
- if (r < 0)
- return r;
- if (r > 0) {
- char s[DNS_RESOURCE_KEY_STRING_MAX], saux[DNS_RESOURCE_KEY_STRING_MAX];
-
- log_debug("Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).",
- aux->id,
- dns_resource_key_to_string(t->key, s, sizeof s),
- t->id,
- dns_resource_key_to_string(aux->key, saux, sizeof saux));
-
- return -ELOOP;
- }
- }
-
- r = set_ensure_allocated(&t->dnssec_transactions, NULL);
- if (r < 0)
- goto gc;
-
- r = set_ensure_allocated(&aux->notify_transactions, NULL);
- if (r < 0)
- goto gc;
-
- r = set_ensure_allocated(&aux->notify_transactions_done, NULL);
- if (r < 0)
- goto gc;
-
- r = set_put(t->dnssec_transactions, aux);
- if (r < 0)
- goto gc;
-
- r = set_put(aux->notify_transactions, t);
- if (r < 0) {
- (void) set_remove(t->dnssec_transactions, aux);
- goto gc;
- }
-
- *ret = aux;
- return 1;
-
-gc:
- dns_transaction_gc(aux);
- return r;
-}
-
-static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
- DnsTransaction *aux;
- int r;
-
- assert(t);
- assert(key);
-
- /* Try to get the data from the trust anchor */
- r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a);
- if (r < 0)
- return r;
- if (r > 0) {
- r = dns_answer_extend(&t->validated_keys, a);
- if (r < 0)
- return r;
-
- return 0;
- }
-
- /* This didn't work, ask for it via the network/cache then. */
- r = dns_transaction_add_dnssec_transaction(t, key, &aux);
- if (r == -ELOOP) /* This would result in a cyclic dependency */
- return 0;
- if (r < 0)
- return r;
-
- if (aux->state == DNS_TRANSACTION_NULL) {
- r = dns_transaction_go(aux);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
- int r;
-
- assert(t);
-
- /* Check whether the specified name is in the NTA
- * database, either in the global one, or the link-local
- * one. */
-
- r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, name);
- if (r != 0)
- return r;
-
- if (!t->scope->link)
- return 0;
-
- return set_contains(t->scope->link->dnssec_negative_trust_anchors, name);
-}
-
-static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
- int r;
-
- assert(t);
-
- /* Checks whether the answer is negative, and lacks NSEC/NSEC3
- * RRs to prove it */
-
- r = dns_transaction_has_positive_answer(t, NULL);
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- /* Is this key explicitly listed as a negative trust anchor?
- * If so, it's nothing we need to care about */
- r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key));
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- /* The answer does not contain any RRs that match to the
- * question. If so, let's see if there are any NSEC/NSEC3 RRs
- * included. If not, the answer is unsigned. */
-
- r = dns_answer_contains_nsec_or_nsec3(t->answer);
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- return true;
-}
-
-static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRecord *rr) {
- int r;
-
- assert(t);
- assert(rr);
-
- /* Check if the specified RR is the "primary" response,
- * i.e. either matches the question precisely or is a
- * CNAME/DNAME for it. */
-
- r = dns_resource_key_match_rr(t->key, rr, NULL);
- if (r != 0)
- return r;
-
- return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
-}
-
-static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
- assert(t);
-
- /* Checks whether our transaction's DNS server is assumed to be compatible with DNSSEC. Returns false as soon
- * as we changed our mind about a server, and now believe it is incompatible with DNSSEC. */
-
- if (t->scope->protocol != DNS_PROTOCOL_DNS)
- return false;
-
- /* If we have picked no server, then we are working from the cache or some other source, and DNSSEC might well
- * be supported, hence return true. */
- if (!t->server)
- return true;
-
- /* Note that we do not check the feature level actually used for the transaction but instead the feature level
- * the server is known to support currently, as the transaction feature level might be lower than what the
- * server actually supports, since we might have downgraded this transaction's feature level because we got a
- * SERVFAIL earlier and wanted to check whether downgrading fixes it. */
-
- return dns_server_dnssec_supported(t->server);
-}
-
-static bool dns_transaction_dnssec_supported_full(DnsTransaction *t) {
- DnsTransaction *dt;
- Iterator i;
-
- assert(t);
-
- /* Checks whether our transaction our any of the auxiliary transactions couldn't do DNSSEC. */
-
- if (!dns_transaction_dnssec_supported(t))
- return false;
-
- SET_FOREACH(dt, t->dnssec_transactions, i)
- if (!dns_transaction_dnssec_supported(dt))
- return false;
-
- return true;
-}
-
-int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
- DnsResourceRecord *rr;
-
- int r;
-
- assert(t);
-
- /*
- * Retrieve all auxiliary RRs for the answer we got, so that
- * we can verify signatures or prove that RRs are rightfully
- * unsigned. Specifically:
- *
- * - For RRSIG we get the matching DNSKEY
- * - For DNSKEY we get the matching DS
- * - For unsigned SOA/NS we get the matching DS
- * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
- * - For other unsigned RRs we get the matching SOA RR
- * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
- * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
- * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
- */
-
- if (t->scope->dnssec_mode == DNSSEC_NO)
- return 0;
- if (t->answer_source != DNS_TRANSACTION_NETWORK)
- return 0; /* We only need to validate stuff from the network */
- if (!dns_transaction_dnssec_supported(t))
- return 0; /* If we can't do DNSSEC anyway there's no point in geting the auxiliary RRs */
-
- DNS_ANSWER_FOREACH(rr, t->answer) {
-
- if (dns_type_is_pseudo(rr->key->type))
- continue;
-
- /* If this RR is in the negative trust anchor, we don't need to validate it. */
- r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- switch (rr->key->type) {
-
- case DNS_TYPE_RRSIG: {
- /* For each RRSIG we request the matching DNSKEY */
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dnskey = NULL;
-
- /* If this RRSIG is about a DNSKEY RR and the
- * signer is the same as the owner, then we
- * already have the DNSKEY, and we don't have
- * to look for more. */
- if (rr->rrsig.type_covered == DNS_TYPE_DNSKEY) {
- r = dns_name_equal(rr->rrsig.signer, dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r > 0)
- continue;
- }
-
- /* If the signer is not a parent of our
- * original query, then this is about an
- * auxiliary RRset, but not anything we asked
- * for. In this case we aren't interested,
- * because we don't want to request additional
- * RRs for stuff we didn't really ask for, and
- * also to avoid request loops, where
- * additional RRs from one transaction result
- * in another transaction whose additonal RRs
- * point back to the original transaction, and
- * we deadlock. */
- r = dns_name_endswith(dns_resource_key_name(t->key), rr->rrsig.signer);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- dnskey = dns_resource_key_new(rr->key->class, DNS_TYPE_DNSKEY, rr->rrsig.signer);
- if (!dnskey)
- return -ENOMEM;
-
- log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").",
- t->id, dns_resource_key_name(rr->key), rr->rrsig.key_tag);
- r = dns_transaction_request_dnssec_rr(t, dnskey);
- if (r < 0)
- return r;
- break;
- }
-
- case DNS_TYPE_DNSKEY: {
- /* For each DNSKEY we request the matching DS */
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
-
- /* If the DNSKEY we are looking at is not for
- * zone we are interested in, nor any of its
- * parents, we aren't interested, and don't
- * request it. After all, we don't want to end
- * up in request loops, and want to keep
- * additional traffic down. */
-
- r = dns_name_endswith(dns_resource_key_name(t->key), dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
- if (!ds)
- return -ENOMEM;
-
- log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").",
- t->id, dns_resource_key_name(rr->key), dnssec_keytag(rr, false));
- r = dns_transaction_request_dnssec_rr(t, ds);
- if (r < 0)
- return r;
-
- break;
- }
-
- case DNS_TYPE_SOA:
- case DNS_TYPE_NS: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
-
- /* For an unsigned SOA or NS, try to acquire
- * the matching DS RR, as we are at a zone cut
- * then, and whether a DS exists tells us
- * whether the zone is signed. Do so only if
- * this RR matches our original question,
- * however. */
-
- r = dns_resource_key_match_rr(t->key, rr, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dnssec_has_rrsig(t->answer, rr->key);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key));
- if (!ds)
- return -ENOMEM;
-
- log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).",
- t->id, dns_resource_key_name(rr->key));
- r = dns_transaction_request_dnssec_rr(t, ds);
- if (r < 0)
- return r;
-
- break;
- }
-
- case DNS_TYPE_DS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
- const char *name;
-
- /* CNAMEs and DNAMEs cannot be located at a
- * zone apex, hence ask for the parent SOA for
- * unsigned CNAME/DNAME RRs, maybe that's the
- * apex. But do all that only if this is
- * actually a response to our original
- * question.
- *
- * Similar for DS RRs, which are signed when
- * the parent SOA is signed. */
-
- r = dns_transaction_is_primary_response(t, rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dnssec_has_rrsig(t->answer, rr->key);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- r = dns_answer_has_dname_for_cname(t->answer, rr);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- name = dns_resource_key_name(rr->key);
- r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
- if (!soa)
- return -ENOMEM;
-
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).",
- t->id, dns_resource_key_name(rr->key));
- r = dns_transaction_request_dnssec_rr(t, soa);
- if (r < 0)
- return r;
-
- break;
- }
-
- default: {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
-
- /* For other unsigned RRsets (including
- * NSEC/NSEC3!), look for proof the zone is
- * unsigned, by requesting the SOA RR of the
- * zone. However, do so only if they are
- * directly relevant to our original
- * question. */
-
- r = dns_transaction_is_primary_response(t, rr);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dnssec_has_rrsig(t->answer, rr->key);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
-
- soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, dns_resource_key_name(rr->key));
- if (!soa)
- return -ENOMEM;
-
- log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).",
- t->id, dns_resource_key_name(rr->key), dns_resource_record_to_string(rr));
- r = dns_transaction_request_dnssec_rr(t, soa);
- if (r < 0)
- return r;
- break;
- }}
- }
-
- /* Above, we requested everything necessary to validate what
- * we got. Now, let's request what we need to validate what we
- * didn't get... */
-
- r = dns_transaction_has_unsigned_negative_answer(t);
- if (r < 0)
- return r;
- if (r > 0) {
- const char *name;
- uint16_t type = 0;
-
- name = dns_resource_key_name(t->key);
-
- /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
- * could also be used as indication that we are not at a zone apex, but in real world setups there are
- * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
- * though they have further children. If this was a DS request, then it's signed when the parent zone
- * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
- * to see if that is signed. */
-
- if (t->key->type == DNS_TYPE_DS) {
- r = dns_name_parent(&name);
- if (r > 0) {
- type = DNS_TYPE_SOA;
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
- t->id, dns_resource_key_name(t->key));
- } else
- name = NULL;
-
- } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
-
- type = DNS_TYPE_DS;
- log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
- t->id, dns_resource_key_name(t->key));
-
- } else {
- type = DNS_TYPE_SOA;
- log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
- t->id, dns_resource_key_name(t->key));
- }
-
- if (name) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
-
- soa = dns_resource_key_new(t->key->class, type, name);
- if (!soa)
- return -ENOMEM;
-
- r = dns_transaction_request_dnssec_rr(t, soa);
- if (r < 0)
- return r;
- }
- }
-
- return dns_transaction_dnssec_is_live(t);
-}
-
-void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source) {
- assert(t);
- assert(source);
-
- /* Invoked whenever any of our auxiliary DNSSEC transactions completed its work. If the state is still PENDING,
- we are still in the loop that adds further DNSSEC transactions, hence don't check if we are ready yet. If
- the state is VALIDATING however, we should check if we are complete now. */
-
- if (t->state == DNS_TRANSACTION_VALIDATING)
- dns_transaction_process_dnssec(t);
-}
-
-static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) {
- DnsResourceRecord *rr;
- int ifindex, r;
-
- assert(t);
-
- /* Add all DNSKEY RRs from the answer that are validated by DS
- * RRs from the list of validated keys to the list of
- * validated keys. */
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, t->answer) {
-
- r = dnssec_verify_dnskey_by_ds_search(rr, t->validated_keys);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* If so, the DNSKEY is validated too. */
- r = dns_answer_add_extend(&t->validated_keys, rr, ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *rr) {
- int r;
-
- assert(t);
- assert(rr);
-
- /* Checks if the RR we are looking for must be signed with an
- * RRSIG. This is used for positive responses. */
-
- if (t->scope->dnssec_mode == DNSSEC_NO)
- return false;
-
- if (dns_type_is_pseudo(rr->key->type))
- return -EINVAL;
-
- r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- switch (rr->key->type) {
-
- case DNS_TYPE_RRSIG:
- /* RRSIGs are the signatures themselves, they need no signing. */
- return false;
-
- case DNS_TYPE_SOA:
- case DNS_TYPE_NS: {
- DnsTransaction *dt;
- Iterator i;
-
- /* For SOA or NS RRs we look for a matching DS transaction */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != rr->key->class)
- continue;
- if (dt->key->type != DNS_TYPE_DS)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* We found a DS transactions for the SOA/NS
- * RRs we are looking at. If it discovered signed DS
- * RRs, then we need to be signed, too. */
-
- if (!dt->answer_authenticated)
- return false;
-
- return dns_answer_match_key(dt->answer, dt->key, NULL);
- }
-
- /* We found nothing that proves this is safe to leave
- * this unauthenticated, hence ask inist on
- * authentication. */
- return true;
- }
-
- case DNS_TYPE_DS:
- case DNS_TYPE_CNAME:
- case DNS_TYPE_DNAME: {
- const char *parent = NULL;
- DnsTransaction *dt;
- Iterator i;
-
- /*
- * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
- *
- * DS RRs are signed if the parent is signed, hence also look at the parent SOA
- */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != rr->key->class)
- continue;
- if (dt->key->type != DNS_TYPE_SOA)
- continue;
-
- if (!parent) {
- parent = dns_resource_key_name(rr->key);
- r = dns_name_parent(&parent);
- if (r < 0)
- return r;
- if (r == 0) {
- if (rr->key->type == DNS_TYPE_DS)
- return true;
-
- /* A CNAME/DNAME without a parent? That's sooo weird. */
- log_debug("Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
- return -EBADMSG;
- }
- }
-
- r = dns_name_equal(dns_resource_key_name(dt->key), parent);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- return t->answer_authenticated;
- }
-
- return true;
- }
-
- default: {
- DnsTransaction *dt;
- Iterator i;
-
- /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != rr->key->class)
- continue;
- if (dt->key->type != DNS_TYPE_SOA)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* We found the transaction that was supposed to find
- * the SOA RR for us. It was successful, but found no
- * RR for us. This means we are not at a zone cut. In
- * this case, we require authentication if the SOA
- * lookup was authenticated too. */
- return t->answer_authenticated;
- }
-
- return true;
- }}
-}
-
-static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKey *key) {
- DnsTransaction *dt;
- const char *tld;
- Iterator i;
- int r;
-
- /* If DNSSEC downgrade mode is on, checks whether the
- * specified RR is one level below a TLD we have proven not to
- * exist. In such a case we assume that this is a private
- * domain, and permit it.
- *
- * This detects cases like the Fritz!Box router networks. Each
- * Fritz!Box router serves a private "fritz.box" zone, in the
- * non-existing TLD "box". Requests for the "fritz.box" domain
- * are served by the router itself, while requests for the
- * "box" domain will result in NXDOMAIN.
- *
- * Note that this logic is unable to detect cases where a
- * router serves a private DNS zone directly under
- * non-existing TLD. In such a case we cannot detect whether
- * the TLD is supposed to exist or not, as all requests we
- * make for it will be answered by the router's zone, and not
- * by the root zone. */
-
- assert(t);
-
- if (t->scope->dnssec_mode != DNSSEC_ALLOW_DOWNGRADE)
- return false; /* In strict DNSSEC mode what doesn't exist, doesn't exist */
-
- tld = dns_resource_key_name(key);
- r = dns_name_parent(&tld);
- if (r < 0)
- return r;
- if (r == 0)
- return false; /* Already the root domain */
-
- if (!dns_name_is_single_label(tld))
- return false;
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != key->class)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(dt->key), tld);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* We found an auxiliary lookup we did for the TLD. If
- * that returned with NXDOMAIN, we know the TLD didn't
- * exist, and hence this might be a private zone. */
-
- return dt->answer_rcode == DNS_RCODE_NXDOMAIN;
- }
-
- return false;
-}
-
-static int dns_transaction_requires_nsec(DnsTransaction *t) {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
- DnsTransaction *dt;
- const char *name;
- uint16_t type = 0;
- Iterator i;
- int r;
-
- assert(t);
-
- /* Checks if we need to insist on NSEC/NSEC3 RRs for proving
- * this negative reply */
-
- if (t->scope->dnssec_mode == DNSSEC_NO)
- return false;
-
- if (dns_type_is_pseudo(t->key->type))
- return -EINVAL;
-
- r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key));
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- r = dns_transaction_in_private_tld(t, t->key);
- if (r < 0)
- return r;
- if (r > 0) {
- /* The lookup is from a TLD that is proven not to
- * exist, and we are in downgrade mode, hence ignore
- * that fact that we didn't get any NSEC RRs.*/
-
- log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.",
- dns_resource_key_to_string(t->key, key_str, sizeof key_str));
- return false;
- }
-
- name = dns_resource_key_name(t->key);
-
- if (t->key->type == DNS_TYPE_DS) {
-
- /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
- * hence check the parent SOA in this case. */
-
- r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r == 0)
- return true;
-
- type = DNS_TYPE_SOA;
-
- } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS))
- /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
- type = DNS_TYPE_DS;
- else
- /* For all other negative replies, check for the SOA lookup */
- type = DNS_TYPE_SOA;
-
- /* For all other RRs we check the SOA on the same level to see
- * if it's signed. */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != t->key->class)
- continue;
- if (dt->key->type != type)
- continue;
-
- r = dns_name_equal(dns_resource_key_name(dt->key), name);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- return dt->answer_authenticated;
- }
-
- /* If in doubt, require NSEC/NSEC3 */
- return true;
-}
-
-static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRecord *rr) {
- DnsResourceRecord *rrsig;
- bool found = false;
- int r;
-
- /* Checks whether any of the DNSKEYs used for the RRSIGs for
- * the specified RRset is authenticated (i.e. has a matching
- * DS RR). */
-
- r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key));
- if (r < 0)
- return r;
- if (r > 0)
- return false;
-
- DNS_ANSWER_FOREACH(rrsig, t->answer) {
- DnsTransaction *dt;
- Iterator i;
-
- r = dnssec_key_match_rrsig(rr->key, rrsig);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (dt->key->class != rr->key->class)
- continue;
-
- if (dt->key->type == DNS_TYPE_DNSKEY) {
-
- r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* OK, we found an auxiliary DNSKEY
- * lookup. If that lookup is
- * authenticated, report this. */
-
- if (dt->answer_authenticated)
- return true;
-
- found = true;
-
- } else if (dt->key->type == DNS_TYPE_DS) {
-
- r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* OK, we found an auxiliary DS
- * lookup. If that lookup is
- * authenticated and non-zero, we
- * won! */
-
- if (!dt->answer_authenticated)
- return false;
-
- return dns_answer_match_key(dt->answer, dt->key, NULL);
- }
- }
- }
-
- return found ? false : -ENXIO;
-}
-
-static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr) {
- assert(t);
- assert(rr);
-
- /* We know that the root domain is signed, hence if it appears
- * not to be signed, there's a problem with the DNS server */
-
- return rr->key->class == DNS_CLASS_IN &&
- dns_name_is_root(dns_resource_key_name(rr->key));
-}
-
-static int dns_transaction_check_revoked_trust_anchors(DnsTransaction *t) {
- DnsResourceRecord *rr;
- int r;
-
- assert(t);
-
- /* Maybe warn the user that we encountered a revoked DNSKEY
- * for a key from our trust anchor. Note that we don't care
- * whether the DNSKEY can be authenticated or not. It's
- * sufficient if it is self-signed. */
-
- DNS_ANSWER_FOREACH(rr, t->answer) {
- r = dns_trust_anchor_check_revoked(&t->scope->manager->trust_anchor, rr, t->answer);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int dns_transaction_invalidate_revoked_keys(DnsTransaction *t) {
- bool changed;
- int r;
-
- assert(t);
-
- /* Removes all DNSKEY/DS objects from t->validated_keys that
- * our trust anchors database considers revoked. */
-
- do {
- DnsResourceRecord *rr;
-
- changed = false;
-
- DNS_ANSWER_FOREACH(rr, t->validated_keys) {
- r = dns_trust_anchor_is_revoked(&t->scope->manager->trust_anchor, rr);
- if (r < 0)
- return r;
- if (r > 0) {
- r = dns_answer_remove_by_rr(&t->validated_keys, rr);
- if (r < 0)
- return r;
-
- assert(r > 0);
- changed = true;
- break;
- }
- }
- } while (changed);
-
- return 0;
-}
-
-static int dns_transaction_copy_validated(DnsTransaction *t) {
- DnsTransaction *dt;
- Iterator i;
- int r;
-
- assert(t);
-
- /* Copy all validated RRs from the auxiliary DNSSEC transactions into our set of validated RRs */
-
- SET_FOREACH(dt, t->dnssec_transactions, i) {
-
- if (DNS_TRANSACTION_IS_LIVE(dt->state))
- continue;
-
- if (!dt->answer_authenticated)
- continue;
-
- r = dns_answer_extend(&t->validated_keys, dt->answer);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-typedef enum {
- DNSSEC_PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */
- DNSSEC_PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */
- DNSSEC_PHASE_ALL, /* Phase #3, validate everything else */
-} Phase;
-
-static int dnssec_validate_records(
- DnsTransaction *t,
- Phase phase,
- bool *have_nsec,
- DnsAnswer **validated) {
-
- DnsResourceRecord *rr;
- int r;
-
- /* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */
-
- DNS_ANSWER_FOREACH(rr, t->answer) {
- DnsResourceRecord *rrsig = NULL;
- DnssecResult result;
-
- switch (rr->key->type) {
- case DNS_TYPE_RRSIG:
- continue;
-
- case DNS_TYPE_DNSKEY:
- /* We validate DNSKEYs only in the DNSKEY and ALL phases */
- if (phase == DNSSEC_PHASE_NSEC)
- continue;
- break;
-
- case DNS_TYPE_NSEC:
- case DNS_TYPE_NSEC3:
- *have_nsec = true;
-
- /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */
- if (phase == DNSSEC_PHASE_DNSKEY)
- continue;
- break;
-
- default:
- /* We validate all other RRs only in the ALL phases */
- if (phase != DNSSEC_PHASE_ALL)
- continue;
- }
-
- r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig);
- if (r < 0)
- return r;
-
- log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
-
- if (result == DNSSEC_VALIDATED) {
-
- if (rr->key->type == DNS_TYPE_DNSKEY) {
- /* If we just validated a DNSKEY RRset, then let's add these keys to
- * the set of validated keys for this transaction. */
-
- r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- /* Some of the DNSKEYs we just added might already have been revoked,
- * remove them again in that case. */
- r = dns_transaction_invalidate_revoked_keys(t);
- if (r < 0)
- return r;
- }
-
- /* Add the validated RRset to the new list of validated
- * RRsets, and remove it from the unvalidated RRsets.
- * We mark the RRset as authenticated and cacheable. */
- r = dns_answer_move_by_key(validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key);
-
- /* Exit the loop, we dropped something from the answer, start from the beginning */
- return 1;
- }
-
- /* If we haven't read all DNSKEYs yet a negative result of the validation is irrelevant, as
- * there might be more DNSKEYs coming. Similar, if we haven't read all NSEC/NSEC3 RRs yet,
- * we cannot do positive wildcard proofs yet, as those require the NSEC/NSEC3 RRs. */
- if (phase != DNSSEC_PHASE_ALL)
- continue;
-
- if (result == DNSSEC_VALIDATED_WILDCARD) {
- bool authenticated = false;
- const char *source;
-
- /* This RRset validated, but as a wildcard. This means we need
- * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/
-
- /* First step, determine the source of synthesis */
- r = dns_resource_record_source(rrsig, &source);
- if (r < 0)
- return r;
-
- r = dnssec_test_positive_wildcard(*validated,
- dns_resource_key_name(rr->key),
- source,
- rrsig->rrsig.signer,
- &authenticated);
-
- /* Unless the NSEC proof showed that the key really doesn't exist something is off. */
- if (r == 0)
- result = DNSSEC_INVALID;
- else {
- r = dns_answer_move_by_key(validated, &t->answer, rr->key,
- authenticated ? (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE) : 0);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key);
-
- /* Exit the loop, we dropped something from the answer, start from the beginning */
- return 1;
- }
- }
-
- if (result == DNSSEC_NO_SIGNATURE) {
- r = dns_transaction_requires_rrsig(t, rr);
- if (r < 0)
- return r;
- if (r == 0) {
- /* Data does not require signing. In that case, just copy it over,
- * but remember that this is by no means authenticated.*/
- r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
- return 1;
- }
-
- r = dns_transaction_known_signed(t, rr);
- if (r < 0)
- return r;
- if (r > 0) {
- /* This is an RR we know has to be signed. If it isn't this means
- * the server is not attaching RRSIGs, hence complain. */
-
- dns_server_packet_rrsig_missing(t->server, t->current_feature_level);
-
- if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
-
- /* Downgrading is OK? If so, just consider the information unsigned */
-
- r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
- return 1;
- }
-
- /* Otherwise, fail */
- t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
- return 0;
- }
-
- r = dns_transaction_in_private_tld(t, rr->key);
- if (r < 0)
- return r;
- if (r > 0) {
- char s[DNS_RESOURCE_KEY_STRING_MAX];
-
- /* The data is from a TLD that is proven not to exist, and we are in downgrade
- * mode, hence ignore the fact that this was not signed. */
-
- log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.",
- dns_resource_key_to_string(rr->key, s, sizeof s));
-
- r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
- return 1;
- }
- }
-
- if (IN_SET(result,
- DNSSEC_MISSING_KEY,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_UNSUPPORTED_ALGORITHM)) {
-
- r = dns_transaction_dnskey_authenticated(t, rr);
- if (r < 0 && r != -ENXIO)
- return r;
- if (r == 0) {
- /* The DNSKEY transaction was not authenticated, this means there's
- * no DS for this, which means it's OK if no keys are found for this signature. */
-
- r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
- if (r < 0)
- return r;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
- return 1;
- }
- }
-
- r = dns_transaction_is_primary_response(t, rr);
- if (r < 0)
- return r;
- if (r > 0) {
- /* Look for a matching DNAME for this CNAME */
- r = dns_answer_has_dname_for_cname(t->answer, rr);
- if (r < 0)
- return r;
- if (r == 0) {
- /* Also look among the stuff we already validated */
- r = dns_answer_has_dname_for_cname(*validated, rr);
- if (r < 0)
- return r;
- }
-
- if (r == 0) {
- if (IN_SET(result,
- DNSSEC_INVALID,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_NO_SIGNATURE))
- manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
-
- /* This is a primary response to our question, and it failed validation.
- * That's fatal. */
- t->answer_dnssec_result = result;
- return 0;
- }
-
- /* This is a primary response, but we do have a DNAME RR
- * in the RR that can replay this CNAME, hence rely on
- * that, and we can remove the CNAME in favour of it. */
- }
-
- /* This is just some auxiliary data. Just remove the RRset and continue. */
- r = dns_answer_remove_by_key(&t->answer, rr->key);
- if (r < 0)
- return r;
-
- /* We dropped something from the answer, start from the beginning. */
- return 1;
- }
-
- return 2; /* Finito. */
-}
-
-int dns_transaction_validate_dnssec(DnsTransaction *t) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL;
- Phase phase;
- DnsAnswerFlags flags;
- int r;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
-
- assert(t);
-
- /* We have now collected all DS and DNSKEY RRs in
- * t->validated_keys, let's see which RRs we can now
- * authenticate with that. */
-
- if (t->scope->dnssec_mode == DNSSEC_NO)
- return 0;
-
- /* Already validated */
- if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID)
- return 0;
-
- /* Our own stuff needs no validation */
- if (IN_SET(t->answer_source, DNS_TRANSACTION_ZONE, DNS_TRANSACTION_TRUST_ANCHOR)) {
- t->answer_dnssec_result = DNSSEC_VALIDATED;
- t->answer_authenticated = true;
- return 0;
- }
-
- /* Cached stuff is not affected by validation. */
- if (t->answer_source != DNS_TRANSACTION_NETWORK)
- return 0;
-
- if (!dns_transaction_dnssec_supported_full(t)) {
- /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
- t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
- log_debug("Not validating response for %" PRIu16 ", used server feature level does not support DNSSEC.", t->id);
- return 0;
- }
-
- log_debug("Validating response from transaction %" PRIu16 " (%s).",
- t->id,
- dns_resource_key_to_string(t->key, key_str, sizeof key_str));
-
- /* First, see if this response contains any revoked trust
- * anchors we care about */
- r = dns_transaction_check_revoked_trust_anchors(t);
- if (r < 0)
- return r;
-
- /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */
- r = dns_transaction_copy_validated(t);
- if (r < 0)
- return r;
-
- /* Second, see if there are DNSKEYs we already know a
- * validated DS for. */
- r = dns_transaction_validate_dnskey_by_ds(t);
- if (r < 0)
- return r;
-
- /* Fourth, remove all DNSKEY and DS RRs again that our trust
- * anchor says are revoked. After all we might have marked
- * some keys revoked above, but they might still be lingering
- * in our validated_keys list. */
- r = dns_transaction_invalidate_revoked_keys(t);
- if (r < 0)
- return r;
-
- phase = DNSSEC_PHASE_DNSKEY;
- for (;;) {
- bool have_nsec = false;
-
- r = dnssec_validate_records(t, phase, &have_nsec, &validated);
- if (r <= 0)
- return r;
-
- /* Try again as long as we managed to achieve something */
- if (r == 1)
- continue;
-
- if (phase == DNSSEC_PHASE_DNSKEY && have_nsec) {
- /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */
- phase = DNSSEC_PHASE_NSEC;
- continue;
- }
-
- if (phase != DNSSEC_PHASE_ALL) {
- /* OK, we processed all DNSKEYs and NSEC/NSEC3 RRs, look at all the rest now.
- * Note that in this third phase we start to remove RRs we couldn't validate. */
- phase = DNSSEC_PHASE_ALL;
- continue;
- }
-
- /* We're done */
- break;
- }
-
- dns_answer_unref(t->answer);
- t->answer = validated;
- validated = NULL;
-
- /* At this point the answer only contains validated
- * RRsets. Now, let's see if it actually answers the question
- * we asked. If so, great! If it doesn't, then see if
- * NSEC/NSEC3 can prove this. */
- r = dns_transaction_has_positive_answer(t, &flags);
- if (r > 0) {
- /* Yes, it answers the question! */
-
- if (flags & DNS_ANSWER_AUTHENTICATED) {
- /* The answer is fully authenticated, yay. */
- t->answer_dnssec_result = DNSSEC_VALIDATED;
- t->answer_rcode = DNS_RCODE_SUCCESS;
- t->answer_authenticated = true;
- } else {
- /* The answer is not fully authenticated. */
- t->answer_dnssec_result = DNSSEC_UNSIGNED;
- t->answer_authenticated = false;
- }
-
- } else if (r == 0) {
- DnssecNsecResult nr;
- bool authenticated = false;
-
- /* Bummer! Let's check NSEC/NSEC3 */
- r = dnssec_nsec_test(t->answer, t->key, &nr, &authenticated, &t->answer_nsec_ttl);
- if (r < 0)
- return r;
-
- switch (nr) {
-
- case DNSSEC_NSEC_NXDOMAIN:
- /* NSEC proves the domain doesn't exist. Very good. */
- log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str);
- t->answer_dnssec_result = DNSSEC_VALIDATED;
- t->answer_rcode = DNS_RCODE_NXDOMAIN;
- t->answer_authenticated = authenticated;
-
- manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
- break;
-
- case DNSSEC_NSEC_NODATA:
- /* NSEC proves that there's no data here, very good. */
- log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str);
- t->answer_dnssec_result = DNSSEC_VALIDATED;
- t->answer_rcode = DNS_RCODE_SUCCESS;
- t->answer_authenticated = authenticated;
-
- manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
- break;
-
- case DNSSEC_NSEC_OPTOUT:
- /* NSEC3 says the data might not be signed */
- log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str);
- t->answer_dnssec_result = DNSSEC_UNSIGNED;
- t->answer_authenticated = false;
-
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
- break;
-
- case DNSSEC_NSEC_NO_RR:
- /* No NSEC data? Bummer! */
-
- r = dns_transaction_requires_nsec(t);
- if (r < 0)
- return r;
- if (r > 0) {
- t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
- manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
- } else {
- t->answer_dnssec_result = DNSSEC_UNSIGNED;
- t->answer_authenticated = false;
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
- }
-
- break;
-
- case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
- /* We don't know the NSEC3 algorithm used? */
- t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
- manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key);
- break;
-
- case DNSSEC_NSEC_FOUND:
- case DNSSEC_NSEC_CNAME:
- /* NSEC says it needs to be there, but we couldn't find it? Bummer! */
- t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH;
- manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
- break;
-
- default:
- assert_not_reached("Unexpected NSEC result.");
- }
- }
-
- return 1;
-}
-
-static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
- [DNS_TRANSACTION_NULL] = "null",
- [DNS_TRANSACTION_PENDING] = "pending",
- [DNS_TRANSACTION_VALIDATING] = "validating",
- [DNS_TRANSACTION_RCODE_FAILURE] = "rcode-failure",
- [DNS_TRANSACTION_SUCCESS] = "success",
- [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
- [DNS_TRANSACTION_TIMEOUT] = "timeout",
- [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
- [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
- [DNS_TRANSACTION_ERRNO] = "errno",
- [DNS_TRANSACTION_ABORTED] = "aborted",
- [DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
- [DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor",
- [DNS_TRANSACTION_RR_TYPE_UNSUPPORTED] = "rr-type-unsupported",
- [DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
- [DNS_TRANSACTION_NOT_FOUND] = "not-found",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
-
-static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MAX] = {
- [DNS_TRANSACTION_NETWORK] = "network",
- [DNS_TRANSACTION_CACHE] = "cache",
- [DNS_TRANSACTION_ZONE] = "zone",
- [DNS_TRANSACTION_TRUST_ANCHOR] = "trust-anchor",
-};
-DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
deleted file mode 100644
index 5a1df70422..0000000000
--- a/src/resolve/resolved-dns-transaction.h
+++ /dev/null
@@ -1,181 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct DnsTransaction DnsTransaction;
-typedef enum DnsTransactionState DnsTransactionState;
-typedef enum DnsTransactionSource DnsTransactionSource;
-
-enum DnsTransactionState {
- DNS_TRANSACTION_NULL,
- DNS_TRANSACTION_PENDING,
- DNS_TRANSACTION_VALIDATING,
- DNS_TRANSACTION_RCODE_FAILURE,
- DNS_TRANSACTION_SUCCESS,
- DNS_TRANSACTION_NO_SERVERS,
- DNS_TRANSACTION_TIMEOUT,
- DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
- DNS_TRANSACTION_INVALID_REPLY,
- DNS_TRANSACTION_ERRNO,
- DNS_TRANSACTION_ABORTED,
- DNS_TRANSACTION_DNSSEC_FAILED,
- DNS_TRANSACTION_NO_TRUST_ANCHOR,
- DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
- DNS_TRANSACTION_NETWORK_DOWN,
- DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
- _DNS_TRANSACTION_STATE_MAX,
- _DNS_TRANSACTION_STATE_INVALID = -1
-};
-
-#define DNS_TRANSACTION_IS_LIVE(state) IN_SET((state), DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING)
-
-enum DnsTransactionSource {
- DNS_TRANSACTION_NETWORK,
- DNS_TRANSACTION_CACHE,
- DNS_TRANSACTION_ZONE,
- DNS_TRANSACTION_TRUST_ANCHOR,
- _DNS_TRANSACTION_SOURCE_MAX,
- _DNS_TRANSACTION_SOURCE_INVALID = -1
-};
-
-#include "resolved-dns-answer.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-scope.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-stream.h"
-
-struct DnsTransaction {
- DnsScope *scope;
-
- DnsResourceKey *key;
-
- DnsTransactionState state;
-
- uint16_t id;
-
- bool tried_stream:1;
-
- bool initial_jitter_scheduled:1;
- bool initial_jitter_elapsed:1;
-
- bool clamp_ttl:1;
-
- DnsPacket *sent, *received;
-
- DnsAnswer *answer;
- int answer_rcode;
- DnssecResult answer_dnssec_result;
- DnsTransactionSource answer_source;
- uint32_t answer_nsec_ttl;
- int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
-
- /* Indicates whether the primary answer is authenticated,
- * i.e. whether the RRs from answer which directly match the
- * question are authenticated, or, if there are none, whether
- * the NODATA or NXDOMAIN case is. It says nothing about
- * additional RRs listed in the answer, however they have
- * their own DNS_ANSWER_AUTHORIZED FLAGS. Note that this bit
- * is defined different than the AD bit in DNS packets, as
- * that covers more than just the actual primary answer. */
- bool answer_authenticated;
-
- /* Contains DNSKEY, DS, SOA RRs we already verified and need
- * to authenticate this reply */
- DnsAnswer *validated_keys;
-
- usec_t start_usec;
- usec_t next_attempt_after;
- sd_event_source *timeout_event_source;
- unsigned n_attempts;
-
- /* UDP connection logic, if we need it */
- int dns_udp_fd;
- sd_event_source *dns_udp_event_source;
-
- /* TCP connection logic, if we need it */
- DnsStream *stream;
-
- /* The active server */
- DnsServer *server;
-
- /* The features of the DNS server at time of transaction start */
- DnsServerFeatureLevel current_feature_level;
-
- /* If we got SERVFAIL back, we retry the lookup, using a lower feature level than we used before. */
- DnsServerFeatureLevel clamp_feature_level;
-
- /* Query candidates this transaction is referenced by and that
- * shall be notified about this specific transaction
- * completing. */
- Set *notify_query_candidates, *notify_query_candidates_done;
-
- /* Zone items this transaction is referenced by and that shall
- * be notified about completion. */
- Set *notify_zone_items, *notify_zone_items_done;
-
- /* Other transactions that this transactions is referenced by
- * and that shall be notified about completion. This is used
- * when transactions want to validate their RRsets, but need
- * another DNSKEY or DS RR to do so. */
- Set *notify_transactions, *notify_transactions_done;
-
- /* The opposite direction: the transactions this transaction
- * created in order to request DNSKEY or DS RRs. */
- Set *dnssec_transactions;
-
- unsigned block_gc;
-
- LIST_FIELDS(DnsTransaction, transactions_by_scope);
-};
-
-int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
-DnsTransaction* dns_transaction_free(DnsTransaction *t);
-
-bool dns_transaction_gc(DnsTransaction *t);
-int dns_transaction_go(DnsTransaction *t);
-
-void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
-void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
-
-void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source);
-int dns_transaction_validate_dnssec(DnsTransaction *t);
-int dns_transaction_request_dnssec_keys(DnsTransaction *t);
-
-const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
-DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
-
-const char* dns_transaction_source_to_string(DnsTransactionSource p) _const_;
-DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
-
-/* LLMNR Jitter interval, see RFC 4795 Section 7 */
-#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
-
-/* mDNS Jitter interval, see RFC 6762 Section 5.2 */
-#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC)
-#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
-
-/* Maximum attempts to send DNS requests, across all DNS servers */
-#define DNS_TRANSACTION_ATTEMPTS_MAX 16
-
-/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
-#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
-
-#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX)
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
deleted file mode 100644
index 9917b9e984..0000000000
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ /dev/null
@@ -1,746 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sd-messages.h>
-
-#include "alloc-util.h"
-#include "conf-files.h"
-#include "def.h"
-#include "dns-domain.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "hexdecoct.h"
-#include "parse-util.h"
-#include "resolved-dns-trust-anchor.h"
-#include "resolved-dns-dnssec.h"
-#include "set.h"
-#include "string-util.h"
-#include "strv.h"
-
-static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
-
-/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
-static const uint8_t root_digest[] =
- { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
- 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
-
-static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
- assert(d);
-
- /* Returns true if there's an entry for the specified domain
- * name in our trust anchor */
-
- return
- hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
- hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
-}
-
-static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- int r;
-
- assert(d);
-
- r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
- if (r < 0)
- return r;
-
- /* Only add the built-in trust anchor if there's neither a DS
- * nor a DNSKEY defined for the root domain. That way users
- * have an easy way to override the root domain DS/DNSKEY
- * data. */
- if (dns_trust_anchor_knows_domain_positive(d, "."))
- return 0;
-
- /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
- if (!rr)
- return -ENOMEM;
-
- rr->ds.key_tag = 19036;
- rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
- rr->ds.digest_size = sizeof(root_digest);
- rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
- if (!rr->ds.digest)
- return -ENOMEM;
-
- answer = dns_answer_new(1);
- if (!answer)
- return -ENOMEM;
-
- r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = hashmap_put(d->positive_by_key, rr->key, answer);
- if (r < 0)
- return r;
-
- answer = NULL;
- return 0;
-}
-
-static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
-
- static const char private_domains[] =
- /* RFC 6761 says that .test is a special domain for
- * testing and not to be installed in the root zone */
- "test\0"
-
- /* RFC 6761 says that these reverse IP lookup ranges
- * are for private addresses, and hence should not
- * show up in the root zone */
- "10.in-addr.arpa\0"
- "16.172.in-addr.arpa\0"
- "17.172.in-addr.arpa\0"
- "18.172.in-addr.arpa\0"
- "19.172.in-addr.arpa\0"
- "20.172.in-addr.arpa\0"
- "21.172.in-addr.arpa\0"
- "22.172.in-addr.arpa\0"
- "23.172.in-addr.arpa\0"
- "24.172.in-addr.arpa\0"
- "25.172.in-addr.arpa\0"
- "26.172.in-addr.arpa\0"
- "27.172.in-addr.arpa\0"
- "28.172.in-addr.arpa\0"
- "29.172.in-addr.arpa\0"
- "30.172.in-addr.arpa\0"
- "31.172.in-addr.arpa\0"
- "168.192.in-addr.arpa\0"
-
- /* The same, but for IPv6. */
- "d.f.ip6.arpa\0"
-
- /* RFC 6762 reserves the .local domain for Multicast
- * DNS, it hence cannot appear in the root zone. (Note
- * that we by default do not route .local traffic to
- * DNS anyway, except when a configured search domain
- * suggests so.) */
- "local\0"
-
- /* These two are well known, popular private zone
- * TLDs, that are blocked from delegation, according
- * to:
- * http://icannwiki.com/Name_Collision#NGPC_Resolution
- *
- * There's also ongoing work on making this official
- * in an RRC:
- * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
- "home\0"
- "corp\0"
-
- /* The following four TLDs are suggested for private
- * zones in RFC 6762, Appendix G, and are hence very
- * unlikely to be made official TLDs any day soon */
- "lan\0"
- "intranet\0"
- "internal\0"
- "private\0";
-
- const char *name;
- int r;
-
- assert(d);
-
- /* Only add the built-in trust anchor if there's no negative
- * trust anchor defined at all. This enables easy overriding
- * of negative trust anchors. */
-
- if (set_size(d->negative_by_name) > 0)
- return 0;
-
- r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
- if (r < 0)
- return r;
-
- /* We add a couple of domains as default negative trust
- * anchors, where it's very unlikely they will be installed in
- * the root zone. If they exist they must be private, and thus
- * unsigned. */
-
- NULSTR_FOREACH(name, private_domains) {
-
- if (dns_trust_anchor_knows_domain_positive(d, name))
- continue;
-
- r = set_put_strdup(d->negative_by_name, name);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnsAnswer *old_answer = NULL;
- const char *p = s;
- int r;
-
- assert(d);
- assert(line);
-
- r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
-
- if (!dns_name_is_valid(domain)) {
- log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
- return -EINVAL;
- }
-
- r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
- if (r < 0)
- return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
- if (r != 2) {
- log_warning("Missing class or type in line %s:%u", path, line);
- return -EINVAL;
- }
-
- if (!strcaseeq(class, "IN")) {
- log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
- return -EINVAL;
- }
-
- if (strcaseeq(type, "DS")) {
- _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
- _cleanup_free_ void *dd = NULL;
- uint16_t kt;
- int a, dt;
- size_t l;
-
- r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
- return -EINVAL;
- }
- if (r != 4) {
- log_warning("Missing DS parameters on line %s:%u", path, line);
- return -EINVAL;
- }
-
- r = safe_atou16(key_tag, &kt);
- if (r < 0)
- return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
-
- a = dnssec_algorithm_from_string(algorithm);
- if (a < 0) {
- log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
- return -EINVAL;
- }
-
- dt = dnssec_digest_from_string(digest_type);
- if (dt < 0) {
- log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
- return -EINVAL;
- }
-
- r = unhexmem(digest, strlen(digest), &dd, &l);
- if (r < 0) {
- log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
- return -EINVAL;
- }
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
- if (!rr)
- return log_oom();
-
- rr->ds.key_tag = kt;
- rr->ds.algorithm = a;
- rr->ds.digest_type = dt;
- rr->ds.digest_size = l;
- rr->ds.digest = dd;
- dd = NULL;
-
- } else if (strcaseeq(type, "DNSKEY")) {
- _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
- _cleanup_free_ void *k = NULL;
- uint16_t f;
- size_t l;
- int a;
-
- r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
- if (r < 0)
- return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
- if (r != 4) {
- log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
- return -EINVAL;
- }
-
- if (!streq(protocol, "3")) {
- log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
- return -EINVAL;
- }
-
- r = safe_atou16(flags, &f);
- if (r < 0)
- return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
- if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) {
- log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line);
- return -EINVAL;
- }
- if ((f & DNSKEY_FLAG_REVOKE)) {
- log_warning("DNSKEY is already revoked on line %s:%u", path, line);
- return -EINVAL;
- }
-
- a = dnssec_algorithm_from_string(algorithm);
- if (a < 0) {
- log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
- return -EINVAL;
- }
-
- r = unbase64mem(key, strlen(key), &k, &l);
- if (r < 0)
- return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
- if (!rr)
- return log_oom();
-
- rr->dnskey.flags = f;
- rr->dnskey.protocol = 3;
- rr->dnskey.algorithm = a;
- rr->dnskey.key_size = l;
- rr->dnskey.key = k;
- k = NULL;
-
- } else {
- log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
- return -EINVAL;
- }
-
- if (!isempty(p)) {
- log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
- return -EINVAL;
- }
-
- r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
- if (r < 0)
- return log_oom();
-
- old_answer = hashmap_get(d->positive_by_key, rr->key);
- answer = dns_answer_ref(old_answer);
-
- r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return log_error_errno(r, "Failed to add trust anchor RR: %m");
-
- r = hashmap_replace(d->positive_by_key, rr->key, answer);
- if (r < 0)
- return log_error_errno(r, "Failed to add answer to trust anchor: %m");
-
- old_answer = dns_answer_unref(old_answer);
- answer = NULL;
-
- return 0;
-}
-
-static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
- _cleanup_free_ char *domain = NULL;
- const char *p = s;
- int r;
-
- assert(d);
- assert(line);
-
- r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
- if (r < 0)
- return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
-
- if (!dns_name_is_valid(domain)) {
- log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
- return -EINVAL;
- }
-
- if (!isempty(p)) {
- log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
- return -EINVAL;
- }
-
- r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
- if (r < 0)
- return log_oom();
-
- r = set_put(d->negative_by_name, domain);
- if (r < 0)
- return log_oom();
- if (r > 0)
- domain = NULL;
-
- return 0;
-}
-
-static int dns_trust_anchor_load_files(
- DnsTrustAnchor *d,
- const char *suffix,
- int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
-
- _cleanup_strv_free_ char **files = NULL;
- char **f;
- int r;
-
- assert(d);
- assert(suffix);
- assert(loader);
-
- r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
- if (r < 0)
- return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
-
- STRV_FOREACH(f, files) {
- _cleanup_fclose_ FILE *g = NULL;
- char line[LINE_MAX];
- unsigned n = 0;
-
- g = fopen(*f, "r");
- if (!g) {
- if (errno == ENOENT)
- continue;
-
- log_warning_errno(errno, "Failed to open %s: %m", *f);
- continue;
- }
-
- FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
- char *l;
-
- n++;
-
- l = strstrip(line);
- if (isempty(l))
- continue;
-
- if (*l == ';')
- continue;
-
- (void) loader(d, *f, n, l);
- }
- }
-
- return 0;
-}
-
-static int domain_name_cmp(const void *a, const void *b) {
- char **x = (char**) a, **y = (char**) b;
-
- return dns_name_compare_func(*x, *y);
-}
-
-static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
- DnsAnswer *a;
- Iterator i;
-
- assert(d);
-
- if (hashmap_isempty(d->positive_by_key))
- log_info("No positive trust anchors defined.");
- else {
- log_info("Positive Trust Anchors:");
- HASHMAP_FOREACH(a, d->positive_by_key, i) {
- DnsResourceRecord *rr;
-
- DNS_ANSWER_FOREACH(rr, a)
- log_info("%s", dns_resource_record_to_string(rr));
- }
- }
-
- if (set_isempty(d->negative_by_name))
- log_info("No negative trust anchors defined.");
- else {
- _cleanup_free_ char **l = NULL, *j = NULL;
-
- l = set_get_strv(d->negative_by_name);
- if (!l)
- return log_oom();
-
- qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
-
- j = strv_join(l, " ");
- if (!j)
- return log_oom();
-
- log_info("Negative trust anchors: %s", j);
- }
-
- return 0;
-}
-
-int dns_trust_anchor_load(DnsTrustAnchor *d) {
- int r;
-
- assert(d);
-
- /* If loading things from disk fails, we don't consider this fatal */
- (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive);
- (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative);
-
- /* However, if the built-in DS fails, then we have a problem. */
- r = dns_trust_anchor_add_builtin_positive(d);
- if (r < 0)
- return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
-
- r = dns_trust_anchor_add_builtin_negative(d);
- if (r < 0)
- return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
-
- dns_trust_anchor_dump(d);
-
- return 0;
-}
-
-void dns_trust_anchor_flush(DnsTrustAnchor *d) {
- DnsAnswer *a;
- DnsResourceRecord *rr;
-
- assert(d);
-
- while ((a = hashmap_steal_first(d->positive_by_key)))
- dns_answer_unref(a);
- d->positive_by_key = hashmap_free(d->positive_by_key);
-
- while ((rr = set_steal_first(d->revoked_by_rr)))
- dns_resource_record_unref(rr);
- d->revoked_by_rr = set_free(d->revoked_by_rr);
-
- d->negative_by_name = set_free_free(d->negative_by_name);
-}
-
-int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
- DnsAnswer *a;
-
- assert(d);
- assert(key);
- assert(ret);
-
- /* We only serve DS and DNSKEY RRs. */
- if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
- return 0;
-
- a = hashmap_get(d->positive_by_key, key);
- if (!a)
- return 0;
-
- *ret = dns_answer_ref(a);
- return 1;
-}
-
-int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
- assert(d);
- assert(name);
-
- return set_contains(d->negative_by_name, name);
-}
-
-static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
- int r;
-
- assert(d);
-
- r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
- if (r < 0)
- return r;
-
- r = set_put(d->revoked_by_rr, rr);
- if (r < 0)
- return r;
- if (r > 0)
- dns_resource_record_ref(rr);
-
- return r;
-}
-
-static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
- DnsAnswer *old_answer;
- int r;
-
- /* Remember that this is a revoked trust anchor RR */
- r = dns_trust_anchor_revoked_put(d, rr);
- if (r < 0)
- return r;
-
- /* Remove this from the positive trust anchor */
- old_answer = hashmap_get(d->positive_by_key, rr->key);
- if (!old_answer)
- return 0;
-
- new_answer = dns_answer_ref(old_answer);
-
- r = dns_answer_remove_by_rr(&new_answer, rr);
- if (r <= 0)
- return r;
-
- /* We found the key! Warn the user */
- log_struct(LOG_WARNING,
- LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
- LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)),
- "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
- NULL);
-
- if (dns_answer_size(new_answer) <= 0) {
- assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
- dns_answer_unref(old_answer);
- return 1;
- }
-
- r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
- if (r < 0)
- return r;
-
- new_answer = NULL;
- dns_answer_unref(old_answer);
- return 1;
-}
-
-static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
- DnsAnswer *a;
- int r;
-
- assert(d);
- assert(revoked_dnskey);
- assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
- assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
-
- a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
- if (a) {
- DnsResourceRecord *anchor;
-
- /* First, look for the precise DNSKEY in our trust anchor database */
-
- DNS_ANSWER_FOREACH(anchor, a) {
-
- if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
- continue;
-
- if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
- continue;
-
- if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
- continue;
-
- /* Note that we allow the REVOKE bit to be
- * different! It will be set in the revoked
- * key, but unset in our version of it */
- if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
- continue;
-
- if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
- continue;
-
- dns_trust_anchor_remove_revoked(d, anchor);
- break;
- }
- }
-
- a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, dns_resource_key_name(revoked_dnskey->key)));
- if (a) {
- DnsResourceRecord *anchor;
-
- /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
-
- DNS_ANSWER_FOREACH(anchor, a) {
-
- /* We set mask_revoke to true here, since our
- * DS fingerprint will be the one of the
- * unrevoked DNSKEY, but the one we got passed
- * here has the bit set. */
- r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- dns_trust_anchor_remove_revoked(d, anchor);
- break;
- }
- }
-
- return 0;
-}
-
-int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
- DnsResourceRecord *rrsig;
- int r;
-
- assert(d);
- assert(dnskey);
-
- /* Looks if "dnskey" is a self-signed RR that has been revoked
- * and matches one of our trust anchor entries. If so, removes
- * it from the trust anchor and returns > 0. */
-
- if (dnskey->key->type != DNS_TYPE_DNSKEY)
- return 0;
-
- /* Is this DNSKEY revoked? */
- if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
- return 0;
-
- /* Could this be interesting to us at all? If not,
- * there's no point in looking for and verifying a
- * self-signed RRSIG. */
- if (!dns_trust_anchor_knows_domain_positive(d, dns_resource_key_name(dnskey->key)))
- return 0;
-
- /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
- DNS_ANSWER_FOREACH(rrsig, rrs) {
- DnssecResult result;
-
- if (rrsig->key->type != DNS_TYPE_RRSIG)
- continue;
-
- r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
- if (r < 0)
- return r;
- if (result != DNSSEC_VALIDATED)
- continue;
-
- /* Bingo! This is a revoked self-signed DNSKEY. Let's
- * see if this precise one exists in our trust anchor
- * database, too. */
- r = dns_trust_anchor_check_revoked_one(d, dnskey);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- return 0;
-}
-
-int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
- assert(d);
-
- if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
- return 0;
-
- return set_contains(d->revoked_by_rr, rr);
-}
diff --git a/src/resolve/resolved-dns-trust-anchor.h b/src/resolve/resolved-dns-trust-anchor.h
deleted file mode 100644
index 635c75fde5..0000000000
--- a/src/resolve/resolved-dns-trust-anchor.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct DnsTrustAnchor DnsTrustAnchor;
-
-#include "hashmap.h"
-#include "resolved-dns-answer.h"
-#include "resolved-dns-rr.h"
-
-/* This contains a fixed database mapping domain names to DS or DNSKEY records. */
-
-struct DnsTrustAnchor {
- Hashmap *positive_by_key;
- Set *negative_by_name;
- Set *revoked_by_rr;
-};
-
-int dns_trust_anchor_load(DnsTrustAnchor *d);
-void dns_trust_anchor_flush(DnsTrustAnchor *d);
-
-int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey* key, DnsAnswer **answer);
-int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name);
-
-int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs);
-int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
deleted file mode 100644
index 746a979f47..0000000000
--- a/src/resolve/resolved-dns-zone.c
+++ /dev/null
@@ -1,664 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "list.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-zone.h"
-#include "string-util.h"
-
-/* Never allow more than 1K entries */
-#define ZONE_MAX 1024
-
-void dns_zone_item_probe_stop(DnsZoneItem *i) {
- DnsTransaction *t;
- assert(i);
-
- if (!i->probe_transaction)
- return;
-
- t = i->probe_transaction;
- i->probe_transaction = NULL;
-
- set_remove(t->notify_zone_items, i);
- set_remove(t->notify_zone_items_done, i);
- dns_transaction_gc(t);
-}
-
-static void dns_zone_item_free(DnsZoneItem *i) {
- if (!i)
- return;
-
- dns_zone_item_probe_stop(i);
- dns_resource_record_unref(i->rr);
-
- free(i);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
-
-static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) {
- DnsZoneItem *first;
-
- assert(z);
-
- if (!i)
- return;
-
- first = hashmap_get(z->by_key, i->rr->key);
- LIST_REMOVE(by_key, first, i);
- if (first)
- assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
- else
- hashmap_remove(z->by_key, i->rr->key);
-
- first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
- LIST_REMOVE(by_name, first, i);
- if (first)
- assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
- else
- hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key));
-
- dns_zone_item_free(i);
-}
-
-void dns_zone_flush(DnsZone *z) {
- DnsZoneItem *i;
-
- assert(z);
-
- while ((i = hashmap_first(z->by_key)))
- dns_zone_item_remove_and_free(z, i);
-
- assert(hashmap_size(z->by_key) == 0);
- assert(hashmap_size(z->by_name) == 0);
-
- z->by_key = hashmap_free(z->by_key);
- z->by_name = hashmap_free(z->by_name);
-}
-
-static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
- DnsZoneItem *i;
-
- assert(z);
- assert(rr);
-
- LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
- if (dns_resource_record_equal(i->rr, rr) > 0)
- return i;
-
- return NULL;
-}
-
-void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
- DnsZoneItem *i;
-
- assert(z);
- assert(rr);
-
- i = dns_zone_get(z, rr);
- if (i)
- dns_zone_item_remove_and_free(z, i);
-}
-
-static int dns_zone_init(DnsZone *z) {
- int r;
-
- assert(z);
-
- r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
- DnsZoneItem *first;
- int r;
-
- first = hashmap_get(z->by_key, i->rr->key);
- if (first) {
- LIST_PREPEND(by_key, first, i);
- assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
- } else {
- r = hashmap_put(z->by_key, i->rr->key, i);
- if (r < 0)
- return r;
- }
-
- first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
- if (first) {
- LIST_PREPEND(by_name, first, i);
- assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
- } else {
- r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int dns_zone_item_probe_start(DnsZoneItem *i) {
- DnsTransaction *t;
- int r;
-
- assert(i);
-
- if (i->probe_transaction)
- return 0;
-
- t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)), false);
- if (!t) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
-
- key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key));
- if (!key)
- return -ENOMEM;
-
- r = dns_transaction_new(&t, i->scope, key);
- if (r < 0)
- return r;
- }
-
- r = set_ensure_allocated(&t->notify_zone_items, NULL);
- if (r < 0)
- goto gc;
-
- r = set_ensure_allocated(&t->notify_zone_items_done, NULL);
- if (r < 0)
- goto gc;
-
- r = set_put(t->notify_zone_items, i);
- if (r < 0)
- goto gc;
-
- i->probe_transaction = t;
-
- if (t->state == DNS_TRANSACTION_NULL) {
-
- i->block_ready++;
- r = dns_transaction_go(t);
- i->block_ready--;
-
- if (r < 0) {
- dns_zone_item_probe_stop(i);
- return r;
- }
- }
-
- dns_zone_item_notify(i);
- return 0;
-
-gc:
- dns_transaction_gc(t);
- return r;
-}
-
-int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
- _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
- DnsZoneItem *existing;
- int r;
-
- assert(z);
- assert(s);
- assert(rr);
-
- if (dns_class_is_pseudo(rr->key->class))
- return -EINVAL;
- if (dns_type_is_pseudo(rr->key->type))
- return -EINVAL;
-
- existing = dns_zone_get(z, rr);
- if (existing)
- return 0;
-
- r = dns_zone_init(z);
- if (r < 0)
- return r;
-
- i = new0(DnsZoneItem, 1);
- if (!i)
- return -ENOMEM;
-
- i->scope = s;
- i->rr = dns_resource_record_ref(rr);
- i->probing_enabled = probe;
-
- r = dns_zone_link_item(z, i);
- if (r < 0)
- return r;
-
- if (probe) {
- DnsZoneItem *first, *j;
- bool established = false;
-
- /* Check if there's already an RR with the same name
- * established. If so, it has been probed already, and
- * we don't ned to probe again. */
-
- LIST_FIND_HEAD(by_name, i, first);
- LIST_FOREACH(by_name, j, first) {
- if (i == j)
- continue;
-
- if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
- established = true;
- }
-
- if (established)
- i->state = DNS_ZONE_ITEM_ESTABLISHED;
- else {
- i->state = DNS_ZONE_ITEM_PROBING;
-
- r = dns_zone_item_probe_start(i);
- if (r < 0) {
- dns_zone_item_remove_and_free(z, i);
- i = NULL;
- return r;
- }
- }
- } else
- i->state = DNS_ZONE_ITEM_ESTABLISHED;
-
- i = NULL;
- return 0;
-}
-
-int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
- unsigned n_answer = 0;
- DnsZoneItem *j, *first;
- bool tentative = true, need_soa = false;
- int r;
-
- /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the
- * ifindex field in the answer with it */
-
- assert(z);
- assert(key);
- assert(ret_answer);
-
- /* First iteration, count what we have */
-
- if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
-
- /* If this is a generic match, then we have to
- * go through the list by the name and look
- * for everything manually */
-
- first = hashmap_get(z->by_name, dns_resource_key_name(key));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
-
- k = dns_resource_key_match_rr(key, j->rr, NULL);
- if (k < 0)
- return k;
- if (k > 0) {
- n_answer++;
- added = true;
- }
-
- }
-
- if (found && !added)
- need_soa = true;
-
- } else {
- bool found = false;
-
- /* If this is a specific match, then look for
- * the right key immediately */
-
- first = hashmap_get(z->by_key, key);
- LIST_FOREACH(by_key, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
- n_answer++;
- }
-
- if (!found) {
- first = hashmap_get(z->by_name, dns_resource_key_name(key));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- need_soa = true;
- break;
- }
- }
- }
-
- if (n_answer <= 0 && !need_soa)
- goto return_empty;
-
- if (n_answer > 0) {
- answer = dns_answer_new(n_answer);
- if (!answer)
- return -ENOMEM;
- }
-
- if (need_soa) {
- soa = dns_answer_new(1);
- if (!soa)
- return -ENOMEM;
- }
-
- /* Second iteration, actually add the RRs to the answers */
- if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
-
- first = hashmap_get(z->by_name, dns_resource_key_name(key));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
-
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
-
- k = dns_resource_key_match_rr(key, j->rr, NULL);
- if (k < 0)
- return k;
- if (k > 0) {
- r = dns_answer_add(answer, j->rr, ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- added = true;
- }
- }
-
- if (found && !added) {
- r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
- if (r < 0)
- return r;
- }
- } else {
- bool found = false;
-
- first = hashmap_get(z->by_key, key);
- LIST_FOREACH(by_key, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
-
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
-
- r = dns_answer_add(answer, j->rr, ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- if (!found) {
- bool add_soa = false;
-
- first = hashmap_get(z->by_name, dns_resource_key_name(key));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
-
- add_soa = true;
- }
-
- if (add_soa) {
- r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
- if (r < 0)
- return r;
- }
- }
- }
-
- /* If the caller sets ret_tentative to NULL, then use this as
- * indication to not return tentative entries */
-
- if (!ret_tentative && tentative)
- goto return_empty;
-
- *ret_answer = answer;
- answer = NULL;
-
- if (ret_soa) {
- *ret_soa = soa;
- soa = NULL;
- }
-
- if (ret_tentative)
- *ret_tentative = tentative;
-
- return 1;
-
-return_empty:
- *ret_answer = NULL;
-
- if (ret_soa)
- *ret_soa = NULL;
-
- if (ret_tentative)
- *ret_tentative = false;
-
- return 0;
-}
-
-void dns_zone_item_conflict(DnsZoneItem *i) {
- assert(i);
-
- if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
- return;
-
- log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
-
- dns_zone_item_probe_stop(i);
-
- /* Withdraw the conflict item */
- i->state = DNS_ZONE_ITEM_WITHDRAWN;
-
- /* Maybe change the hostname */
- if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
- manager_next_hostname(i->scope->manager);
-}
-
-void dns_zone_item_notify(DnsZoneItem *i) {
- assert(i);
- assert(i->probe_transaction);
-
- if (i->block_ready > 0)
- return;
-
- if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
- return;
-
- if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
- bool we_lost = false;
-
- /* The probe got a successful reply. If we so far
- * weren't established we just give up. If we already
- * were established, and the peer has the
- * lexicographically larger IP address we continue
- * and defend it. */
-
- if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
- log_debug("Got a successful probe for not yet established RR, we lost.");
- we_lost = true;
- } else {
- assert(i->probe_transaction->received);
- we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
- if (we_lost)
- log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
- }
-
- if (we_lost) {
- dns_zone_item_conflict(i);
- return;
- }
-
- log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
- }
-
- log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
-
- dns_zone_item_probe_stop(i);
- i->state = DNS_ZONE_ITEM_ESTABLISHED;
-}
-
-static int dns_zone_item_verify(DnsZoneItem *i) {
- int r;
-
- assert(i);
-
- if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
- return 0;
-
- log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
-
- i->state = DNS_ZONE_ITEM_VERIFYING;
- r = dns_zone_item_probe_start(i);
- if (r < 0) {
- log_error_errno(r, "Failed to start probing for verifying RR: %m");
- i->state = DNS_ZONE_ITEM_ESTABLISHED;
- return r;
- }
-
- return 0;
-}
-
-int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
- DnsZoneItem *i, *first;
- int c = 0;
-
- assert(zone);
- assert(rr);
-
- /* This checks whether a response RR we received from somebody
- * else is one that we actually thought was uniquely ours. If
- * so, we'll verify our RRs. */
-
- /* No conflict if we don't have the name at all. */
- first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
- if (!first)
- return 0;
-
- /* No conflict if we have the exact same RR */
- if (dns_zone_get(zone, rr))
- return 0;
-
- /* OK, somebody else has RRs for the same name. Yuck! Let's
- * start probing again */
-
- LIST_FOREACH(by_name, i, first) {
- if (dns_resource_record_equal(i->rr, rr))
- continue;
-
- dns_zone_item_verify(i);
- c++;
- }
-
- return c;
-}
-
-int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
- DnsZoneItem *i, *first;
- int c = 0;
-
- assert(zone);
-
- /* Somebody else notified us about a possible conflict. Let's
- * verify if that's true. */
-
- first = hashmap_get(zone->by_name, dns_resource_key_name(key));
- if (!first)
- return 0;
-
- LIST_FOREACH(by_name, i, first) {
- dns_zone_item_verify(i);
- c++;
- }
-
- return c;
-}
-
-void dns_zone_verify_all(DnsZone *zone) {
- DnsZoneItem *i;
- Iterator iterator;
-
- assert(zone);
-
- HASHMAP_FOREACH(i, zone->by_key, iterator) {
- DnsZoneItem *j;
-
- LIST_FOREACH(by_key, j, i)
- dns_zone_item_verify(j);
- }
-}
-
-void dns_zone_dump(DnsZone *zone, FILE *f) {
- Iterator iterator;
- DnsZoneItem *i;
-
- if (!zone)
- return;
-
- if (!f)
- f = stdout;
-
- HASHMAP_FOREACH(i, zone->by_key, iterator) {
- DnsZoneItem *j;
-
- LIST_FOREACH(by_key, j, i) {
- const char *t;
-
- t = dns_resource_record_to_string(j->rr);
- if (!t) {
- log_oom();
- continue;
- }
-
- fputc('\t', f);
- fputs(t, f);
- fputc('\n', f);
- }
- }
-}
-
-bool dns_zone_is_empty(DnsZone *zone) {
- if (!zone)
- return true;
-
- return hashmap_isempty(zone->by_key);
-}
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
deleted file mode 100644
index a41df37e6b..0000000000
--- a/src/resolve/resolved-dns-zone.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "hashmap.h"
-
-typedef struct DnsZone {
- Hashmap *by_key;
- Hashmap *by_name;
-} DnsZone;
-
-typedef struct DnsZoneItem DnsZoneItem;
-typedef enum DnsZoneItemState DnsZoneItemState;
-
-#include "resolved-dns-answer.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-rr.h"
-#include "resolved-dns-transaction.h"
-
-/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */
-#define LLMNR_DEFAULT_TTL (30)
-
-enum DnsZoneItemState {
- DNS_ZONE_ITEM_PROBING,
- DNS_ZONE_ITEM_ESTABLISHED,
- DNS_ZONE_ITEM_VERIFYING,
- DNS_ZONE_ITEM_WITHDRAWN,
-};
-
-struct DnsZoneItem {
- DnsScope *scope;
- DnsResourceRecord *rr;
-
- DnsZoneItemState state;
-
- unsigned block_ready;
-
- bool probing_enabled;
-
- LIST_FIELDS(DnsZoneItem, by_key);
- LIST_FIELDS(DnsZoneItem, by_name);
-
- DnsTransaction *probe_transaction;
-};
-
-void dns_zone_flush(DnsZone *z);
-
-int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe);
-void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
-
-int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
-
-void dns_zone_item_conflict(DnsZoneItem *i);
-void dns_zone_item_notify(DnsZoneItem *i);
-
-int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
-int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
-
-void dns_zone_verify_all(DnsZone *zone);
-
-void dns_zone_item_probe_stop(DnsZoneItem *i);
-
-void dns_zone_dump(DnsZone *zone, FILE *f);
-bool dns_zone_is_empty(DnsZone *zone);
diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c
deleted file mode 100644
index 40d650949d..0000000000
--- a/src/resolve/resolved-etc-hosts.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "fd-util.h"
-#include "fileio.h"
-#include "hostname-util.h"
-#include "resolved-etc-hosts.h"
-#include "resolved-dns-synthesize.h"
-#include "string-util.h"
-#include "strv.h"
-#include "time-util.h"
-
-/* Recheck /etc/hosts at most once every 2s */
-#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
-
-typedef struct EtcHostsItem {
- int family;
- union in_addr_union address;
-
- char **names;
-} EtcHostsItem;
-
-typedef struct EtcHostsItemByName {
- char *name;
-
- EtcHostsItem **items;
- size_t n_items, n_allocated;
-} EtcHostsItemByName;
-
-void manager_etc_hosts_flush(Manager *m) {
- EtcHostsItem *item;
- EtcHostsItemByName *bn;
-
- while ((item = set_steal_first(m->etc_hosts_by_address))) {
- strv_free(item->names);
- free(item);
- }
-
- while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
- free(bn->name);
- free(bn->items);
- free(bn);
- }
-
- m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
- m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
-
- m->etc_hosts_mtime = USEC_INFINITY;
-}
-
-static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
- const EtcHostsItem *item = p;
-
- siphash24_compress(&item->family, sizeof(item->family), state);
-
- if (item->family == AF_INET)
- siphash24_compress(&item->address.in, sizeof(item->address.in), state);
- else if (item->family == AF_INET6)
- siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
-}
-
-static int etc_hosts_item_compare_func(const void *a, const void *b) {
- const EtcHostsItem *x = a, *y = b;
-
- if (x->family != y->family)
- return x->family - y->family;
-
- if (x->family == AF_INET)
- return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
-
- if (x->family == AF_INET6)
- return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
-
- return trivial_compare_func(a, b);
-}
-
-static const struct hash_ops etc_hosts_item_ops = {
- .hash = etc_hosts_item_hash_func,
- .compare = etc_hosts_item_compare_func,
-};
-
-static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
-
- EtcHostsItem key = {
- .family = family,
- .address = *address,
- };
- EtcHostsItem *item;
- char **n;
- int r;
-
- assert(m);
- assert(address);
-
- r = in_addr_is_null(family, address);
- if (r < 0)
- return r;
- if (r > 0)
- /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to
- * nothing. */
- item = NULL;
- else {
- /* If this is a normal address, then, simply add entry mapping it to the specified names */
-
- item = set_get(m->etc_hosts_by_address, &key);
- if (item) {
- r = strv_extend_strv(&item->names, names, true);
- if (r < 0)
- return log_oom();
- } else {
-
- r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
- if (r < 0)
- return log_oom();
-
- item = new0(EtcHostsItem, 1);
- if (!item)
- return log_oom();
-
- item->family = family;
- item->address = *address;
- item->names = names;
-
- r = set_put(m->etc_hosts_by_address, item);
- if (r < 0) {
- free(item);
- return log_oom();
- }
- }
- }
-
- STRV_FOREACH(n, names) {
- EtcHostsItemByName *bn;
-
- bn = hashmap_get(m->etc_hosts_by_name, *n);
- if (!bn) {
- r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
- if (r < 0)
- return log_oom();
-
- bn = new0(EtcHostsItemByName, 1);
- if (!bn)
- return log_oom();
-
- bn->name = strdup(*n);
- if (!bn->name) {
- free(bn);
- return log_oom();
- }
-
- r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
- if (r < 0) {
- free(bn->name);
- free(bn);
- return log_oom();
- }
- }
-
- if (item) {
- if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
- return log_oom();
-
- bn->items[bn->n_items++] = item;
- }
- }
-
- return 0;
-}
-
-static int parse_line(Manager *m, unsigned nr, const char *line) {
- _cleanup_free_ char *address = NULL;
- _cleanup_strv_free_ char **names = NULL;
- union in_addr_union in;
- bool suppressed = false;
- int family, r;
-
- assert(m);
- assert(line);
-
- r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
- if (r < 0)
- return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
- if (r == 0) {
- log_error("Premature end of line, in line /etc/hosts:%u.", nr);
- return -EINVAL;
- }
-
- r = in_addr_from_string_auto(address, &family, &in);
- if (r < 0)
- return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
-
- for (;;) {
- _cleanup_free_ char *name = NULL;
-
- r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
- if (r < 0)
- return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
- if (r == 0)
- break;
-
- r = dns_name_is_valid(name);
- if (r <= 0)
- return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
-
- if (is_localhost(name)) {
- /* Suppress the "localhost" line that is often seen */
- suppressed = true;
- continue;
- }
-
- r = strv_push(&names, name);
- if (r < 0)
- return log_oom();
-
- name = NULL;
- }
-
- if (strv_isempty(names)) {
-
- if (suppressed)
- return 0;
-
- log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
- return -EINVAL;
- }
-
- /* Takes possession of the names strv */
- r = add_item(m, family, &in, names);
- if (r < 0)
- return r;
-
- names = NULL;
- return r;
-}
-
-int manager_etc_hosts_read(Manager *m) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- struct stat st;
- usec_t ts;
- unsigned nr = 0;
- int r;
-
- assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
- /* See if we checked /etc/hosts recently already */
- if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts)
- return 0;
-
- m->etc_hosts_last = ts;
-
- if (m->etc_hosts_mtime != USEC_INFINITY) {
- if (stat("/etc/hosts", &st) < 0) {
- if (errno == ENOENT) {
- r = 0;
- goto clear;
- }
-
- return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
- }
-
- /* Did the mtime change? If not, there's no point in re-reading the file. */
- if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime)
- return 0;
- }
-
- f = fopen("/etc/hosts", "re");
- if (!f) {
- if (errno == ENOENT) {
- r = 0;
- goto clear;
- }
-
- return log_error_errno(errno, "Failed to open /etc/hosts: %m");
- }
-
- /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
- * invocation */
- r = fstat(fileno(f), &st);
- if (r < 0)
- return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
-
- manager_etc_hosts_flush(m);
-
- FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
- char *l;
-
- nr++;
-
- l = strstrip(line);
- if (isempty(l))
- continue;
- if (l[0] == '#')
- continue;
-
- r = parse_line(m, nr, l);
- if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
- goto clear;
- }
-
- m->etc_hosts_mtime = timespec_load(&st.st_mtim);
- m->etc_hosts_last = ts;
-
- return 1;
-
-clear:
- manager_etc_hosts_flush(m);
- return r;
-}
-
-int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
- bool found_a = false, found_aaaa = false;
- EtcHostsItemByName *bn;
- EtcHostsItem k = {};
- DnsResourceKey *t;
- const char *name;
- unsigned i;
- int r;
-
- assert(m);
- assert(q);
- assert(answer);
-
- r = manager_etc_hosts_read(m);
- if (r < 0)
- return r;
-
- name = dns_question_first_name(q);
- if (!name)
- return 0;
-
- r = dns_name_address(name, &k.family, &k.address);
- if (r > 0) {
- EtcHostsItem *item;
- DnsResourceKey *found_ptr = NULL;
-
- item = set_get(m->etc_hosts_by_address, &k);
- if (!item)
- return 0;
-
- /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
- * we'll only return if the request was for PTR. */
-
- DNS_QUESTION_FOREACH(t, q) {
- if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
- continue;
- if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
- continue;
-
- r = dns_name_equal(dns_resource_key_name(t), name);
- if (r < 0)
- return r;
- if (r > 0) {
- found_ptr = t;
- break;
- }
- }
-
- if (found_ptr) {
- char **n;
-
- r = dns_answer_reserve(answer, strv_length(item->names));
- if (r < 0)
- return r;
-
- STRV_FOREACH(n, item->names) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new(found_ptr);
- if (!rr)
- return -ENOMEM;
-
- rr->ptr.name = strdup(*n);
- if (!rr->ptr.name)
- return -ENOMEM;
-
- r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
- }
-
- return 1;
- }
-
- bn = hashmap_get(m->etc_hosts_by_name, name);
- if (!bn)
- return 0;
-
- r = dns_answer_reserve(answer, bn->n_items);
- if (r < 0)
- return r;
-
- DNS_QUESTION_FOREACH(t, q) {
- if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
- continue;
- if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
- continue;
-
- r = dns_name_equal(dns_resource_key_name(t), name);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY))
- found_a = true;
- if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY))
- found_aaaa = true;
-
- if (found_a && found_aaaa)
- break;
- }
-
- for (i = 0; i < bn->n_items; i++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- if ((found_a && bn->items[i]->family != AF_INET) &&
- (found_aaaa && bn->items[i]->family != AF_INET6))
- continue;
-
- r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h
deleted file mode 100644
index 9d5a175f18..0000000000
--- a/src/resolve/resolved-etc-hosts.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-answer.h"
-
-void manager_etc_hosts_flush(Manager *m);
-int manager_etc_hosts_read(Manager *m);
-int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
deleted file mode 100644
index 446f85cdf4..0000000000
--- a/src/resolve/resolved-gperf.gperf
+++ /dev/null
@@ -1,23 +0,0 @@
-%{
-#include <stddef.h>
-#include "conf-parser.h"
-#include "resolved-conf.h"
-%}
-struct ConfigPerfItem;
-%null_strings
-%language=ANSI-C
-%define slot-name section_and_lvalue
-%define hash-function-name resolved_gperf_hash
-%define lookup-function-name resolved_gperf_lookup
-%readonly-tables
-%omit-struct-type
-%struct-type
-%includes
-%%
-Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
-Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
-Resolve.Domains, config_parse_search_domains, 0, 0
-Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
-Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
-Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache)
-Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
deleted file mode 100644
index 364812250f..0000000000
--- a/src/resolve/resolved-link-bus.c
+++ /dev/null
@@ -1,629 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "bus-util.h"
-#include "parse-util.h"
-#include "resolve-util.h"
-#include "resolved-bus.h"
-#include "resolved-link-bus.h"
-#include "resolved-resolv-conf.h"
-#include "strv.h"
-
-static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport);
-
-static int property_get_dnssec_mode(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
-
- assert(reply);
- assert(l);
-
- return sd_bus_message_append(reply, "s", dnssec_mode_to_string(link_get_dnssec_mode(l)));
-}
-
-static int property_get_dns(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
- DnsServer *s;
- int r;
-
- assert(reply);
- assert(l);
-
- r = sd_bus_message_open_container(reply, 'a', "(iay)");
- if (r < 0)
- return r;
-
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = bus_dns_server_append(reply, s, false);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int property_get_domains(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
- DnsSearchDomain *d;
- int r;
-
- assert(reply);
- assert(l);
-
- r = sd_bus_message_open_container(reply, 'a', "(sb)");
- if (r < 0)
- return r;
-
- LIST_FOREACH(domains, d, l->search_domains) {
- r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int property_get_scopes_mask(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
- uint64_t mask;
-
- assert(reply);
- assert(l);
-
- mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) |
- (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) |
- (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) |
- (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) |
- (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0);
-
- return sd_bus_message_append(reply, "t", mask);
-}
-
-static int property_get_ntas(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
- const char *name;
- Iterator i;
- int r;
-
- assert(reply);
- assert(l);
-
- r = sd_bus_message_open_container(reply, 'a', "s");
- if (r < 0)
- return r;
-
- SET_FOREACH(name, l->dnssec_negative_trust_anchors, i) {
- r = sd_bus_message_append(reply, "s", name);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
-static int property_get_dnssec_supported(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = userdata;
-
- assert(reply);
- assert(l);
-
- return sd_bus_message_append(reply, "b", link_dnssec_supported(l));
-}
-
-static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
- assert(l);
-
- if (l->flags & IFF_LOOPBACK)
- return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name);
- if (l->is_managed)
- return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name);
-
- return 0;
-}
-
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ struct in_addr_data *dns = NULL;
- size_t allocated = 0, n = 0;
- Link *l = userdata;
- unsigned i;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(message, 'a', "(iay)");
- if (r < 0)
- return r;
-
- for (;;) {
- int family;
- size_t sz;
- const void *d;
-
- assert_cc(sizeof(int) == sizeof(int32_t));
-
- r = sd_bus_message_enter_container(message, 'r', "iay");
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = sd_bus_message_read(message, "i", &family);
- if (r < 0)
- return r;
-
- if (!IN_SET(family, AF_INET, AF_INET6))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
- r = sd_bus_message_read_array(message, 'y', &d, &sz);
- if (r < 0)
- return r;
- if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
- if (!dns_server_address_valid(family, d))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- if (!GREEDY_REALLOC(dns, allocated, n+1))
- return -ENOMEM;
-
- dns[n].family = family;
- memcpy(&dns[n].address, d, sz);
- n++;
- }
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- return r;
-
- dns_server_mark_all(l->dns_servers);
-
- for (i = 0; i < n; i++) {
- DnsServer *s;
-
- s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0);
- if (s)
- dns_server_move_back_and_unmark(s);
- else {
- r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0);
- if (r < 0)
- goto clear;
- }
-
- }
-
- dns_server_unlink_marked(l->dns_servers);
- link_allocate_scopes(l);
-
- (void) link_save_user(l);
- (void) manager_write_resolv_conf(l->manager);
-
- return sd_bus_reply_method_return(message, NULL);
-
-clear:
- dns_server_unlink_all(l->dns_servers);
- return r;
-}
-
-int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Link *l = userdata;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(message, 'a', "(sb)");
- if (r < 0)
- return r;
-
- for (;;) {
- const char *name;
- int route_only;
-
- r = sd_bus_message_read(message, "(sb)", &name, &route_only);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- r = dns_name_is_valid(name);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
- if (!route_only && dns_name_is_root(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
- }
-
- dns_search_domain_mark_all(l->search_domains);
-
- r = sd_bus_message_rewind(message, false);
- if (r < 0)
- return r;
-
- for (;;) {
- DnsSearchDomain *d;
- const char *name;
- int route_only;
-
- r = sd_bus_message_read(message, "(sb)", &name, &route_only);
- if (r < 0)
- goto clear;
- if (r == 0)
- break;
-
- r = dns_search_domain_find(l->search_domains, name, &d);
- if (r < 0)
- goto clear;
-
- if (r > 0)
- dns_search_domain_move_back_and_unmark(d);
- else {
- r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
- if (r < 0)
- goto clear;
- }
-
- d->route_only = route_only;
- }
-
- r = sd_bus_message_exit_container(message);
- if (r < 0)
- goto clear;
-
- dns_search_domain_unlink_marked(l->search_domains);
-
- (void) link_save_user(l);
- (void) manager_write_resolv_conf(l->manager);
-
- return sd_bus_reply_method_return(message, NULL);
-
-clear:
- dns_search_domain_unlink_all(l->search_domains);
- return r;
-}
-
-int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Link *l = userdata;
- ResolveSupport mode;
- const char *llmnr;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(message, "s", &llmnr);
- if (r < 0)
- return r;
-
- if (isempty(llmnr))
- mode = RESOLVE_SUPPORT_YES;
- else {
- mode = resolve_support_from_string(llmnr);
- if (mode < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
- }
-
- l->llmnr_support = mode;
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
- (void) link_save_user(l);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Link *l = userdata;
- ResolveSupport mode;
- const char *mdns;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(message, "s", &mdns);
- if (r < 0)
- return r;
-
- if (isempty(mdns))
- mode = RESOLVE_SUPPORT_NO;
- else {
- mode = resolve_support_from_string(mdns);
- if (mode < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
- }
-
- l->mdns_support = mode;
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
- (void) link_save_user(l);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Link *l = userdata;
- const char *dnssec;
- DnssecMode mode;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(message, "s", &dnssec);
- if (r < 0)
- return r;
-
- if (isempty(dnssec))
- mode = _DNSSEC_MODE_INVALID;
- else {
- mode = dnssec_mode_from_string(dnssec);
- if (mode < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
- }
-
- link_set_dnssec_mode(l, mode);
-
- (void) link_save_user(l);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_set_free_free_ Set *ns = NULL;
- _cleanup_free_ char **ntas = NULL;
- Link *l = userdata;
- int r;
- char **i;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read_strv(message, &ntas);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, ntas) {
- r = dns_name_is_valid(*i);
- if (r < 0)
- return r;
- if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
- }
-
- ns = set_new(&dns_name_hash_ops);
- if (!ns)
- return -ENOMEM;
-
- STRV_FOREACH(i, ntas) {
- r = set_put_strdup(ns, *i);
- if (r < 0)
- return r;
- }
-
- set_free_free(l->dnssec_negative_trust_anchors);
- l->dnssec_negative_trust_anchors = ns;
- ns = NULL;
-
- (void) link_save_user(l);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Link *l = userdata;
- int r;
-
- assert(message);
- assert(l);
-
- r = verify_unmanaged_link(l, error);
- if (r < 0)
- return r;
-
- link_flush_settings(l);
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
- (void) link_save_user(l);
- (void) manager_write_resolv_conf(l->manager);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-const sd_bus_vtable link_vtable[] = {
- SD_BUS_VTABLE_START(0),
-
- SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
- SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
- SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
- SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0),
- SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0),
- SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
- SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
- SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
-
- SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
- SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0),
- SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
- SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
- SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0),
- SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0),
- SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0),
-
- SD_BUS_VTABLE_END
-};
-
-int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
- _cleanup_free_ char *e = NULL;
- Manager *m = userdata;
- int ifindex;
- Link *link;
- int r;
-
- assert(bus);
- assert(path);
- assert(interface);
- assert(found);
- assert(m);
-
- r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e);
- if (r <= 0)
- return 0;
-
- r = parse_ifindex(e, &ifindex);
- if (r < 0)
- return 0;
-
- link = hashmap_get(m->links, INT_TO_PTR(ifindex));
- if (!link)
- return 0;
-
- *found = link;
- return 1;
-}
-
-char *link_bus_path(Link *link) {
- _cleanup_free_ char *ifindex = NULL;
- char *p;
- int r;
-
- assert(link);
-
- if (asprintf(&ifindex, "%i", link->ifindex) < 0)
- return NULL;
-
- r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p);
- if (r < 0)
- return NULL;
-
- return p;
-}
-
-int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
- _cleanup_strv_free_ char **l = NULL;
- Manager *m = userdata;
- Link *link;
- Iterator i;
- unsigned c = 0;
-
- assert(bus);
- assert(path);
- assert(m);
- assert(nodes);
-
- l = new0(char*, hashmap_size(m->links) + 1);
- if (!l)
- return -ENOMEM;
-
- HASHMAP_FOREACH(link, m->links, i) {
- char *p;
-
- p = link_bus_path(link);
- if (!p)
- return -ENOMEM;
-
- l[c++] = p;
- }
-
- l[c] = NULL;
- *nodes = l;
- l = NULL;
-
- return 1;
-}
diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h
deleted file mode 100644
index 646031b631..0000000000
--- a/src/resolve/resolved-link-bus.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-bus.h"
-
-#include "resolved-link.h"
-
-extern const sd_bus_vtable link_vtable[];
-
-int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-char *link_bus_path(Link *link);
-int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
deleted file mode 100644
index 13e1f91192..0000000000
--- a/src/resolve/resolved-link.c
+++ /dev/null
@@ -1,1113 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/if.h>
-
-#include "sd-network.h"
-
-#include "alloc-util.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "missing.h"
-#include "mkdir.h"
-#include "parse-util.h"
-#include "resolved-link.h"
-#include "string-util.h"
-#include "strv.h"
-
-int link_new(Manager *m, Link **ret, int ifindex) {
- _cleanup_(link_freep) Link *l = NULL;
- int r;
-
- assert(m);
- assert(ifindex > 0);
-
- r = hashmap_ensure_allocated(&m->links, NULL);
- if (r < 0)
- return r;
-
- l = new0(Link, 1);
- if (!l)
- return -ENOMEM;
-
- l->ifindex = ifindex;
- l->llmnr_support = RESOLVE_SUPPORT_YES;
- l->mdns_support = RESOLVE_SUPPORT_NO;
- l->dnssec_mode = _DNSSEC_MODE_INVALID;
- l->operstate = IF_OPER_UNKNOWN;
-
- if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
- return -ENOMEM;
-
- r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
- if (r < 0)
- return r;
-
- l->manager = m;
-
- if (ret)
- *ret = l;
- l = NULL;
-
- return 0;
-}
-
-void link_flush_settings(Link *l) {
- assert(l);
-
- l->llmnr_support = RESOLVE_SUPPORT_YES;
- l->mdns_support = RESOLVE_SUPPORT_NO;
- l->dnssec_mode = _DNSSEC_MODE_INVALID;
-
- dns_server_unlink_all(l->dns_servers);
- dns_search_domain_unlink_all(l->search_domains);
-
- l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
-}
-
-Link *link_free(Link *l) {
- if (!l)
- return NULL;
-
- link_flush_settings(l);
-
- while (l->addresses)
- (void) link_address_free(l->addresses);
-
- if (l->manager)
- hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
-
- dns_scope_free(l->unicast_scope);
- dns_scope_free(l->llmnr_ipv4_scope);
- dns_scope_free(l->llmnr_ipv6_scope);
- dns_scope_free(l->mdns_ipv4_scope);
- dns_scope_free(l->mdns_ipv6_scope);
-
- free(l->state_file);
-
- return mfree(l);
-}
-
-void link_allocate_scopes(Link *l) {
- int r;
-
- assert(l);
-
- if (link_relevant(l, AF_UNSPEC, false) &&
- l->dns_servers) {
- if (!l->unicast_scope) {
- r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
- if (r < 0)
- log_warning_errno(r, "Failed to allocate DNS scope: %m");
- }
- } else
- l->unicast_scope = dns_scope_free(l->unicast_scope);
-
- if (link_relevant(l, AF_INET, true) &&
- l->llmnr_support != RESOLVE_SUPPORT_NO &&
- l->manager->llmnr_support != RESOLVE_SUPPORT_NO) {
- if (!l->llmnr_ipv4_scope) {
- r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
- if (r < 0)
- log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
- }
- } else
- l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
-
- if (link_relevant(l, AF_INET6, true) &&
- l->llmnr_support != RESOLVE_SUPPORT_NO &&
- l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
- socket_ipv6_is_supported()) {
- if (!l->llmnr_ipv6_scope) {
- r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
- if (r < 0)
- log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
- }
- } else
- l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
-
- if (link_relevant(l, AF_INET, true) &&
- l->mdns_support != RESOLVE_SUPPORT_NO &&
- l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
- if (!l->mdns_ipv4_scope) {
- r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
- if (r < 0)
- log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m");
- }
- } else
- l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
-
- if (link_relevant(l, AF_INET6, true) &&
- l->mdns_support != RESOLVE_SUPPORT_NO &&
- l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
- if (!l->mdns_ipv6_scope) {
- r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
- if (r < 0)
- log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m");
- }
- } else
- l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
-}
-
-void link_add_rrs(Link *l, bool force_remove) {
- LinkAddress *a;
-
- LIST_FOREACH(addresses, a, l->addresses)
- link_address_add_rrs(a, force_remove);
-}
-
-int link_process_rtnl(Link *l, sd_netlink_message *m) {
- const char *n = NULL;
- int r;
-
- assert(l);
- assert(m);
-
- r = sd_rtnl_message_link_get_flags(m, &l->flags);
- if (r < 0)
- return r;
-
- (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
- (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
-
- if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
- strncpy(l->name, n, sizeof(l->name)-1);
- char_array_0(l->name);
- }
-
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
- return 0;
-}
-
-static int link_update_dns_server_one(Link *l, const char *name) {
- union in_addr_union a;
- DnsServer *s;
- int family, r;
-
- assert(l);
- assert(name);
-
- r = in_addr_from_string_auto(name, &family, &a);
- if (r < 0)
- return r;
-
- s = dns_server_find(l->dns_servers, family, &a, 0);
- if (s) {
- dns_server_move_back_and_unmark(s);
- return 0;
- }
-
- return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0);
-}
-
-static int link_update_dns_servers(Link *l) {
- _cleanup_strv_free_ char **nameservers = NULL;
- char **nameserver;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_dns(l->ifindex, &nameservers);
- if (r == -ENODATA) {
- r = 0;
- goto clear;
- }
- if (r < 0)
- goto clear;
-
- dns_server_mark_all(l->dns_servers);
-
- STRV_FOREACH(nameserver, nameservers) {
- r = link_update_dns_server_one(l, *nameserver);
- if (r < 0)
- goto clear;
- }
-
- dns_server_unlink_marked(l->dns_servers);
- return 0;
-
-clear:
- dns_server_unlink_all(l->dns_servers);
- return r;
-}
-
-static int link_update_llmnr_support(Link *l) {
- _cleanup_free_ char *b = NULL;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_llmnr(l->ifindex, &b);
- if (r == -ENODATA) {
- r = 0;
- goto clear;
- }
- if (r < 0)
- goto clear;
-
- l->llmnr_support = resolve_support_from_string(b);
- if (l->llmnr_support < 0) {
- r = -EINVAL;
- goto clear;
- }
-
- return 0;
-
-clear:
- l->llmnr_support = RESOLVE_SUPPORT_YES;
- return r;
-}
-
-static int link_update_mdns_support(Link *l) {
- _cleanup_free_ char *b = NULL;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_mdns(l->ifindex, &b);
- if (r == -ENODATA) {
- r = 0;
- goto clear;
- }
- if (r < 0)
- goto clear;
-
- l->mdns_support = resolve_support_from_string(b);
- if (l->mdns_support < 0) {
- r = -EINVAL;
- goto clear;
- }
-
- return 0;
-
-clear:
- l->mdns_support = RESOLVE_SUPPORT_NO;
- return r;
-}
-
-void link_set_dnssec_mode(Link *l, DnssecMode mode) {
-
- assert(l);
-
- if (l->dnssec_mode == mode)
- return;
-
- if ((l->dnssec_mode == _DNSSEC_MODE_INVALID) ||
- (l->dnssec_mode == DNSSEC_NO && mode != DNSSEC_NO) ||
- (l->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE && mode == DNSSEC_YES)) {
-
- /* When switching from non-DNSSEC mode to DNSSEC mode, flush the cache. Also when switching from the
- * allow-downgrade mode to full DNSSEC mode, flush it too. */
- if (l->unicast_scope)
- dns_cache_flush(&l->unicast_scope->cache);
- }
-
- l->dnssec_mode = mode;
-}
-
-static int link_update_dnssec_mode(Link *l) {
- _cleanup_free_ char *m = NULL;
- DnssecMode mode;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_dnssec(l->ifindex, &m);
- if (r == -ENODATA) {
- r = 0;
- goto clear;
- }
- if (r < 0)
- goto clear;
-
- mode = dnssec_mode_from_string(m);
- if (mode < 0) {
- r = -EINVAL;
- goto clear;
- }
-
- link_set_dnssec_mode(l, mode);
-
- return 0;
-
-clear:
- l->dnssec_mode = _DNSSEC_MODE_INVALID;
- return r;
-}
-
-static int link_update_dnssec_negative_trust_anchors(Link *l) {
- _cleanup_strv_free_ char **ntas = NULL;
- _cleanup_set_free_free_ Set *ns = NULL;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_dnssec_negative_trust_anchors(l->ifindex, &ntas);
- if (r == -ENODATA) {
- r = 0;
- goto clear;
- }
- if (r < 0)
- goto clear;
-
- ns = set_new(&dns_name_hash_ops);
- if (!ns)
- return -ENOMEM;
-
- r = set_put_strdupv(ns, ntas);
- if (r < 0)
- return r;
-
- set_free_free(l->dnssec_negative_trust_anchors);
- l->dnssec_negative_trust_anchors = ns;
- ns = NULL;
-
- return 0;
-
-clear:
- l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
- return r;
-}
-
-static int link_update_search_domain_one(Link *l, const char *name, bool route_only) {
- DnsSearchDomain *d;
- int r;
-
- assert(l);
- assert(name);
-
- r = dns_search_domain_find(l->search_domains, name, &d);
- if (r < 0)
- return r;
- if (r > 0)
- dns_search_domain_move_back_and_unmark(d);
- else {
- r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
- if (r < 0)
- return r;
- }
-
- d->route_only = route_only;
- return 0;
-}
-
-static int link_update_search_domains(Link *l) {
- _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL;
- char **i;
- int r, q;
-
- assert(l);
-
- r = sd_network_link_get_search_domains(l->ifindex, &sdomains);
- if (r < 0 && r != -ENODATA)
- goto clear;
-
- q = sd_network_link_get_route_domains(l->ifindex, &rdomains);
- if (q < 0 && q != -ENODATA) {
- r = q;
- goto clear;
- }
-
- if (r == -ENODATA && q == -ENODATA) {
- /* networkd knows nothing about this interface, and that's fine. */
- r = 0;
- goto clear;
- }
-
- dns_search_domain_mark_all(l->search_domains);
-
- STRV_FOREACH(i, sdomains) {
- r = link_update_search_domain_one(l, *i, false);
- if (r < 0)
- goto clear;
- }
-
- STRV_FOREACH(i, rdomains) {
- r = link_update_search_domain_one(l, *i, true);
- if (r < 0)
- goto clear;
- }
-
- dns_search_domain_unlink_marked(l->search_domains);
- return 0;
-
-clear:
- dns_search_domain_unlink_all(l->search_domains);
- return r;
-}
-
-static int link_is_managed(Link *l) {
- _cleanup_free_ char *state = NULL;
- int r;
-
- assert(l);
-
- r = sd_network_link_get_setup_state(l->ifindex, &state);
- if (r == -ENODATA)
- return 0;
- if (r < 0)
- return r;
-
- return !STR_IN_SET(state, "pending", "unmanaged");
-}
-
-static void link_read_settings(Link *l) {
- int r;
-
- assert(l);
-
- /* Read settings from networkd, except when networkd is not managing this interface. */
-
- r = link_is_managed(l);
- if (r < 0) {
- log_warning_errno(r, "Failed to determine whether interface %s is managed: %m", l->name);
- return;
- }
- if (r == 0) {
-
- /* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */
- if (l->is_managed)
- link_flush_settings(l);
-
- l->is_managed = false;
- return;
- }
-
- l->is_managed = true;
-
- r = link_update_dns_servers(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read DNS servers for interface %s, ignoring: %m", l->name);
-
- r = link_update_llmnr_support(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read LLMNR support for interface %s, ignoring: %m", l->name);
-
- r = link_update_mdns_support(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read mDNS support for interface %s, ignoring: %m", l->name);
-
- r = link_update_dnssec_mode(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read DNSSEC mode for interface %s, ignoring: %m", l->name);
-
- r = link_update_dnssec_negative_trust_anchors(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read DNSSEC negative trust anchors for interface %s, ignoring: %m", l->name);
-
- r = link_update_search_domains(l);
- if (r < 0)
- log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
-}
-
-int link_update(Link *l) {
- assert(l);
-
- link_read_settings(l);
- link_load_user(l);
- link_allocate_scopes(l);
- link_add_rrs(l, false);
-
- return 0;
-}
-
-bool link_relevant(Link *l, int family, bool local_multicast) {
- _cleanup_free_ char *state = NULL;
- LinkAddress *a;
-
- assert(l);
-
- /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link
- * beat, can do multicast and has at least one link-local (or better) IP address.
- *
- * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
- * least one routable address.*/
-
- if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
- return false;
-
- if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
- return false;
-
- if (local_multicast) {
- if (l->flags & IFF_POINTOPOINT)
- return false;
-
- if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
- return false;
- }
-
- /* Check kernel operstate
- * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
- if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
- return false;
-
- (void) sd_network_link_get_operational_state(l->ifindex, &state);
- if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
- return false;
-
- LIST_FOREACH(addresses, a, l->addresses)
- if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a, local_multicast))
- return true;
-
- return false;
-}
-
-LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) {
- LinkAddress *a;
-
- assert(l);
-
- LIST_FOREACH(addresses, a, l->addresses)
- if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr))
- return a;
-
- return NULL;
-}
-
-DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
- assert(l);
-
- if (l->current_dns_server == s)
- return s;
-
- if (s)
- log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name);
-
- dns_server_unref(l->current_dns_server);
- l->current_dns_server = dns_server_ref(s);
-
- if (l->unicast_scope)
- dns_cache_flush(&l->unicast_scope->cache);
-
- return s;
-}
-
-DnsServer *link_get_dns_server(Link *l) {
- assert(l);
-
- if (!l->current_dns_server)
- link_set_dns_server(l, l->dns_servers);
-
- return l->current_dns_server;
-}
-
-void link_next_dns_server(Link *l) {
- assert(l);
-
- if (!l->current_dns_server)
- return;
-
- /* Change to the next one, but make sure to follow the linked
- * list only if this server is actually still linked. */
- if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
- link_set_dns_server(l, l->current_dns_server->servers_next);
- return;
- }
-
- link_set_dns_server(l, l->dns_servers);
-}
-
-DnssecMode link_get_dnssec_mode(Link *l) {
- assert(l);
-
- if (l->dnssec_mode != _DNSSEC_MODE_INVALID)
- return l->dnssec_mode;
-
- return manager_get_dnssec_mode(l->manager);
-}
-
-bool link_dnssec_supported(Link *l) {
- DnsServer *server;
-
- assert(l);
-
- if (link_get_dnssec_mode(l) == DNSSEC_NO)
- return false;
-
- server = link_get_dns_server(l);
- if (server)
- return dns_server_dnssec_supported(server);
-
- return true;
-}
-
-int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
- LinkAddress *a;
-
- assert(l);
- assert(in_addr);
-
- a = new0(LinkAddress, 1);
- if (!a)
- return -ENOMEM;
-
- a->family = family;
- a->in_addr = *in_addr;
-
- a->link = l;
- LIST_PREPEND(addresses, l->addresses, a);
-
- if (ret)
- *ret = a;
-
- return 0;
-}
-
-LinkAddress *link_address_free(LinkAddress *a) {
- if (!a)
- return NULL;
-
- if (a->link) {
- LIST_REMOVE(addresses, a->link->addresses, a);
-
- if (a->llmnr_address_rr) {
- if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
- else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
- }
-
- if (a->llmnr_ptr_rr) {
- if (a->family == AF_INET && a->link->llmnr_ipv4_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
- else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
- }
- }
-
- dns_resource_record_unref(a->llmnr_address_rr);
- dns_resource_record_unref(a->llmnr_ptr_rr);
-
- return mfree(a);
-}
-
-void link_address_add_rrs(LinkAddress *a, bool force_remove) {
- int r;
-
- assert(a);
-
- if (a->family == AF_INET) {
-
- if (!force_remove &&
- link_address_relevant(a, true) &&
- a->link->llmnr_ipv4_scope &&
- a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
-
- if (!a->link->manager->llmnr_host_ipv4_key) {
- a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
- if (!a->link->manager->llmnr_host_ipv4_key) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- if (!a->llmnr_address_rr) {
- a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key);
- if (!a->llmnr_address_rr) {
- r = -ENOMEM;
- goto fail;
- }
-
- a->llmnr_address_rr->a.in_addr = a->in_addr.in;
- a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
- }
-
- if (!a->llmnr_ptr_rr) {
- r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
- if (r < 0)
- goto fail;
-
- a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
- }
-
- r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
- if (r < 0)
- log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
-
- r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
- if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
- } else {
- if (a->llmnr_address_rr) {
- if (a->link->llmnr_ipv4_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr);
- a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
- }
-
- if (a->llmnr_ptr_rr) {
- if (a->link->llmnr_ipv4_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr);
- a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
- }
- }
- }
-
- if (a->family == AF_INET6) {
-
- if (!force_remove &&
- link_address_relevant(a, true) &&
- a->link->llmnr_ipv6_scope &&
- a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
- a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
-
- if (!a->link->manager->llmnr_host_ipv6_key) {
- a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
- if (!a->link->manager->llmnr_host_ipv6_key) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- if (!a->llmnr_address_rr) {
- a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key);
- if (!a->llmnr_address_rr) {
- r = -ENOMEM;
- goto fail;
- }
-
- a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6;
- a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL;
- }
-
- if (!a->llmnr_ptr_rr) {
- r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname);
- if (r < 0)
- goto fail;
-
- a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL;
- }
-
- r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
- if (r < 0)
- log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
-
- r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
- if (r < 0)
- log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
- } else {
- if (a->llmnr_address_rr) {
- if (a->link->llmnr_ipv6_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr);
- a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr);
- }
-
- if (a->llmnr_ptr_rr) {
- if (a->link->llmnr_ipv6_scope)
- dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr);
- a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr);
- }
- }
- }
-
- return;
-
-fail:
- log_debug_errno(r, "Failed to update address RRs: %m");
-}
-
-int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
- int r;
- assert(a);
- assert(m);
-
- r = sd_rtnl_message_addr_get_flags(m, &a->flags);
- if (r < 0)
- return r;
-
- sd_rtnl_message_addr_get_scope(m, &a->scope);
-
- link_allocate_scopes(a->link);
- link_add_rrs(a->link, false);
-
- return 0;
-}
-
-bool link_address_relevant(LinkAddress *a, bool local_multicast) {
- assert(a);
-
- if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
- return false;
-
- if (a->scope >= (local_multicast ? RT_SCOPE_HOST : RT_SCOPE_LINK))
- return false;
-
- return true;
-}
-
-static bool link_needs_save(Link *l) {
- assert(l);
-
- /* Returns true if any of the settings where set different from the default */
-
- if (l->is_managed)
- return false;
-
- if (l->llmnr_support != RESOLVE_SUPPORT_YES ||
- l->mdns_support != RESOLVE_SUPPORT_NO ||
- l->dnssec_mode != _DNSSEC_MODE_INVALID)
- return true;
-
- if (l->dns_servers ||
- l->search_domains)
- return true;
-
- if (!set_isempty(l->dnssec_negative_trust_anchors))
- return true;
-
- return false;
-}
-
-int link_save_user(Link *l) {
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- const char *v;
- int r;
-
- assert(l);
- assert(l->state_file);
-
- if (!link_needs_save(l)) {
- (void) unlink(l->state_file);
- return 0;
- }
-
- r = mkdir_parents(l->state_file, 0700);
- if (r < 0)
- goto fail;
-
- r = fopen_temporary(l->state_file, &f, &temp_path);
- if (r < 0)
- goto fail;
-
- fputs("# This is private data. Do not parse.\n", f);
-
- v = resolve_support_to_string(l->llmnr_support);
- if (v)
- fprintf(f, "LLMNR=%s\n", v);
-
- v = resolve_support_to_string(l->mdns_support);
- if (v)
- fprintf(f, "MDNS=%s\n", v);
-
- v = dnssec_mode_to_string(l->dnssec_mode);
- if (v)
- fprintf(f, "DNSSEC=%s\n", v);
-
- if (l->dns_servers) {
- DnsServer *server;
-
- fputs("SERVERS=", f);
- LIST_FOREACH(servers, server, l->dns_servers) {
-
- if (server != l->dns_servers)
- fputc(' ', f);
-
- v = dns_server_string(server);
- if (!v) {
- r = -ENOMEM;
- goto fail;
- }
-
- fputs(v, f);
- }
- fputc('\n', f);
- }
-
- if (l->search_domains) {
- DnsSearchDomain *domain;
-
- fputs("DOMAINS=", f);
- LIST_FOREACH(domains, domain, l->search_domains) {
-
- if (domain != l->search_domains)
- fputc(' ', f);
-
- if (domain->route_only)
- fputc('~', f);
-
- fputs(DNS_SEARCH_DOMAIN_NAME(domain), f);
- }
- fputc('\n', f);
- }
-
- if (!set_isempty(l->dnssec_negative_trust_anchors)) {
- bool space = false;
- Iterator i;
- char *nta;
-
- fputs("NTAS=", f);
- SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) {
-
- if (space)
- fputc(' ', f);
-
- fputs(nta, f);
- space = true;
- }
- fputc('\n', f);
- }
-
- r = fflush_and_check(f);
- if (r < 0)
- goto fail;
-
- if (rename(temp_path, l->state_file) < 0) {
- r = -errno;
- goto fail;
- }
-
- return 0;
-
-fail:
- (void) unlink(l->state_file);
-
- if (temp_path)
- (void) unlink(temp_path);
-
- return log_error_errno(r, "Failed to save link data %s: %m", l->state_file);
-}
-
-int link_load_user(Link *l) {
- _cleanup_free_ char
- *llmnr = NULL,
- *mdns = NULL,
- *dnssec = NULL,
- *servers = NULL,
- *domains = NULL,
- *ntas = NULL;
-
- ResolveSupport s;
- int r;
-
- assert(l);
- assert(l->state_file);
-
- /* Try to load only a single time */
- if (l->loaded)
- return 0;
- l->loaded = true;
-
- if (l->is_managed)
- return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */
-
- r = parse_env_file(l->state_file, NEWLINE,
- "LLMNR", &llmnr,
- "MDNS", &mdns,
- "DNSSEC", &dnssec,
- "SERVERS", &servers,
- "DOMAINS", &domains,
- "NTAS", &ntas,
- NULL);
- if (r == -ENOENT)
- return 0;
- if (r < 0)
- goto fail;
-
- link_flush_settings(l);
-
- /* If we can't recognize the LLMNR or MDNS setting we don't override the default */
- s = resolve_support_from_string(llmnr);
- if (s >= 0)
- l->llmnr_support = s;
-
- s = resolve_support_from_string(mdns);
- if (s >= 0)
- l->mdns_support = s;
-
- /* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
- l->dnssec_mode = dnssec_mode_from_string(dnssec);
-
- if (servers) {
- const char *p = servers;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, 0);
- if (r < 0)
- goto fail;
- if (r == 0)
- break;
-
- r = link_update_dns_server_one(l, word);
- if (r < 0) {
- log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
- continue;
- }
- }
- }
-
- if (domains) {
- const char *p = domains;
-
- for (;;) {
- _cleanup_free_ char *word = NULL;
- const char *n;
- bool is_route;
-
- r = extract_first_word(&p, &word, NULL, 0);
- if (r < 0)
- goto fail;
- if (r == 0)
- break;
-
- is_route = word[0] == '~';
- n = is_route ? word + 1 : word;
-
- r = link_update_search_domain_one(l, n, is_route);
- if (r < 0) {
- log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
- continue;
- }
- }
- }
-
- if (ntas) {
- _cleanup_set_free_free_ Set *ns = NULL;
-
- ns = set_new(&dns_name_hash_ops);
- if (!ns) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = set_put_strsplit(ns, ntas, NULL, 0);
- if (r < 0)
- goto fail;
-
- l->dnssec_negative_trust_anchors = ns;
- ns = NULL;
- }
-
- return 0;
-
-fail:
- return log_error_errno(r, "Failed to load link data %s: %m", l->state_file);
-}
-
-void link_remove_user(Link *l) {
- assert(l);
- assert(l->state_file);
-
- (void) unlink(l->state_file);
-}
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
deleted file mode 100644
index c9b2a58c34..0000000000
--- a/src/resolve/resolved-link.h
+++ /dev/null
@@ -1,119 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/if.h>
-
-#include "in-addr-util.h"
-#include "ratelimit.h"
-#include "resolve-util.h"
-
-typedef struct Link Link;
-typedef struct LinkAddress LinkAddress;
-
-#include "resolved-dns-rr.h"
-#include "resolved-dns-scope.h"
-#include "resolved-dns-search-domain.h"
-#include "resolved-dns-server.h"
-#include "resolved-manager.h"
-
-#define LINK_SEARCH_DOMAINS_MAX 32
-#define LINK_DNS_SERVERS_MAX 32
-
-struct LinkAddress {
- Link *link;
-
- int family;
- union in_addr_union in_addr;
-
- unsigned char flags, scope;
-
- DnsResourceRecord *llmnr_address_rr;
- DnsResourceRecord *llmnr_ptr_rr;
-
- LIST_FIELDS(LinkAddress, addresses);
-};
-
-struct Link {
- Manager *manager;
-
- int ifindex;
- unsigned flags;
-
- LIST_HEAD(LinkAddress, addresses);
-
- LIST_HEAD(DnsServer, dns_servers);
- DnsServer *current_dns_server;
- unsigned n_dns_servers;
-
- LIST_HEAD(DnsSearchDomain, search_domains);
- unsigned n_search_domains;
-
- ResolveSupport llmnr_support;
- ResolveSupport mdns_support;
- DnssecMode dnssec_mode;
- Set *dnssec_negative_trust_anchors;
-
- DnsScope *unicast_scope;
- DnsScope *llmnr_ipv4_scope;
- DnsScope *llmnr_ipv6_scope;
- DnsScope *mdns_ipv4_scope;
- DnsScope *mdns_ipv6_scope;
-
- bool is_managed;
-
- char name[IF_NAMESIZE];
- uint32_t mtu;
- uint8_t operstate;
-
- bool loaded;
- char *state_file;
-};
-
-int link_new(Manager *m, Link **ret, int ifindex);
-Link *link_free(Link *l);
-int link_process_rtnl(Link *l, sd_netlink_message *m);
-int link_update(Link *l);
-bool link_relevant(Link *l, int family, bool local_multicast);
-LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
-void link_add_rrs(Link *l, bool force_remove);
-
-void link_flush_settings(Link *l);
-void link_set_dnssec_mode(Link *l, DnssecMode mode);
-void link_allocate_scopes(Link *l);
-
-DnsServer* link_set_dns_server(Link *l, DnsServer *s);
-DnsServer* link_get_dns_server(Link *l);
-void link_next_dns_server(Link *l);
-
-DnssecMode link_get_dnssec_mode(Link *l);
-bool link_dnssec_supported(Link *l);
-
-int link_save_user(Link *l);
-int link_load_user(Link *l);
-void link_remove_user(Link *l);
-
-int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
-LinkAddress *link_address_free(LinkAddress *a);
-int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
-bool link_address_relevant(LinkAddress *l, bool local_multicast);
-void link_address_add_rrs(LinkAddress *a, bool force_remove);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c
deleted file mode 100644
index 3516af58ee..0000000000
--- a/src/resolve/resolved-llmnr.c
+++ /dev/null
@@ -1,471 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <netinet/in.h>
-#include <resolv.h>
-
-#include "fd-util.h"
-#include "resolved-llmnr.h"
-#include "resolved-manager.h"
-
-void manager_llmnr_stop(Manager *m) {
- assert(m);
-
- m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
- m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
-
- m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
- m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
-
- m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
- m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
-
- m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
- m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
-}
-
-int manager_llmnr_start(Manager *m) {
- int r;
-
- assert(m);
-
- if (m->llmnr_support == RESOLVE_SUPPORT_NO)
- return 0;
-
- r = manager_llmnr_ipv4_udp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- r = manager_llmnr_ipv4_tcp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- if (socket_ipv6_is_supported()) {
- r = manager_llmnr_ipv6_udp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- r = manager_llmnr_ipv6_tcp_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
- }
-
- return 0;
-
-eaddrinuse:
- log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
- m->llmnr_support = RESOLVE_SUPPORT_NO;
- manager_llmnr_stop(m);
-
- return 0;
-}
-
-static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- DnsTransaction *t = NULL;
- Manager *m = userdata;
- DnsScope *scope;
- int r;
-
- assert(s);
- assert(fd >= 0);
- assert(m);
-
- r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
- if (r <= 0)
- return r;
-
- scope = manager_find_scope(m, p);
- if (!scope)
- log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
- else if (dns_packet_validate_reply(p) > 0) {
- log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p));
-
- dns_scope_check_conflicts(scope, p);
-
- t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
- if (t)
- dns_transaction_process_reply(t, p);
-
- } else if (dns_packet_validate_query(p) > 0) {
- log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p));
-
- dns_scope_process_query(scope, NULL, p);
- } else
- log_debug("Invalid LLMNR UDP packet, ignoring.");
-
- return 0;
-}
-
-int manager_llmnr_ipv4_udp_fd(Manager *m) {
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(LLMNR_PORT),
- };
- static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
- int r;
-
- assert(m);
-
- if (m->llmnr_ipv4_udp_fd >= 0)
- return m->llmnr_ipv4_udp_fd;
-
- m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv4_udp_fd < 0)
- return -errno;
-
- /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
-
- return m->llmnr_ipv4_udp_fd;
-
-fail:
- m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
- return r;
-}
-
-int manager_llmnr_ipv6_udp_fd(Manager *m) {
- union sockaddr_union sa = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_port = htobe16(LLMNR_PORT),
- };
- static const int one = 1, ttl = 255;
- int r;
-
- assert(m);
-
- if (m->llmnr_ipv6_udp_fd >= 0)
- return m->llmnr_ipv6_udp_fd;
-
- m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv6_udp_fd < 0)
- return -errno;
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
-
- return m->llmnr_ipv6_udp_fd;
-
-fail:
- m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
- return r;
-}
-
-static int on_llmnr_stream_packet(DnsStream *s) {
- DnsScope *scope;
-
- assert(s);
- assert(s->read_packet);
-
- scope = manager_find_scope(s->manager, s->read_packet);
- if (!scope)
- log_warning("Got LLMNR TCP packet on unknown scope. Ignoring.");
- else if (dns_packet_validate_query(s->read_packet) > 0) {
- log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
-
- dns_scope_process_query(scope, s, s->read_packet);
- } else
- log_debug("Invalid LLMNR TCP packet, ignoring.");
-
- dns_stream_unref(s);
- return 0;
-}
-
-static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- DnsStream *stream;
- Manager *m = userdata;
- int cfd, r;
-
- cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
- if (cfd < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- return -errno;
- }
-
- r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
- if (r < 0) {
- safe_close(cfd);
- return r;
- }
-
- stream->on_packet = on_llmnr_stream_packet;
- return 0;
-}
-
-int manager_llmnr_ipv4_tcp_fd(Manager *m) {
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(LLMNR_PORT),
- };
- static const int one = 1, pmtu = IP_PMTUDISC_DONT;
- int r;
-
- assert(m);
-
- if (m->llmnr_ipv4_tcp_fd >= 0)
- return m->llmnr_ipv4_tcp_fd;
-
- m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv4_tcp_fd < 0)
- return -errno;
-
- /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
-
- return m->llmnr_ipv4_tcp_fd;
-
-fail:
- m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
- return r;
-}
-
-int manager_llmnr_ipv6_tcp_fd(Manager *m) {
- union sockaddr_union sa = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_port = htobe16(LLMNR_PORT),
- };
- static const int one = 1;
- int r;
-
- assert(m);
-
- if (m->llmnr_ipv6_tcp_fd >= 0)
- return m->llmnr_ipv6_tcp_fd;
-
- m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->llmnr_ipv6_tcp_fd < 0)
- return -errno;
-
- /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
-
- return m->llmnr_ipv6_tcp_fd;
-
-fail:
- m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
- return r;
-}
diff --git a/src/resolve/resolved-llmnr.h b/src/resolve/resolved-llmnr.h
deleted file mode 100644
index 8133582fa7..0000000000
--- a/src/resolve/resolved-llmnr.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-
-#define LLMNR_PORT 5355
-
-int manager_llmnr_ipv4_udp_fd(Manager *m);
-int manager_llmnr_ipv6_udp_fd(Manager *m);
-int manager_llmnr_ipv4_tcp_fd(Manager *m);
-int manager_llmnr_ipv6_tcp_fd(Manager *m);
-
-void manager_llmnr_stop(Manager *m);
-int manager_llmnr_start(Manager *m);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
deleted file mode 100644
index 6630585d13..0000000000
--- a/src/resolve/resolved-manager.c
+++ /dev/null
@@ -1,1377 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <netinet/in.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "dirent-util.h"
-#include "dns-domain.h"
-#include "fd-util.h"
-#include "fileio-label.h"
-#include "hostname-util.h"
-#include "io-util.h"
-#include "netlink-util.h"
-#include "network-internal.h"
-#include "ordered-set.h"
-#include "parse-util.h"
-#include "random-util.h"
-#include "resolved-bus.h"
-#include "resolved-conf.h"
-#include "resolved-dns-stub.h"
-#include "resolved-etc-hosts.h"
-#include "resolved-llmnr.h"
-#include "resolved-manager.h"
-#include "resolved-mdns.h"
-#include "resolved-resolv-conf.h"
-#include "socket-util.h"
-#include "string-table.h"
-#include "string-util.h"
-#include "utf8.h"
-
-#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
-
-static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
- Manager *m = userdata;
- uint16_t type;
- Link *l;
- int ifindex, r;
-
- assert(rtnl);
- assert(m);
- assert(mm);
-
- r = sd_netlink_message_get_type(mm, &type);
- if (r < 0)
- goto fail;
-
- r = sd_rtnl_message_link_get_ifindex(mm, &ifindex);
- if (r < 0)
- goto fail;
-
- l = hashmap_get(m->links, INT_TO_PTR(ifindex));
-
- switch (type) {
-
- case RTM_NEWLINK:{
- bool is_new = !l;
-
- if (!l) {
- r = link_new(m, &l, ifindex);
- if (r < 0)
- goto fail;
- }
-
- r = link_process_rtnl(l, mm);
- if (r < 0)
- goto fail;
-
- r = link_update(l);
- if (r < 0)
- goto fail;
-
- if (is_new)
- log_debug("Found new link %i/%s", ifindex, l->name);
-
- break;
- }
-
- case RTM_DELLINK:
- if (l) {
- log_debug("Removing link %i/%s", l->ifindex, l->name);
- link_remove_user(l);
- link_free(l);
- }
-
- break;
- }
-
- return 0;
-
-fail:
- log_warning_errno(r, "Failed to process RTNL link message: %m");
- return 0;
-}
-
-static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
- Manager *m = userdata;
- union in_addr_union address;
- uint16_t type;
- int r, ifindex, family;
- LinkAddress *a;
- Link *l;
-
- assert(rtnl);
- assert(mm);
- assert(m);
-
- r = sd_netlink_message_get_type(mm, &type);
- if (r < 0)
- goto fail;
-
- r = sd_rtnl_message_addr_get_ifindex(mm, &ifindex);
- if (r < 0)
- goto fail;
-
- l = hashmap_get(m->links, INT_TO_PTR(ifindex));
- if (!l)
- return 0;
-
- r = sd_rtnl_message_addr_get_family(mm, &family);
- if (r < 0)
- goto fail;
-
- switch (family) {
-
- case AF_INET:
- r = sd_netlink_message_read_in_addr(mm, IFA_LOCAL, &address.in);
- if (r < 0) {
- r = sd_netlink_message_read_in_addr(mm, IFA_ADDRESS, &address.in);
- if (r < 0)
- goto fail;
- }
-
- break;
-
- case AF_INET6:
- r = sd_netlink_message_read_in6_addr(mm, IFA_LOCAL, &address.in6);
- if (r < 0) {
- r = sd_netlink_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6);
- if (r < 0)
- goto fail;
- }
-
- break;
-
- default:
- return 0;
- }
-
- a = link_find_address(l, family, &address);
-
- switch (type) {
-
- case RTM_NEWADDR:
-
- if (!a) {
- r = link_address_new(l, &a, family, &address);
- if (r < 0)
- return r;
- }
-
- r = link_address_update_rtnl(a, mm);
- if (r < 0)
- return r;
-
- break;
-
- case RTM_DELADDR:
- link_address_free(a);
- break;
- }
-
- return 0;
-
-fail:
- log_warning_errno(r, "Failed to process RTNL address message: %m");
- return 0;
-}
-
-static int manager_rtnl_listen(Manager *m) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
- sd_netlink_message *i;
- int r;
-
- assert(m);
-
- /* First, subscribe to interfaces coming and going */
- r = sd_netlink_open(&m->rtnl);
- if (r < 0)
- return r;
-
- r = sd_netlink_attach_event(m->rtnl, m->event, SD_EVENT_PRIORITY_IMPORTANT);
- if (r < 0)
- return r;
-
- r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m);
- if (r < 0)
- return r;
-
- r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m);
- if (r < 0)
- return r;
-
- r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m);
- if (r < 0)
- return r;
-
- r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m);
- if (r < 0)
- return r;
-
- /* Then, enumerate all links */
- r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_request_dump(req, true);
- if (r < 0)
- return r;
-
- r = sd_netlink_call(m->rtnl, req, 0, &reply);
- if (r < 0)
- return r;
-
- for (i = reply; i; i = sd_netlink_message_next(i)) {
- r = manager_process_link(m->rtnl, i, m);
- if (r < 0)
- return r;
- }
-
- req = sd_netlink_message_unref(req);
- reply = sd_netlink_message_unref(reply);
-
- /* Finally, enumerate all addresses, too */
- r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_request_dump(req, true);
- if (r < 0)
- return r;
-
- r = sd_netlink_call(m->rtnl, req, 0, &reply);
- if (r < 0)
- return r;
-
- for (i = reply; i; i = sd_netlink_message_next(i)) {
- r = manager_process_address(m->rtnl, i, m);
- if (r < 0)
- return r;
- }
-
- return r;
-}
-
-static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- Manager *m = userdata;
- Iterator i;
- Link *l;
- int r;
-
- assert(m);
-
- sd_network_monitor_flush(m->network_monitor);
-
- HASHMAP_FOREACH(l, m->links, i) {
- r = link_update(l);
- if (r < 0)
- log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex);
- }
-
- (void) manager_write_resolv_conf(m);
-
- return 0;
-}
-
-static int manager_network_monitor_listen(Manager *m) {
- int r, fd, events;
-
- assert(m);
-
- r = sd_network_monitor_new(&m->network_monitor, NULL);
- if (r < 0)
- return r;
-
- fd = sd_network_monitor_get_fd(m->network_monitor);
- if (fd < 0)
- return fd;
-
- events = sd_network_monitor_get_events(m->network_monitor);
- if (events < 0)
- return events;
-
- r = sd_event_add_io(m->event, &m->network_event_source, fd, events, &on_network_event, m);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(m->network_event_source, SD_EVENT_PRIORITY_IMPORTANT+5);
- if (r < 0)
- return r;
-
- (void) sd_event_source_set_description(m->network_event_source, "network-monitor");
-
- return 0;
-}
-
-static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
- _cleanup_free_ char *h = NULL, *n = NULL;
- char label[DNS_LABEL_MAX];
- const char *p;
- int r, k;
-
- assert(llmnr_hostname);
- assert(mdns_hostname);
-
- /* Extract and normalize the first label of the locally
- * configured hostname, and check it's not "localhost". */
-
- h = gethostname_malloc();
- if (!h)
- return log_oom();
-
- p = h;
- r = dns_label_unescape(&p, label, sizeof(label));
- if (r < 0)
- return log_error_errno(r, "Failed to unescape host name: %m");
- if (r == 0) {
- log_error("Couldn't find a single label in hosntame.");
- return -EINVAL;
- }
-
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- return log_error_errno(k, "Failed to undo IDNA: %m");
- if (k > 0)
- r = k;
-
- if (!utf8_is_valid(label)) {
- log_error("System hostname is not UTF-8 clean.");
- return -EINVAL;
- }
-
- r = dns_label_escape_new(label, r, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to escape host name: %m");
-
- if (is_localhost(n)) {
- log_debug("System hostname is 'localhost', ignoring.");
- return -EINVAL;
- }
-
- r = dns_name_concat(n, "local", mdns_hostname);
- if (r < 0)
- return log_error_errno(r, "Failed to determine mDNS hostname: %m");
-
- *llmnr_hostname = n;
- n = NULL;
-
- return 0;
-}
-
-static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL;
- Manager *m = userdata;
- int r;
-
- assert(m);
-
- r = determine_hostname(&llmnr_hostname, &mdns_hostname);
- if (r < 0)
- return 0; /* ignore invalid hostnames */
-
- if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname))
- return 0;
-
- log_info("System hostname changed to '%s'.", llmnr_hostname);
-
- free(m->llmnr_hostname);
- free(m->mdns_hostname);
-
- m->llmnr_hostname = llmnr_hostname;
- m->mdns_hostname = mdns_hostname;
-
- llmnr_hostname = mdns_hostname = NULL;
-
- manager_refresh_rrs(m);
-
- return 0;
-}
-
-static int manager_watch_hostname(Manager *m) {
- int r;
-
- assert(m);
-
- m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY);
- if (m->hostname_fd < 0) {
- log_warning_errno(errno, "Failed to watch hostname: %m");
- return 0;
- }
-
- r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m);
- if (r < 0) {
- if (r == -EPERM)
- /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */
- m->hostname_fd = safe_close(m->hostname_fd);
- else
- return log_error_errno(r, "Failed to add hostname event source: %m");
- }
-
- (void) sd_event_source_set_description(m->hostname_event_source, "hostname");
-
- r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
- if (r < 0) {
- log_info("Defaulting to hostname 'gnu-linux'.");
- m->llmnr_hostname = strdup("gnu-linux");
- if (!m->llmnr_hostname)
- return log_oom();
-
- m->mdns_hostname = strdup("gnu-linux.local");
- if (!m->mdns_hostname)
- return log_oom();
- } else
- log_info("Using system hostname '%s'.", m->llmnr_hostname);
-
- return 0;
-}
-
-static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- _cleanup_free_ char *buffer = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- Manager *m = userdata;
- size_t size = 0;
- DnsScope *scope;
-
- assert(s);
- assert(si);
- assert(m);
-
- f = open_memstream(&buffer, &size);
- if (!f)
- return log_oom();
-
- LIST_FOREACH(scopes, scope, m->dns_scopes)
- dns_scope_dump(scope, f);
-
- if (fflush_and_check(f) < 0)
- return log_oom();
-
- log_dump(LOG_INFO, buffer);
- return 0;
-}
-
-static int manager_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *m = userdata;
-
- assert(s);
- assert(si);
- assert(m);
-
- manager_flush_caches(m);
-
- return 0;
-}
-
-int manager_new(Manager **ret) {
- _cleanup_(manager_freep) Manager *m = NULL;
- int r;
-
- assert(ret);
-
- m = new0(Manager, 1);
- if (!m)
- return -ENOMEM;
-
- m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
- m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
- m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
- m->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1;
- m->hostname_fd = -1;
-
- m->llmnr_support = RESOLVE_SUPPORT_YES;
- m->mdns_support = RESOLVE_SUPPORT_NO;
- m->dnssec_mode = DEFAULT_DNSSEC_MODE;
- m->enable_cache = true;
- m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP;
- m->read_resolv_conf = true;
- m->need_builtin_fallbacks = true;
- m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
-
- r = dns_trust_anchor_load(&m->trust_anchor);
- if (r < 0)
- return r;
-
- r = manager_parse_config_file(m);
- if (r < 0)
- return r;
-
- r = sd_event_default(&m->event);
- if (r < 0)
- return r;
-
- sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
-
- sd_event_set_watchdog(m->event, true);
-
- r = manager_watch_hostname(m);
- if (r < 0)
- return r;
-
- r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
- if (r < 0)
- return r;
-
- r = manager_network_monitor_listen(m);
- if (r < 0)
- return r;
-
- r = manager_rtnl_listen(m);
- if (r < 0)
- return r;
-
- r = manager_connect_bus(m);
- if (r < 0)
- return r;
-
- (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
- (void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m);
-
- manager_cleanup_saved_user(m);
-
- *ret = m;
- m = NULL;
-
- return 0;
-}
-
-int manager_start(Manager *m) {
- int r;
-
- assert(m);
-
- r = manager_dns_stub_start(m);
- if (r < 0)
- return r;
-
- r = manager_llmnr_start(m);
- if (r < 0)
- return r;
-
- r = manager_mdns_start(m);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-Manager *manager_free(Manager *m) {
- Link *l;
-
- if (!m)
- return NULL;
-
- dns_server_unlink_all(m->dns_servers);
- dns_server_unlink_all(m->fallback_dns_servers);
- dns_search_domain_unlink_all(m->search_domains);
-
- while ((l = hashmap_first(m->links)))
- link_free(l);
-
- while (m->dns_queries)
- dns_query_free(m->dns_queries);
-
- dns_scope_free(m->unicast_scope);
-
- /* At this point only orphaned streams should remain. All others should have been freed already by their
- * owners */
- while (m->dns_streams)
- dns_stream_unref(m->dns_streams);
-
- hashmap_free(m->links);
- hashmap_free(m->dns_transactions);
-
- sd_event_source_unref(m->network_event_source);
- sd_network_monitor_unref(m->network_monitor);
-
- sd_netlink_unref(m->rtnl);
- sd_event_source_unref(m->rtnl_event_source);
-
- manager_llmnr_stop(m);
- manager_mdns_stop(m);
- manager_dns_stub_stop(m);
-
- sd_bus_slot_unref(m->prepare_for_sleep_slot);
- sd_event_source_unref(m->bus_retry_event_source);
- sd_bus_unref(m->bus);
-
- sd_event_source_unref(m->sigusr1_event_source);
- sd_event_source_unref(m->sigusr2_event_source);
-
- sd_event_unref(m->event);
-
- dns_resource_key_unref(m->llmnr_host_ipv4_key);
- dns_resource_key_unref(m->llmnr_host_ipv6_key);
-
- sd_event_source_unref(m->hostname_event_source);
- safe_close(m->hostname_fd);
- free(m->llmnr_hostname);
- free(m->mdns_hostname);
-
- dns_trust_anchor_flush(&m->trust_anchor);
- manager_etc_hosts_flush(m);
-
- return mfree(m);
-}
-
-int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- union {
- struct cmsghdr header; /* For alignment */
- uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
- + CMSG_SPACE(int) /* ttl/hoplimit */
- + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
- } control;
- union sockaddr_union sa;
- struct msghdr mh = {};
- struct cmsghdr *cmsg;
- struct iovec iov;
- ssize_t ms, l;
- int r;
-
- assert(m);
- assert(fd >= 0);
- assert(ret);
-
- ms = next_datagram_size_fd(fd);
- if (ms < 0)
- return ms;
-
- r = dns_packet_new(&p, protocol, ms);
- if (r < 0)
- return r;
-
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->allocated;
-
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa);
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_control = &control;
- mh.msg_controllen = sizeof(control);
-
- l = recvmsg(fd, &mh, 0);
- if (l == 0)
- return 0;
- if (l < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- return -errno;
- }
-
- assert(!(mh.msg_flags & MSG_CTRUNC));
- assert(!(mh.msg_flags & MSG_TRUNC));
-
- p->size = (size_t) l;
-
- p->family = sa.sa.sa_family;
- p->ipproto = IPPROTO_UDP;
- if (p->family == AF_INET) {
- p->sender.in = sa.in.sin_addr;
- p->sender_port = be16toh(sa.in.sin_port);
- } else if (p->family == AF_INET6) {
- p->sender.in6 = sa.in6.sin6_addr;
- p->sender_port = be16toh(sa.in6.sin6_port);
- p->ifindex = sa.in6.sin6_scope_id;
- } else
- return -EAFNOSUPPORT;
-
- CMSG_FOREACH(cmsg, &mh) {
-
- if (cmsg->cmsg_level == IPPROTO_IPV6) {
- assert(p->family == AF_INET6);
-
- switch (cmsg->cmsg_type) {
-
- case IPV6_PKTINFO: {
- struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
-
- if (p->ifindex <= 0)
- p->ifindex = i->ipi6_ifindex;
-
- p->destination.in6 = i->ipi6_addr;
- break;
- }
-
- case IPV6_HOPLIMIT:
- p->ttl = *(int *) CMSG_DATA(cmsg);
- break;
-
- }
- } else if (cmsg->cmsg_level == IPPROTO_IP) {
- assert(p->family == AF_INET);
-
- switch (cmsg->cmsg_type) {
-
- case IP_PKTINFO: {
- struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
-
- if (p->ifindex <= 0)
- p->ifindex = i->ipi_ifindex;
-
- p->destination.in = i->ipi_addr;
- break;
- }
-
- case IP_TTL:
- p->ttl = *(int *) CMSG_DATA(cmsg);
- break;
- }
- }
- }
-
- /* The Linux kernel sets the interface index to the loopback
- * device if the packet came from the local host since it
- * avoids the routing table in such a case. Let's unset the
- * interface index in such a case. */
- if (p->ifindex == LOOPBACK_IFINDEX)
- p->ifindex = 0;
-
- if (protocol != DNS_PROTOCOL_DNS) {
- /* If we don't know the interface index still, we look for the
- * first local interface with a matching address. Yuck! */
- if (p->ifindex <= 0)
- p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
- }
-
- *ret = p;
- p = NULL;
-
- return 1;
-}
-
-static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
- int r;
-
- assert(fd >= 0);
- assert(mh);
-
- for (;;) {
- if (sendmsg(fd, mh, flags) >= 0)
- return 0;
-
- if (errno == EINTR)
- continue;
-
- if (errno != EAGAIN)
- return -errno;
-
- r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
- if (r < 0)
- return r;
- if (r == 0)
- return -ETIMEDOUT;
- }
-}
-
-static int write_loop(int fd, void *message, size_t length) {
- int r;
-
- assert(fd >= 0);
- assert(message);
-
- for (;;) {
- if (write(fd, message, length) >= 0)
- return 0;
-
- if (errno == EINTR)
- continue;
-
- if (errno != EAGAIN)
- return -errno;
-
- r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
- if (r < 0)
- return r;
- if (r == 0)
- return -ETIMEDOUT;
- }
-}
-
-int manager_write(Manager *m, int fd, DnsPacket *p) {
- int r;
-
- log_debug("Sending %s packet with id %" PRIu16 ".", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
-
- r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int manager_ipv4_send(
- Manager *m,
- int fd,
- int ifindex,
- const struct in_addr *destination,
- uint16_t port,
- const struct in_addr *source,
- DnsPacket *p) {
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- };
- union {
- struct cmsghdr header; /* For alignment */
- uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
- } control;
- struct msghdr mh = {};
- struct iovec iov;
-
- assert(m);
- assert(fd >= 0);
- assert(destination);
- assert(port > 0);
- assert(p);
-
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->size;
-
- sa.in.sin_addr = *destination;
- sa.in.sin_port = htobe16(port),
-
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa.in);
-
- if (ifindex > 0) {
- struct cmsghdr *cmsg;
- struct in_pktinfo *pi;
-
- zero(control);
-
- mh.msg_control = &control;
- mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_len = mh.msg_controllen;
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
-
- pi = (struct in_pktinfo*) CMSG_DATA(cmsg);
- pi->ipi_ifindex = ifindex;
-
- if (source)
- pi->ipi_spec_dst = *source;
- }
-
- return sendmsg_loop(fd, &mh, 0);
-}
-
-static int manager_ipv6_send(
- Manager *m,
- int fd,
- int ifindex,
- const struct in6_addr *destination,
- uint16_t port,
- const struct in6_addr *source,
- DnsPacket *p) {
-
- union sockaddr_union sa = {
- .in6.sin6_family = AF_INET6,
- };
- union {
- struct cmsghdr header; /* For alignment */
- uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
- } control;
- struct msghdr mh = {};
- struct iovec iov;
-
- assert(m);
- assert(fd >= 0);
- assert(destination);
- assert(port > 0);
- assert(p);
-
- iov.iov_base = DNS_PACKET_DATA(p);
- iov.iov_len = p->size;
-
- sa.in6.sin6_addr = *destination;
- sa.in6.sin6_port = htobe16(port),
- sa.in6.sin6_scope_id = ifindex;
-
- mh.msg_iov = &iov;
- mh.msg_iovlen = 1;
- mh.msg_name = &sa.sa;
- mh.msg_namelen = sizeof(sa.in6);
-
- if (ifindex > 0) {
- struct cmsghdr *cmsg;
- struct in6_pktinfo *pi;
-
- zero(control);
-
- mh.msg_control = &control;
- mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_len = mh.msg_controllen;
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
-
- pi = (struct in6_pktinfo*) CMSG_DATA(cmsg);
- pi->ipi6_ifindex = ifindex;
-
- if (source)
- pi->ipi6_addr = *source;
- }
-
- return sendmsg_loop(fd, &mh, 0);
-}
-
-int manager_send(
- Manager *m,
- int fd,
- int ifindex,
- int family,
- const union in_addr_union *destination,
- uint16_t port,
- const union in_addr_union *source,
- DnsPacket *p) {
-
- assert(m);
- assert(fd >= 0);
- assert(destination);
- assert(port > 0);
- assert(p);
-
- log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
-
- if (family == AF_INET)
- return manager_ipv4_send(m, fd, ifindex, &destination->in, port, &source->in, p);
- if (family == AF_INET6)
- return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p);
-
- return -EAFNOSUPPORT;
-}
-
-uint32_t manager_find_mtu(Manager *m) {
- uint32_t mtu = 0;
- Link *l;
- Iterator i;
-
- /* If we don't know on which link a DNS packet would be
- * delivered, let's find the largest MTU that works on all
- * interfaces we know of */
-
- HASHMAP_FOREACH(l, m->links, i) {
- if (l->mtu <= 0)
- continue;
-
- if (mtu <= 0 || l->mtu < mtu)
- mtu = l->mtu;
- }
-
- return mtu;
-}
-
-int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) {
- LinkAddress *a;
-
- assert(m);
-
- a = manager_find_link_address(m, family, in_addr);
- if (a)
- return a->link->ifindex;
-
- return 0;
-}
-
-void manager_refresh_rrs(Manager *m) {
- Iterator i;
- Link *l;
-
- assert(m);
-
- m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key);
- m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key);
-
- HASHMAP_FOREACH(l, m->links, i) {
- link_add_rrs(l, true);
- link_add_rrs(l, false);
- }
-}
-
-int manager_next_hostname(Manager *m) {
- const char *p;
- uint64_t u, a;
- char *h, *k;
- int r;
-
- assert(m);
-
- p = strchr(m->llmnr_hostname, 0);
- assert(p);
-
- while (p > m->llmnr_hostname) {
- if (!strchr("0123456789", p[-1]))
- break;
-
- p--;
- }
-
- if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0)
- u = 1;
-
- /* Add a random number to the old value. This way we can avoid
- * that two hosts pick the same hostname, win on IPv4 and lose
- * on IPv6 (or vice versa), and pick the same hostname
- * replacement hostname, ad infinitum. We still want the
- * numbers to go up monotonically, hence we just add a random
- * value 1..10 */
-
- random_bytes(&a, sizeof(a));
- u += 1 + a % 10;
-
- if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0)
- return -ENOMEM;
-
- r = dns_name_concat(h, "local", &k);
- if (r < 0) {
- free(h);
- return r;
- }
-
- log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h);
-
- free(m->llmnr_hostname);
- m->llmnr_hostname = h;
-
- free(m->mdns_hostname);
- m->mdns_hostname = k;
-
- manager_refresh_rrs(m);
-
- return 0;
-}
-
-LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr) {
- Iterator i;
- Link *l;
-
- assert(m);
-
- HASHMAP_FOREACH(l, m->links, i) {
- LinkAddress *a;
-
- a = link_find_address(l, family, in_addr);
- if (a)
- return a;
- }
-
- return NULL;
-}
-
-bool manager_our_packet(Manager *m, DnsPacket *p) {
- assert(m);
- assert(p);
-
- return !!manager_find_link_address(m, p->family, &p->sender);
-}
-
-DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
- Link *l;
-
- assert(m);
- assert(p);
-
- l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
- if (!l)
- return NULL;
-
- switch (p->protocol) {
- case DNS_PROTOCOL_LLMNR:
- if (p->family == AF_INET)
- return l->llmnr_ipv4_scope;
- else if (p->family == AF_INET6)
- return l->llmnr_ipv6_scope;
-
- break;
-
- case DNS_PROTOCOL_MDNS:
- if (p->family == AF_INET)
- return l->mdns_ipv4_scope;
- else if (p->family == AF_INET6)
- return l->mdns_ipv6_scope;
-
- break;
-
- default:
- break;
- }
-
- return NULL;
-}
-
-void manager_verify_all(Manager *m) {
- DnsScope *s;
-
- assert(m);
-
- LIST_FOREACH(scopes, s, m->dns_scopes)
- dns_zone_verify_all(&s->zone);
-}
-
-int manager_is_own_hostname(Manager *m, const char *name) {
- int r;
-
- assert(m);
- assert(name);
-
- if (m->llmnr_hostname) {
- r = dns_name_equal(name, m->llmnr_hostname);
- if (r != 0)
- return r;
- }
-
- if (m->mdns_hostname)
- return dns_name_equal(name, m->mdns_hostname);
-
- return 0;
-}
-
-int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
- DnsServer *s;
- Iterator i;
- Link *l;
- int r;
-
- assert(m);
- assert(dns);
-
- r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops);
- if (r < 0)
- return r;
-
- /* First add the system-wide servers and domains */
- LIST_FOREACH(servers, s, m->dns_servers) {
- r = ordered_set_put(*dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- /* Then, add the per-link servers */
- HASHMAP_FOREACH(l, m->links, i) {
- LIST_FOREACH(servers, s, l->dns_servers) {
- r = ordered_set_put(*dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- /* If we found nothing, add the fallback servers */
- if (ordered_set_isempty(*dns)) {
- LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = ordered_set_put(*dns, s);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- return 0;
-}
-
-/* filter_route is a tri-state:
- * < 0: no filtering
- * = 0 or false: return only domains which should be used for searching
- * > 0 or true: return only domains which are for routing only
- */
-int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) {
- DnsSearchDomain *d;
- Iterator i;
- Link *l;
- int r;
-
- assert(m);
- assert(domains);
-
- r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops);
- if (r < 0)
- return r;
-
- LIST_FOREACH(domains, d, m->search_domains) {
-
- if (filter_route >= 0 &&
- d->route_only != !!filter_route)
- continue;
-
- r = ordered_set_put(*domains, d->name);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
-
- HASHMAP_FOREACH(l, m->links, i) {
-
- LIST_FOREACH(domains, d, l->search_domains) {
-
- if (filter_route >= 0 &&
- d->route_only != !!filter_route)
- continue;
-
- r = ordered_set_put(*domains, d->name);
- if (r == -EEXIST)
- continue;
- if (r < 0)
- return r;
- }
- }
-
- return 0;
-}
-
-DnssecMode manager_get_dnssec_mode(Manager *m) {
- assert(m);
-
- if (m->dnssec_mode != _DNSSEC_MODE_INVALID)
- return m->dnssec_mode;
-
- return DNSSEC_NO;
-}
-
-bool manager_dnssec_supported(Manager *m) {
- DnsServer *server;
- Iterator i;
- Link *l;
-
- assert(m);
-
- if (manager_get_dnssec_mode(m) == DNSSEC_NO)
- return false;
-
- server = manager_get_dns_server(m);
- if (server && !dns_server_dnssec_supported(server))
- return false;
-
- HASHMAP_FOREACH(l, m->links, i)
- if (!link_dnssec_supported(l))
- return false;
-
- return true;
-}
-
-void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
-
- assert(verdict >= 0);
- assert(verdict < _DNSSEC_VERDICT_MAX);
-
- if (log_get_max_level() >= LOG_DEBUG) {
- char s[DNS_RESOURCE_KEY_STRING_MAX];
-
- log_debug("Found verdict for lookup %s: %s",
- dns_resource_key_to_string(key, s, sizeof s),
- dnssec_verdict_to_string(verdict));
- }
-
- m->n_dnssec_verdict[verdict]++;
-}
-
-bool manager_routable(Manager *m, int family) {
- Iterator i;
- Link *l;
-
- assert(m);
-
- /* Returns true if the host has at least one interface with a routable address of the specified type */
-
- HASHMAP_FOREACH(l, m->links, i)
- if (link_relevant(l, family, false))
- return true;
-
- return false;
-}
-
-void manager_flush_caches(Manager *m) {
- DnsScope *scope;
-
- assert(m);
-
- LIST_FOREACH(scopes, scope, m->dns_scopes)
- dns_cache_flush(&scope->cache);
-
- log_info("Flushed all caches.");
-}
-
-void manager_cleanup_saved_user(Manager *m) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r;
-
- assert(m);
-
- /* Clean up all saved per-link files in /run/systemd/resolve/netif/ that don't have a matching interface
- * anymore. These files are created to persist settings pushed in by the user via the bus, so that resolved can
- * be restarted without losing this data. */
-
- d = opendir("/run/systemd/resolve/netif/");
- if (!d) {
- if (errno == ENOENT)
- return;
-
- log_warning_errno(errno, "Failed to open interface directory: %m");
- return;
- }
-
- FOREACH_DIRENT_ALL(de, d, log_error_errno(errno, "Failed to read interface directory: %m")) {
- _cleanup_free_ char *p = NULL;
- int ifindex;
- Link *l;
-
- if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG))
- continue;
-
- if (STR_IN_SET(de->d_name, ".", ".."))
- continue;
-
- r = parse_ifindex(de->d_name, &ifindex);
- if (r < 0) /* Probably some temporary file from a previous run. Delete it */
- goto rm;
-
- l = hashmap_get(m->links, INT_TO_PTR(ifindex));
- if (!l) /* link vanished */
- goto rm;
-
- if (l->is_managed) /* now managed by networkd, hence the bus settings are useless */
- goto rm;
-
- continue;
-
- rm:
- p = strappend("/run/systemd/resolve/netif/", de->d_name);
- if (!p) {
- log_oom();
- return;
- }
-
- (void) unlink(p);
- }
-}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
deleted file mode 100644
index 6b2208ed94..0000000000
--- a/src/resolve/resolved-manager.h
+++ /dev/null
@@ -1,185 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-event.h"
-#include "sd-netlink.h"
-#include "sd-network.h"
-
-#include "hashmap.h"
-#include "list.h"
-#include "ordered-set.h"
-#include "resolve-util.h"
-
-typedef struct Manager Manager;
-
-#include "resolved-conf.h"
-#include "resolved-dns-query.h"
-#include "resolved-dns-search-domain.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-stream.h"
-#include "resolved-dns-trust-anchor.h"
-#include "resolved-link.h"
-
-#define MANAGER_SEARCH_DOMAINS_MAX 32
-#define MANAGER_DNS_SERVERS_MAX 32
-
-struct Manager {
- sd_event *event;
-
- ResolveSupport llmnr_support;
- ResolveSupport mdns_support;
- DnssecMode dnssec_mode;
- bool enable_cache;
- DnsStubListenerMode dns_stub_listener_mode;
-
- /* Network */
- Hashmap *links;
-
- sd_netlink *rtnl;
- sd_event_source *rtnl_event_source;
-
- sd_network_monitor *network_monitor;
- sd_event_source *network_event_source;
-
- /* DNS query management */
- Hashmap *dns_transactions;
- LIST_HEAD(DnsQuery, dns_queries);
- unsigned n_dns_queries;
-
- LIST_HEAD(DnsStream, dns_streams);
- unsigned n_dns_streams;
-
- /* Unicast dns */
- LIST_HEAD(DnsServer, dns_servers);
- LIST_HEAD(DnsServer, fallback_dns_servers);
- unsigned n_dns_servers; /* counts both main and fallback */
- DnsServer *current_dns_server;
-
- LIST_HEAD(DnsSearchDomain, search_domains);
- unsigned n_search_domains;
-
- bool need_builtin_fallbacks:1;
-
- bool read_resolv_conf:1;
- usec_t resolv_conf_mtime;
-
- DnsTrustAnchor trust_anchor;
-
- LIST_HEAD(DnsScope, dns_scopes);
- DnsScope *unicast_scope;
-
- /* LLMNR */
- int llmnr_ipv4_udp_fd;
- int llmnr_ipv6_udp_fd;
- int llmnr_ipv4_tcp_fd;
- int llmnr_ipv6_tcp_fd;
-
- sd_event_source *llmnr_ipv4_udp_event_source;
- sd_event_source *llmnr_ipv6_udp_event_source;
- sd_event_source *llmnr_ipv4_tcp_event_source;
- sd_event_source *llmnr_ipv6_tcp_event_source;
-
- /* mDNS */
- int mdns_ipv4_fd;
- int mdns_ipv6_fd;
-
- sd_event_source *mdns_ipv4_event_source;
- sd_event_source *mdns_ipv6_event_source;
-
- /* dbus */
- sd_bus *bus;
- sd_event_source *bus_retry_event_source;
-
- /* The hostname we publish on LLMNR and mDNS */
- char *llmnr_hostname;
- char *mdns_hostname;
- DnsResourceKey *llmnr_host_ipv4_key;
- DnsResourceKey *llmnr_host_ipv6_key;
-
- /* Watch the system hostname */
- int hostname_fd;
- sd_event_source *hostname_event_source;
-
- /* Watch for system suspends */
- sd_bus_slot *prepare_for_sleep_slot;
-
- sd_event_source *sigusr1_event_source;
- sd_event_source *sigusr2_event_source;
-
- unsigned n_transactions_total;
- unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
-
- /* Data from /etc/hosts */
- Set* etc_hosts_by_address;
- Hashmap* etc_hosts_by_name;
- usec_t etc_hosts_last, etc_hosts_mtime;
-
- /* Local DNS stub on 127.0.0.53:53 */
- int dns_stub_udp_fd;
- int dns_stub_tcp_fd;
-
- sd_event_source *dns_stub_udp_event_source;
- sd_event_source *dns_stub_tcp_event_source;
-};
-
-/* Manager */
-
-int manager_new(Manager **ret);
-Manager* manager_free(Manager *m);
-
-int manager_start(Manager *m);
-
-uint32_t manager_find_mtu(Manager *m);
-
-int manager_write(Manager *m, int fd, DnsPacket *p);
-int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
-int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
-
-int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
-LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr);
-
-void manager_refresh_rrs(Manager *m);
-int manager_next_hostname(Manager *m);
-
-bool manager_our_packet(Manager *m, DnsPacket *p);
-DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
-
-void manager_verify_all(Manager *m);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
-
-#define EXTRA_CMSG_SPACE 1024
-
-int manager_is_own_hostname(Manager *m, const char *name);
-
-int manager_compile_dns_servers(Manager *m, OrderedSet **servers);
-int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route);
-
-DnssecMode manager_get_dnssec_mode(Manager *m);
-bool manager_dnssec_supported(Manager *m);
-
-void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
-
-bool manager_routable(Manager *m, int family);
-
-void manager_flush_caches(Manager *m);
-
-void manager_cleanup_saved_user(Manager *m);
diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c
deleted file mode 100644
index b13b1d0144..0000000000
--- a/src/resolve/resolved-mdns.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2015 Daniel Mack
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <resolv.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include "fd-util.h"
-#include "resolved-manager.h"
-#include "resolved-mdns.h"
-
-void manager_mdns_stop(Manager *m) {
- assert(m);
-
- m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source);
- m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
-
- m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source);
- m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
-}
-
-int manager_mdns_start(Manager *m) {
- int r;
-
- assert(m);
-
- if (m->mdns_support == RESOLVE_SUPPORT_NO)
- return 0;
-
- r = manager_mdns_ipv4_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
-
- if (socket_ipv6_is_supported()) {
- r = manager_mdns_ipv6_fd(m);
- if (r == -EADDRINUSE)
- goto eaddrinuse;
- if (r < 0)
- return r;
- }
-
- return 0;
-
-eaddrinuse:
- log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
- m->mdns_support = RESOLVE_SUPPORT_NO;
- manager_mdns_stop(m);
-
- return 0;
-}
-
-static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- Manager *m = userdata;
- DnsScope *scope;
- int r;
-
- r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
- if (r <= 0)
- return r;
-
- scope = manager_find_scope(m, p);
- if (!scope) {
- log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
- return 0;
- }
-
- if (dns_packet_validate_reply(p) > 0) {
- DnsResourceRecord *rr;
-
- log_debug("Got mDNS reply packet");
-
- /*
- * mDNS is different from regular DNS and LLMNR with regard to handling responses.
- * While on other protocols, we can ignore every answer that doesn't match a question
- * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
- * incoming information, regardless of the DNS packet ID.
- *
- * Hence, extract the packet here, and try to find a transaction for answer the we got
- * and complete it. Also store the new information in scope's cache.
- */
- r = dns_packet_extract(p);
- if (r < 0) {
- log_debug("mDNS packet extraction failed.");
- return 0;
- }
-
- dns_scope_check_conflicts(scope, p);
-
- DNS_ANSWER_FOREACH(rr, p->answer) {
- const char *name = dns_resource_key_name(rr->key);
- DnsTransaction *t;
-
- /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
- * we assume someone's playing tricks on us and discard the packet completely. */
- if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
- dns_name_endswith(name, "local") > 0))
- return 0;
-
- t = dns_scope_find_transaction(scope, rr->key, false);
- if (t)
- dns_transaction_process_reply(t, p);
- }
-
- dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
-
- } else if (dns_packet_validate_query(p) > 0) {
- log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
-
- dns_scope_process_query(scope, NULL, p);
- } else
- log_debug("Invalid mDNS UDP packet.");
-
- return 0;
-}
-
-int manager_mdns_ipv4_fd(Manager *m) {
- union sockaddr_union sa = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(MDNS_PORT),
- };
- static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
- int r;
-
- assert(m);
-
- if (m->mdns_ipv4_fd >= 0)
- return m->mdns_ipv4_fd;
-
- m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->mdns_ipv4_fd < 0)
- return -errno;
-
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Disable Don't-Fragment bit in the IP header */
- r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
- if (r < 0)
- goto fail;
-
- return m->mdns_ipv4_fd;
-
-fail:
- m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
- return r;
-}
-
-int manager_mdns_ipv6_fd(Manager *m) {
- union sockaddr_union sa = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_port = htobe16(MDNS_PORT),
- };
- static const int one = 1, ttl = 255;
- int r;
-
- assert(m);
-
- if (m->mdns_ipv6_fd >= 0)
- return m->mdns_ipv6_fd;
-
- m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (m->mdns_ipv6_fd < 0)
- return -errno;
-
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
- if (r < 0) {
- r = -errno;
- goto fail;
- }
-
- r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
- if (r < 0)
- goto fail;
-
- return m->mdns_ipv6_fd;
-
-fail:
- m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
- return r;
-}
diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h
deleted file mode 100644
index 5d274648f4..0000000000
--- a/src/resolve/resolved-mdns.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2015 Daniel Mack
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-
-#define MDNS_PORT 5353
-
-int manager_mdns_ipv4_fd(Manager *m);
-int manager_mdns_ipv6_fd(Manager *m);
-
-void manager_mdns_stop(Manager *m);
-int manager_mdns_start(Manager *m);
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
deleted file mode 100644
index 801014caf5..0000000000
--- a/src/resolve/resolved-resolv-conf.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
- ***/
-
-#include <resolv.h>
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "fd-util.h"
-#include "fileio-label.h"
-#include "fileio.h"
-#include "ordered-set.h"
-#include "resolved-conf.h"
-#include "resolved-resolv-conf.h"
-#include "string-util.h"
-#include "strv.h"
-
-int manager_read_resolv_conf(Manager *m) {
- _cleanup_fclose_ FILE *f = NULL;
- struct stat st, own;
- char line[LINE_MAX];
- usec_t t;
- int r;
-
- assert(m);
-
- /* Reads the system /etc/resolv.conf, if it exists and is not
- * symlinked to our own resolv.conf instance */
-
- if (!m->read_resolv_conf)
- return 0;
-
- r = stat("/etc/resolv.conf", &st);
- if (r < 0) {
- if (errno == ENOENT)
- return 0;
-
- r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
- goto clear;
- }
-
- /* Have we already seen the file? */
- t = timespec_load(&st.st_mtim);
- if (t == m->resolv_conf_mtime)
- return 0;
-
- /* Is it symlinked to our own file? */
- if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
- st.st_dev == own.st_dev &&
- st.st_ino == own.st_ino)
- return 0;
-
- f = fopen("/etc/resolv.conf", "re");
- if (!f) {
- if (errno == ENOENT)
- return 0;
-
- r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
- goto clear;
- }
-
- if (fstat(fileno(f), &st) < 0) {
- r = log_error_errno(errno, "Failed to stat open file: %m");
- goto clear;
- }
-
- dns_server_mark_all(m->dns_servers);
- dns_search_domain_mark_all(m->search_domains);
-
- FOREACH_LINE(line, f, r = -errno; goto clear) {
- const char *a;
- char *l;
-
- l = strstrip(line);
- if (*l == '#' || *l == ';')
- continue;
-
- a = first_word(l, "nameserver");
- if (a) {
- r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a);
- if (r < 0)
- log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
-
- continue;
- }
-
- a = first_word(l, "domain");
- if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
- a = first_word(l, "search");
- if (a) {
- r = manager_parse_search_domains_and_warn(m, a);
- if (r < 0)
- log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
- }
- }
-
- m->resolv_conf_mtime = t;
-
- /* Flush out all servers and search domains that are still
- * marked. Those are then ones that didn't appear in the new
- * /etc/resolv.conf */
- dns_server_unlink_marked(m->dns_servers);
- dns_search_domain_unlink_marked(m->search_domains);
-
- /* Whenever /etc/resolv.conf changes, start using the first
- * DNS server of it. This is useful to deal with broken
- * network managing implementations (like NetworkManager),
- * that when connecting to a VPN place both the VPN DNS
- * servers and the local ones in /etc/resolv.conf. Without
- * resetting the DNS server to use back to the first entry we
- * will continue to use the local one thus being unable to
- * resolve VPN domains. */
- manager_set_dns_server(m, m->dns_servers);
-
- /* Unconditionally flush the cache when /etc/resolv.conf is
- * modified, even if the data it contained was completely
- * identical to the previous version we used. We do this
- * because altering /etc/resolv.conf is typically done when
- * the network configuration changes, and that should be
- * enough to flush the global unicast DNS cache. */
- if (m->unicast_scope)
- dns_cache_flush(&m->unicast_scope->cache);
-
- return 0;
-
-clear:
- dns_server_unlink_all(m->dns_servers);
- dns_search_domain_unlink_all(m->search_domains);
- return r;
-}
-
-static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
- assert(s);
- assert(f);
- assert(count);
-
- if (!dns_server_string(s)) {
- log_warning("Our of memory, or invalid DNS address. Ignoring server.");
- return;
- }
-
- /* Check if the DNS server is limited to particular domains;
- * resolv.conf does not have a syntax to express that, so it must not
- * appear as a global name server to avoid routing unrelated domains to
- * it (which is a privacy violation, will most probably fail anyway,
- * and adds unnecessary load) */
- if (dns_server_limited_domains(s)) {
- log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s));
- return;
- }
-
- if (*count == MAXNS)
- fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
- (*count)++;
-
- fprintf(f, "nameserver %s\n", dns_server_string(s));
-}
-
-static void write_resolv_conf_search(
- OrderedSet *domains,
- FILE *f) {
- unsigned length = 0, count = 0;
- Iterator i;
- char *domain;
-
- assert(domains);
- assert(f);
-
- fputs("search", f);
-
- ORDERED_SET_FOREACH(domain, domains, i) {
- if (++count > MAXDNSRCH) {
- fputs("\n# Too many search domains configured, remaining ones ignored.", f);
- break;
- }
- length += strlen(domain) + 1;
- if (length > 256) {
- fputs("\n# Total length of all search domains is too long, remaining ones ignored.", f);
- break;
- }
- fputc(' ', f);
- fputs(domain, f);
- }
-
- fputs("\n", f);
-}
-
-static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
- Iterator i;
-
- fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
- "# This is a dynamic resolv.conf file for connecting local clients directly to\n"
- "# all known DNS servers.\n#\n"
- "# Third party programs must not access this file directly, but only through the\n"
- "# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n"
- "# replace this symlink by a static file or a different symlink.\n#\n"
- "# See systemd-resolved.service(8) for details about the supported modes of\n"
- "# operation for /etc/resolv.conf.\n\n", f);
-
- if (ordered_set_isempty(dns))
- fputs("# No DNS servers known.\n", f);
- else {
- unsigned count = 0;
- DnsServer *s;
-
- ORDERED_SET_FOREACH(s, dns, i)
- write_resolv_conf_server(s, f, &count);
- }
-
- if (!ordered_set_isempty(domains))
- write_resolv_conf_search(domains, f);
-
- return fflush_and_check(f);
-}
-
-int manager_write_resolv_conf(Manager *m) {
-
- _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
- _cleanup_free_ char *temp_path = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- assert(m);
-
- /* Read the system /etc/resolv.conf first */
- (void) manager_read_resolv_conf(m);
-
- /* Add the full list to a set, to filter out duplicates */
- r = manager_compile_dns_servers(m, &dns);
- if (r < 0)
- return log_warning_errno(r, "Failed to compile list of DNS servers: %m");
-
- r = manager_compile_search_domains(m, &domains, false);
- if (r < 0)
- return log_warning_errno(r, "Failed to compile list of search domains: %m");
-
- r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
- if (r < 0)
- return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m");
-
- (void) fchmod(fileno(f), 0644);
-
- r = write_resolv_conf_contents(f, dns, domains);
- if (r < 0) {
- log_error_errno(r, "Failed to write private resolv.conf contents: %m");
- goto fail;
- }
-
- if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
- r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m");
- goto fail;
- }
-
- return 0;
-
-fail:
- (void) unlink(PRIVATE_RESOLV_CONF);
- (void) unlink(temp_path);
-
- return r;
-}
diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h
deleted file mode 100644
index 75fa080e4c..0000000000
--- a/src/resolve/resolved-resolv-conf.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "resolved-manager.h"
-
-#define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
-
-int manager_read_resolv_conf(Manager *m);
-int manager_write_resolv_conf(Manager *m);
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
deleted file mode 100644
index deb75f9ae5..0000000000
--- a/src/resolve/resolved.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Tom Gundersen <teg@jklm.no>
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sd-daemon.h"
-#include "sd-event.h"
-
-#include "capability-util.h"
-#include "mkdir.h"
-#include "resolved-conf.h"
-#include "resolved-manager.h"
-#include "resolved-resolv-conf.h"
-#include "selinux-util.h"
-#include "signal-util.h"
-#include "user-util.h"
-
-int main(int argc, char *argv[]) {
- _cleanup_(manager_freep) Manager *m = NULL;
- const char *user = "systemd-resolve";
- uid_t uid;
- gid_t gid;
- int r;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- if (argc != 1) {
- log_error("This program takes no arguments.");
- r = -EINVAL;
- goto finish;
- }
-
- umask(0022);
-
- r = mac_selinux_init();
- if (r < 0) {
- log_error_errno(r, "SELinux setup failed: %m");
- goto finish;
- }
-
- r = get_user_creds(&user, &uid, &gid, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Cannot resolve user name %s: %m", user);
- goto finish;
- }
-
- /* Always create the directory where resolv.conf will live */
- r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid);
- if (r < 0) {
- log_error_errno(r, "Could not create runtime directory: %m");
- goto finish;
- }
-
- /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */
- r = drop_privileges(uid, gid,
- (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */
- (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */
- (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */);
- if (r < 0)
- goto finish;
-
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, -1) >= 0);
-
- r = manager_new(&m);
- if (r < 0) {
- log_error_errno(r, "Could not create manager: %m");
- goto finish;
- }
-
- r = manager_start(m);
- if (r < 0) {
- log_error_errno(r, "Failed to start manager: %m");
- goto finish;
- }
-
- /* Write finish default resolv.conf to avoid a dangling symlink */
- (void) manager_write_resolv_conf(m);
-
- /* Let's drop the remaining caps now */
- r = capability_bounding_set_drop(0, true);
- if (r < 0) {
- log_error_errno(r, "Failed to drop remaining caps: %m");
- goto finish;
- }
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- r = sd_event_loop(m->event);
- if (r < 0) {
- log_error_errno(r, "Event loop failed: %m");
- goto finish;
- }
-
- sd_event_get_exit_code(m->event, &r);
-
-finish:
- sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Shutting down...");
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
deleted file mode 100644
index 60afa151e3..0000000000
--- a/src/resolve/resolved.conf.in
+++ /dev/null
@@ -1,21 +0,0 @@
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-#
-# Entries in this file show the compile time defaults.
-# You can change settings by editing this file.
-# Defaults can be restored by simply deleting this file.
-#
-# See resolved.conf(5) for details
-
-[Resolve]
-#DNS=
-#FallbackDNS=@DNS_SERVERS@
-#Domains=
-#LLMNR=yes
-#DNSSEC=@DEFAULT_DNSSEC_MODE@
-#Cache=yes
-#DNSStubListener=udp
diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
deleted file mode 100644
index a383c6286d..0000000000
--- a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
deleted file mode 100644
index 15de02e997..0000000000
--- a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts
deleted file mode 100644
index 1c3ecc5491..0000000000
--- a/src/resolve/test-data/fake-caa.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts
deleted file mode 100644
index 17874844d9..0000000000
--- a/src/resolve/test-data/fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts
deleted file mode 100644
index 5ef51e0c8e..0000000000
--- a/src/resolve/test-data/gandi.net.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts
deleted file mode 100644
index f98c4cd855..0000000000
--- a/src/resolve/test-data/google.com.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts
deleted file mode 100644
index e28a725c9a..0000000000
--- a/src/resolve/test-data/kyhwana.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts
deleted file mode 100644
index 54ba668c75..0000000000
--- a/src/resolve/test-data/root.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
deleted file mode 100644
index a854249532..0000000000
--- a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts
deleted file mode 100644
index 11deb39677..0000000000
--- a/src/resolve/test-data/teamits.com.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
deleted file mode 100644
index f0a6f982df..0000000000
--- a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
+++ /dev/null
Binary files differ
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
deleted file mode 100644
index 956b155872..0000000000
--- a/src/resolve/test-dns-packet.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/***
- This file is part of systemd
-
- Copyright 2016 Zbigniew Jędrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/if.h>
-#include <glob.h>
-
-#include "alloc-util.h"
-#include "fileio.h"
-#include "glob-util.h"
-#include "log.h"
-#include "macro.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-rr.h"
-#include "string-util.h"
-#include "strv.h"
-#include "unaligned.h"
-
-#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1)
-
-static void verify_rr_copy(DnsResourceRecord *rr) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
- const char *a, *b;
-
- assert_se(copy = dns_resource_record_copy(rr));
- assert_se(dns_resource_record_equal(copy, rr) > 0);
-
- assert_se(a = dns_resource_record_to_string(rr));
- assert_se(b = dns_resource_record_to_string(copy));
-
- assert_se(streq(a, b));
-}
-
-static uint64_t hash(DnsResourceRecord *rr) {
- struct siphash state;
-
- siphash24_init(&state, HASH_KEY.bytes);
- dns_resource_record_hash_func(rr, &state);
- return siphash24_finalize(&state);
-}
-
-static void test_packet_from_file(const char* filename, bool canonical) {
- _cleanup_free_ char *data = NULL;
- size_t data_size, packet_size, offset;
-
- assert_se(read_full_file(filename, &data, &data_size) >= 0);
- assert_se(data);
- assert_se(data_size > 8);
-
- log_info("============== %s %s==============", filename, canonical ? "canonical " : "");
-
- for (offset = 0; offset < data_size; offset += 8 + packet_size) {
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL;
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL;
- const char *s, *s2;
- uint64_t hash1, hash2;
-
- packet_size = unaligned_read_le64(data + offset);
- assert_se(packet_size > 0);
- assert_se(offset + 8 + packet_size <= data_size);
-
- assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0);
-
- assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0);
- assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0);
-
- verify_rr_copy(rr);
-
- s = dns_resource_record_to_string(rr);
- assert_se(s);
- puts(s);
-
- hash1 = hash(rr);
-
- assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0);
-
- assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0);
- assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0);
- assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0);
-
- verify_rr_copy(rr);
-
- s2 = dns_resource_record_to_string(rr);
- assert_se(s2);
- assert_se(streq(s, s2));
-
- hash2 = hash(rr);
- assert_se(hash1 == hash2);
- }
-}
-
-int main(int argc, char **argv) {
- int i, N;
- _cleanup_globfree_ glob_t g = {};
- char **fnames;
-
- log_parse_environment();
-
- if (argc >= 2) {
- N = argc - 1;
- fnames = argv + 1;
- } else {
- assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0);
- N = g.gl_pathc;
- fnames = g.gl_pathv;
- }
-
- for (i = 0; i < N; i++) {
- test_packet_from_file(fnames[i], false);
- puts("");
- test_packet_from_file(fnames[i], true);
- if (i + 1 < N)
- puts("");
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
deleted file mode 100644
index 58c089eb40..0000000000
--- a/src/resolve/test-dnssec-complex.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2016 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <netinet/ip.h>
-
-#include "sd-bus.h"
-
-#include "af-list.h"
-#include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "dns-type.h"
-#include "random-util.h"
-#include "string-util.h"
-#include "time-util.h"
-
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
-
-static void prefix_random(const char *name, char **ret) {
- uint64_t i, u;
- char *m = NULL;
-
- u = 1 + (random_u64() & 3);
-
- for (i = 0; i < u; i++) {
- _cleanup_free_ char *b = NULL;
- char *x;
-
- assert_se(asprintf(&b, "x%" PRIu64 "x", random_u64()));
- x = strjoin(b, ".", name, NULL);
- assert_se(x);
-
- free(m);
- m = x;
- }
-
- *ret = m;
- }
-
-static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const char *result) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *m = NULL;
- int r;
-
- /* If the name starts with a dot, we prefix one to three random labels */
- if (startswith(name, ".")) {
- prefix_random(name + 1, &m);
- name = m;
- }
-
- assert_se(sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveRecord") >= 0);
-
- assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
-
- if (r < 0) {
- assert_se(result);
- assert_se(sd_bus_error_has_name(&error, result));
- log_info("[OK] %s/%s resulted in <%s>.", name, dns_type_to_string(type), error.name);
- } else {
- assert_se(!result);
- log_info("[OK] %s/%s succeeded.", name, dns_type_to_string(type));
- }
-}
-
-static void test_hostname_lookup(sd_bus *bus, const char *name, int family, const char *result) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *m = NULL;
- const char *af;
- int r;
-
- af = family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family);
-
- /* If the name starts with a dot, we prefix one to three random labels */
- if (startswith(name, ".")) {
- prefix_random(name + 1, &m);
- name = m;
- }
-
- assert_se(sd_bus_message_new_method_call(
- bus,
- &req,
- "org.freedesktop.resolve1",
- "/org/freedesktop/resolve1",
- "org.freedesktop.resolve1.Manager",
- "ResolveHostname") >= 0);
-
- assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0);
-
- r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
-
- if (r < 0) {
- assert_se(result);
- assert_se(sd_bus_error_has_name(&error, result));
- log_info("[OK] %s/%s resulted in <%s>.", name, af, error.name);
- } else {
- assert_se(!result);
- log_info("[OK] %s/%s succeeded.", name, af);
- }
-
-}
-
-int main(int argc, char* argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-
- /* Note that this is a manual test as it requires:
- *
- * Full network access
- * A DNSSEC capable DNS server
- * That zones contacted are still set up as they were when I wrote this.
- */
-
- assert_se(sd_bus_open_system(&bus) >= 0);
-
- /* Normally signed */
- test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, "www.eurid.eu", AF_UNSPEC, NULL);
-
- test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, "sigok.verteiltesysteme.net", AF_UNSPEC, NULL);
-
- /* Normally signed, NODATA */
- test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
- test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
-
- /* Invalid signature */
- test_rr_lookup(bus, "sigfail.verteiltesysteme.net", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
- test_hostname_lookup(bus, "sigfail.verteiltesysteme.net", AF_INET, BUS_ERROR_DNSSEC_FAILED);
-
- /* Invalid signature, RSA, wildcard */
- test_rr_lookup(bus, ".wilda.rhybar.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
- test_hostname_lookup(bus, ".wilda.rhybar.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
-
- /* Invalid signature, ECDSA, wildcard */
- test_rr_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
- test_hostname_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
-
- /* NXDOMAIN in NSEC domain */
- test_rr_lookup(bus, "hhh.nasa.gov", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "hhh.nasa.gov", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
-
- /* wildcard, NSEC zone */
- test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, ".wilda.nsec.0skar.cz", AF_INET, NULL);
-
- /* wildcard, NSEC zone, NODATA */
- test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
-
- /* wildcard, NSEC3 zone */
- test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, ".wilda.0skar.cz", AF_INET, NULL);
-
- /* wildcard, NSEC3 zone, NODATA */
- test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
-
- /* wildcard, NSEC zone, CNAME */
- test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_UNSPEC, NULL);
- test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_INET, NULL);
-
- /* wildcard, NSEC zone, NODATA, CNAME */
- test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
-
- /* wildcard, NSEC3 zone, CNAME */
- test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_A, NULL);
- test_hostname_lookup(bus, ".wild.0skar.cz", AF_UNSPEC, NULL);
- test_hostname_lookup(bus, ".wild.0skar.cz", AF_INET, NULL);
-
- /* wildcard, NSEC3 zone, NODATA, CNAME */
- test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
-
- /* NODATA due to empty non-terminal in NSEC domain */
- test_rr_lookup(bus, "herndon.nasa.gov", DNS_TYPE_A, BUS_ERROR_NO_SUCH_RR);
- test_hostname_lookup(bus, "herndon.nasa.gov", AF_UNSPEC, BUS_ERROR_NO_SUCH_RR);
- test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET, BUS_ERROR_NO_SUCH_RR);
- test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET6, BUS_ERROR_NO_SUCH_RR);
-
- /* NXDOMAIN in NSEC root zone: */
- test_rr_lookup(bus, "jasdhjas.kjkfgjhfjg", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
-
- /* NXDOMAIN in NSEC3 .com zone: */
- test_rr_lookup(bus, "kjkfgjhfjgsdfdsfd.com", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
-
- /* Unsigned A */
- test_rr_lookup(bus, "poettering.de", DNS_TYPE_A, NULL);
- test_rr_lookup(bus, "poettering.de", DNS_TYPE_AAAA, NULL);
- test_hostname_lookup(bus, "poettering.de", AF_UNSPEC, NULL);
- test_hostname_lookup(bus, "poettering.de", AF_INET, NULL);
- test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL);
-
-#ifdef HAVE_LIBIDN
- /* Unsigned A with IDNA conversion necessary */
- test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL);
- test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL);
- test_hostname_lookup(bus, "pöttering.de", AF_INET6, NULL);
-#endif
-
- /* DNAME, pointing to NXDOMAIN */
- test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
- test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_RP, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
- test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
-
- return 0;
-}
diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c
deleted file mode 100644
index b3018e8239..0000000000
--- a/src/resolve/test-dnssec.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-
-#include "alloc-util.h"
-#include "resolved-dns-dnssec.h"
-#include "resolved-dns-rr.h"
-#include "string-util.h"
-#include "hexdecoct.h"
-
-static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) {
- char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX];
-
- assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r);
- if (r < 0)
- return;
-
- assert_se(streq(canonicalized, canonical));
-}
-
-static void test_dnssec_canonicalize(void) {
- test_dnssec_canonicalize_one("", ".", 1);
- test_dnssec_canonicalize_one(".", ".", 1);
- test_dnssec_canonicalize_one("foo", "foo.", 4);
- test_dnssec_canonicalize_one("foo.", "foo.", 4);
- test_dnssec_canonicalize_one("FOO.", "foo.", 4);
- test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8);
- test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL);
-}
-
-#ifdef HAVE_GCRYPT
-
-static void test_dnssec_verify_dns_key(void) {
-
- static const uint8_t ds1_fprint[] = {
- 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D,
- 0x80, 0x67, 0x14, 0x01,
- };
- static const uint8_t ds2_fprint[] = {
- 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE,
- 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98,
- };
- static const uint8_t dnskey_blob[] = {
- 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e,
- 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc,
- 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48,
- 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49,
- 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde,
- 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe,
- 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf,
- 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45,
- 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77,
- 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39,
- 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d,
- 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68,
- 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39,
- 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4,
- 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba,
- 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73,
- 0xe7, 0xea, 0x77, 0x03,
- };
-
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL;
-
- /* The two DS RRs in effect for nasa.gov on 2015-12-01. */
- ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov");
- assert_se(ds1);
-
- ds1->ds.key_tag = 47857;
- ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- ds1->ds.digest_type = DNSSEC_DIGEST_SHA1;
- ds1->ds.digest_size = sizeof(ds1_fprint);
- ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size);
- assert_se(ds1->ds.digest);
-
- log_info("DS1: %s", strna(dns_resource_record_to_string(ds1)));
-
- ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV");
- assert_se(ds2);
-
- ds2->ds.key_tag = 47857;
- ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- ds2->ds.digest_type = DNSSEC_DIGEST_SHA256;
- ds2->ds.digest_size = sizeof(ds2_fprint);
- ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size);
- assert_se(ds2->ds.digest);
-
- log_info("DS2: %s", strna(dns_resource_record_to_string(ds2)));
-
- dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV");
- assert_se(dnskey);
-
- dnskey->dnskey.flags = 257;
- dnskey->dnskey.protocol = 3;
- dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- dnskey->dnskey.key_size = sizeof(dnskey_blob);
- dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
- assert_se(dnskey->dnskey.key);
-
- log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
- log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
-
- assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0);
- assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0);
-}
-
-static void test_dnssec_verify_rrset(void) {
-
- static const uint8_t signature_blob[] = {
- 0x7f, 0x79, 0xdd, 0x5e, 0x89, 0x79, 0x18, 0xd0, 0x34, 0x86, 0x8c, 0x72, 0x77, 0x75, 0x48, 0x4d,
- 0xc3, 0x7d, 0x38, 0x04, 0xab, 0xcd, 0x9e, 0x4c, 0x82, 0xb0, 0x92, 0xca, 0xe9, 0x66, 0xe9, 0x6e,
- 0x47, 0xc7, 0x68, 0x8c, 0x94, 0xf6, 0x69, 0xcb, 0x75, 0x94, 0xe6, 0x30, 0xa6, 0xfb, 0x68, 0x64,
- 0x96, 0x1a, 0x84, 0xe1, 0xdc, 0x16, 0x4c, 0x83, 0x6c, 0x44, 0xf2, 0x74, 0x4d, 0x74, 0x79, 0x8f,
- 0xf3, 0xf4, 0x63, 0x0d, 0xef, 0x5a, 0xe7, 0xe2, 0xfd, 0xf2, 0x2b, 0x38, 0x7c, 0x28, 0x96, 0x9d,
- 0xb6, 0xcd, 0x5c, 0x3b, 0x57, 0xe2, 0x24, 0x78, 0x65, 0xd0, 0x9e, 0x77, 0x83, 0x09, 0x6c, 0xff,
- 0x3d, 0x52, 0x3f, 0x6e, 0xd1, 0xed, 0x2e, 0xf9, 0xee, 0x8e, 0xa6, 0xbe, 0x9a, 0xa8, 0x87, 0x76,
- 0xd8, 0x77, 0xcc, 0x96, 0xa0, 0x98, 0xa1, 0xd1, 0x68, 0x09, 0x43, 0xcf, 0x56, 0xd9, 0xd1, 0x66,
- };
-
- static const uint8_t dnskey_blob[] = {
- 0x03, 0x01, 0x00, 0x01, 0x9b, 0x49, 0x9b, 0xc1, 0xf9, 0x9a, 0xe0, 0x4e, 0xcf, 0xcb, 0x14, 0x45,
- 0x2e, 0xc9, 0xf9, 0x74, 0xa7, 0x18, 0xb5, 0xf3, 0xde, 0x39, 0x49, 0xdf, 0x63, 0x33, 0x97, 0x52,
- 0xe0, 0x8e, 0xac, 0x50, 0x30, 0x8e, 0x09, 0xd5, 0x24, 0x3d, 0x26, 0xa4, 0x49, 0x37, 0x2b, 0xb0,
- 0x6b, 0x1b, 0xdf, 0xde, 0x85, 0x83, 0xcb, 0x22, 0x4e, 0x60, 0x0a, 0x91, 0x1a, 0x1f, 0xc5, 0x40,
- 0xb1, 0xc3, 0x15, 0xc1, 0x54, 0x77, 0x86, 0x65, 0x53, 0xec, 0x10, 0x90, 0x0c, 0x91, 0x00, 0x5e,
- 0x15, 0xdc, 0x08, 0x02, 0x4c, 0x8c, 0x0d, 0xc0, 0xac, 0x6e, 0xc4, 0x3e, 0x1b, 0x80, 0x19, 0xe4,
- 0xf7, 0x5f, 0x77, 0x51, 0x06, 0x87, 0x61, 0xde, 0xa2, 0x18, 0x0f, 0x40, 0x8b, 0x79, 0x72, 0xfa,
- 0x8d, 0x1a, 0x44, 0x47, 0x0d, 0x8e, 0x3a, 0x2d, 0xc7, 0x39, 0xbf, 0x56, 0x28, 0x97, 0xd9, 0x20,
- 0x4f, 0x00, 0x51, 0x3b,
- };
-
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *a = NULL, *rrsig = NULL, *dnskey = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnssecResult result;
-
- a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "nAsA.gov");
- assert_se(a);
-
- a->a.in_addr.s_addr = inet_addr("52.0.14.116");
-
- log_info("A: %s", strna(dns_resource_record_to_string(a)));
-
- rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
- assert_se(rrsig);
-
- rrsig->rrsig.type_covered = DNS_TYPE_A;
- rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- rrsig->rrsig.labels = 2;
- rrsig->rrsig.original_ttl = 600;
- rrsig->rrsig.expiration = 0x5683135c;
- rrsig->rrsig.inception = 0x565b7da8;
- rrsig->rrsig.key_tag = 63876;
- rrsig->rrsig.signer = strdup("Nasa.Gov.");
- assert_se(rrsig->rrsig.signer);
- rrsig->rrsig.signature_size = sizeof(signature_blob);
- rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
- assert_se(rrsig->rrsig.signature);
-
- log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
-
- dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
- assert_se(dnskey);
-
- dnskey->dnskey.flags = 256;
- dnskey->dnskey.protocol = 3;
- dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- dnskey->dnskey.key_size = sizeof(dnskey_blob);
- dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
- assert_se(dnskey->dnskey.key);
-
- log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
- log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
-
- assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0);
- assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
-
- answer = dns_answer_new(1);
- assert_se(answer);
- assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
-
- /* Validate the RR as it if was 2015-12-2 today */
- assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, 1449092754*USEC_PER_SEC, &result) >= 0);
- assert_se(result == DNSSEC_VALIDATED);
-}
-
-static void test_dnssec_verify_rrset2(void) {
-
- static const uint8_t signature_blob[] = {
- 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11,
- 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b,
- 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca,
- 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2,
- 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda,
- 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27,
- 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50,
- 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22,
- };
-
- static const uint8_t dnskey_blob[] = {
- 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea,
- 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3,
- 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07,
- 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5,
- 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7,
- 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56,
- 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e,
- 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49,
- 0x74, 0x62, 0xfe, 0xd7,
- };
-
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnssecResult result;
-
- nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
- assert_se(nsec);
-
- nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov");
- assert_se(nsec->nsec.next_domain_name);
-
- nsec->nsec.types = bitmap_new();
- assert_se(nsec->nsec.types);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0);
- assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0);
-
- log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec)));
-
- rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
- assert_se(rrsig);
-
- rrsig->rrsig.type_covered = DNS_TYPE_NSEC;
- rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- rrsig->rrsig.labels = 2;
- rrsig->rrsig.original_ttl = 300;
- rrsig->rrsig.expiration = 0x5689002f;
- rrsig->rrsig.inception = 0x56617230;
- rrsig->rrsig.key_tag = 30390;
- rrsig->rrsig.signer = strdup("Nasa.Gov.");
- assert_se(rrsig->rrsig.signer);
- rrsig->rrsig.signature_size = sizeof(signature_blob);
- rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
- assert_se(rrsig->rrsig.signature);
-
- log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
-
- dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
- assert_se(dnskey);
-
- dnskey->dnskey.flags = 256;
- dnskey->dnskey.protocol = 3;
- dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
- dnskey->dnskey.key_size = sizeof(dnskey_blob);
- dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
- assert_se(dnskey->dnskey.key);
-
- log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
- log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
-
- assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0);
- assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
-
- answer = dns_answer_new(1);
- assert_se(answer);
- assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
-
- /* Validate the RR as it if was 2015-12-11 today */
- assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0);
- assert_se(result == DNSSEC_VALIDATED);
-}
-
-static void test_dnssec_nsec3_hash(void) {
- static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE };
- static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 };
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- uint8_t h[DNSSEC_HASH_SIZE_MAX];
- _cleanup_free_ char *b = NULL;
- int k;
-
- /* The NSEC3 RR for eurid.eu on 2015-12-14. */
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC3, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM.eurid.eu.");
- assert_se(rr);
-
- rr->nsec3.algorithm = DNSSEC_DIGEST_SHA1;
- rr->nsec3.flags = 1;
- rr->nsec3.iterations = 1;
- rr->nsec3.salt = memdup(salt, sizeof(salt));
- assert_se(rr->nsec3.salt);
- rr->nsec3.salt_size = sizeof(salt);
- rr->nsec3.next_hashed_name = memdup(next_hashed_name, sizeof(next_hashed_name));
- assert_se(rr->nsec3.next_hashed_name);
- rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name);
-
- log_info("NSEC3: %s", strna(dns_resource_record_to_string(rr)));
-
- k = dnssec_nsec3_hash(rr, "eurid.eu", &h);
- assert_se(k >= 0);
-
- b = base32hexmem(h, k, false);
- assert_se(b);
- assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0);
-}
-
-#endif
-
-int main(int argc, char*argv[]) {
-
- test_dnssec_canonicalize();
-
-#ifdef HAVE_GCRYPT
- test_dnssec_verify_dns_key();
- test_dnssec_verify_rrset();
- test_dnssec_verify_rrset2();
- test_dnssec_nsec3_hash();
-#endif
-
- return 0;
-}
diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c
deleted file mode 100644
index 2d615130e1..0000000000
--- a/src/resolve/test-resolve-tables.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/***
- This file is part of systemd
-
- Copyright 2013 Zbigniew Jędrzejewski-Szmek
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "dns-type.h"
-#include "test-tables.h"
-
-int main(int argc, char **argv) {
- uint16_t i;
-
- test_table_sparse(dns_type, DNS_TYPE);
-
- log_info("/* DNS_TYPE */");
- for (i = 0; i < _DNS_TYPE_MAX; i++) {
- const char *s;
-
- s = dns_type_to_string(i);
- assert_se(s == NULL || strlen(s) < _DNS_TYPE_STRING_MAX);
-
- if (s)
- log_info("%-*s %s%s%s%s%s%s%s%s%s",
- (int) _DNS_TYPE_STRING_MAX - 1, s,
- dns_type_is_pseudo(i) ? "pseudo " : "",
- dns_type_is_valid_query(i) ? "valid_query " : "",
- dns_type_is_valid_rr(i) ? "is_valid_rr " : "",
- dns_type_may_redirect(i) ? "may_redirect " : "",
- dns_type_is_dnssec(i) ? "dnssec " : "",
- dns_type_is_obsolete(i) ? "obsolete " : "",
- dns_type_may_wildcard(i) ? "wildcard " : "",
- dns_type_apex_only(i) ? "apex_only " : "",
- dns_type_needs_authentication(i) ? "needs_authentication" : "");
- }
-
- log_info("/* DNS_CLASS */");
- for (i = 0; i < _DNS_CLASS_MAX; i++) {
- const char *s;
-
- s = dns_class_to_string(i);
- assert_se(s == NULL || strlen(s) < _DNS_CLASS_STRING_MAX);
-
- if (s)
- log_info("%-*s %s%s",
- (int) _DNS_CLASS_STRING_MAX - 1, s,
- dns_class_is_pseudo(i) ? "is_pseudo " : "",
- dns_class_is_valid_rr(i) ? "is_valid_rr " : "");
- }
-
- return EXIT_SUCCESS;
-}