resolved: implement full LLMNR conflict detection logic

This commit is contained in:
Lennart Poettering 2014-08-06 16:15:35 +02:00
parent 3ef77d0476
commit a407657425
11 changed files with 470 additions and 61 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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:

View File

@ -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) {

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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. */
dns_zone_item_probe_stop(i);
i->state = DNS_ZONE_ITEM_ESTABLISHED;
} else
dns_zone_item_conflict(i);
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);
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;
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;
}

View File

@ -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);

View File

@ -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));
dns_scope_check_conflicts(scope, p);
t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (!t)
return 0;
if (t)
dns_transaction_process_reply(t, p);
dns_transaction_process_reply(t, p);
} else if (dns_packet_validate_query(p) > 0) {
log_debug("Got query packet for id %u", DNS_PACKET_ID(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;
if (p->family == AF_INET)
scope = l->llmnr_ipv4_scope;
else if (p->family == AF_INET6)
scope = l->llmnr_ipv6_scope;
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,30 +1409,27 @@ fail:
}
static int on_llmnr_stream_packet(DnsStream *s) {
DnsScope *scope;
assert(s);
if (dns_packet_validate_query(s->read_packet) > 0) {
Link *l;
l = hashmap_get(s->manager->links, INT_TO_PTR(s->read_packet->ifindex));
if (l) {
DnsScope *scope = NULL;
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 (scope) {
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;
}
}
scope = manager_find_scope(s->manager, s->read_packet);
if (!scope) {
log_warning("Got LLMNR TCP packet on unknown scope. Ignroing.");
return 0;
}
if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got query packet for id %u", DNS_PACKET_ID(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;
} 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",

View File

@ -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);