diff options
author | Lennart Poettering <lennart@poettering.net> | 2016-02-17 12:45:31 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2016-02-17 12:45:31 +0100 |
commit | 5031c4e21b4379c1824033be675517978090fd36 (patch) | |
tree | e94470c3ec72b4b98b07482cba22dc556ed3c55e | |
parent | 2c45295e47642812ea417efba7f2f3339c6e2912 (diff) | |
parent | 5259c0559c8c0fe6ec54ac20f452fd7d106188c3 (diff) |
Merge pull request #2640 from keszybz/dnssec-work-3
resolve: dumping of binary packets
23 files changed, 769 insertions, 610 deletions
diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml index 802d9cbbe6..c288fd974e 100644 --- a/man/systemd-resolve.xml +++ b/man/systemd-resolve.xml @@ -65,7 +65,7 @@ <command>systemd-resolve</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <command> --type=<replaceable>TYPE</replaceable></command> - <arg choice="plain" rep="repeat"><replaceable>RRDOMAIN</replaceable></arg> + <arg choice="plain" rep="repeat"><replaceable>DOMAIN</replaceable></arg> </cmdsynopsis> <cmdsynopsis> @@ -233,6 +233,16 @@ </varlistentry> <varlistentry> + <term><option>--raw</option><optional>=payload|packet</optional></term> + + <listitem><para>Dump the answer as binary data. If there is no argument or if the argument is + <literal>payload</literal>, the payload of the packet is exported. If the argument is + <literal>packet</literal>, the whole packet is dumped in wire format, prefixed by + length specified as a little-endian 64-bit number. This format allows multiple packets + to be dumped and unambigously parsed.</para></listitem> + </varlistentry> + + <varlistentry> <term><option>--legend=</option><replaceable>BOOL</replaceable></term> <listitem><para>Takes a boolean parameter. If true (the default), column headers and meta information about the @@ -263,27 +273,58 @@ <example> <title>Retrieve the addresses of the <literal>www.0pointer.net</literal> domain</title> - <programlisting>$ systemd-resolve www.0pointer.net</programlisting> + <programlisting>$ systemd-resolve www.0pointer.net +www.0pointer.net: 2a01:238:43ed:c300:10c3:bcf3:3266:da74 + 85.214.157.71 + +-- Information acquired via protocol DNS in 611.6ms. +-- Data is authenticated: no +</programlisting> </example> <example> <title>Retrieve the domain of the <literal>85.214.157.71</literal> IP address</title> - <programlisting>$ systemd-resolve 85.214.157.71</programlisting> + <programlisting>$ systemd-resolve 85.214.157.71 +85.214.157.71: gardel.0pointer.net + +-- Information acquired via protocol DNS in 1.2997s. +-- Data is authenticated: no +</programlisting> </example> <example> <title>Retrieve the MX record of the <literal>0pointer.net</literal> domain</title> - <programlisting>$ systemd-resolve -t MX 0pointer.net</programlisting> + <programlisting>$ systemd-resolve -t MX yahoo.com --legend=no +yahoo.com. IN MX 1 mta7.am0.yahoodns.net +yahoo.com. IN MX 1 mta6.am0.yahoodns.net +yahoo.com. IN MX 1 mta5.am0.yahoodns.net +</programlisting> </example> <example> <title>Resolve an SRV service</title> - <programlisting>$ systemd-resolve --service _xmpp-server._tcp gmail.com</programlisting> + <programlisting>$ systemd-resolve --service _xmpp-server._tcp gmail.com +_xmpp-server._tcp/gmail.com: alt1.xmpp-server.l.google.com:5269 [priority=20, weight=0] + 173.194.210.125 + alt4.xmpp-server.l.google.com:5269 [priority=20, weight=0] + 173.194.65.125 + ... +</programlisting> </example> + <example> + <title>Retrieve a PGP key</title> + + <programlisting>$ systemd-resolve --openpgp zbyszek@fedoraproject.org +d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org. IN OPENPGPKEY + mQINBFBHPMsBEACeInGYJCb+7TurKfb6wGyTottCDtiSJB310i37/6ZYoeIay/5soJjlMyf + MFQ9T2XNT/0LM6gTa0MpC1st9LnzYTMsT6tzRly1D1UbVI6xw0g0vE5y2Cjk3xUwAynCsSs + ... +</programlisting> + </example> </refsect1> <refsect1> diff --git a/src/basic/af-list.h b/src/basic/af-list.h index 135248dc64..6a4cc03839 100644 --- a/src/basic/af-list.h +++ b/src/basic/af-list.h @@ -19,7 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "string-util.h" + const char *af_to_name(int id); int af_from_name(const char *name); +static inline const char* af_to_name_short(int id) { + const char *f; + + if (id == AF_UNSPEC) + return "*"; + + f = af_to_name(id); + if (!f) + return "unknown"; + + assert(startswith(f, "AF_")); + return f + 3; +} + int af_max(void); diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index fb7babf12a..db9666b970 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -124,6 +124,9 @@ enum { _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); @@ -133,7 +136,7 @@ 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); -int dns_type_to_af(uint16_t t); +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); @@ -142,7 +145,7 @@ bool dns_class_is_valid_rr(uint16_t class); const char *dns_type_to_string(int type); int dns_type_from_string(const char *s); -const char *dns_class_to_string(uint16_t type); +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 */ diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index c1be03fbb2..a519074278 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -44,6 +44,14 @@ static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; +typedef enum RawType { + RAW_NONE, + RAW_PAYLOAD, + RAW_PACKET, +} RawType; + +static RawType arg_raw = RAW_NONE; + static enum { MODE_RESOLVE_HOST, MODE_RESOLVE_RECORD, @@ -331,6 +339,50 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres 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) { _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; @@ -377,9 +429,6 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ return bus_log_parse_error(r); while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - const char *s; uint16_t c, t; int ifindex; const void *d; @@ -399,29 +448,16 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ if (r < 0) return bus_log_parse_error(r); - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - return log_oom(); - - p->refuse_compression = true; + if (arg_raw == RAW_PACKET) { + uint64_t u64 = htole64(l); - 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"); - - s = dns_resource_record_to_string(rr); - if (!s) - return log_oom(); - - 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\n", s, isempty(ifname) ? "" : " # interface ", ifname); + 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; @@ -976,27 +1012,33 @@ static void help_dns_classes(void) { } static void help(void) { - printf("%s [OPTIONS...] NAME...\n" - "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n" + 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 resource records, and services.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i --interface=INTERFACE Look on interface\n" - " -p --protocol=PROTOCOL|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 Do [not] resolve address for services\n" - " --service-txt=BOOL Do [not] resolve TXT records for services\n" - " --openpgp Query OpenPGP public key\n" - " --cname=BOOL Do [not] follow CNAME redirects\n" - " --search=BOOL Do [not] use search domains\n" - " --legend=BOOL Do [not] print column headers and meta information\n" - " --statistics Show resolver statistics\n" - " --reset-statistics Reset resolver statistics\n" - , program_invocation_short_name, program_invocation_short_name); + " -h --help Show this help\n" + " --version Show package version\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" + " --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" + , program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { @@ -1008,6 +1050,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SERVICE_ADDRESS, ARG_SERVICE_TXT, ARG_OPENPGP, + ARG_RAW, ARG_SEARCH, ARG_STATISTICS, ARG_RESET_STATISTICS, @@ -1026,6 +1069,7 @@ static int parse_argv(int argc, char *argv[]) { { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, { "openpgp", no_argument, NULL, ARG_OPENPGP }, + { "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 }, @@ -1139,6 +1183,24 @@ static int parse_argv(int argc, char *argv[]) { arg_mode = MODE_RESOLVE_OPENPGP; 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) diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 2d94baeb7e..a138be2421 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -202,7 +202,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { /* 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); + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); if (r < 0) goto finish; @@ -797,7 +797,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (canonical) { normalized = mfree(normalized); - r = dns_name_normalize(DNS_RESOURCE_KEY_NAME(canonical->key), &normalized); + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); if (r < 0) return r; } @@ -959,7 +959,7 @@ static void resolve_service_all_complete(DnsQuery *q) { goto finish; assert(canonical); - r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain); + r = dns_service_split(dns_resource_key_name(canonical->key), &name, &type, &domain); if (r < 0) goto finish; diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 7eb303ab95..c08f7a7edd 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -330,7 +330,7 @@ int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) { if (rr->key->type != DNS_TYPE_NSEC3) continue; - p = DNS_RESOURCE_KEY_NAME(rr->key); + p = dns_resource_key_name(rr->key); r = dns_name_parent(&p); if (r < 0) return r; @@ -363,7 +363,7 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco if (r > 0) { if (soa) { - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key)); + r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key)); if (r < 0) return r; if (r > 0) @@ -840,13 +840,13 @@ bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) { 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); + 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)); + r = dns_name_equal(n, dns_resource_key_name(cname->key)); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9bcc71724e..4b7672fbbf 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -17,6 +17,9 @@ 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" @@ -180,6 +183,7 @@ void dns_cache_prune(DnsCache *c) { for (;;) { DnsCacheItem *i; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; i = prioq_peek(c->by_expiry); if (!i) @@ -192,8 +196,12 @@ void dns_cache_prune(DnsCache *c) { break; /* Depending whether this is an mDNS shared entry - * either remove only this one RR or the whole - * RRset */ + * 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 { @@ -375,8 +383,8 @@ static int dns_cache_put_positive( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - _cleanup_free_ char *key_str = NULL; DnsCacheItem *existing; + char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE]; int r, k; assert(c); @@ -392,18 +400,9 @@ static int dns_cache_put_positive( /* New TTL is 0? Delete this specific entry... */ if (rr->ttl <= 0) { k = dns_cache_remove_by_rr(c, rr); - - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(rr->key, &key_str); - if (r < 0) - return r; - - if (k > 0) - log_debug("Removed zero TTL entry from cache: %s", key_str); - else - log_debug("Not caching zero TTL cache entry: %s", key_str); - } - + log_debug("%s: %s", + k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry", + dns_resource_key_to_string(i->key, key_str, sizeof key_str)); return 0; } @@ -450,11 +449,18 @@ static int dns_cache_put_positive( return r; if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(i->key, &key_str); - if (r < 0) - return r; - - log_debug("Added positive cache entry for %s", key_str); + _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; @@ -473,7 +479,7 @@ static int dns_cache_put_negative( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - _cleanup_free_ char *key_str = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; int r; assert(c); @@ -490,14 +496,8 @@ static int dns_cache_put_negative( return 0; if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str); - } - + log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", + dns_resource_key_to_string(i->key, key_str, sizeof key_str)); return 0; } @@ -524,7 +524,7 @@ static int dns_cache_put_negative( 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)); + i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key)); if (!i->key) return -ENOMEM; @@ -542,13 +542,10 @@ static int dns_cache_put_negative( if (r < 0) return r; - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(i->key, &key_str); - if (r < 0) - return r; - - log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str); - } + 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; @@ -628,16 +625,10 @@ int dns_cache_put( dns_cache_remove_previous(c, key, answer); if (dns_answer_size(answer) <= 0) { - if (log_get_max_level() >= LOG_DEBUG) { - _cleanup_free_ char *key_str = NULL; - - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Not caching negative entry without a SOA record: %s", key_str); - } + 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; } @@ -759,7 +750,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D if (i) return i; - n = DNS_RESOURCE_KEY_NAME(k); + 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. */ @@ -801,10 +792,10 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, 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; - _cleanup_free_ char *key_str = NULL; DnsCacheItem *j, *first, *nsec = NULL; bool have_authenticated = false, have_non_authenticated = false; @@ -814,19 +805,12 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r assert(ret); assert(authenticated); - if (key->type == DNS_TYPE_ANY || - key->class == DNS_CLASS_ANY) { - + 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. */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Ignoring cache for ANY lookup: %s", key_str); - } + log_debug("Ignoring cache for ANY lookup: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); c->n_miss++; @@ -839,13 +823,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r if (!first) { /* If one question cannot be answered we need to refresh */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Cache miss for %s", key_str); - } + log_debug("Cache miss for %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); c->n_miss++; @@ -873,13 +852,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r /* 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. */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("NSEC NODATA cache hit for %s", key_str); - } + 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 @@ -900,16 +874,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return 0; } - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("%s cache hit for %s", - n > 0 ? "Positive" : - nxdomain ? "NXDOMAIN" : "NODATA", - key_str); - } + 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++; @@ -1031,7 +999,6 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { void dns_cache_dump(DnsCache *cache, FILE *f) { Iterator iterator; DnsCacheItem *i; - int r; if (!cache) return; @@ -1057,14 +1024,9 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { fputs(t, f); fputc('\n', f); } else { - _cleanup_free_ char *z = NULL; - r = dns_resource_key_to_string(j->key, &z); - if (r < 0) { - log_oom(); - continue; - } + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - fputs(z, f); + 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); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 7098265929..0af7551425 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -467,7 +467,7 @@ static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) { if (rrsig->rrsig.inception > rrsig->rrsig.expiration) return -EINVAL; - name = DNS_RESOURCE_KEY_NAME(rrsig->key); + name = dns_resource_key_name(rrsig->key); n_key_labels = dns_name_count_labels(name); if (n_key_labels < 0) @@ -651,7 +651,7 @@ int dnssec_verify_rrset( return 0; } - name = DNS_RESOURCE_KEY_NAME(key); + 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)) { @@ -851,7 +851,7 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer); + return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer); } int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { @@ -867,7 +867,7 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) if (rrsig->rrsig.type_covered != key->type) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); + return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key)); } int dnssec_verify_rrset_search( @@ -1070,7 +1070,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (ds->ds.digest_size != hash_size) return 0; - r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name)); + r = dnssec_canonicalize(dns_resource_key_name(dnskey->key), owner_name, sizeof(owner_name)); if (r < 0) return r; @@ -1120,7 +1120,7 @@ int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *vali if (ds->key->class != dnskey->key->class) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key)); + r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key)); if (r < 0) return r; if (r == 0) @@ -1272,14 +1272,14 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0) return 0; - a = DNS_RESOURCE_KEY_NAME(rr->key); + 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); + b = dns_resource_key_name(nsec3->key); r = dns_name_parent(&b); /* strip off hash */ if (r < 0) return r; @@ -1353,7 +1353,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR * 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); + zone = dns_resource_key_name(key); for (;;) { DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) { r = nsec3_is_good(zone_rr, NULL); @@ -1362,7 +1362,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR if (r == 0) continue; - r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone); + r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone); if (r < 0) return r; if (r > 0) @@ -1382,7 +1382,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR found_zone: /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */ - p = DNS_RESOURCE_KEY_NAME(key); + p = dns_resource_key_name(key); for (;;) { _cleanup_free_ char *hashed_domain = NULL; @@ -1405,7 +1405,7 @@ found_zone: 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); + r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1504,7 +1504,7 @@ found_closest_encloser: if (r < 0) return r; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1516,7 +1516,7 @@ found_closest_encloser: no_closer = true; } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain); + r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain); if (r < 0) return r; if (r > 0) { @@ -1525,7 +1525,7 @@ found_closest_encloser: wildcard_rr = rr; } - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1604,7 +1604,7 @@ static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) { if (rr->n_skip_labels_source != 1) return 0; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_label_unescape(&n, label, sizeof(label)); if (r <= 0) return r; @@ -1643,7 +1643,7 @@ static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) { return r; /* If the name we 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); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1662,7 +1662,7 @@ static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(name, dns_resource_key_name(rr->key)); if (r <= 0) return r; @@ -1685,7 +1685,7 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { /* 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); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1706,7 +1706,7 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { /* p is now the "Next Closer". */ - return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name); + 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) { @@ -1725,7 +1725,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) * 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); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1735,7 +1735,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) return r; wc = strjoina("*.", common_suffix, NULL); - return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name); + 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) { @@ -1750,7 +1750,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ - name = DNS_RESOURCE_KEY_NAME(key); + name = dns_resource_key_name(key); DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { @@ -1770,7 +1770,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r 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); + r = dns_name_equal(dns_resource_key_name(rr->key), name); if (r < 0) return r; if (r == 0) { @@ -1900,7 +1900,7 @@ static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const cha if (r == 0) continue; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name); + r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name); if (r < 0) return r; @@ -1943,7 +1943,7 @@ static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const cha if (r < 0) return r; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index c2fc1d8b05..2e41dae656 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -577,7 +577,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) saved_size = p->size; - r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL); + r = dns_packet_append_name(p, dns_resource_key_name(k), true, true, NULL); if (r < 0) goto fail; @@ -2130,7 +2130,7 @@ int dns_packet_extract(DnsPacket *p) { continue; } - if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) { + 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."); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index a378b2b7f7..a7496aa586 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -421,6 +421,7 @@ int dns_query_new( DnsResourceKey *key; bool good = false; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(m); @@ -471,31 +472,20 @@ int dns_query_new( q->answer_family = AF_UNSPEC; /* First dump UTF8 question */ - DNS_QUESTION_FOREACH(key, question_utf8) { - _cleanup_free_ char *p = NULL; - - r = dns_resource_key_to_string(key, &p); - if (r < 0) - return r; - - log_debug("Looking up RR for %s.", strstrip(p)); - } + 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) { - _cleanup_free_ char *p = NULL; - r = dns_question_contains(question_utf8, key); if (r < 0) return r; if (r > 0) continue; - r = dns_resource_key_to_string(key, &p); - if (r < 0) - return r; - - log_debug("Looking up IDNA RR for %s.", strstrip(p)); + 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); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 8e452e79a4..c8b502d1cd 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -145,7 +145,7 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { if (q->n_keys > 65535) return 0; - name = DNS_RESOURCE_KEY_NAME(q->keys[0]); + name = dns_resource_key_name(q->keys[0]); if (!name) return 0; @@ -154,7 +154,7 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { assert(q->keys[i]); if (i > 0) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); + r = dns_name_equal(dns_resource_key_name(q->keys[i]), name); if (r <= 0) return r; } @@ -235,7 +235,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, 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); + 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) @@ -244,7 +244,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, d = destination; } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d); + r = dns_name_equal(dns_resource_key_name(key), d); if (r < 0) return r; @@ -291,7 +291,7 @@ const char *dns_question_first_name(DnsQuestion *q) { if (q->n_keys < 1) return NULL; - return DNS_RESOURCE_KEY_NAME(q->keys[0]); + return dns_resource_key_name(q->keys[0]); } int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) { diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 6397005a68..d0a86ef206 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -66,7 +66,7 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D 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); + 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) @@ -96,7 +96,7 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key return 0; } - r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined); + r = dns_name_concat(dns_resource_key_name(key), name, &joined); if (r < 0) return r; @@ -158,6 +158,23 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) { 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); @@ -172,7 +189,7 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { if (a == b) return 1; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b)); + r = dns_name_equal(dns_resource_key_name(a), dns_resource_key_name(b)); if (r <= 0) return r; @@ -204,18 +221,18 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, 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)); + 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); + 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 dns_name_equal(dns_resource_key_name(rr->key), joined); } return 0; @@ -231,9 +248,9 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe return 0; if (cname->type == DNS_TYPE_CNAME) - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(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)); + r = dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(cname)); else return 0; @@ -243,14 +260,14 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe if (search_domain) { _cleanup_free_ char *joined = NULL; - r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + 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)); + 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 dns_name_endswith(joined, dns_resource_key_name(cname)); } return 0; @@ -268,7 +285,7 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey * if (soa->type != DNS_TYPE_SOA) return 0; - return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)); + 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) { @@ -276,7 +293,7 @@ static void dns_resource_key_hash_func(const void *i, struct siphash *state) { assert(k); - dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), state); + 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); } @@ -285,7 +302,7 @@ 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)); + ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y)); if (ret != 0) return ret; @@ -307,32 +324,22 @@ const struct hash_ops dns_resource_key_hash_ops = { .compare = dns_resource_key_compare_func }; -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { - char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)]; - const char *c, *t, *n; - char *s; +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); - if (!c) { - sprintf(cbuf, "CLASS%u", key->class); - c = cbuf; - } - t = dns_type_to_string(key->type); - if (!t){ - sprintf(tbuf, "TYPE%u", key->type); - t = tbuf; - } - n = DNS_RESOURCE_KEY_NAME(key); - if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0) - return -ENOMEM; + 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); - *ret = s; - return 0; + return ans; } bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) { @@ -830,8 +837,8 @@ static char *format_txt(DnsTxtItem *first) { } const char *dns_resource_record_to_string(DnsResourceRecord *rr) { - _cleanup_free_ char *k = NULL, *t = NULL; - char *s; + _cleanup_free_ char *t = NULL; + char *s, k[DNS_RESOURCE_KEY_STRING_MAX]; int r; assert(rr); @@ -839,9 +846,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (rr->to_string) return rr->to_string; - r = dns_resource_key_to_string(rr->key, &k); - if (r < 0) - return NULL; + dns_resource_key_to_string(rr->key, k, sizeof(k)); switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { @@ -1204,6 +1209,44 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { 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_SSHFP: + case DNS_TYPE_DNSKEY: + case DNS_TYPE_RRSIG: + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + return -EINVAL; + + 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 = { @@ -1261,7 +1304,7 @@ int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) { if (rr->n_skip_labels_signer == (unsigned) -1) return -ENODATA; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_name_skip(n, rr->n_skip_labels_signer, &n); if (r < 0) return r; @@ -1284,7 +1327,7 @@ int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) { if (rr->n_skip_labels_source == (unsigned) -1) return -ENODATA; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_name_skip(n, rr->n_skip_labels_source, &n); if (r < 0) return r; @@ -1324,7 +1367,7 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr) { if (rr->n_skip_labels_source > 1) return 1; - r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(rr->key), "*"); + r = dns_name_startswith(dns_resource_key_name(rr->key), "*"); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 23749790b4..646e34598d 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -26,6 +26,7 @@ #include "hashmap.h" #include "in-addr-util.h" #include "list.h" +#include "string-util.h" typedef struct DnsResourceKey DnsResourceKey; typedef struct DnsResourceRecord DnsResourceRecord; @@ -81,7 +82,7 @@ enum { struct DnsResourceKey { unsigned n_ref; /* (unsigned -1) for const keys, see below */ uint16_t class, type; - char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ + char *_name; /* don't access directy, use dns_resource_key_name()! */ }; /* Creates a temporary resource key. This is only useful to quickly @@ -260,16 +261,6 @@ struct DnsResourceRecord { }; }; -static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { - if (!key) - return NULL; - - if (key->_name) - return key->_name; - - return (char*) key + sizeof(DnsResourceKey); -} - static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) { if (!rr) return NULL; @@ -297,12 +288,20 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key 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); -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); + +/* _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) { diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index a406872a38..66e4585c18 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -514,8 +514,8 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) { * 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))); + 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 diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index f4a43dee8c..e3003411f7 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -86,7 +86,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if 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)); + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key)); if (!rr) return -ENOMEM; @@ -100,7 +100,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if 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)); + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key)); if (!rr) return -ENOMEM; @@ -140,7 +140,7 @@ static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int i if (r < 0) return r; - r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; } @@ -254,11 +254,11 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, .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), buffer, n); } } - return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, 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) { @@ -319,7 +319,7 @@ static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifin return n; } - return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, 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) { @@ -360,7 +360,7 @@ int dns_synthesize_answer( key->class != DNS_CLASS_ANY) continue; - name = DNS_RESOURCE_KEY_NAME(key); + name = dns_resource_key_name(key); if (is_localhost(name)) { diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 060c430f3a..3443f71976 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -113,7 +113,6 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_answer_unref(t->validated_keys); dns_resource_key_unref(t->key); - free(t->key_string); free(t); return NULL; @@ -238,6 +237,7 @@ static void dns_transaction_shuffle_id(DnsTransaction *t) { 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); @@ -250,10 +250,10 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", t->id, - dns_transaction_key_string(t), + 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 : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), + af_to_name_short(t->scope->family), pretty); /* RFC 4795, Section 4.1 says that the peer with the @@ -286,20 +286,24 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { DnsTransaction *d; Iterator i; 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) + 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", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)), + 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", dns_transaction_key_string(t), + "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 @@ -312,10 +316,10 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).", t->id, - dns_transaction_key_string(t), + 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 : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), + 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"); @@ -522,7 +526,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { * the IP address, in case this is a reverse * PTR lookup */ - r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address); + r = dns_name_address(dns_resource_key_name(t->key), &family, &address); if (r < 0) return r; if (r == 0) @@ -1209,7 +1213,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { return 0; } - if (dns_name_is_root(DNS_RESOURCE_KEY_NAME(t->key)) && + 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 @@ -1237,8 +1241,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { * 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 it and trust - * it). */ + * we couldn't validate and trust them. */ dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR); return 0; @@ -1425,6 +1428,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) { int dns_transaction_go(DnsTransaction *t) { usec_t ts; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(t); @@ -1434,12 +1438,12 @@ int dns_transaction_go(DnsTransaction *t) { if (r <= 0) return r; - log_debug("Excercising transaction %" PRIu16 " for <%s> on scope %s on %s/%s.", + log_debug("Transaction %" PRIu16 " for <%s> scope %s on %s/%s.", t->id, - dns_transaction_key_string(t), + 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 : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); + af_to_name_short(t->scope->family)); if (!t->initial_jitter_scheduled && (t->scope->protocol == DNS_PROTOCOL_LLMNR || @@ -1494,8 +1498,8 @@ int dns_transaction_go(DnsTransaction *t) { 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)) { + (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 */ @@ -1602,11 +1606,14 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource if (r < 0) return r; if (r > 0) { - log_debug("Detected potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", + 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, - strna(dns_transaction_key_string(aux)), + dns_resource_key_to_string(t->key, s, sizeof s), t->id, - strna(dns_transaction_key_string(t))); + dns_resource_key_to_string(aux->key, saux, sizeof saux)); + return -ELOOP; } } @@ -1708,7 +1715,7 @@ static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) { /* 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)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key)); if (r < 0) return r; if (r > 0) @@ -1816,7 +1823,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { 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)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -1833,7 +1840,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * 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)); + r = dns_name_equal(rr->rrsig.signer, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -1851,7 +1858,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * 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); + r = dns_name_endswith(dns_resource_key_name(t->key), rr->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -1861,7 +1868,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { 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); + 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; @@ -1879,17 +1887,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * 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)); + 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)); + 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)); + 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; @@ -1920,11 +1929,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key)); + 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)); + 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; @@ -1966,7 +1976,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - name = DNS_RESOURCE_KEY_NAME(rr->key); + name = dns_resource_key_name(rr->key); r = dns_name_parent(&name); if (r < 0) return r; @@ -1977,7 +1987,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { 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)); + 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; @@ -2007,11 +2018,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, DNS_RESOURCE_KEY_NAME(rr->key)); + 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)); + 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; @@ -2029,7 +2041,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) { const char *name; - name = DNS_RESOURCE_KEY_NAME(t->key); + name = dns_resource_key_name(t->key); /* If this was a SOA or NS request, then this * indicates that we are not at a zone apex, hence ask @@ -2042,11 +2054,13 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r < 0) return r; if (r > 0) - log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key)); + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", + t->id, dns_resource_key_name(t->key)); else name = NULL; } else - log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key)); + 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; @@ -2118,7 +2132,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dns_type_is_pseudo(rr->key->type)) return -EINVAL; - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -2144,7 +2158,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dt->key->type != DNS_TYPE_DS) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key)); if (r < 0) return r; if (r == 0) @@ -2187,7 +2201,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * continue; if (!parent) { - parent = DNS_RESOURCE_KEY_NAME(rr->key); + parent = dns_resource_key_name(rr->key); r = dns_name_parent(&parent); if (r < 0) return r; @@ -2201,7 +2215,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * } } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), parent); + r = dns_name_equal(dns_resource_key_name(dt->key), parent); if (r < 0) return r; if (r == 0) @@ -2226,7 +2240,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dt->key->type != DNS_TYPE_SOA) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key)); if (r < 0) return r; if (r == 0) @@ -2273,7 +2287,7 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe 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); + tld = dns_resource_key_name(key); r = dns_name_parent(&tld); if (r < 0) return r; @@ -2288,7 +2302,7 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe if (dt->key->class != key->class) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), tld); + r = dns_name_equal(dns_resource_key_name(dt->key), tld); if (r < 0) return r; if (r == 0) @@ -2309,6 +2323,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { const char *name; Iterator i; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(t); @@ -2321,7 +2336,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dns_type_is_pseudo(t->key->type)) return -EINVAL; - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key)); if (r < 0) return r; if (r > 0) @@ -2335,11 +2350,12 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { * 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_transaction_key_string(t)); + 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); + name = dns_resource_key_name(t->key); if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { @@ -2368,7 +2384,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dt->key->type != DNS_TYPE_SOA) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), name); + r = dns_name_equal(dns_resource_key_name(dt->key), name); if (r < 0) return r; if (r == 0) @@ -2390,7 +2406,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe * 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)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -2413,7 +2429,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe if (dt->key->type == DNS_TYPE_DNSKEY) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer); + r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -2430,7 +2446,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe } else if (dt->key->type == DNS_TYPE_DS) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer); + r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -2460,7 +2476,7 @@ static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr * 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)); + dns_name_is_root(dns_resource_key_name(rr->key)); } static int dns_transaction_check_revoked_trust_anchors(DnsTransaction *t) { @@ -2541,343 +2557,347 @@ static int dns_transaction_copy_validated(DnsTransaction *t) { return 0; } -int dns_transaction_validate_dnssec(DnsTransaction *t) { - _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; - enum { - PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */ - PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */ - PHASE_ALL, /* Phase #3, validate everything else */ - } phase; +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; - DnsAnswerFlags flags; int r; - assert(t); + /* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */ - /* We have now collected all DS and DNSKEY RRs in - * t->validated_keys, let's see which RRs we can now - * authenticate with that. */ + DNS_ANSWER_FOREACH(rr, t->answer) { + DnsResourceRecord *rrsig = NULL; + DnssecResult result; - if (t->scope->dnssec_mode == DNSSEC_NO) - return 0; + switch (rr->key->type) { + case DNS_TYPE_RRSIG: + continue; - /* Already validated */ - if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID) - return 0; + case DNS_TYPE_DNSKEY: + /* We validate DNSKEYs only in the DNSKEY and ALL phases */ + if (phase == DNSSEC_PHASE_NSEC) + continue; + break; - /* 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; - } + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + *have_nsec = true; - /* Cached stuff is not affected by validation. */ - if (t->answer_source != DNS_TRANSACTION_NETWORK) - return 0; + /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */ + if (phase == DNSSEC_PHASE_DNSKEY) + continue; + break; - 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 ", server lacks DNSSEC support.", t->id); - return 0; - } + default: + /* We validate all other RRs only in the ALL phases */ + if (phase != DNSSEC_PHASE_ALL) + continue; + } - log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t)); + r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig); + if (r < 0) + return r; - /* 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; + log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result)); - /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */ - r = dns_transaction_copy_validated(t); - if (r < 0) - return r; + if (result == DNSSEC_VALIDATED) { - /* 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; + 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. */ - /* 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; + r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; - phase = PHASE_DNSKEY; - for (;;) { - bool changed = false, have_nsec = false; + /* 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; + } - DNS_ANSWER_FOREACH(rr, t->answer) { - DnsResourceRecord *rrsig = NULL; - DnssecResult result; + /* 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; - switch (rr->key->type) { + manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key); - case DNS_TYPE_RRSIG: - continue; + /* Exit the loop, we dropped something from the answer, start from the beginning */ + return 1; + } - case DNS_TYPE_DNSKEY: - /* We validate DNSKEYs only in the DNSKEY and ALL phases */ - if (phase == PHASE_NSEC) - continue; - break; + /* 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; - case DNS_TYPE_NSEC: - case DNS_TYPE_NSEC3: - have_nsec = true; + if (result == DNSSEC_VALIDATED_WILDCARD) { + bool authenticated = false; + const char *source; - /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */ - if (phase == PHASE_DNSKEY) - continue; + /* This RRset validated, but as a wildcard. This means we need + * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/ - break; + /* First step, determine the source of synthesis */ + r = dns_resource_record_source(rrsig, &source); + if (r < 0) + return r; - default: - /* We validate all other RRs only in the ALL phases */ - if (phase != PHASE_ALL) - continue; + r = dnssec_test_positive_wildcard(*validated, + dns_resource_key_name(rr->key), + source, + rrsig->rrsig.signer, + &authenticated); - break; + /* 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; } + } - r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig); + 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; - log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result)); + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } - if (result == DNSSEC_VALIDATED) { + 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. */ - 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. */ + dns_server_packet_rrsig_missing(t->server, t->current_feature_level); - r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; + if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { + + /* Downgrading is OK? If so, just consider the information unsigned */ - /* 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); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); 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); + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; + /* Otherwise, fail */ + t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; + return 0; } - /* 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 != PHASE_ALL) - continue; + r = dns_transaction_in_private_tld(t, rr->key); + if (r < 0) + return r; + if (r > 0) { + char s[DNS_RESOURCE_KEY_STRING_MAX]; - if (result == DNSSEC_VALIDATED_WILDCARD) { - bool authenticated = false; - const char *source; + /* 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. */ - /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3 - * that no matching non-wildcard RR exists.*/ + log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", + dns_resource_key_to_string(rr->key, s, sizeof s)); - /* First step, determine the source of synthesis */ - r = dns_resource_record_source(rrsig, &source); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); 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, DNSSEC_INSECURE, rr->key); + return 1; + } + } - manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key); + if (IN_SET(result, + DNSSEC_MISSING_KEY, + DNSSEC_SIGNATURE_EXPIRED, + DNSSEC_UNSUPPORTED_ALGORITHM)) { - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; - } - } + 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. */ - if (result == DNSSEC_NO_SIGNATURE) { - r = dns_transaction_requires_rrsig(t, rr); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); 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); - changed = true; - break; - } + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + } - r = dns_transaction_known_signed(t, rr); + 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) { - /* 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) { + 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; + } - /* Downgrading is OK? If so, just consider the information unsigned */ + /* 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. */ + } - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + /* 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; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } + /* We dropped something from the answer, start from the beginning. */ + return 1; + } - /* Otherwise, fail */ - t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; - return 0; - } + return 2; /* Finito. */ +} - r = dns_transaction_in_private_tld(t, rr->key); - if (r < 0) - return r; - if (r > 0) { - _cleanup_free_ char *s = NULL; +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]; - /* 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. */ + assert(t); - (void) dns_resource_key_to_string(rr->key, &s); - log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", strna(s ? strstrip(s) : NULL)); + /* We have now collected all DS and DNSKEY RRs in + * t->validated_keys, let's see which RRs we can now + * authenticate with that. */ - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + if (t->scope->dnssec_mode == DNSSEC_NO) + return 0; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } - } + /* Already validated */ + if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID) + return 0; - if (IN_SET(result, - DNSSEC_MISSING_KEY, - DNSSEC_SIGNATURE_EXPIRED, - DNSSEC_UNSUPPORTED_ALGORITHM)) { + /* 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; + } - 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. */ + /* Cached stuff is not affected by validation. */ + if (t->answer_source != DNS_TRANSACTION_NETWORK) + return 0; - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + 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 ", server lacks DNSSEC support.", t->id); + return 0; + } - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } - } + log_debug("Validating response from transaction %" PRIu16 " (%s).", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str)); - r = dns_transaction_is_primary_response(t, rr); - if (r < 0) - return r; - if (r > 0) { + /* 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; - /* 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; - } + /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */ + r = dns_transaction_copy_validated(t); + 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; - } + /* 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; - /* 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. */ - } + /* 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; - /* 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; + phase = DNSSEC_PHASE_DNSKEY; + for (;;) { + bool have_nsec = false; - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; - } + r = dnssec_validate_records(t, phase, &have_nsec, &validated); + if (r <= 0) + return r; - /* Restart the inner loop as long as we managed to achieve something */ - if (changed) + /* Try again as long as we managed to achieve something */ + if (r == 1) continue; - if (phase == PHASE_DNSKEY && have_nsec) { + if (phase == DNSSEC_PHASE_DNSKEY && have_nsec) { /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */ - phase = PHASE_NSEC; + phase = DNSSEC_PHASE_NSEC; continue; } - if (phase != 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 = PHASE_ALL; + 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; } @@ -2921,7 +2941,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { 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, dns_transaction_key_string(t)); + 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; @@ -2931,7 +2951,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { 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, dns_transaction_key_string(t)); + 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; @@ -2941,7 +2961,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { 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, dns_transaction_key_string(t)); + 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; @@ -2986,17 +3006,6 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { return 1; } -const char *dns_transaction_key_string(DnsTransaction *t) { - assert(t); - - if (!t->key_string) { - if (dns_resource_key_to_string(t->key, &t->key_string) < 0) - return "n/a"; - } - - return strstrip(t->key_string); -} - static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { [DNS_TRANSACTION_NULL] = "null", [DNS_TRANSACTION_PENDING] = "pending", diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 4617194711..491c62d772 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -64,7 +64,6 @@ struct DnsTransaction { DnsScope *scope; DnsResourceKey *key; - char *key_string; DnsTransactionState state; @@ -153,8 +152,6 @@ 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_key_string(DnsTransaction *t); - const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index a75337eb6a..77370e7dd5 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -651,7 +651,7 @@ static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceReco } } - 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))); + 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; @@ -698,7 +698,7 @@ int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, /* 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))) + 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 */ diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index f52383cfd1..03813da6a2 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -68,12 +68,12 @@ static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { else hashmap_remove(z->by_key, i->rr->key); - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(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); + 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)); + hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key)); dns_zone_item_free(i); } @@ -147,12 +147,12 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { return r; } - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); + 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); + 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); + r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i); if (r < 0) return r; } @@ -169,11 +169,11 @@ static int dns_zone_item_probe_start(DnsZoneItem *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); + 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)); + key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)); if (!key) return -ENOMEM; @@ -303,7 +303,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns * go through the list by the name and look * for everything manually */ - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + 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; @@ -339,7 +339,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (!found) { - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + 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; @@ -370,7 +370,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns bool found = false, added = false; int k; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + 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; @@ -393,7 +393,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (found && !added) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL); if (r < 0) return r; } @@ -418,7 +418,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns if (!found) { bool add_soa = false; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + 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; @@ -430,7 +430,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (add_soa) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL); if (r < 0) return r; } @@ -482,7 +482,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) { 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) + if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0) manager_next_hostname(i->scope->manager); } @@ -562,7 +562,7 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { * 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)); + first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key)); if (!first) return 0; @@ -593,7 +593,7 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { /* 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)); + first = hashmap_get(zone->by_name, dns_resource_key_name(key)); if (!first) return 0; diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index ee82c96822..6ccbdca20e 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -363,7 +363,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name); + r = dns_name_equal(dns_resource_key_name(t), name); if (r < 0) return r; if (r > 0) { @@ -413,7 +413,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name); + r = dns_name_equal(dns_resource_key_name(t), name); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 49e378614e..b3ff46b5da 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1213,11 +1213,11 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource assert(verdict < _DNSSEC_VERDICT_MAX); if (log_get_max_level() >= LOG_DEBUG) { - _cleanup_free_ char *s = NULL; + char s[DNS_RESOURCE_KEY_STRING_MAX]; - (void) dns_resource_key_to_string(key, &s); - - log_debug("Found verdict for lookup %s: %s", s ? strstrip(s) : "n/a", dnssec_verdict_to_string(verdict)); + 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]++; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index bc8b8b809b..b13b1d0144 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -106,7 +106,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us dns_scope_check_conflicts(scope, p); DNS_ANSWER_FOREACH(rr, p->answer) { - const char *name = DNS_RESOURCE_KEY_NAME(rr->key); + 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, diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c index 63660afc87..2d615130e1 100644 --- a/src/resolve/test-resolve-tables.c +++ b/src/resolve/test-resolve-tables.c @@ -21,7 +21,44 @@ #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; } |