diff options
Diffstat (limited to 'src/resolve')
47 files changed, 1664 insertions, 1170 deletions
diff --git a/src/resolve/RFCs b/src/resolve/RFCs index 22004a00cd..09c85f9518 100644 --- a/src/resolve/RFCs +++ b/src/resolve/RFCs @@ -8,7 +8,7 @@ D = Comprehensively Implemented, by a dependency of resolved Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION ? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types -Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support +Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts — Application and Support ~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index b2f479cae5..78d9d5733f 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -193,6 +193,23 @@ bool dns_type_is_obsolete(uint16_t type) { DNS_TYPE_NULL); } +bool dns_type_needs_authentication(uint16_t type) { + + /* Returns true for all (non-obsolete) RR types where records are not useful if they aren't + * authenticated. I.e. everything that contains crypto keys. */ + + return IN_SET(type, + DNS_TYPE_CERT, + DNS_TYPE_SSHFP, + DNS_TYPE_IPSECKEY, + DNS_TYPE_DS, + DNS_TYPE_DNSKEY, + DNS_TYPE_TLSA, + DNS_TYPE_CDNSKEY, + DNS_TYPE_OPENPGPKEY, + DNS_TYPE_CAA); +} + int dns_type_to_af(uint16_t t) { switch (t) { diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index a6c1630021..7b79d29d7e 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "macro.h" /* DNS record types, taken from @@ -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); @@ -132,7 +135,8 @@ bool dns_type_is_dnssec(uint16_t type); bool dns_type_is_obsolete(uint16_t type); bool dns_type_may_wildcard(uint16_t type); bool dns_type_apex_only(uint16_t type); -int dns_type_to_af(uint16_t t); +bool dns_type_needs_authentication(uint16_t type); +int dns_type_to_af(uint16_t type); bool dns_class_is_pseudo(uint16_t class); bool dns_class_is_valid_rr(uint16_t class); @@ -141,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 */ @@ -152,3 +156,6 @@ const char *tlsa_selector_to_string(uint8_t selector); /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */ const char *tlsa_matching_type_to_string(uint8_t selector); + +/* https://tools.ietf.org/html/rfc6844#section-5.1 */ +#define CAA_FLAG_CRITICAL (1u << 7) diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 824cb267b5..14ee01c49d 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -28,6 +28,7 @@ #include "bus-util.h" #include "escape.h" #include "in-addr-util.h" +#include "gcrypt-util.h" #include "parse-util.h" #include "resolved-def.h" #include "resolved-dns-packet.h" @@ -42,14 +43,54 @@ static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; +typedef enum ServiceFamily { + SERVICE_FAMILY_TCP, + SERVICE_FAMILY_UDP, + SERVICE_FAMILY_SCTP, + _SERVICE_FAMILY_INVALID = -1, +} ServiceFamily; +static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP; + +typedef enum RawType { + RAW_NONE, + RAW_PAYLOAD, + RAW_PACKET, +} RawType; +static RawType arg_raw = RAW_NONE; + static enum { MODE_RESOLVE_HOST, MODE_RESOLVE_RECORD, MODE_RESOLVE_SERVICE, + MODE_RESOLVE_OPENPGP, + MODE_RESOLVE_TLSA, MODE_STATISTICS, MODE_RESET_STATISTICS, } arg_mode = MODE_RESOLVE_HOST; +static ServiceFamily service_family_from_string(const char *s) { + if (s == NULL || streq(s, "tcp")) + return SERVICE_FAMILY_TCP; + if (streq(s, "udp")) + return SERVICE_FAMILY_UDP; + if (streq(s, "sctp")) + return SERVICE_FAMILY_SCTP; + return _SERVICE_FAMILY_INVALID; +} + +static const char* service_family_to_string(ServiceFamily service) { + switch(service) { + case SERVICE_FAMILY_TCP: + return "_tcp"; + case SERVICE_FAMILY_UDP: + return "_udp"; + case SERVICE_FAMILY_SCTP: + return "_sctp"; + default: + assert_not_reached("invalid service"); + } +} + static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; @@ -328,6 +369,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; @@ -336,6 +421,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ uint64_t flags; int r; usec_t ts; + bool needs_authentication = false; assert(name); @@ -373,9 +459,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; @@ -395,29 +478,20 @@ 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"); + fwrite(&u64, sizeof(u64), 1, stdout); + fwrite(d, 1, l, stdout); + } else { + r = output_rr_packet(d, l, ifindex); + if (r < 0) + return r; + } - s = dns_resource_record_to_string(rr); - if (!s) - return log_oom(); + if (dns_type_needs_authentication(t)) + needs_authentication = true; - 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); n++; } if (r < 0) @@ -438,6 +512,18 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ print_source(flags, ts); + if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { + fflush(stdout); + + fprintf(stderr, "\n%s" + "WARNING: The resources shown contain cryptographic key data which could not be\n" + " authenticated. It is not suitable to authenticate any communication.\n" + " This is usually indication that DNSSEC authentication was not enabled\n" + " or is not available for the selected protocol or DNS servers.%s\n", + ansi_highlight_red(), + ansi_normal()); + } + return 0; } @@ -545,15 +631,10 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) { } else n = p; - if (type == 0) - type = arg_type; - if (type == 0) - type = DNS_TYPE_A; - - if (class == 0) - class = arg_class; if (class == 0) - class = DNS_CLASS_IN; + class = arg_class ?: DNS_CLASS_IN; + if (type == 0) + type = arg_type ?: DNS_TYPE_A; return resolve_record(bus, n, class, type); @@ -763,6 +844,68 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons return 0; } +static int resolve_openpgp(sd_bus *bus, const char *address) { + const char *domain, *full; + int r; + _cleanup_free_ char *hashed = NULL; + + assert(bus); + assert(address); + + domain = strrchr(address, '@'); + if (!domain) { + log_error("Address does not contain '@': \"%s\"", address); + return -EINVAL; + } else if (domain == address || domain[1] == '\0') { + log_error("Address starts or ends with '@': \"%s\"", address); + return -EINVAL; + } + domain++; + + r = string_hashsum_sha224(address, domain - 1 - address, &hashed); + if (r < 0) + return log_error_errno(r, "Hashing failed: %m"); + + full = strjoina(hashed, "._openpgpkey.", domain); + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_OPENPGPKEY); +} + +static int resolve_tlsa(sd_bus *bus, const char *address) { + const char *port; + uint16_t port_num = 443; + _cleanup_free_ char *full = NULL; + int r; + + assert(bus); + assert(address); + + port = strrchr(address, ':'); + if (port) { + r = safe_atou16(port + 1, &port_num); + if (r < 0 || port_num == 0) + return log_error_errno(r, "Invalid port \"%s\".", port + 1); + + address = strndupa(address, port - address); + } + + r = asprintf(&full, "_%u.%s.%s", + port_num, + service_family_to_string(arg_service_family), + address); + if (r < 0) + return log_oom(); + + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_TLSA); +} + static int show_statistics(sd_bus *bus) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -931,26 +1074,34 @@ 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" - " --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" + " --tlsa Query TLS public key\n" + " --cname=BOOL Follow CNAME redirects (default: yes)\n" + " --search=BOOL Use search domains for single-label names\n" + " (default: yes)\n" + " --raw[=payload|packet] Dump the answer as binary data\n" + " --legend=BOOL Print headers and additional info (default: yes)\n" + " --statistics Show resolver statistics\n" + " --reset-statistics Reset resolver statistics\n" + , program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { @@ -961,6 +1112,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_CNAME, ARG_SERVICE_ADDRESS, ARG_SERVICE_TXT, + ARG_OPENPGP, + ARG_TLSA, + ARG_RAW, ARG_SEARCH, ARG_STATISTICS, ARG_RESET_STATISTICS, @@ -978,6 +1132,9 @@ static int parse_argv(int argc, char *argv[]) { { "service", no_argument, NULL, ARG_SERVICE }, { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "openpgp", no_argument, NULL, ARG_OPENPGP }, + { "tlsa", optional_argument, NULL, ARG_TLSA }, + { "raw", optional_argument, NULL, ARG_RAW }, { "search", required_argument, NULL, ARG_SEARCH }, { "statistics", no_argument, NULL, ARG_STATISTICS, }, { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, @@ -1087,44 +1244,63 @@ static int parse_argv(int argc, char *argv[]) { arg_mode = MODE_RESOLVE_SERVICE; break; + case ARG_OPENPGP: + arg_mode = MODE_RESOLVE_OPENPGP; + break; + + case ARG_TLSA: + arg_mode = MODE_RESOLVE_TLSA; + arg_service_family = service_family_from_string(optarg); + if (arg_service_family < 0) { + log_error("Unknown service family \"%s\".", optarg); + return -EINVAL; + } + break; + + case ARG_RAW: + if (on_tty()) { + log_error("Refusing to write binary data to tty."); + return -ENOTTY; + } + + if (optarg == NULL || streq(optarg, "payload")) + arg_raw = RAW_PAYLOAD; + else if (streq(optarg, "packet")) + arg_raw = RAW_PACKET; + else { + log_error("Unknown --raw specifier \"%s\".", optarg); + return -EINVAL; + } + + arg_legend = false; + break; + case ARG_CNAME: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --cname= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_CNAME; - else - arg_flags &= ~SD_RESOLVED_NO_CNAME; + SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); break; case ARG_SERVICE_ADDRESS: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --service-address= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_ADDRESS; - else - arg_flags &= ~SD_RESOLVED_NO_ADDRESS; + SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); break; case ARG_SERVICE_TXT: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --service-txt= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_TXT; - else - arg_flags &= ~SD_RESOLVED_NO_TXT; + SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); break; case ARG_SEARCH: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --search argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_SEARCH; - else - arg_flags &= ~SD_RESOLVED_NO_SEARCH; + SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); break; case ARG_STATISTICS: @@ -1147,7 +1323,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) { + if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) { log_error("--service and --type= may not be combined."); return -EINVAL; } @@ -1246,6 +1422,42 @@ int main(int argc, char **argv) { break; + case MODE_RESOLVE_OPENPGP: + if (argc < optind + 1) { + log_error("E-mail address required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_openpgp(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + + case MODE_RESOLVE_TLSA: + if (argc < optind + 1) { + log_error("Domain name required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_tlsa(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + case MODE_STATISTICS: if (argc > optind) { log_error("Too many arguments."); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index fc5e6beca0..33f7c61557 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -23,6 +23,7 @@ #include "dns-domain.h" #include "resolved-bus.h" #include "resolved-def.h" +#include "resolved-dns-synthesize.h" #include "resolved-link-bus.h" static int reply_query_state(DnsQuery *q) { @@ -139,6 +140,7 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin static void bus_method_resolve_hostname_complete(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *normalized = NULL; DnsResourceRecord *rr; unsigned added = 0; int ifindex, r; @@ -186,7 +188,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (!canonical) canonical = dns_resource_record_ref(rr); - added ++; + added++; } if (added <= 0) { @@ -198,11 +200,17 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; + /* The key names are not necessarily normalized, make sure that they are when we return them to our bus + * clients. */ + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + if (r < 0) + goto finish; + /* Return the precise spelling and uppercasing and CNAME target reported by the server */ assert(canonical); r = sd_bus_message_append( reply, "st", - DNS_RESOURCE_KEY_NAME(canonical->key), + normalized, SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; @@ -233,6 +241,65 @@ static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus return 0; } +static int parse_as_address(sd_bus_message *m, int ifindex, const char *hostname, int family, uint64_t flags) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *canonical = NULL; + union in_addr_union parsed; + int r, ff; + + /* Check if the hostname is actually already an IP address formatted as string. In that case just parse it, + * let's not attempt to look it up. */ + + r = in_addr_from_string_auto(hostname, &ff, &parsed); + if (r < 0) /* not an address */ + return 0; + + if (family != AF_UNSPEC && ff != family) + return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address is not of the requested family."); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "ii", ifindex, ff); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &parsed, FAMILY_ADDRESS_SIZE(ff)); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + /* When an IP address is specified we just return it as canonical name, in order to avoid a DNS + * look-up. However, we reformat it to make sure it's in a truly canonical form (i.e. on IPv6 the inner + * omissions are always done the same way). */ + r = in_addr_to_string(ff, &parsed, &canonical); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", canonical, + SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(flags), ff, true)); + if (r < 0) + return r; + + return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL); +} + static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; Manager *m = userdata; @@ -254,15 +321,19 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - r = dns_name_is_valid(hostname); + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); if (r < 0) return r; - if (r == 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); - r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); + r = parse_as_address(message, ifindex, hostname, family, flags); + if (r != 0) + return r; + + r = dns_name_is_valid(hostname); if (r < 0) return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); r = dns_question_new_address(&question_utf8, family, hostname, false); if (r < 0) @@ -331,24 +402,31 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { question = dns_query_question_for_protocol(q, q->answer_protocol); DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + _cleanup_free_ char *normalized = NULL; + r = dns_question_matches_rr(question, rr, NULL); if (r < 0) goto finish; if (r == 0) continue; - r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name); + r = dns_name_normalize(rr->ptr.name, &normalized); + if (r < 0) + goto finish; + + r = sd_bus_message_append(reply, "(is)", ifindex, normalized); if (r < 0) goto finish; - added ++; + added++; } if (added <= 0) { _cleanup_free_ char *ip = NULL; - in_addr_to_string(q->request_family, &q->request_address, &ip); - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip)); + (void) in_addr_to_string(q->request_family, &q->request_address, &ip); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, + "Address '%s' does not have any RR of requested type", strnull(ip)); goto finish; } @@ -510,7 +588,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - added ++; + added++; } if (added <= 0) { @@ -607,6 +685,7 @@ fail: static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_free_ char *normalized = NULL; DnsQuery *aux; int r; @@ -663,10 +742,14 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (r < 0) return r; + r = dns_name_normalize(rr->srv.name, &normalized); + if (r < 0) + return r; + r = sd_bus_message_append( reply, "qqqs", - rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name); + rr->srv.priority, rr->srv.weight, rr->srv.port, normalized); if (r < 0) return r; @@ -712,9 +795,17 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (r < 0) return r; + if (canonical) { + normalized = mfree(normalized); + + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + if (r < 0) + return r; + } + /* Note that above we appended the hostname as encoded in the * SRV, and here the canonical hostname this maps to. */ - r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name); + r = sd_bus_message_append(reply, "s", normalized); if (r < 0) return r; @@ -869,7 +960,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; @@ -1004,9 +1095,9 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { } if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { - q->block_all_complete ++; + q->block_all_complete++; r = resolve_service_hostname(q, rr, ifindex); - q->block_all_complete --; + q->block_all_complete--; if (r < 0) goto finish; @@ -1047,7 +1138,6 @@ finish: static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; const char *name, *type, *domain; - _cleanup_free_ char *n = NULL; Manager *m = userdata; int family, ifindex; uint64_t flags; @@ -1198,7 +1288,7 @@ static int bus_property_get_dns_servers( return sd_bus_message_close_container(reply); } -static int bus_property_get_search_domains( +static int bus_property_get_domains( sd_bus *bus, const char *path, const char *interface, @@ -1396,8 +1486,8 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); } -static int bus_method_set_link_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return call_link_method(userdata, message, bus_link_method_set_search_domains, error); +static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_domains, error); } static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1449,7 +1539,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0), - SD_BUS_PROPERTY("SearchDomains", "a(isb)", bus_property_get_search_domains, 0, 0), + SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0), SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0), SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0), SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0), @@ -1462,7 +1552,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0), - SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_search_domains, 0), + SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0), SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0), SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0), SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0), diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index bb93fbfda2..990dc03b60 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -59,7 +59,7 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con assert(m); assert(string); - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, 0); @@ -114,7 +114,7 @@ int manager_parse_search_domains_and_warn(Manager *m, const char *string) { assert(m); assert(string); - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 7eb303ab95..0dadf8b1dd 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) @@ -538,7 +538,7 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) { dns_resource_record_unref((*a)->items[i].rr); memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1)); - (*a)->n_rrs --; + (*a)->n_rrs--; continue; } else @@ -624,7 +624,7 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) { dns_resource_record_unref((*a)->items[i].rr); memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1)); - (*a)->n_rrs --; + (*a)->n_rrs--; continue; } else @@ -757,7 +757,7 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) { assert(a); /* Tries to extend the DnsAnswer object. And if that's not - * possibly, since we are not the sole owner, then allocate a + * possible, since we are not the sole owner, then allocate a * new, appropriately sized one. Either way, after this call * the object will only have a single reference, and has room * for at least the specified number of RRs. */ @@ -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-answer.h b/src/resolve/resolved-dns-answer.h index 8f9c15eab4..0679c610f5 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -30,7 +30,7 @@ typedef struct DnsAnswerItem DnsAnswerItem; * can qualify A and AAAA RRs referring to a local link with the * right ifindex. * - * Note that we usually encode the the empty DnsAnswer object as a simple NULL. */ + * Note that we usually encode the empty DnsAnswer object as a simple NULL. */ typedef enum DnsAnswerFlags { DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */ diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9bcc71724e..77c42d7aad 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(rr->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(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; } @@ -649,7 +640,7 @@ int dns_cache_put( cache_keys = dns_answer_size(answer); if (key) - cache_keys ++; + cache_keys++; /* Make some space for our new entries */ dns_cache_make_space(c, cache_keys); @@ -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++; @@ -1019,7 +987,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { if (r < 0) return r; - ancount ++; + ancount++; } } @@ -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 7123d2d3a8..a54aed3a63 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -23,6 +23,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "gcrypt-util.h" #include "hexdecoct.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" @@ -126,19 +127,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { #ifdef HAVE_GCRYPT -static void initialize_libgcrypt(void) { - const char *p; - - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - static int rr_compare(const void *a, const void *b) { DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; size_t m; @@ -479,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) @@ -635,9 +623,9 @@ int dnssec_verify_rrset( assert(rrsig->key->type == DNS_TYPE_RRSIG); assert(dnskey->key->type == DNS_TYPE_DNSKEY); - /* Verifies the the RRSet matching the specified "key" in "a", + /* Verifies that the RRSet matches the specified "key" in "a", * using the signature "rrsig" and the key "dnskey". It's - * assumed the RRSIG and DNSKEY match. */ + * assumed that RRSIG and DNSKEY match. */ md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); if (md_algorithm == -EOPNOTSUPP) { @@ -663,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)) { @@ -737,7 +725,7 @@ int dnssec_verify_rrset( qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare); /* OK, the RRs are now in canonical order. Let's calculate the digest */ - initialize_libgcrypt(); + initialize_libgcrypt(false); hash_size = gcry_md_get_algo_dlen(md_algorithm); assert(hash_size > 0); @@ -863,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) { @@ -879,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 +1058,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag) return 0; - initialize_libgcrypt(); + initialize_libgcrypt(false); md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type); if (md_algorithm < 0) @@ -1082,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; @@ -1132,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) @@ -1189,7 +1177,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (algorithm < 0) return algorithm; - initialize_libgcrypt(); + initialize_libgcrypt(false); hash_size = gcry_md_get_algo_dlen(algorithm); assert(hash_size > 0); @@ -1284,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; @@ -1365,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); @@ -1374,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) @@ -1394,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; @@ -1417,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) { @@ -1516,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) { @@ -1528,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) { @@ -1537,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) { @@ -1616,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; @@ -1655,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; @@ -1674,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; @@ -1697,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; @@ -1718,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) { @@ -1737,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; @@ -1746,8 +1734,8 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - wc = strjoina("*.", common_suffix, NULL); - return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name); + wc = strjoina("*.", common_suffix); + return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name); } int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { @@ -1762,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) { @@ -1782,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) { @@ -1912,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; @@ -1955,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; @@ -1989,7 +1977,7 @@ static int dnssec_test_positive_wildcard_nsec3( /* Run a positive NSEC3 wildcard proof. Specifically: * - * A proof that the the "next closer" of the generating wildcard does not exist. + * A proof that the "next closer" of the generating wildcard does not exist. * * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index c940dd8929..b7907bb511 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -28,6 +28,19 @@ #define EDNS0_OPT_DO (1<<15) +typedef struct DnsPacketRewinder { + DnsPacket *packet; + size_t saved_rindex; +} DnsPacketRewinder; + +static void rewind_dns_packet(DnsPacketRewinder *rewinder) { + if (rewinder->packet) + dns_packet_rewind(rewinder->packet, rewinder->saved_rindex); +} + +#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0) +#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0) + int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { DnsPacket *p; size_t a; @@ -431,8 +444,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_ ((uint8_t*) d)[0] = (uint8_t) size; - if (size > 0) - memcpy(((uint8_t*) d) + 1, s, size); + memcpy_safe(((uint8_t*) d) + 1, s, size); return 0; } @@ -565,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; @@ -1072,6 +1084,18 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL); break; + case DNS_TYPE_CAA: + r = dns_packet_append_uint8(p, rr->caa.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->caa.tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL); + break; + case DNS_TYPE_OPT: case DNS_TYPE_OPENPGPKEY: case _DNS_TYPE_INVALID: /* unparseable */ @@ -1230,80 +1254,67 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { } int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; const void *d; char *t; uint8_t c; int r; assert(p); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read(p, c, &d, NULL); if (r < 0) - goto fail; + return r; - if (memchr(d, 0, c)) { - r = -EBADMSG; - goto fail; - } + if (memchr(d, 0, c)) + return -EBADMSG; t = strndup(d, c); - if (!t) { - r = -ENOMEM; - goto fail; - } + if (!t) + return -ENOMEM; if (!utf8_is_valid(t)) { free(t); - r = -EBADMSG; - goto fail; + return -EBADMSG; } *ret = t; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; uint8_t c; int r; assert(p); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read(p, c, ret, NULL); if (r < 0) - goto fail; + return r; if (size) *size = c; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_name( @@ -1312,7 +1323,8 @@ int dns_packet_read_name( bool allow_compression, size_t *start) { - size_t saved_rindex, after_rindex = 0, jump_barrier; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t after_rindex = 0, jump_barrier; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -1320,19 +1332,18 @@ int dns_packet_read_name( assert(p); assert(_ret); + INIT_REWINDER(rewinder, p); + jump_barrier = p->rindex; if (p->refuse_compression) allow_compression = false; - saved_rindex = p->rindex; - jump_barrier = p->rindex; - for (;;) { uint8_t c, d; r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; if (c == 0) /* End of name */ @@ -1343,12 +1354,10 @@ int dns_packet_read_name( /* Literal label */ r = dns_packet_read(p, c, (const void**) &label, NULL); if (r < 0) - goto fail; + return r; - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; if (first) first = false; @@ -1357,7 +1366,7 @@ int dns_packet_read_name( r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); if (r < 0) - goto fail; + return r; n += r; continue; @@ -1367,13 +1376,11 @@ int dns_packet_read_name( /* Pointer */ r = dns_packet_read_uint8(p, &d, NULL); if (r < 0) - goto fail; + return r; ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { - r = -EBADMSG; - goto fail; - } + if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) + return -EBADMSG; if (after_rindex == 0) after_rindex = p->rindex; @@ -1381,16 +1388,12 @@ int dns_packet_read_name( /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ jump_barrier = ptr; p->rindex = ptr; - } else { - r = -EBADMSG; - goto fail; - } + } else + return -EBADMSG; } - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; ret[n] = 0; @@ -1401,13 +1404,10 @@ int dns_packet_read_name( ret = NULL; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { @@ -1417,32 +1417,31 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta uint8_t bit = 0; unsigned i; bool found = false; - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; int r; assert(p); assert(types); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = bitmap_ensure_allocated(types); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &window, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &length, NULL); if (r < 0) - goto fail; + return r; if (length == 0 || length > 32) return -EBADMSG; r = dns_packet_read(p, length, (const void **)&bitmap, NULL); if (r < 0) - goto fail; + return r; for (i = 0; i < length; i++) { uint8_t bitmask = 1 << 7; @@ -1467,10 +1466,10 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta r = bitmap_set(*types, n); if (r < 0) - goto fail; + return r; } - bit ++; + bit++; bitmask >>= 1; } } @@ -1479,70 +1478,61 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta return -EBADMSG; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; int r; - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); - while (p->rindex < saved_rindex + size) { + while (p->rindex < rewinder.saved_rindex + size) { r = dns_packet_read_type_window(p, types, NULL); if (r < 0) - goto fail; + return r; /* don't read past end of current RR */ - if (p->rindex > saved_rindex + size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex > rewinder.saved_rindex + size) + return -EBADMSG; } - if (p->rindex != saved_rindex + size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex != rewinder.saved_rindex + size) + return -EBADMSG; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; _cleanup_free_ char *name = NULL; bool cache_flush = false; uint16_t class, type; DnsResourceKey *key; - size_t saved_rindex; int r; assert(p); assert(ret); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_name(p, &name, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &class, NULL); if (r < 0) - goto fail; + return r; if (p->protocol == DNS_PROTOCOL_MDNS) { /* See RFC6762, Section 10.2 */ @@ -1554,10 +1544,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus } key = dns_resource_key_new_consume(class, type, name); - if (!key) { - r = -ENOMEM; - goto fail; - } + if (!key) + return -ENOMEM; name = NULL; *ret = key; @@ -1565,12 +1553,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus if (ret_cache_flush) *ret_cache_flush = cache_flush; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static bool loc_size_ok(uint8_t size) { @@ -1582,7 +1568,8 @@ static bool loc_size_ok(uint8_t size) { int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - size_t saved_rindex, offset; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t offset; uint16_t rdlength; bool cache_flush; int r; @@ -1590,27 +1577,22 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl assert(p); assert(ret); - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_key(p, &key, &cache_flush, NULL); if (r < 0) - goto fail; + return r; - if (!dns_class_is_valid_rr(key->class)|| - !dns_type_is_valid_rr(key->type)) { - r = -EBADMSG; - goto fail; - } + if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type)) + return -EBADMSG; rr = dns_resource_record_new(key); - if (!rr) { - r = -ENOMEM; - goto fail; - } + if (!rr) + return -ENOMEM; r = dns_packet_read_uint32(p, &rr->ttl, NULL); if (r < 0) - goto fail; + return r; /* RFC 2181, Section 8, suggests to * treat a TTL with the MSB set as a zero TTL. */ @@ -1619,12 +1601,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint16(p, &rdlength, NULL); if (r < 0) - goto fail; + return r; - if (p->rindex + rdlength > p->size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex + rdlength > p->size) + return -EBADMSG; offset = p->rindex; @@ -1633,13 +1613,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_SRV: r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->srv.port, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->srv.name, true, NULL); break; @@ -1653,7 +1633,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_HINFO: r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_string(p, &rr->hinfo.os, NULL); break; @@ -1709,27 +1689,27 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_SOA: r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); break; @@ -1737,7 +1717,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_MX: r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); break; @@ -1748,49 +1728,43 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint8(p, &t, &pos); if (r < 0) - goto fail; + return r; if (t == 0) { rr->loc.version = t; r = dns_packet_read_uint8(p, &rr->loc.size, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.size)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.size)) + return -EBADMSG; r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.horiz_pre)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.horiz_pre)) + return -EBADMSG; r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.vert_pre)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.vert_pre)) + return -EBADMSG; r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); if (r < 0) - goto fail; + return r; break; } else { @@ -1803,122 +1777,114 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_DS: r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 4, &rr->ds.digest, &rr->ds.digest_size, NULL); if (r < 0) - goto fail; + return r; - if (rr->ds.digest_size <= 0) { + if (rr->ds.digest_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_SSHFP: r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 2, &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, NULL); - if (rr->sshfp.fingerprint_size <= 0) { + if (rr->sshfp.fingerprint_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_DNSKEY: r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 4, &rr->dnskey.key, &rr->dnskey.key_size, NULL); - if (rr->dnskey.key_size <= 0) { + if (rr->dnskey.key_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_RRSIG: r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, &rr->rrsig.signature, &rr->rrsig.signature_size, NULL); - if (rr->rrsig.signature_size <= 0) { + if (rr->rrsig.signature_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; @@ -1933,11 +1899,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); - if (r < 0) - goto fail; /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records @@ -1950,41 +1914,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); if (r < 0) - goto fail; + return r; /* this may be zero */ r = dns_packet_read_uint8(p, &size, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &size, NULL); if (r < 0) - goto fail; + return r; - if (size <= 0) { - r = -EBADMSG; - goto fail; - } + if (size <= 0) + return -EBADMSG; - r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL); + r = dns_packet_read_memdup(p, size, + &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, + NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); - if (r < 0) - goto fail; /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ @@ -1994,25 +1956,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_TLSA: r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 3, &rr->tlsa.data, &rr->tlsa.data_size, NULL); - if (rr->tlsa.data_size <= 0) { + + if (rr->tlsa.data_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; + + break; + + case DNS_TYPE_CAA: + r = dns_packet_read_uint8(p, &rr->caa.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->caa.tag, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, + rdlength + offset - p->rindex, + &rr->caa.value, &rr->caa.value_size, NULL); break; @@ -2021,16 +1997,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl default: unparseable: r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); - if (r < 0) - goto fail; + break; } if (r < 0) - goto fail; - if (p->rindex != offset + rdlength) { - r = -EBADMSG; - goto fail; - } + return r; + if (p->rindex != offset + rdlength) + return -EBADMSG; *ret = rr; rr = NULL; @@ -2038,12 +2011,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (ret_cache_flush) *ret_cache_flush = cache_flush; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { @@ -2091,23 +2062,21 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { int dns_packet_extract(DnsPacket *p) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; unsigned n, i; int r; if (p->extracted) return 0; - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); n = DNS_PACKET_QDCOUNT(p); if (n > 0) { question = dns_question_new(n); - if (!question) { - r = -ENOMEM; - goto finish; - } + if (!question) + return -ENOMEM; for (i = 0; i < n; i++) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; @@ -2115,21 +2084,17 @@ int dns_packet_extract(DnsPacket *p) { r = dns_packet_read_key(p, &key, &cache_flush, NULL); if (r < 0) - goto finish; + return r; - if (cache_flush) { - r = -EBADMSG; - goto finish; - } + if (cache_flush) + return -EBADMSG; - if (!dns_type_is_valid_query(key->type)) { - r = -EBADMSG; - goto finish; - } + if (!dns_type_is_valid_query(key->type)) + return -EBADMSG; r = dns_question_add(question, key); if (r < 0) - goto finish; + return r; } } @@ -2139,10 +2104,8 @@ int dns_packet_extract(DnsPacket *p) { bool bad_opt = false; answer = dns_answer_new(n); - if (!answer) { - r = -ENOMEM; - goto finish; - } + if (!answer) + return -ENOMEM; for (i = 0; i < n; i++) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; @@ -2150,7 +2113,7 @@ int dns_packet_extract(DnsPacket *p) { r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); if (r < 0) - goto finish; + return r; /* Try to reduce memory usage a bit */ if (previous) @@ -2167,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."); @@ -2213,7 +2176,7 @@ int dns_packet_extract(DnsPacket *p) { (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0)); if (r < 0) - goto finish; + return r; } /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note @@ -2234,11 +2197,8 @@ int dns_packet_extract(DnsPacket *p) { p->extracted = true; - r = 0; - -finish: - p->rindex = saved_rindex; - return r; + /* no CANCEL, always rewind */ + return 0; } int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) { diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 0bf34d270c..416335d0a2 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -262,11 +262,9 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); case DNS_PROTOCOL_MDNS: - return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4; + return f|(family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4); default: - break; + return f; } - - return 0; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index a378b2b7f7..ea04e58d61 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -62,6 +62,7 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) { while ((t = set_steal_first(c->transactions))) { set_remove(t->notify_query_candidates, c); + set_remove(t->notify_query_candidates_done, c); dns_transaction_gc(t); } } @@ -139,6 +140,10 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource if (r < 0) goto gc; + r = set_ensure_allocated(&t->notify_query_candidates_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->notify_query_candidates, c); if (r < 0) goto gc; @@ -421,6 +426,7 @@ int dns_query_new( DnsResourceKey *key; bool good = false; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(m); @@ -471,31 +477,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); @@ -815,7 +810,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { switch (t->state) { case DNS_TRANSACTION_SUCCESS: { - /* We found a successfuly reply, merge it into the answer */ + /* We found a successfully reply, merge it into the answer */ r = dns_answer_extend(&q->answer, t->answer); if (r < 0) goto fail; @@ -937,7 +932,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) assert(q); - q->n_cname_redirects ++; + q->n_cname_redirects++; if (q->n_cname_redirects > CNAME_MAX) return -ELOOP; 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 40f8e28dfd..6a29a93a26 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "dns-domain.h" #include "dns-type.h" +#include "escape.h" #include "hexdecoct.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" @@ -65,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) @@ -95,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; @@ -157,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); @@ -171,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; @@ -203,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; @@ -230,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; @@ -242,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; @@ -267,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) { @@ -275,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); } @@ -284,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; @@ -306,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) { @@ -490,6 +498,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { free(rr->tlsa.data); break; + case DNS_TYPE_CAA: + free(rr->caa.tag); + free(rr->caa.value); + break; + case DNS_TYPE_OPENPGPKEY: default: free(rr->generic.data); @@ -697,6 +710,12 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor a->tlsa.matching_type == b->tlsa.matching_type && FIELD_EQUAL(a->tlsa, b->tlsa, data); + case DNS_TYPE_CAA: + return a->caa.flags == b->caa.flags && + streq(a->caa.tag, b->caa.tag) && + FIELD_EQUAL(a->caa, b->caa, value); + + case DNS_TYPE_OPENPGPKEY: default: return FIELD_EQUAL(a->generic, b->generic, data); } @@ -818,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); @@ -827,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) { @@ -966,7 +983,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { case DNS_TYPE_DNSKEY: { _cleanup_free_ char *alg = NULL; char *ss; - int n, n1; + int n; uint16_t key_tag; key_tag = dnssec_keytag(rr, true); @@ -975,9 +992,8 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (r < 0) return NULL; - r = asprintf(&s, "%s %n%u %u %s %n", + r = asprintf(&s, "%s %u %u %s %n", k, - &n1, rr->dnskey.flags, rr->dnskey.protocol, alg, @@ -992,14 +1008,12 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { return NULL; r = asprintf(&ss, "%s\n" - "%*s-- Flags:%s%s%s\n" - "%*s-- Key tag: %u", + " -- Flags:%s%s%s\n" + " -- Key tag: %u", s, - n1, "", rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "", rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "", rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "", - n1, "", key_tag); if (r < 0) return NULL; @@ -1102,40 +1116,52 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { case DNS_TYPE_TLSA: { const char *cert_usage, *selector, *matching_type; - char *ss; - int n; cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage); selector = tlsa_selector_to_string(rr->tlsa.selector); matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type); - r = asprintf(&s, "%s %u %u %u %n", + t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); + if (!t) + return NULL; + + r = asprintf(&s, + "%s %u %u %u %s\n" + " -- Cert. usage: %s\n" + " -- Selector: %s\n" + " -- Matching type: %s", k, rr->tlsa.cert_usage, rr->tlsa.selector, rr->tlsa.matching_type, - &n); + t, + cert_usage, + selector, + matching_type); if (r < 0) return NULL; - r = base64_append(&s, n, - rr->tlsa.data, rr->tlsa.data_size, - 8, columns()); - if (r < 0) + break; + } + + case DNS_TYPE_CAA: { + _cleanup_free_ char *value; + + value = octescape(rr->caa.value, rr->caa.value_size); + if (!value) return NULL; - r = asprintf(&ss, "%s\n" - "%*s-- Cert. usage: %s\n" - "%*s-- Selector: %s\n" - "%*s-- Matching type: %s", - s, - n - 6, "", cert_usage, - n - 6, "", selector, - n - 6, "", matching_type); + r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u", + k, + rr->caa.flags, + rr->caa.tag, + value, + rr->caa.flags ? "\n -- Flags:" : "", + rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL); if (r < 0) return NULL; - free(s); - s = ss; break; } @@ -1173,6 +1199,47 @@ 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_DNSKEY: + case DNS_TYPE_RRSIG: + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + return -EINVAL; + + case DNS_TYPE_SSHFP: + *out = rr->sshfp.fingerprint; + return rr->sshfp.fingerprint_size; + + case DNS_TYPE_TLSA: + *out = rr->tlsa.data; + return rr->tlsa.data_size; + + + case DNS_TYPE_OPENPGPKEY: + default: + *out = rr->generic.data; + return rr->generic.data_size; + } +} + int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) { DnsPacket packet = { @@ -1230,7 +1297,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; @@ -1253,7 +1320,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; @@ -1293,14 +1360,14 @@ 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; return !r; } -static void dns_resource_record_hash_func(const void *i, struct siphash *state) { +void dns_resource_record_hash_func(const void *i, struct siphash *state) { const DnsResourceRecord *rr = i; assert(rr); @@ -1427,7 +1494,13 @@ static void dns_resource_record_hash_func(const void *i, struct siphash *state) siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state); siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state); siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state); - siphash24_compress(&rr->tlsa.data, rr->tlsa.data_size, state); + siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state); + break; + + case DNS_TYPE_CAA: + siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state); + string_hash_func(rr->caa.tag, state); + siphash24_compress(rr->caa.value, rr->caa.value_size, state); break; case DNS_TYPE_OPENPGPKEY: diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 2e0dfbaba3..020a2abd77 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 directly, use dns_resource_key_name()! */ }; /* Creates a temporary resource key. This is only useful to quickly @@ -249,19 +250,17 @@ struct DnsResourceRecord { void *data; size_t data_size; } tlsa; + + /* https://tools.ietf.org/html/rfc6844 */ + struct { + uint8_t flags; + char *tag; + void *value; + size_t value_size; + } caa; }; }; -static inline const 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; @@ -289,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) { @@ -323,6 +330,8 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr); DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); +void dns_resource_record_hash_func(const void *i, struct siphash *state); + extern const struct hash_ops dns_resource_key_hash_ops; extern const struct hash_ops dns_resource_record_hash_ops; 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-server.c b/src/resolve/resolved-dns-server.c index 27342a0e04..3095c042db 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -120,7 +120,7 @@ DnsServer* dns_server_ref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref ++; + s->n_ref++; return s; } @@ -130,7 +130,7 @@ DnsServer* dns_server_unref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref --; + s->n_ref--; if (s->n_ref > 0) return NULL; @@ -157,6 +157,7 @@ void dns_server_unlink(DnsServer *s) { assert(s->link); assert(s->link->n_dns_servers > 0); LIST_REMOVE(servers, s->link->dns_servers, s); + s->link->n_dns_servers--; break; case DNS_SERVER_SYSTEM: @@ -290,9 +291,9 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le if (s->possible_feature_level == level) { if (protocol == IPPROTO_UDP) - s->n_failed_udp ++; + s->n_failed_udp++; else if (protocol == IPPROTO_TCP) - s->n_failed_tcp ++; + s->n_failed_tcp++; } if (s->resend_timeout > usec) 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 d48fdd1281..a4a67623e7 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -52,6 +52,7 @@ static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) { while ((z = set_steal_first(t->dnssec_transactions))) { set_remove(z->notify_transactions, t); + set_remove(z->notify_transactions_done, t); dns_transaction_gc(z); } } @@ -100,20 +101,31 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { set_remove(c->transactions, t); set_free(t->notify_query_candidates); + while ((c = set_steal_first(t->notify_query_candidates_done))) + set_remove(c->transactions, t); + set_free(t->notify_query_candidates_done); + while ((i = set_steal_first(t->notify_zone_items))) i->probe_transaction = NULL; set_free(t->notify_zone_items); + while ((i = set_steal_first(t->notify_zone_items_done))) + i->probe_transaction = NULL; + set_free(t->notify_zone_items_done); + while ((z = set_steal_first(t->notify_transactions))) set_remove(z->dnssec_transactions, t); set_free(t->notify_transactions); + while ((z = set_steal_first(t->notify_transactions_done))) + set_remove(z->dnssec_transactions, t); + set_free(t->notify_transactions_done); + dns_transaction_flush_dnssec_transactions(t); set_free(t->dnssec_transactions); dns_answer_unref(t->validated_keys); dns_resource_key_unref(t->key); - free(t->key_string); free(t); return NULL; @@ -128,8 +140,11 @@ bool dns_transaction_gc(DnsTransaction *t) { return true; if (set_isempty(t->notify_query_candidates) && + set_isempty(t->notify_query_candidates_done) && set_isempty(t->notify_zone_items) && - set_isempty(t->notify_transactions)) { + set_isempty(t->notify_zone_items_done) && + set_isempty(t->notify_transactions) && + set_isempty(t->notify_transactions_done)) { dns_transaction_free(t); return false; } @@ -210,7 +225,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) LIST_PREPEND(transactions_by_scope, s->transactions, t); t->scope = s; - s->manager->n_transactions_total ++; + s->manager->n_transactions_total++; if (ret) *ret = t; @@ -238,6 +253,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); @@ -246,15 +262,15 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { if (manager_our_packet(t->scope->manager, p) != 0) return; - in_addr_to_string(p->family, &p->sender, &pretty); + (void) in_addr_to_string(p->family, &p->sender, &pretty); log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", t->id, - dns_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), - pretty); + af_to_name_short(t->scope->family), + strnull(pretty)); /* RFC 4795, Section 4.1 says that the peer with the * lexicographically smaller IP address loses */ @@ -266,6 +282,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { log_debug("We have the lexicographically larger IP address and thus lost in the conflict."); t->block_gc++; + while ((z = set_first(t->notify_zone_items))) { /* First, make sure the zone item drops the reference * to us */ @@ -284,22 +301,25 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { DnsQueryCandidate *c; DnsZoneItem *z; 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 +332,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"); @@ -329,39 +349,17 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { * transaction isn't freed while we are still looking at it */ t->block_gc++; - SET_FOREACH(c, t->notify_query_candidates, i) + SET_FOREACH_MOVE(c, t->notify_query_candidates_done, t->notify_query_candidates) dns_query_candidate_notify(c); - SET_FOREACH(z, t->notify_zone_items, i) - dns_zone_item_notify(z); - - if (!set_isempty(t->notify_transactions)) { - DnsTransaction **nt; - unsigned j, n = 0; - - /* We need to be careful when notifying other - * transactions, as that might destroy other - * transactions in our list. Hence, in order to be - * able to safely iterate through the list of - * transactions, take a GC lock on all of them - * first. Then, in a second loop, notify them, but - * first unlock that specific transaction. */ - - nt = newa(DnsTransaction*, set_size(t->notify_transactions)); - SET_FOREACH(d, t->notify_transactions, i) { - nt[n++] = d; - d->block_gc++; - } + SWAP_TWO(t->notify_query_candidates, t->notify_query_candidates_done); - assert(n == set_size(t->notify_transactions)); - - for (j = 0; j < n; j++) { - if (set_contains(t->notify_transactions, nt[j])) - dns_transaction_notify(nt[j], t); + SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) + dns_zone_item_notify(z); + SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); - nt[j]->block_gc--; - dns_transaction_gc(nt[j]); - } - } + SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) + dns_transaction_notify(d, t); + SWAP_TWO(t->notify_transactions, t->notify_transactions_done); t->block_gc--; dns_transaction_gc(t); @@ -522,7 +520,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 +1207,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 +1235,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; @@ -1372,7 +1369,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { other->state = DNS_TRANSACTION_PENDING; other->next_attempt_after = ts; - qdcount ++; + qdcount++; if (dns_key_is_shared(other->key)) add_known_answers = true; @@ -1425,6 +1422,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 +1432,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 +1492,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 +1600,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; } } @@ -1619,6 +1620,10 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource if (r < 0) goto gc; + r = set_ensure_allocated(&aux->notify_transactions_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->dnssec_transactions, aux); if (r < 0) goto gc; @@ -1678,7 +1683,7 @@ static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const assert(t); - /* Check whether the specified name is in the the NTA + /* Check whether the specified name is in the NTA * database, either in the global one, or the link-local * one. */ @@ -1708,7 +1713,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) @@ -1799,7 +1804,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * - For unsigned SOA/NS we get the matching DS * - For unsigned CNAME/DNAME/DS we get the parent SOA RR * - For other unsigned RRs we get the matching SOA RR - * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR + * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR + * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR */ @@ -1816,7 +1822,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 +1839,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 +1857,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 +1867,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 +1886,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 +1928,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 +1975,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 +1986,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 +2017,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; @@ -2028,30 +2039,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { return r; if (r > 0) { const char *name; + uint16_t type = 0; - 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 - * the parent name instead. If this was a DS request, - * then it's signed when the parent zone is signed, - * hence ask the parent in that case, too. */ + /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this + * could also be used as indication that we are not at a zone apex, but in real world setups there are + * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even + * though they have further children. If this was a DS request, then it's signed when the parent zone + * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR, + * to see if that is signed. */ - if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { + if (t->key->type == DNS_TYPE_DS) { r = dns_name_parent(&name); - 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)); - else + if (r > 0) { + type = DNS_TYPE_SOA; + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).", + t->id, dns_resource_key_name(t->key)); + } else name = NULL; - } else - log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key)); + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) { + + type = DNS_TYPE_DS; + log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).", + t->id, dns_resource_key_name(t->key)); + + } else { + type = DNS_TYPE_SOA; + log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", + t->id, dns_resource_key_name(t->key)); + } if (name) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL; - soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name); + soa = dns_resource_key_new(t->key->class, type, name); if (!soa) return -ENOMEM; @@ -2118,7 +2141,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 +2167,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 +2210,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 +2224,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 +2249,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 +2296,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 +2311,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) @@ -2305,8 +2328,10 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe } static int dns_transaction_requires_nsec(DnsTransaction *t) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; DnsTransaction *dt; const char *name; + uint16_t type = 0; Iterator i; int r; @@ -2321,7 +2346,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,28 +2360,32 @@ 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)) { + if (t->key->type == DNS_TYPE_DS) { - /* We got a negative reply for this SOA/NS lookup? If - * so, then we are not at a zone apex, and thus should - * look at the result of the parent SOA lookup. - * - * We got a negative reply for this DS lookup? DS RRs - * are signed when their parent zone is signed, hence - * also check the parent SOA in this case. */ + /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed, + * hence check the parent SOA in this case. */ r = dns_name_parent(&name); if (r < 0) return r; if (r == 0) return true; - } + + type = DNS_TYPE_SOA; + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) + /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */ + type = DNS_TYPE_DS; + else + /* For all other negative replies, check for the SOA lookup */ + type = DNS_TYPE_SOA; /* For all other RRs we check the SOA on the same level to see * if it's signed. */ @@ -2365,10 +2394,10 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dt->key->class != t->key->class) continue; - if (dt->key->type != DNS_TYPE_SOA) + if (dt->key->type != type) 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 +2419,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 +2442,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 +2459,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 +2489,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 +2570,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; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } - log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result)); + 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 (result == DNSSEC_VALIDATED) { + dns_server_packet_rrsig_missing(t->server, t->current_feature_level); - 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. */ + if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { - r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; + /* 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 +2954,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 +2964,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 +2974,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 +3019,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..eaece91533 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; @@ -119,17 +118,17 @@ struct DnsTransaction { /* Query candidates this transaction is referenced by and that * shall be notified about this specific transaction * completing. */ - Set *notify_query_candidates; + Set *notify_query_candidates, *notify_query_candidates_done; /* Zone items this transaction is referenced by and that shall * be notified about completion. */ - Set *notify_zone_items; + Set *notify_zone_items, *notify_zone_items_done; /* Other transactions that this transactions is referenced by * and that shall be notified about completion. This is used * when transactions want to validate their RRsets, but need * another DNSKEY or DS RR to do so. */ - Set *notify_transactions; + Set *notify_transactions, *notify_transactions_done; /* The opposite direction: the transactions this transaction * created in order to request DNSKEY or DS RRs. */ @@ -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..850eed8cb8 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -38,6 +38,7 @@ void dns_zone_item_probe_stop(DnsZoneItem *i) { i->probe_transaction = NULL; set_remove(t->notify_zone_items, i); + set_remove(t->notify_zone_items_done, i); dns_transaction_gc(t); } @@ -68,12 +69,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 +148,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 +170,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; @@ -186,6 +187,10 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (r < 0) goto gc; + r = set_ensure_allocated(&t->notify_zone_items_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->notify_zone_items, i); if (r < 0) goto gc; @@ -303,7 +308,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 +344,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 +375,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 +398,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 +423,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 +435,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 +487,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 +567,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 +598,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..40d650949d 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -301,7 +301,7 @@ int manager_etc_hosts_read(Manager *m) { FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) { char *l; - nr ++; + nr++; l = strstrip(line); if (isempty(l)) @@ -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-link-bus.c b/src/resolve/resolved-link-bus.c index df7516f4f4..7f21891819 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -239,7 +239,7 @@ clear: return r; } -int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { +int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { Link *l = userdata; int r; @@ -457,10 +457,10 @@ const sd_bus_vtable link_vtable[] = { SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0), SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0), SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), - SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0), SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0), - SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_search_domains, 0), + SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0), SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0), SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0), SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0), diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h index 31e6cd2b45..646031b631 100644 --- a/src/resolve/resolved-link-bus.h +++ b/src/resolve/resolved-link-bus.h @@ -30,7 +30,7 @@ char *link_bus_path(Link *link); int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index c5863b3aa2..b0dc65036d 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -468,7 +468,7 @@ static void link_read_settings(Link *l) { } if (r > 0) { - /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */ + /* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */ if (l->is_managed) link_flush_settings(l); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index ef12abfbb5..8b1d71a3eb 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -286,7 +286,7 @@ static int on_llmnr_stream_packet(DnsStream *s) { scope = manager_find_scope(s->manager, s->read_packet); if (!scope) { - log_warning("Got LLMNR TCP packet on unknown scope. Ignroing."); + log_warning("Got LLMNR TCP packet on unknown scope. Ignoring."); return 0; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index e82c6ec563..7166b94d71 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -485,7 +485,7 @@ int manager_new(Manager **ret) { m->llmnr_support = RESOLVE_SUPPORT_YES; m->mdns_support = RESOLVE_SUPPORT_NO; - m->dnssec_mode = DNSSEC_NO; + m->dnssec_mode = DEFAULT_DNSSEC_MODE; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY; @@ -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/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 065427b690..ff03acc772 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -158,7 +158,7 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { if (*count == MAXNS) fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); - (*count) ++; + (*count)++; fprintf(f, "nameserver %s\n", s->server_string); } @@ -184,7 +184,7 @@ static void write_resolv_conf_search( } (*length) += strlen(domain); - (*count) ++; + (*count)++; fputc(' ', f); fputs(domain, f); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index c7e2ab14d6..161ea03412 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = mac_selinux_init(NULL); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index efc9c6733a..a288588924 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,4 +16,4 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes -#DNSSEC=no +#DNSSEC=@DEFAULT_DNSSEC_MODE@ diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..a383c6286d --- /dev/null +++ b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..15de02e997 --- /dev/null +++ b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts Binary files differnew file mode 100644 index 0000000000..1c3ecc5491 --- /dev/null +++ b/src/resolve/test-data/fake-caa.pkts diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..17874844d9 --- /dev/null +++ b/src/resolve/test-data/fedoraproject.org.pkts diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts Binary files differnew file mode 100644 index 0000000000..5ef51e0c8e --- /dev/null +++ b/src/resolve/test-data/gandi.net.pkts diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts Binary files differnew file mode 100644 index 0000000000..f98c4cd855 --- /dev/null +++ b/src/resolve/test-data/google.com.pkts diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts Binary files differnew file mode 100644 index 0000000000..e28a725c9a --- /dev/null +++ b/src/resolve/test-data/kyhwana.org.pkts diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts Binary files differnew file mode 100644 index 0000000000..54ba668c75 --- /dev/null +++ b/src/resolve/test-data/root.pkts diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts Binary files differnew file mode 100644 index 0000000000..a854249532 --- /dev/null +++ b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts Binary files differnew file mode 100644 index 0000000000..11deb39677 --- /dev/null +++ b/src/resolve/test-data/teamits.com.pkts diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..f0a6f982df --- /dev/null +++ b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c new file mode 100644 index 0000000000..c232a69ce1 --- /dev/null +++ b/src/resolve/test-dns-packet.c @@ -0,0 +1,114 @@ +/*** + This file is part of systemd + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <net/if.h> +#include <glob.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "glob-util.h" +#include "log.h" +#include "macro.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-rr.h" +#include "string-util.h" +#include "strv.h" + +#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1) + +static uint64_t hash(DnsResourceRecord *rr) { + struct siphash state; + + siphash24_init(&state, HASH_KEY.bytes); + dns_resource_record_hash_func(rr, &state); + return siphash24_finalize(&state); +} + +static void test_packet_from_file(const char* filename, bool canonical) { + _cleanup_free_ char *data = NULL; + size_t data_size, packet_size, offset; + + assert_se(read_full_file(filename, &data, &data_size) >= 0); + assert_se(data); + assert_se(data_size > 8); + + log_info("============== %s %s==============", filename, canonical ? "canonical " : ""); + + for (offset = 0; offset < data_size; offset += 8 + packet_size) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL; + const char *s, *s2; + uint64_t hash1, hash2; + + packet_size = le64toh( *(uint64_t*)(data + offset) ); + assert_se(packet_size > 0); + assert_se(offset + 8 + packet_size <= data_size); + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0); + + assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0); + + s = dns_resource_record_to_string(rr); + assert_se(s); + puts(s); + + hash1 = hash(rr); + + assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0); + + assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0); + assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0); + + s2 = dns_resource_record_to_string(rr); + assert_se(s2); + assert_se(streq(s, s2)); + + hash2 = hash(rr); + assert_se(hash1 == hash2); + } +} + +int main(int argc, char **argv) { + int i, N; + _cleanup_globfree_ glob_t g = {}; + char **fnames; + + log_parse_environment(); + + if (argc >= 2) { + N = argc - 1; + fnames = argv + 1; + } else { + assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0); + N = g.gl_pathc; + fnames = g.gl_pathv; + } + + for (i = 0; i < N; i++) { + test_packet_from_file(fnames[i], false); + puts(""); + test_packet_from_file(fnames[i], true); + if (i + 1 < N) + puts(""); + } + + return EXIT_SUCCESS; +} diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index a093d86a91..b3018e8239 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -27,77 +27,89 @@ #include "string-util.h" #include "hexdecoct.h" -static void test_dnssec_verify_rrset2(void) { +static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { + char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - static const uint8_t signature_blob[] = { - 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, - 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, - 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, - 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, - 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, - 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, - 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, - 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, - }; + assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); + if (r < 0) + return; + + assert_se(streq(canonicalized, canonical)); +} +static void test_dnssec_canonicalize(void) { + test_dnssec_canonicalize_one("", ".", 1); + test_dnssec_canonicalize_one(".", ".", 1); + test_dnssec_canonicalize_one("foo", "foo.", 4); + test_dnssec_canonicalize_one("foo.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); + test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); +} + +#ifdef HAVE_GCRYPT + +static void test_dnssec_verify_dns_key(void) { + + static const uint8_t ds1_fprint[] = { + 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, + 0x80, 0x67, 0x14, 0x01, + }; + static const uint8_t ds2_fprint[] = { + 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, + 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + }; static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, - 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, - 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, - 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, - 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, - 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, - 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, - 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, - 0x74, 0x62, 0xfe, 0xd7, + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, + 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, + 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, + 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, + 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, + 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, + 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, + 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, + 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, + 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, + 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, + 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, + 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, + 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, + 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, + 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, + 0xe7, 0xea, 0x77, 0x03, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - DnssecResult result; - - nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); - assert_se(nsec); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; - nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); - assert_se(nsec->nsec.next_domain_name); + /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ + ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); + assert_se(ds1); - nsec->nsec.types = bitmap_new(); - assert_se(nsec->nsec.types); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); - assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); + ds1->ds.key_tag = 47857; + ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; + ds1->ds.digest_size = sizeof(ds1_fprint); + ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); + assert_se(ds1->ds.digest); - log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); + log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); - rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); - assert_se(rrsig); + ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); + assert_se(ds2); - rrsig->rrsig.type_covered = DNS_TYPE_NSEC; - rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; - rrsig->rrsig.labels = 2; - rrsig->rrsig.original_ttl = 300; - rrsig->rrsig.expiration = 0x5689002f; - rrsig->rrsig.inception = 0x56617230; - rrsig->rrsig.key_tag = 30390; - rrsig->rrsig.signer = strdup("Nasa.Gov."); - assert_se(rrsig->rrsig.signer); - rrsig->rrsig.signature_size = sizeof(signature_blob); - rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); - assert_se(rrsig->rrsig.signature); + ds2->ds.key_tag = 47857; + ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; + ds2->ds.digest_size = sizeof(ds2_fprint); + ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); + assert_se(ds2->ds.digest); - log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); assert_se(dnskey); - dnskey->dnskey.flags = 256; + dnskey->dnskey.flags = 257; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -107,16 +119,8 @@ static void test_dnssec_verify_rrset2(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); - assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - - answer = dns_answer_new(1); - assert_se(answer); - assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); - - /* Validate the RR as it if was 2015-12-11 today */ - assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); - assert_se(result == DNSSEC_VALIDATED); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); } static void test_dnssec_verify_rrset(void) { @@ -198,67 +202,77 @@ static void test_dnssec_verify_rrset(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_dns_key(void) { +static void test_dnssec_verify_rrset2(void) { - static const uint8_t ds1_fprint[] = { - 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, - 0x80, 0x67, 0x14, 0x01, - }; - static const uint8_t ds2_fprint[] = { - 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, - 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + static const uint8_t signature_blob[] = { + 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, + 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, + 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, + 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, + 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, + 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, + 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, + 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, }; + static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, - 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, - 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, - 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, - 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, - 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, - 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, - 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, - 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, - 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, - 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, - 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, - 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, - 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, - 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, - 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, - 0xe7, 0xea, 0x77, 0x03, + 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, + 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, + 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, + 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, + 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, + 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, + 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, + 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, + 0x74, 0x62, 0xfe, 0xd7, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; - /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ - ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); - assert_se(ds1); + nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); + assert_se(nsec); - ds1->ds.key_tag = 47857; - ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; - ds1->ds.digest_size = sizeof(ds1_fprint); - ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); - assert_se(ds1->ds.digest); + nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); + assert_se(nsec->nsec.next_domain_name); - log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); + nsec->nsec.types = bitmap_new(); + assert_se(nsec->nsec.types); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); + assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); - ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); - assert_se(ds2); + log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); - ds2->ds.key_tag = 47857; - ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; - ds2->ds.digest_size = sizeof(ds2_fprint); - ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); - assert_se(ds2->ds.digest); + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); + assert_se(rrsig); - log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); + rrsig->rrsig.type_covered = DNS_TYPE_NSEC; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 300; + rrsig->rrsig.expiration = 0x5689002f; + rrsig->rrsig.inception = 0x56617230; + rrsig->rrsig.key_tag = 30390; + rrsig->rrsig.signer = strdup("Nasa.Gov."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); assert_se(dnskey); - dnskey->dnskey.flags = 257; + dnskey->dnskey.flags = 256; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -268,28 +282,16 @@ static void test_dnssec_verify_dns_key(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); -} - -static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { - char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - - assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); - if (r < 0) - return; + assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - assert_se(streq(canonicalized, canonical)); -} + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); -static void test_dnssec_canonicalize(void) { - test_dnssec_canonicalize_one("", ".", 1); - test_dnssec_canonicalize_one(".", ".", 1); - test_dnssec_canonicalize_one("foo", "foo.", 4); - test_dnssec_canonicalize_one("foo.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); - test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); + /* Validate the RR as it if was 2015-12-11 today */ + assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); } static void test_dnssec_nsec3_hash(void) { @@ -324,13 +326,18 @@ static void test_dnssec_nsec3_hash(void) { assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); } +#endif + int main(int argc, char*argv[]) { test_dnssec_canonicalize(); + +#ifdef HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rrset(); test_dnssec_verify_rrset2(); test_dnssec_nsec3_hash(); +#endif return 0; } diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c 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; } |