diff options
Diffstat (limited to 'src/resolve')
30 files changed, 469 insertions, 189 deletions
diff --git a/src/resolve/.gitignore b/src/resolve/.gitignore deleted file mode 100644 index f0835923b7..0000000000 --- a/src/resolve/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/resolved-gperf.c -/resolved.conf -/dns_type-from-name.gperf -/dns_type-from-name.h -/dns_type-list.txt -/dns_type-to-name.h diff --git a/src/resolve/Makefile b/src/resolve/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/resolve/meson.build b/src/resolve/meson.build index fe228784fa..75e654e60d 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -123,7 +123,7 @@ systemd_resolve_sources = (basic_dns_sources + systemd_resolve_only_sources + dns_type_headers) -if conf.get('ENABLE_RESOLVED', false) +if conf.get('ENABLE_RESOLVE') == 1 install_data('org.freedesktop.resolve1.conf', install_dir : dbuspolicydir) install_data('org.freedesktop.resolve1.service', @@ -149,7 +149,7 @@ tests += [ [libgcrypt, libgpg_error, libm], - 'ENABLE_RESOLVED'], + 'ENABLE_RESOLVE'], [['src/resolve/test-dns-packet.c', basic_dns_sources, @@ -158,7 +158,7 @@ tests += [ [libgcrypt, libgpg_error, libm], - 'ENABLE_RESOLVED'], + 'ENABLE_RESOLVE'], [['src/resolve/test-resolved-packet.c', basic_dns_sources, @@ -167,7 +167,7 @@ tests += [ [libgcrypt, libgpg_error, libm], - 'ENABLE_RESOLVED'], + 'ENABLE_RESOLVE'], [['src/resolve/test-dnssec.c', basic_dns_sources, @@ -176,12 +176,12 @@ tests += [ [libgcrypt, libgpg_error, libm], - 'ENABLE_RESOLVED'], + 'ENABLE_RESOLVE'], [['src/resolve/test-dnssec-complex.c', 'src/resolve/dns-type.c', dns_type_headers], [], [], - 'ENABLE_RESOLVED', 'manual'], + 'ENABLE_RESOLVE', 'manual'], ] diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index c62058917f..708378573f 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -72,6 +72,7 @@ static enum { MODE_STATISTICS, MODE_RESET_STATISTICS, MODE_FLUSH_CACHES, + MODE_RESET_SERVER_FEATURES, MODE_STATUS, } arg_mode = MODE_RESOLVE_HOST; @@ -357,7 +358,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) { int r; char ifname[IF_NAMESIZE] = ""; - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX); if (r < 0) return log_oom(); @@ -1055,6 +1056,24 @@ static int flush_caches(sd_bus *bus) { return 0; } +static int reset_server_features(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = sd_bus_call_method(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResetServerFeatures", + &error, + NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r)); + + return 0; +} + static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { char ***l = userdata; int r; @@ -1588,6 +1607,8 @@ static void help(void) { " --reset-statistics Reset resolver statistics\n" " --status Show link and server status\n" " --flush-caches Flush all local DNS caches\n" + " --reset-server-features\n" + " Forget learnt DNS server feature levels\n" , program_invocation_short_name); } @@ -1607,30 +1628,32 @@ static int parse_argv(int argc, char *argv[]) { ARG_RESET_STATISTICS, ARG_STATUS, ARG_FLUSH_CACHES, + ARG_RESET_SERVER_FEATURES, ARG_NO_PAGER, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "class", required_argument, NULL, 'c' }, - { "legend", required_argument, NULL, ARG_LEGEND }, - { "interface", required_argument, NULL, 'i' }, - { "protocol", required_argument, NULL, 'p' }, - { "cname", required_argument, NULL, ARG_CNAME }, - { "service", no_argument, NULL, ARG_SERVICE }, - { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, - { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, - { "openpgp", no_argument, NULL, ARG_OPENPGP }, - { "tlsa", optional_argument, NULL, ARG_TLSA }, - { "raw", optional_argument, NULL, ARG_RAW }, - { "search", required_argument, NULL, ARG_SEARCH }, - { "statistics", no_argument, NULL, ARG_STATISTICS, }, - { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, - { "status", no_argument, NULL, ARG_STATUS }, - { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "type", required_argument, NULL, 't' }, + { "class", required_argument, NULL, 'c' }, + { "legend", required_argument, NULL, ARG_LEGEND }, + { "interface", required_argument, NULL, 'i' }, + { "protocol", required_argument, NULL, 'p' }, + { "cname", required_argument, NULL, ARG_CNAME }, + { "service", no_argument, NULL, ARG_SERVICE }, + { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, + { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "openpgp", no_argument, NULL, ARG_OPENPGP }, + { "tlsa", optional_argument, NULL, ARG_TLSA }, + { "raw", optional_argument, NULL, ARG_RAW }, + { "search", required_argument, NULL, ARG_SEARCH }, + { "statistics", no_argument, NULL, ARG_STATISTICS, }, + { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, + { "status", no_argument, NULL, ARG_STATUS }, + { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES }, + { "reset-server-features", no_argument, NULL, ARG_RESET_SERVER_FEATURES }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, {} }; @@ -1814,6 +1837,10 @@ static int parse_argv(int argc, char *argv[]) { arg_mode = MODE_FLUSH_CACHES; break; + case ARG_RESET_SERVER_FEATURES: + arg_mode = MODE_RESET_SERVER_FEATURES; + break; + case ARG_STATUS: arg_mode = MODE_STATUS; break; @@ -1999,6 +2026,16 @@ int main(int argc, char **argv) { r = flush_caches(bus); break; + case MODE_RESET_SERVER_FEATURES: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + r = reset_server_features(bus); + break; + case MODE_STATUS: if (argc > optind) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 5aa2348576..c6f14eb416 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1569,6 +1569,17 @@ static int bus_method_flush_caches(sd_bus_message *message, void *userdata, sd_b return sd_bus_reply_method_return(message, NULL); } +static int bus_method_reset_server_features(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + assert(message); + assert(m); + + manager_reset_server_features(m); + + return sd_bus_reply_method_return(message, NULL); +} + static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), @@ -1587,6 +1598,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, 0), + SD_BUS_METHOD("ResetServerFeatures", NULL, NULL, bus_method_reset_server_features, 0), SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0), SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0), @@ -1644,8 +1656,7 @@ int manager_connect_bus(Manager *m) { r = sd_bus_default_system(&m->bus); if (r < 0) { /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. As soon as we have - * kdbus we can stop doing this... */ + * boot. Let's try in 5s again. */ log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 75636e0e56..3cf4261ff0 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -246,7 +246,7 @@ int manager_parse_config_file(Manager *m) { return r; } -#ifndef HAVE_GCRYPT +#if ! HAVE_GCRYPT if (m->dnssec_mode != DNSSEC_NO) { log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); m->dnssec_mode = DNSSEC_NO; diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index eddab58a81..33bb5a1f95 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#ifdef HAVE_GCRYPT +#if HAVE_GCRYPT #include <gcrypt.h> #endif @@ -125,7 +125,7 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { return (int) c; } -#ifdef HAVE_GCRYPT +#if HAVE_GCRYPT static int rr_compare(const void *a, const void *b) { DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; @@ -1247,10 +1247,10 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */ - if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1) + if (!IN_SET(rr->n_skip_labels_source, 0, (unsigned) -1)) return 0; /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */ - if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1) + if (!IN_SET(rr->n_skip_labels_signer, 1, (unsigned) -1)) return 0; if (!nsec3) diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 49a04615d4..e2f227bfc6 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -28,7 +28,6 @@ #define EDNS0_OPT_DO (1<<15) -#define DNS_PACKET_SIZE_START 512u assert_cc(DNS_PACKET_SIZE_START > DNS_PACKET_HEADER_SIZE) typedef struct DnsPacketRewinder { @@ -44,11 +43,20 @@ static void rewind_dns_packet(DnsPacketRewinder *rewinder) { #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 min_alloc_dsize) { +int dns_packet_new( + DnsPacket **ret, + DnsProtocol protocol, + size_t min_alloc_dsize, + size_t max_size) { + DnsPacket *p; size_t a; assert(ret); + assert(max_size >= DNS_PACKET_HEADER_SIZE); + + if (max_size > DNS_PACKET_SIZE_MAX) + max_size = DNS_PACKET_SIZE_MAX; /* The caller may not check what is going to be truly allocated, so do not allow to * allocate a DNS packet bigger than DNS_PACKET_SIZE_MAX. @@ -71,8 +79,8 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket)); /* make sure we never allocate more than useful */ - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; + if (a > max_size) + a = max_size; p = malloc0(ALIGN(sizeof(DnsPacket)) + a); if (!p) @@ -80,6 +88,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize p->size = p->rindex = DNS_PACKET_HEADER_SIZE; p->allocated = a; + p->max_size = max_size; p->protocol = protocol; p->opt_start = p->opt_size = (size_t) -1; p->n_ref = 1; @@ -145,7 +154,7 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc assert(ret); - r = dns_packet_new(&p, protocol, min_alloc_dsize); + r = dns_packet_new(&p, protocol, min_alloc_dsize, DNS_PACKET_SIZE_MAX); if (r < 0) return r; @@ -314,11 +323,13 @@ static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start assert(p); if (p->size + add > p->allocated) { - size_t a; + size_t a, ms; a = PAGE_ALIGN((p->size + add) * 2); - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; + + ms = dns_packet_size_max(p); + if (a > ms) + a = ms; if (p->size + add > a) return -EMSGSIZE; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index a65d6d38cf..b873c0f745 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -56,10 +56,14 @@ struct DnsPacketHeader { #define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr)) /* The various DNS protocols deviate in how large a packet can grow, - but the TCP transport has a 16bit size field, hence that appears to - be the absolute maximum. */ + * but the TCP transport has a 16bit size field, hence that appears to + * be the absolute maximum. */ #define DNS_PACKET_SIZE_MAX 0xFFFFu +/* The default size to use for allocation when we don't know how large + * the packet will turn out to be. */ +#define DNS_PACKET_SIZE_START 512u + /* RFC 1035 say 512 is the maximum, for classic unicast DNS */ #define DNS_PACKET_UNICAST_SIZE_MAX 512u @@ -69,7 +73,7 @@ struct DnsPacketHeader { struct DnsPacket { int n_ref; DnsProtocol protocol; - size_t size, allocated, rindex; + size_t size, allocated, rindex, max_size; void *_data; /* don't access directly, use DNS_PACKET_DATA()! */ Hashmap *names; /* For name compression */ size_t opt_start, opt_size; @@ -183,7 +187,7 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { (unsigned) DNS_PACKET_ARCOUNT(p); } -int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize); +int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, size_t max_size); int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, bool dnssec_checking_disabled); void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated); @@ -299,3 +303,13 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, return f; } } + +static inline size_t dns_packet_size_max(DnsPacket *p) { + assert(p); + + /* Why not insist on a fully initialized max_size during DnsPacket construction? Well, this way it's easy to + * allocate a transient, throw-away DnsPacket on the stack by simple zero initialization, without having to + * deal with explicit field initialization. */ + + return p->max_size != 0 ? p->max_size : DNS_PACKET_SIZE_MAX; +} diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 2b091e6c45..c2b29bc452 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -623,7 +623,18 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { q->question_utf8, q->ifindex, &answer); + if (r == -ENXIO) { + /* If we get ENXIO this tells us to generate NXDOMAIN unconditionally. */ + dns_query_reset_answer(q); + q->answer_rcode = DNS_RCODE_NXDOMAIN; + q->answer_protocol = dns_synthesize_protocol(q->flags); + q->answer_family = dns_synthesize_family(q->flags); + q->answer_authenticated = true; + *state = DNS_TRANSACTION_RCODE_FAILURE; + + return 0; + } if (r <= 0) return r; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index ffaefbe3f2..ca54158898 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -632,7 +632,7 @@ int dns_scope_make_reply_packet( dns_answer_isempty(soa)) return -EINVAL; - r = dns_packet_new(&p, s->protocol, 0); + r = dns_packet_new(&p, s->protocol, 0, DNS_PACKET_SIZE_MAX); if (r < 0) return r; @@ -818,7 +818,7 @@ static int dns_scope_make_conflict_packet( assert(rr); assert(ret); - r = dns_packet_new(&p, s->protocol, 0); + r = dns_packet_new(&p, s->protocol, 0, DNS_PACKET_SIZE_MAX); if (r < 0) return r; @@ -904,7 +904,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { * messages, not all of them. That should be enough to * indicate where there might be a conflict */ r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr); - if (r == -EEXIST || r == 0) + if (IN_SET(r, 0, -EEXIST)) return 0; if (r < 0) return log_debug_errno(r, "Failed to queue conflicting RR: %m"); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index b3d37525f4..1b61dea626 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -70,15 +70,12 @@ int dns_server_new( s->n_ref = 1; s->manager = m; - s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; - s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; - s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; - s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX; s->type = type; s->family = family; s->address = *in_addr; s->ifindex = ifindex; - s->resend_timeout = DNS_TIMEOUT_MIN_USEC; + + dns_server_reset_features(s); switch (type) { @@ -828,6 +825,85 @@ void dns_server_flush_cache(DnsServer *s) { dns_cache_flush(&scope->cache); } +void dns_server_reset_features(DnsServer *s) { + assert(s); + + s->max_rtt = 0; + s->resend_timeout = DNS_TIMEOUT_MIN_USEC; + + s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; + + s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX; + + s->packet_bad_opt = false; + s->packet_rrsig_missing = false; + + s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; + + s->warned_downgrade = false; + + dns_server_reset_counters(s); +} + +void dns_server_reset_features_all(DnsServer *s) { + DnsServer *i; + + LIST_FOREACH(servers, i, s) + dns_server_reset_features(i); +} + +void dns_server_dump(DnsServer *s, FILE *f) { + assert(s); + + if (!f) + f = stdout; + + fputs("[Server ", f); + fputs(dns_server_string(s), f); + fputs(" type=", f); + fputs(dns_server_type_to_string(s->type), f); + + if (s->type == DNS_SERVER_LINK) { + assert(s->link); + + fputs(" interface=", f); + fputs(s->link->name, f); + } + + fputs("]\n", f); + + fputs("\tVerified feature level: ", f); + fputs(strna(dns_server_feature_level_to_string(s->verified_feature_level)), f); + fputc('\n', f); + + fputs("\tPossible feature level: ", f); + fputs(strna(dns_server_feature_level_to_string(s->possible_feature_level)), f); + fputc('\n', f); + + fputs("\tDNSSEC Mode: ", f); + fputs(strna(dnssec_mode_to_string(dns_server_get_dnssec_mode(s))), f); + fputc('\n', f); + + fputs("\tCan do DNSSEC: ", f); + fputs(yes_no(dns_server_dnssec_supported(s)), f); + fputc('\n', f); + + fprintf(f, + "\tMaximum UDP packet size received: %zu\n" + "\tFailed UDP attempts: %u\n" + "\tFailed TCP attempts: %u\n" + "\tSeen truncated packet: %s\n" + "\tSeen OPT RR getting lost: %s\n" + "\tSeen RRSIG RR missing: %s\n", + s->received_udp_packet_max, + s->n_failed_udp, + s->n_failed_tcp, + yes_no(s->packet_truncated), + yes_no(s->packet_bad_opt), + yes_no(s->packet_rrsig_missing)); +} + static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { [DNS_SERVER_SYSTEM] = "system", [DNS_SERVER_FALLBACK] = "fallback", diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index bc95d53c6a..00edd47d9a 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -151,3 +151,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; void dns_server_flush_cache(DnsServer *s); + +void dns_server_reset_features(DnsServer *s); +void dns_server_reset_features_all(DnsServer *s); + +void dns_server_dump(DnsServer *s, FILE *f); diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index 878bae47dc..5892534687 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -221,7 +221,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use ss = writev(fd, iov, 2); if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) + if (!IN_SET(errno, EINTR, EAGAIN)) return dns_stream_complete(s, errno); } else s->n_written += ss; @@ -243,7 +243,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read); if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) + if (!IN_SET(errno, EINTR, EAGAIN)) return dns_stream_complete(s, errno); } else if (ss == 0) return dns_stream_complete(s, ECONNRESET); @@ -260,7 +260,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use ssize_t ss; if (!s->read_packet) { - r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size)); + r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size), DNS_PACKET_SIZE_MAX); if (r < 0) return dns_stream_complete(s, -r); @@ -293,7 +293,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size), sizeof(s->read_size) + be16toh(s->read_size) - s->n_read); if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) + if (!IN_SET(errno, EINTR, EAGAIN)) return dns_stream_complete(s, errno); } else if (ss == 0) return dns_stream_complete(s, ECONNRESET); diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 7afbfedfb0..5bc79a313e 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -30,9 +30,12 @@ static int manager_dns_stub_tcp_fd(Manager *m); static int dns_stub_make_reply_packet( DnsPacket **p, + size_t max_size, DnsQuestion *q, - DnsAnswer *answer) { + DnsAnswer *answer, + bool *ret_truncated) { + bool truncated = false; DnsResourceRecord *rr; unsigned c = 0; int r; @@ -43,7 +46,7 @@ static int dns_stub_make_reply_packet( * roundtrips aren't expensive. */ if (!*p) { - r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0); + r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size); if (r < 0) return r; @@ -71,12 +74,21 @@ static int dns_stub_make_reply_packet( continue; add: r = dns_packet_append_rr(*p, rr, 0, NULL, NULL); + if (r == -EMSGSIZE) { + truncated = true; + break; + } if (r < 0) return r; c++; } + if (ret_truncated) + *ret_truncated = truncated; + else if (truncated) + return -EMSGSIZE; + DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c); return 0; @@ -86,6 +98,7 @@ static int dns_stub_finish_reply_packet( DnsPacket *p, uint16_t id, int rcode, + bool tc, /* set the Truncated bit? */ bool add_opt, /* add an OPT RR to this packet? */ bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */ bool ad) { /* set the DNSSEC authenticated data bit? */ @@ -110,14 +123,14 @@ static int dns_stub_finish_reply_packet( DNS_PACKET_HEADER(p)->id = id; DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 1 /* qr */, - 0 /* opcode */, - 0 /* aa */, - 0 /* tc */, - 1 /* rd */, - 1 /* ra */, + 1 /* qr */, + 0 /* opcode */, + 0 /* aa */, + tc /* tc */, + 1 /* rd */, + 1 /* ra */, ad /* ad */, - 0 /* cd */, + 0 /* cd */, rcode)); if (add_opt) { @@ -149,12 +162,6 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl else { int fd; - /* Truncate the message to the right size */ - if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) { - dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX); - DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC); - } - fd = manager_dns_stub_udp_fd(m); if (fd < 0) return log_debug_errno(fd, "Failed to get reply socket: %m"); @@ -178,11 +185,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco assert(m); assert(p); - r = dns_stub_make_reply_packet(&reply, p->question, NULL); + r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL); if (r < 0) return log_debug_errno(r, "Failed to make failure packet: %m"); - r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), authenticated); + r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated); if (r < 0) return log_debug_errno(r, "Failed to build failure packet: %m"); @@ -197,9 +204,10 @@ static void dns_stub_query_complete(DnsQuery *q) { switch (q->state) { - case DNS_TRANSACTION_SUCCESS: + case DNS_TRANSACTION_SUCCESS: { + bool truncated; - r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer); + r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated); if (r < 0) { log_debug_errno(r, "Failed to build reply packet: %m"); break; @@ -221,6 +229,7 @@ static void dns_stub_query_complete(DnsQuery *q) { q->reply_dns_packet, DNS_PACKET_ID(q->request_dns_packet), q->answer_rcode, + truncated, !!q->request_dns_packet->opt, DNS_PACKET_DO(q->request_dns_packet), dns_query_fully_authenticated(q)); @@ -231,6 +240,7 @@ static void dns_stub_query_complete(DnsQuery *q) { (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet); break; + } case DNS_TRANSACTION_RCODE_FAILURE: (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q)); @@ -467,7 +477,7 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return -errno; @@ -543,6 +553,14 @@ int manager_dns_stub_start(Manager *m) { assert(m); + if (m->dns_stub_listener_mode == DNS_STUB_LISTENER_NO) + log_debug("Not creating stub listener."); + else + log_debug("Creating stub listener using %s.", + m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" : + m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" : + "UDP/TCP"); + if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP)) r = manager_dns_stub_udp_fd(m); diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index e3003411f7..e8592a60d8 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -186,6 +186,7 @@ static int answer_add_addresses_ptr( unsigned n_addresses, int af, const union in_addr_union *match) { + bool added = false; unsigned j; int r; @@ -215,9 +216,11 @@ static int answer_add_addresses_ptr( r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; + + added = true; } - return 0; + return added; } static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { @@ -240,21 +243,23 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, /* If we have no local addresses then use ::1 * and 127.0.0.2 as local ones. */ - if (af == AF_INET || af == AF_UNSPEC) + if (IN_SET(af, AF_INET, AF_UNSPEC)) buffer[n++] = (struct local_address) { .family = AF_INET, .ifindex = dns_synthesize_ifindex(ifindex), .address.in.s_addr = htobe32(0x7F000002), }; - if (af == AF_INET6 || af == AF_UNSPEC) + if (IN_SET(af, AF_INET6, AF_UNSPEC)) buffer[n++] = (struct local_address) { .family = AF_INET6, .ifindex = dns_synthesize_ifindex(ifindex), .address.in6 = in6addr_loopback, }; - return answer_add_addresses_rr(answer, dns_resource_key_name(key), buffer, n); + return answer_add_addresses_rr(answer, + dns_resource_key_name(key), + buffer, n); } } @@ -263,6 +268,7 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { _cleanup_free_ struct local_address *addresses = NULL; + bool added = false; int n, r; assert(m); @@ -271,10 +277,13 @@ static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_add if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { - /* Always map the IPv4 address 127.0.0.2 to the local - * hostname, in addition to "localhost": */ + /* Always map the IPv4 address 127.0.0.2 to the local hostname, in addition to "localhost": */ - r = dns_answer_reserve(answer, 3); + r = dns_answer_reserve(answer, 4); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; @@ -290,23 +299,37 @@ static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_add if (r < 0) return r; - return 0; + return 1; } n = local_addresses(m->rtnl, ifindex, af, &addresses); - if (n < 0) + if (n <= 0) return n; + r = answer_add_addresses_ptr(answer, m->full_hostname, addresses, n, af, address); + if (r < 0) + return r; + if (r > 0) + added = true; + r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address); if (r < 0) return r; + if (r > 0) + added = true; + + r = answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address); + if (r < 0) + return r; + if (r > 0) + added = true; - return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address); + return added; } static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { _cleanup_free_ struct local_address *addresses = NULL; - int n = 0, af; + int n = 0, af, r; assert(m); assert(key); @@ -315,11 +338,15 @@ static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifin af = dns_type_to_af(key->type); if (af >= 0) { n = local_gateways(m->rtnl, ifindex, af, &addresses); - if (n < 0) - return n; + if (n <= 0) + return n; /* < 0 means: error; == 0 means we have no gateway */ } - return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); + r = answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); + if (r < 0) + return r; + + return 1; /* > 0 means: we have some gateway */ } static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { @@ -331,10 +358,10 @@ static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union assert(answer); n = local_gateways(m->rtnl, ifindex, af, &addresses); - if (n < 0) + if (n <= 0) return n; - return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address); + return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address); } int dns_synthesize_answer( @@ -345,7 +372,7 @@ int dns_synthesize_answer( _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; DnsResourceKey *key; - bool found = false; + bool found = false, nxdomain = false; int r; assert(m); @@ -356,8 +383,7 @@ int dns_synthesize_answer( const char *name; int af; - if (key->class != DNS_CLASS_IN && - key->class != DNS_CLASS_ANY) + if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY)) continue; name = dns_resource_key_name(key); @@ -379,6 +405,10 @@ int dns_synthesize_answer( r = synthesize_gateway_rr(m, key, ifindex, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize gateway RRs: %m"); + if (r == 0) { /* if we have no gateway return NXDOMAIN */ + nxdomain = true; + continue; + } } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) || dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) { @@ -388,26 +418,35 @@ int dns_synthesize_answer( return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); } else if (dns_name_address(name, &af, &address) > 0) { + int v, w; - r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer); - if (r < 0) + v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer); + if (v < 0) return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m"); - r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer); - if (r < 0) + w = synthesize_gateway_ptr(m, af, &address, ifindex, &answer); + if (w < 0) return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m"); + + if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */ + continue; + } else continue; found = true; } - r = found; + if (found) { - if (ret) { - *ret = answer; - answer = NULL; - } + if (ret) { + *ret = answer; + answer = NULL; + } + + return 1; + } else if (nxdomain) + return -ENXIO; - return r; + return 0; } diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 3075f62b5e..3cda429c3c 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -190,7 +190,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) return -EOPNOTSUPP; /* We only support the IN class */ - if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY) + if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY)) return -EOPNOTSUPP; if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX) @@ -1528,8 +1528,7 @@ int dns_transaction_go(DnsTransaction *t) { af_to_name_short(t->scope->family)); if (!t->initial_jitter_scheduled && - (t->scope->protocol == DNS_PROTOCOL_LLMNR || - t->scope->protocol == DNS_PROTOCOL_MDNS)) { + IN_SET(t->scope->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) { usec_t jitter, accuracy; /* RFC 4795 Section 2.7 suggests all queries should be @@ -1594,7 +1593,7 @@ int dns_transaction_go(DnsTransaction *t) { log_debug("Sending query via TCP since it is too large."); else if (r == -EAGAIN) log_debug("Sending query via TCP since server doesn't support UDP."); - if (r == -EMSGSIZE || r == -EAGAIN) + if (IN_SET(r, -EMSGSIZE, -EAGAIN)) r = dns_transaction_open_tcp(t); } diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index dda9875063..e169c8f02f 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -435,7 +435,7 @@ static int dns_trust_anchor_load_files( assert(suffix); assert(loader); - r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs); + r = conf_files_list_nulstr(&files, suffix, NULL, 0, trust_anchor_dirs); if (r < 0) return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 61a3f20362..3d26831b06 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -111,13 +111,30 @@ Link *link_free(Link *l) { } void link_allocate_scopes(Link *l) { + bool unicast_relevant; int r; assert(l); - if (link_relevant(l, AF_UNSPEC, false) && - l->dns_servers) { + /* If a link that used to be relevant is no longer, or a link that did not use to be relevant now becomes + * relevant, let's reinit the learnt global DNS server information, since we might talk to different servers + * now, even if they have the same addresses as before. */ + + unicast_relevant = link_relevant(l, AF_UNSPEC, false); + if (unicast_relevant != l->unicast_relevant) { + l->unicast_relevant = unicast_relevant; + + dns_server_reset_features_all(l->manager->fallback_dns_servers); + dns_server_reset_features_all(l->manager->dns_servers); + } + + /* And now, allocate all scopes that makes sense now if we didn't have them yet, and drop those which we don't + * need anymore */ + + if (unicast_relevant && l->dns_servers) { if (!l->unicast_scope) { + dns_server_reset_features_all(l->dns_servers); + r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC); if (r < 0) log_warning_errno(r, "Failed to allocate DNS scope: %m"); @@ -313,8 +330,8 @@ void link_set_dnssec_mode(Link *l, DnssecMode mode) { assert(l); -#ifndef HAVE_GCRYPT - if (mode == DNSSEC_YES || mode == DNSSEC_ALLOW_DOWNGRADE) +#if ! HAVE_GCRYPT + if (IN_SET(mode, DNSSEC_YES, DNSSEC_ALLOW_DOWNGRADE)) log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); return; #endif @@ -1050,7 +1067,7 @@ int link_save_user(Link *l) { if (r < 0) goto fail; - fputs("# This is private data. Do not parse.\n", f); + fputs_unlocked("# This is private data. Do not parse.\n", f); v = resolve_support_to_string(l->llmnr_support); if (v) @@ -1067,11 +1084,11 @@ int link_save_user(Link *l) { if (l->dns_servers) { DnsServer *server; - fputs("SERVERS=", f); + fputs_unlocked("SERVERS=", f); LIST_FOREACH(servers, server, l->dns_servers) { if (server != l->dns_servers) - fputc(' ', f); + fputc_unlocked(' ', f); v = dns_server_string(server); if (!v) { @@ -1079,26 +1096,26 @@ int link_save_user(Link *l) { goto fail; } - fputs(v, f); + fputs_unlocked(v, f); } - fputc('\n', f); + fputc_unlocked('\n', f); } if (l->search_domains) { DnsSearchDomain *domain; - fputs("DOMAINS=", f); + fputs_unlocked("DOMAINS=", f); LIST_FOREACH(domains, domain, l->search_domains) { if (domain != l->search_domains) - fputc(' ', f); + fputc_unlocked(' ', f); if (domain->route_only) - fputc('~', f); + fputc_unlocked('~', f); - fputs(DNS_SEARCH_DOMAIN_NAME(domain), f); + fputs_unlocked(DNS_SEARCH_DOMAIN_NAME(domain), f); } - fputc('\n', f); + fputc_unlocked('\n', f); } if (!set_isempty(l->dnssec_negative_trust_anchors)) { @@ -1106,16 +1123,16 @@ int link_save_user(Link *l) { Iterator i; char *nta; - fputs("NTAS=", f); + fputs_unlocked("NTAS=", f); SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) { if (space) - fputc(' ', f); + fputc_unlocked(' ', f); - fputs(nta, f); + fputs_unlocked(nta, f); space = true; } - fputc('\n', f); + fputc_unlocked('\n', f); } r = fflush_and_check(f); diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index 55a56b7906..c20b8b6d29 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -88,6 +88,8 @@ struct Link { bool loaded; char *state_file; + + bool unicast_relevant; }; int link_new(Manager *m, Link **ret, int ifindex); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 29396e9973..0cf4583572 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -345,7 +345,7 @@ static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *u cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return -errno; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index b6620875ea..23c6731954 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -21,7 +21,7 @@ #include <poll.h> #include <sys/ioctl.h> -#ifdef HAVE_LIBIDN2 +#if HAVE_LIBIDN2 #include <idn2.h> #endif @@ -328,9 +328,9 @@ static int manager_network_monitor_listen(Manager *m) { static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL; -#if defined(HAVE_LIBIDN2) +#if HAVE_LIBIDN2 _cleanup_free_ char *utf8 = NULL; -#elif defined(HAVE_LIBIDN) +#elif HAVE_LIBIDN int k; #endif char label[DNS_LABEL_MAX]; @@ -356,7 +356,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char return -EINVAL; } -#if defined(HAVE_LIBIDN2) +#if HAVE_LIBIDN2 r = idn2_to_unicode_8z8z(label, &utf8, 0); if (r != IDN2_OK) return log_error("Failed to undo IDNA: %s", idn2_strerror(r)); @@ -364,7 +364,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char r = strlen(utf8); decoded = utf8; -#elif defined(HAVE_LIBIDN) +#elif HAVE_LIBIDN k = dns_label_undo_idna(label, r, label, sizeof label); if (k < 0) return log_error_errno(k, "Failed to undo IDNA: %m"); @@ -519,8 +519,11 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si _cleanup_free_ char *buffer = NULL; _cleanup_fclose_ FILE *f = NULL; Manager *m = userdata; + DnsServer *server; size_t size = 0; DnsScope *scope; + Iterator i; + Link *l; assert(s); assert(si); @@ -533,6 +536,14 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si LIST_FOREACH(scopes, scope, m->dns_scopes) dns_scope_dump(scope, f); + LIST_FOREACH(servers, server, m->dns_servers) + dns_server_dump(server, f); + LIST_FOREACH(servers, server, m->fallback_dns_servers) + dns_server_dump(server, f); + HASHMAP_FOREACH(l, m->links, i) + LIST_FOREACH(servers, server, l->dns_servers) + dns_server_dump(server, f); + if (fflush_and_check(f) < 0) return log_oom(); @@ -552,6 +563,17 @@ static int manager_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si return 0; } +static int manager_sigrtmin1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(si); + assert(m); + + manager_reset_server_features(m); + return 0; +} + int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -616,6 +638,7 @@ int manager_new(Manager **ret) { (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); (void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m); + (void) sd_event_add_signal(m->event, &m->sigrtmin1_event_source, SIGRTMIN+1, manager_sigrtmin1, m); manager_cleanup_saved_user(m); @@ -679,6 +702,7 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->sigusr1_event_source); sd_event_source_unref(m->sigusr2_event_source); + sd_event_source_unref(m->sigrtmin1_event_source); sd_event_unref(m->event); @@ -723,7 +747,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { if (ms < 0) return ms; - r = dns_packet_new(&p, protocol, ms); + r = dns_packet_new(&p, protocol, ms, DNS_PACKET_SIZE_MAX); if (r < 0) return r; @@ -741,7 +765,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { if (l == 0) return 0; if (l < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return -errno; @@ -1396,6 +1420,19 @@ void manager_flush_caches(Manager *m) { log_info("Flushed all caches."); } +void manager_reset_server_features(Manager *m) { + Iterator i; + Link *l; + + dns_server_reset_features_all(m->dns_servers); + dns_server_reset_features_all(m->fallback_dns_servers); + + HASHMAP_FOREACH(l, m->links, i) + dns_server_reset_features_all(l->dns_servers); + + log_info("Resetting learnt feature levels on all servers."); +} + void manager_cleanup_saved_user(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 97c52b7729..32a0e5fe0f 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -126,6 +126,7 @@ struct Manager { sd_event_source *sigusr1_event_source; sd_event_source *sigusr2_event_source; + sd_event_source *sigrtmin1_event_source; unsigned n_transactions_total; unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX]; @@ -184,5 +185,6 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource bool manager_routable(Manager *m, int family); void manager_flush_caches(Manager *m); +void manager_reset_server_features(Manager *m); void manager_cleanup_saved_user(Manager *m); diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 415dc1a532..6fbf755878 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -86,27 +86,21 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { key = p->question->keys[0]; r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); - if (r < 0) { - log_debug_errno(r, "Failed to lookup key: %m"); - return r; - } + if (r < 0) + return log_debug_errno(r, "Failed to lookup key: %m"); if (r == 0) return 0; r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &reply); - if (r < 0) { - log_debug_errno(r, "Failed to build reply packet: %m"); - return r; - } + if (r < 0) + return log_debug_errno(r, "Failed to build reply packet: %m"); if (!ratelimit_test(&s->ratelimit)) return 0; r = dns_scope_emit_udp(s, -1, reply); - if (r < 0) { - log_debug_errno(r, "Failed to send reply packet: %m"); - return r; - } + if (r < 0) + return log_debug_errno(r, "Failed to send reply packet: %m"); return 0; } diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 3c62550872..e3d6a33409 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -26,6 +26,7 @@ #include "fileio.h" #include "ordered-set.h" #include "resolved-conf.h" +#include "resolved-dns-server.h" #include "resolved-resolv-conf.h" #include "string-util.h" #include "strv.h" @@ -87,7 +88,7 @@ int manager_read_resolv_conf(Manager *m) { char *l; l = strstrip(line); - if (*l == '#' || *l == ';') + if (IN_SET(*l, '#', ';')) continue; a = first_word(l, "nameserver"); @@ -136,6 +137,11 @@ int manager_read_resolv_conf(Manager *m) { if (m->unicast_scope) dns_cache_flush(&m->unicast_scope->cache); + /* If /etc/resolv.conf changed, make sure to forget everything we learned about the DNS servers. After all we + * might now talk to a very different DNS server that just happens to have the same IP address as an old one + * (think 192.168.1.1). */ + dns_server_reset_features_all(m->dns_servers); + return 0; clear: @@ -165,7 +171,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); + fputs_unlocked("# Too many DNS servers configured, the following entries may be ignored.\n", f); (*count)++; fprintf(f, "nameserver %s\n", dns_server_string(s)); @@ -181,39 +187,39 @@ static void write_resolv_conf_search( assert(domains); assert(f); - fputs("search", f); + fputs_unlocked("search", f); ORDERED_SET_FOREACH(domain, domains, i) { if (++count > MAXDNSRCH) { - fputs("\n# Too many search domains configured, remaining ones ignored.", f); + fputs_unlocked("\n# Too many search domains configured, remaining ones ignored.", f); break; } length += strlen(domain) + 1; if (length > 256) { - fputs("\n# Total length of all search domains is too long, remaining ones ignored.", f); + fputs_unlocked("\n# Total length of all search domains is too long, remaining ones ignored.", f); break; } - fputc(' ', f); - fputs(domain, f); + fputc_unlocked(' ', f); + fputs_unlocked(domain, f); } - fputs("\n", f); + fputs_unlocked("\n", f); } static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { Iterator i; - fputs("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n" - "# This is a dynamic resolv.conf file for connecting local clients directly to\n" - "# all known DNS servers.\n#\n" - "# Third party programs must not access this file directly, but only through the\n" - "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n" - "# replace this symlink by a static file or a different symlink.\n#\n" - "# See man:systemd-resolved.service(8) for details about the supported modes of\n" - "# operation for /etc/resolv.conf.\n\n", f); + fputs_unlocked("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n" + "# This is a dynamic resolv.conf file for connecting local clients directly to\n" + "# all known DNS servers.\n#\n" + "# Third party programs must not access this file directly, but only through the\n" + "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n" + "# replace this symlink by a static file or a different symlink.\n#\n" + "# See man:systemd-resolved.service(8) for details about the supported modes of\n" + "# operation for /etc/resolv.conf.\n\n", f); if (ordered_set_isempty(dns)) - fputs("# No DNS servers known.\n", f); + fputs_unlocked("# No DNS servers known.\n", f); else { unsigned count = 0; DnsServer *s; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 74603f9311..2eb7bfd030 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -67,15 +67,20 @@ int main(int argc, char *argv[]) { goto finish; } - /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ - r = drop_privileges(uid, gid, - (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */ - (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ - (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); - if (r < 0) - goto finish; + /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all + * privileges are already dropped. */ + if (getuid() == 0) { + + /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ + r = drop_privileges(uid, gid, + (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */ + (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ + (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); + if (r < 0) + goto finish; + } - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, -1) >= 0); + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); r = manager_new(&m); if (r < 0) { diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 8cbe492526..00dde9b6bd 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -75,7 +75,7 @@ static void test_packet_from_file(const char* filename, bool canonical) { 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_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 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); @@ -90,7 +90,7 @@ static void test_packet_from_file(const char* filename, bool canonical) { 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_new(&p2, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 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); diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c index 090b2fac23..25ec6f4352 100644 --- a/src/resolve/test-dnssec-complex.c +++ b/src/resolve/test-dnssec-complex.c @@ -218,7 +218,7 @@ int main(int argc, char* argv[]) { test_hostname_lookup(bus, "poettering.de", AF_INET, NULL); test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL); -#if defined(HAVE_LIBIDN2) || defined(HAVE_LIBIDN) +#if HAVE_LIBIDN2 || HAVE_LIBIDN /* Unsigned A with IDNA conversion necessary */ test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL); test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL); diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index b3018e8239..8cb4b50393 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -47,7 +47,7 @@ static void test_dnssec_canonicalize(void) { test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); } -#ifdef HAVE_GCRYPT +#if HAVE_GCRYPT static void test_dnssec_verify_dns_key(void) { @@ -332,7 +332,7 @@ int main(int argc, char*argv[]) { test_dnssec_canonicalize(); -#ifdef HAVE_GCRYPT +#if HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rrset(); test_dnssec_verify_rrset2(); diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c index 1b0041214b..ab11fbcd32 100644 --- a/src/resolve/test-resolved-packet.c +++ b/src/resolve/test-resolved-packet.c @@ -27,13 +27,16 @@ static void test_dns_packet_new(void) { for (i = 0; i <= DNS_PACKET_SIZE_MAX; i++) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i) == 0); + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i, DNS_PACKET_SIZE_MAX) == 0); log_debug("dns_packet_new: %zu → %zu", i, p->allocated); assert_se(p->allocated >= MIN(DNS_PACKET_SIZE_MAX, i)); + + if (i > DNS_PACKET_SIZE_START + 10 && i < DNS_PACKET_SIZE_MAX - 10) + i = MIN(i * 2, DNS_PACKET_SIZE_MAX - 10); } - assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1) == -EFBIG); + assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1, DNS_PACKET_SIZE_MAX) == -EFBIG); } int main(int argc, char **argv) { |