From a407657425a3e47fd2b559cd3bc800f791303f63 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Aug 2014 16:15:35 +0200 Subject: [PATCH] resolved: implement full LLMNR conflict detection logic --- src/resolve/resolved-dns-cache.c | 77 +++++++++- src/resolve/resolved-dns-cache.h | 4 +- src/resolve/resolved-dns-packet.c | 5 + src/resolve/resolved-dns-packet.h | 2 + src/resolve/resolved-dns-scope.c | 198 ++++++++++++++++++++++++- src/resolve/resolved-dns-scope.h | 8 +- src/resolve/resolved-dns-transaction.c | 24 ++- src/resolve/resolved-dns-zone.c | 112 +++++++++++++- src/resolve/resolved-dns-zone.h | 3 + src/resolve/resolved-manager.c | 89 ++++++----- src/resolve/resolved-manager.h | 3 +- 11 files changed, 467 insertions(+), 58 deletions(-) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 696eb9d52..733ef6348 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -43,6 +43,8 @@ struct DnsCacheItem { usec_t until; DnsCacheItemType type; unsigned prioq_idx; + int owner_family; + union in_addr_union owner_address; LIST_FIELDS(DnsCacheItem, by_key); }; @@ -256,13 +258,20 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso prioq_reshuffle(c->by_expiry, i, &i->prioq_idx); } -static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t timestamp) { +static int dns_cache_put_positive( + DnsCache *c, + DnsResourceRecord *rr, + usec_t timestamp, + int owner_family, + const union in_addr_union *owner_address) { + _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; DnsCacheItem *existing; int r; assert(c); assert(rr); + assert(owner_address); /* New TTL is 0? Delete the entry... */ if (rr->ttl <= 0) { @@ -298,6 +307,8 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim i->rr = dns_resource_record_ref(rr); i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); i->prioq_idx = PRIOQ_IDX_NULL; + i->owner_family = owner_family; + i->owner_address = *owner_address; r = dns_cache_link_item(c, i); if (r < 0) @@ -307,12 +318,21 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim return 0; } -static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, usec_t timestamp, uint32_t soa_ttl) { +static int dns_cache_put_negative( + DnsCache *c, + DnsResourceKey *key, + int rcode, + usec_t timestamp, + uint32_t soa_ttl, + int owner_family, + const union in_addr_union *owner_address) { + _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; int r; assert(c); assert(key); + assert(owner_address); dns_cache_remove(c, key); @@ -340,6 +360,8 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u i->key = dns_resource_key_ref(key); i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); i->prioq_idx = PRIOQ_IDX_NULL; + i->owner_family = owner_family; + i->owner_address = *owner_address; r = dns_cache_link_item(c, i); if (r < 0) @@ -349,7 +371,16 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u return 0; } -int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp) { +int dns_cache_put( + DnsCache *c, + DnsQuestion *q, + int rcode, + DnsAnswer *answer, + unsigned max_rrs, + usec_t timestamp, + int owner_family, + const union in_addr_union *owner_address) { + unsigned i; int r; @@ -382,7 +413,7 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, uns /* Second, add in positive entries for all contained RRs */ for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->rrs[i], timestamp); + r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address); if (r < 0) goto fail; } @@ -403,7 +434,7 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, uns if (r == 0) continue; - r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl)); + r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); if (r < 0) goto fail; } @@ -495,3 +526,39 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { return n; } + +int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) { + DnsCacheItem *i, *first; + bool same_owner = true; + + assert(cache); + assert(rr); + + dns_cache_prune(cache); + + /* See if there's a cache entry for the same key. If there + * isn't there's no conflict */ + first = hashmap_get(cache->by_key, rr->key); + if (!first) + return 0; + + /* See if the RR key is owned by the same owner, if so, there + * isn't a conflict either */ + LIST_FOREACH(by_key, i, first) { + if (i->owner_family != owner_family || + !in_addr_equal(owner_family, &i->owner_address, owner_address)) { + same_owner = false; + break; + } + } + if (same_owner) + return 0; + + /* See if there's the exact same RR in the cache. If yes, then + * there's no conflict. */ + if (dns_cache_get(cache, rr)) + return 0; + + /* There's a conflict */ + return 1; +} diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index d88d1d0e1..e92280c31 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -40,5 +40,7 @@ typedef struct DnsCache { void dns_cache_flush(DnsCache *c); void dns_cache_prune(DnsCache *c); -int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp); +int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **answer); + +int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 4f9503803..0d276df8c 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1358,6 +1358,9 @@ int dns_packet_extract(DnsPacket *p) { unsigned n, i; int r; + if (p->extracted) + return 0; + saved_rindex = p->rindex; dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); @@ -1409,6 +1412,8 @@ int dns_packet_extract(DnsPacket *p) { p->answer = answer; answer = NULL; + p->extracted = true; + r = 0; finish: diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 26a2e7646..6a865a2d5 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -81,6 +81,8 @@ struct DnsPacket { union in_addr_union sender, destination; uint16_t sender_port, destination_port; uint32_t ttl; + + bool extracted; }; static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 40c326a81..174249a9f 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -61,6 +61,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int DnsScope* dns_scope_free(DnsScope *s) { DnsTransaction *t; + DnsResourceRecord *rr; if (!s) return NULL; @@ -81,6 +82,12 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_transaction_free(t); } + while ((rr = hashmap_steal_first(s->conflict_queue))) + dns_resource_record_unref(rr); + + hashmap_free(s->conflict_queue); + sd_event_source_unref(s->conflict_event_source); + dns_cache_flush(&s->cache); dns_zone_flush(&s->zone); @@ -115,7 +122,7 @@ void dns_scope_next_dns_server(DnsScope *s) { manager_next_dns_server(s->manager); } -int dns_scope_send(DnsScope *s, DnsPacket *p) { +int dns_scope_emit(DnsScope *s, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; @@ -420,6 +427,7 @@ static int dns_scope_make_reply_packet( int r; assert(s); + assert(ret); if ((!q || q->n_keys <= 0) && (!answer || answer->n_rrs <= 0) @@ -478,6 +486,20 @@ static int dns_scope_make_reply_packet( return 0; } +static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { + unsigned n; + + assert(s); + assert(p); + + if (p->question) + for (n = 0; n < p->question->n_keys; n++) + dns_zone_verify_conflicts(&s->zone, p->question->keys[n]); + if (p->answer) + for (n = 0; n < p->answer->n_rrs; n++) + dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key); +} + void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; @@ -509,7 +531,8 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { } if (DNS_PACKET_C(p)) { - /* FIXME: Somebody notified us about a likely conflict */ + /* Somebody notified us about a possible conflict */ + dns_scope_verify_conflicts(s, p); return; } @@ -588,3 +611,174 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *questio return NULL; } + +static int dns_scope_make_conflict_packet( + DnsScope *s, + DnsResourceRecord *rr, + DnsPacket **ret) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + assert(s); + assert(rr); + assert(ret); + + r = dns_packet_new(&p, s->protocol, 0); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( + 0 /* qr */, + 0 /* opcode */, + 1 /* conflict */, + 0 /* tc */, + 0 /* t */, + 0 /* (ra) */, + 0 /* (ad) */, + 0 /* (cd) */, + 0)); + random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t)); + DNS_PACKET_HEADER(p)->qdcount = htobe16(1); + DNS_PACKET_HEADER(p)->arcount = htobe16(1); + + r = dns_packet_append_key(p, rr->key, NULL); + if (r < 0) + return r; + + r = dns_packet_append_rr(p, rr, NULL); + if (r < 0) + return r; + + *ret = p; + p = NULL; + + return 0; +} + +static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) { + DnsScope *scope = userdata; + int r; + + assert(es); + assert(scope); + + scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source); + + for (;;) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + + rr = hashmap_steal_first(scope->conflict_queue); + if (!rr) + break; + + r = dns_scope_make_conflict_packet(scope, rr, &p); + if (r < 0) { + log_error("Failed to make conflict packet: %s", strerror(-r)); + return 0; + } + + r = dns_scope_emit(scope, p); + if (r < 0) + log_debug("Failed to send conflict packet: %s", strerror(-r)); + } + + return 0; +} + +int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { + usec_t jitter; + int r; + + assert(scope); + assert(rr); + + /* We don't send these queries immediately. Instead, we queue + * them, and send them after some jitter delay. */ + r = hashmap_ensure_allocated(&scope->conflict_queue, dns_resource_key_hash_func, dns_resource_key_compare_func); + if (r < 0) { + log_oom(); + return r; + } + + /* We only place one RR per key in the conflict + * messages, not all of them. That should be enough to + * indicate where there might be a conflict */ + r = hashmap_put(scope->conflict_queue, rr->key, rr); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) { + log_debug("Failed to queue conflicting RR: %s", strerror(-r)); + return r; + } + + dns_resource_record_ref(rr); + + if (scope->conflict_event_source) + return 0; + + random_bytes(&jitter, sizeof(jitter)); + jitter %= LLMNR_JITTER_INTERVAL_USEC; + + r = sd_event_add_time(scope->manager->event, + &scope->conflict_event_source, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + jitter, + LLMNR_JITTER_INTERVAL_USEC, + on_conflict_dispatch, scope); + if (r < 0) { + log_debug("Failed to add conflict dispatch event: %s", strerror(-r)); + return r; + } + + return 0; +} + +void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { + unsigned i; + int r; + + assert(scope); + assert(p); + + if (p->protocol != DNS_PROTOCOL_LLMNR) + return; + + if (DNS_PACKET_RRCOUNT(p) <= 0) + return; + + if (DNS_PACKET_C(p) != 0) + return; + + if (DNS_PACKET_T(p) != 0) + return; + + if (manager_our_packet(scope->manager, p)) + return; + + r = dns_packet_extract(p); + if (r < 0) { + log_debug("Failed to extract packet: %s", strerror(-r)); + return; + } + + log_debug("Checking for conflicts..."); + + for (i = 0; i < p->answer->n_rrs; i++) { + + /* Check for conflicts against the local zone. If we + * found one, we won't check any further */ + r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]); + if (r != 0) + continue; + + /* Check for conflicts against the local cache. If so, + * send out an advisory query, to inform everybody */ + r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender); + if (r <= 0) + continue; + + dns_scope_notify_conflict(scope, p->answer->rrs[i]); + } +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index ae9469a39..6ba5ef241 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -55,6 +55,9 @@ struct DnsScope { DnsCache cache; DnsZone zone; + Hashmap *conflict_queue; + sd_event_source *conflict_event_source; + RateLimit ratelimit; LIST_HEAD(DnsTransaction, transactions); @@ -65,7 +68,7 @@ struct DnsScope { int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); DnsScope* dns_scope_free(DnsScope *s); -int dns_scope_send(DnsScope *s, DnsPacket *p); +int dns_scope_emit(DnsScope *s, DnsPacket *p); int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port); DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain); @@ -80,3 +83,6 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b); void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok); + +int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); +void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index a2e4f2ce9..e76940e18 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -132,6 +132,15 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { t->scope->link ? t->scope->link->name : "*", t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); + /* RFC 4795, Section 4.1 says that the peer with the + * lexicographically smaller IP address loses */ + if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) < 0) { + log_debug("Peer has lexicographically smaller IP address and thus lost in the conflict."); + return; + } + + log_debug("We have the lexicographically smaller IP address and thus lost in the conflict."); + t->block_gc++; SET_FOREACH(z, t->zone_items, i) dns_zone_item_conflict(z); @@ -196,6 +205,14 @@ static int on_stream_complete(DnsStream *s, int error) { return 0; } + if (dns_packet_validate_reply(p) <= 0) { + log_debug("Invalid LLMNR TCP packet."); + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return 0; + } + + dns_scope_check_conflicts(t->scope, p); + t->block_gc++; dns_transaction_process_reply(t, p); t->block_gc--; @@ -370,7 +387,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0); + dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); @@ -507,7 +524,8 @@ int dns_transaction_go(DnsTransaction *t) { t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + jitter, LLMNR_JITTER_INTERVAL_USEC, + now(clock_boottime_or_monotonic()) + jitter, + LLMNR_JITTER_INTERVAL_USEC, on_transaction_timeout, t); if (r < 0) return r; @@ -542,7 +560,7 @@ int dns_transaction_go(DnsTransaction *t) { r = dns_transaction_open_tcp(t); } else { /* Try via UDP, and if that fails due to large size try via TCP */ - r = dns_scope_send(t->scope, t->sent); + r = dns_scope_emit(t->scope, t->sent); if (r == -EMSGSIZE) r = dns_transaction_open_tcp(t); } diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index ed4775910..d96ddd270 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -493,6 +493,9 @@ void dns_zone_item_conflict(DnsZoneItem *i) { assert(i); + if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED)) + return; + dns_resource_record_to_string(i->rr, &pretty); log_info("Detected conflict on %s", strna(pretty)); @@ -507,6 +510,8 @@ void dns_zone_item_conflict(DnsZoneItem *i) { } void dns_zone_item_ready(DnsZoneItem *i) { + _cleanup_free_ char *pretty = NULL; + assert(i); assert(i->probe_transaction); @@ -516,14 +521,107 @@ void dns_zone_item_ready(DnsZoneItem *i) { if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) return; - if (i->probe_transaction->state != DNS_TRANSACTION_SUCCESS) { - _cleanup_free_ char *pretty = NULL; + if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) { + bool we_lost = false; - dns_resource_record_to_string(i->rr, &pretty); - log_debug("Record %s successfully probed.", strna(pretty)); + /* The probe got a successful reply. If we so far + * weren't established we just give up. If we already + * were established, and the peer has the + * lexicographically smaller IP address we continue + * and defend it. */ + + if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + we_lost = true; + else { + assert(i->probe_transaction->received); + we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) > 0; + } + + if (we_lost) { + dns_zone_item_conflict(i); + return; + } + + log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost."); + } + + dns_resource_record_to_string(i->rr, &pretty); + log_debug("Record %s successfully probed.", strna(pretty)); - dns_zone_item_probe_stop(i); + dns_zone_item_probe_stop(i); + i->state = DNS_ZONE_ITEM_ESTABLISHED; +} + +static int dns_zone_item_verify(DnsZoneItem *i) { + int r; + + assert(i); + + if (i->state != DNS_ZONE_ITEM_ESTABLISHED) + return 0; + + i->state = DNS_ZONE_ITEM_VERIFYING; + r = dns_zone_item_probe_start(i); + if (r < 0) { + log_error("Failed to start probing for verifying RR: %s", strerror(-r)); i->state = DNS_ZONE_ITEM_ESTABLISHED; - } else - dns_zone_item_conflict(i); + return r; + } + + return 0; +} + +int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { + DnsZoneItem *i, *first; + int c; + + assert(zone); + assert(rr); + + /* This checks whether a response RR we received from somebody + * else is one that we actually thought was uniquely ours. If + * so, we'll verify our RRs. */ + + /* No conflict if we don't have the name at all. */ + first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(rr->key)); + if (!first) + return 0; + + /* No conflict if we have the exact same RR */ + if (dns_zone_get(zone, rr)) + return 0; + + /* OK, somebody else has RRs for the same name. Yuck! Let's + * start probing again */ + + LIST_FOREACH(by_name, i, first) { + if (dns_resource_record_equal(i->rr, rr)) + continue; + + dns_zone_item_verify(i); + c++; + } + + return c; +} + +int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { + DnsZoneItem *i, *first; + int c; + + assert(zone); + + /* Somebody else notified us about a possible conflict. Let's + * verify if that's true. */ + + first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(key)); + if (!first) + return 0; + + LIST_FOREACH(by_name, i, first) { + dns_zone_item_verify(i); + c++; + } + + return c; } diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index bf93ab43f..37fdafe04 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -71,3 +71,6 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer ** void dns_zone_item_conflict(DnsZoneItem *i); void dns_zone_item_ready(DnsZoneItem *i); + +int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr); +int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 12883952b..a93f4a597 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1219,38 +1219,34 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; DnsTransaction *t = NULL; Manager *m = userdata; + DnsScope *scope; int r; r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); if (r <= 0) return r; + scope = manager_find_scope(m, p); + if (!scope) { + log_warning("Got LLMNR UDP packet on unknown scope. Ignoring."); + return 0; + } + if (dns_packet_validate_reply(p) > 0) { log_debug("Got reply packet for id %u", DNS_PACKET_ID(p)); - t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (!t) - return 0; - - dns_transaction_process_reply(t, p); + dns_scope_check_conflicts(scope, p); - } else if (dns_packet_validate_query(p) > 0) { - Link *l; - - l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); - if (l) { - DnsScope *scope = NULL; + t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); + if (t) + dns_transaction_process_reply(t, p); - if (p->family == AF_INET) - scope = l->llmnr_ipv4_scope; - else if (p->family == AF_INET6) - scope = l->llmnr_ipv6_scope; + } else if (dns_packet_validate_query(p) > 0) { + log_debug("Got query packet for id %u", DNS_PACKET_ID(p)); - if (scope) - dns_scope_process_query(scope, NULL, p); - } + dns_scope_process_query(scope, NULL, p); } else - log_debug("Invalid LLMNR packet."); + log_debug("Invalid LLMNR UDP packet."); return 0; } @@ -1413,29 +1409,26 @@ fail: } static int on_llmnr_stream_packet(DnsStream *s) { - assert(s); + DnsScope *scope; - if (dns_packet_validate_query(s->read_packet) > 0) { - Link *l; + assert(s); - l = hashmap_get(s->manager->links, INT_TO_PTR(s->read_packet->ifindex)); - if (l) { - DnsScope *scope = NULL; + scope = manager_find_scope(s->manager, s->read_packet); + if (!scope) { + log_warning("Got LLMNR TCP packet on unknown scope. Ignroing."); + return 0; + } - if (s->read_packet->family == AF_INET) - scope = l->llmnr_ipv4_scope; - else if (s->read_packet->family == AF_INET6) - scope = l->llmnr_ipv6_scope; + if (dns_packet_validate_query(s->read_packet) > 0) { + log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet)); - if (scope) { - dns_scope_process_query(scope, s, s->read_packet); + dns_scope_process_query(scope, s, s->read_packet); - /* If no reply packet was set, we free the stream */ - if (s->write_packet) - return 0; - } - } - } + /* If no reply packet was set, we free the stream */ + if (s->write_packet) + return 0; + } else + log_debug("Invalid LLMNR TCP packet."); dns_stream_free(s); return 0; @@ -1702,13 +1695,33 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad return NULL; } -int manager_our_packet(Manager *m, DnsPacket *p) { +bool manager_our_packet(Manager *m, DnsPacket *p) { assert(m); assert(p); return !!manager_find_link_address(m, p->family, &p->sender); } +DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { + Link *l; + + assert(m); + assert(p); + + l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); + if (!l) + return NULL; + + if (p->protocol == DNS_PROTOCOL_LLMNR) { + if (p->family == AF_INET) + return l->llmnr_ipv4_scope; + else if (p->family == AF_INET6) + return l->llmnr_ipv6_scope; + } + + return NULL; +} + static const char* const support_table[_SUPPORT_MAX] = { [SUPPORT_NO] = "no", [SUPPORT_YES] = "yes", diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 9d824e175..f960bc2f1 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -143,7 +143,8 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad void manager_refresh_rrs(Manager *m); int manager_next_hostname(Manager *m); -int manager_our_packet(Manager *m, DnsPacket *p); +bool manager_our_packet(Manager *m, DnsPacket *p); +DnsScope* manager_find_scope(Manager *m, DnsPacket *p); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); -- 2.30.2