mirror of
https://github.com/systemd/systemd.git
synced 2025-03-21 02:50:18 +03:00
resolved: include SOA records in LLMNR replies for non-existing RRs to allow negative caching
This commit is contained in:
parent
ab0cf8f8ff
commit
8bf52d3d17
@ -97,6 +97,30 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_answer_add_soa(DnsAnswer *a, const char *name) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
|
||||
|
||||
soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
|
||||
if (!soa)
|
||||
return -ENOMEM;
|
||||
|
||||
soa->soa.mname = strdup(name);
|
||||
if (!soa->soa.mname)
|
||||
return -ENOMEM;
|
||||
|
||||
soa->soa.rname = strappend("root.", name);
|
||||
if (!soa->soa.rname)
|
||||
return -ENOMEM;
|
||||
|
||||
soa->soa.serial = 1;
|
||||
soa->soa.refresh = 1;
|
||||
soa->soa.retry = 1;
|
||||
soa->soa.expire = 1;
|
||||
soa->soa.minimum = 1;
|
||||
|
||||
return dns_answer_add(a, soa);
|
||||
}
|
||||
|
||||
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
@ -38,6 +38,7 @@ DnsAnswer *dns_answer_ref(DnsAnswer *a);
|
||||
DnsAnswer *dns_answer_unref(DnsAnswer *a);
|
||||
|
||||
int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr);
|
||||
int dns_answer_add_soa(DnsAnswer *a, const char *name);
|
||||
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
|
||||
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
|
||||
|
||||
|
@ -172,6 +172,16 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
|
||||
return rr;
|
||||
}
|
||||
|
||||
DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
|
||||
key = dns_resource_key_new(class, type, name);
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
return dns_resource_record_new(key);
|
||||
}
|
||||
|
||||
DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) {
|
||||
if (!rr)
|
||||
return NULL;
|
||||
|
@ -140,6 +140,7 @@ int dns_resource_key_compare_func(const void *a, const void *b);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
|
||||
|
||||
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
|
||||
DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
|
||||
DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
|
||||
DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
|
||||
int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
|
||||
|
@ -389,14 +389,14 @@ int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union
|
||||
return !!manager_find_dns_server(s->manager, family, address);
|
||||
}
|
||||
|
||||
static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *a, DnsPacket **ret) {
|
||||
static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, DnsPacket **ret) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (q->n_keys <= 0 && a->n_rrs <= 0)
|
||||
if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = dns_packet_new(&p, s->protocol, 0);
|
||||
@ -425,14 +425,24 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
|
||||
DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
|
||||
}
|
||||
|
||||
if (a) {
|
||||
for (i = 0; i < a->n_rrs; i++) {
|
||||
r = dns_packet_append_rr(p, a->rrs[i], NULL);
|
||||
if (answer) {
|
||||
for (i = 0; i < answer->n_rrs; i++) {
|
||||
r = dns_packet_append_rr(p, answer->rrs[i], NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
DNS_PACKET_HEADER(p)->ancount = htobe16(a->n_rrs);
|
||||
DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
|
||||
}
|
||||
|
||||
if (soa) {
|
||||
for (i = 0; i < soa->n_rrs; i++) {
|
||||
r = dns_packet_append_rr(p, soa->rrs[i], NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
|
||||
}
|
||||
|
||||
*ret = p;
|
||||
@ -443,7 +453,7 @@ static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQ
|
||||
|
||||
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;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
|
||||
int r, fd;
|
||||
|
||||
assert(s);
|
||||
@ -475,7 +485,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
|
||||
return;
|
||||
}
|
||||
|
||||
r = dns_zone_lookup(&s->zone, p->question, &answer);
|
||||
r = dns_zone_lookup(&s->zone, p->question, &answer, &soa);
|
||||
if (r < 0) {
|
||||
log_debug("Failed to lookup key: %s", strerror(-r));
|
||||
return;
|
||||
@ -485,7 +495,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
|
||||
|
||||
dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
|
||||
|
||||
r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, &reply);
|
||||
r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, &reply);
|
||||
if (r < 0) {
|
||||
log_debug("Failed to build reply packet: %s", strerror(-r));
|
||||
return;
|
||||
|
@ -192,21 +192,23 @@ int dns_zone_put(DnsZone *z, DnsResourceRecord *rr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
bool has_other_rrs = false;
|
||||
unsigned i, n = 0;
|
||||
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
|
||||
unsigned i, n_answer = 0, n_soa = 0;
|
||||
int r;
|
||||
|
||||
assert(z);
|
||||
assert(q);
|
||||
assert(ret);
|
||||
assert(ret_answer);
|
||||
assert(ret_soa);
|
||||
|
||||
if (q->n_keys <= 0) {
|
||||
*ret = NULL;
|
||||
*ret_answer = NULL;
|
||||
*ret_soa = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First iteration, count what we have */
|
||||
for (i = 0; i < q->n_keys; i++) {
|
||||
DnsZoneItem *j;
|
||||
|
||||
@ -220,40 +222,46 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
|
||||
|
||||
j = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
|
||||
LIST_FOREACH(by_name, j, j) {
|
||||
has_other_rrs = true;
|
||||
|
||||
k = dns_resource_key_match_rr(q->keys[i], j->rr);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k == 0)
|
||||
continue;
|
||||
|
||||
n++;
|
||||
n_soa++;
|
||||
else
|
||||
n_answer++;
|
||||
}
|
||||
|
||||
} else {
|
||||
j = hashmap_get(z->by_key, q->keys[i]);
|
||||
if (!j) {
|
||||
if (j) {
|
||||
LIST_FOREACH(by_key, j, j)
|
||||
n_answer++;
|
||||
} else {
|
||||
if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
|
||||
has_other_rrs = true;
|
||||
|
||||
continue;
|
||||
n_soa ++;
|
||||
}
|
||||
|
||||
LIST_FOREACH(by_key, j, j)
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n <= 0) {
|
||||
*ret = NULL;
|
||||
return has_other_rrs;
|
||||
if (n_answer <= 0 && n_soa <= 0) {
|
||||
*ret_answer = NULL;
|
||||
*ret_soa = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
answer = dns_answer_new(n);
|
||||
if (!answer)
|
||||
return -ENOMEM;
|
||||
if (n_answer > 0) {
|
||||
answer = dns_answer_new(n_answer);
|
||||
if (!answer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (n_soa > 0) {
|
||||
soa = dns_answer_new(n_soa);
|
||||
if (!soa)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Second iteration, actually add the RRs to the answers */
|
||||
for (i = 0; i < q->n_keys; i++) {
|
||||
DnsZoneItem *j;
|
||||
|
||||
@ -267,25 +275,36 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k == 0)
|
||||
continue;
|
||||
|
||||
r = dns_answer_add(answer, j->rr);
|
||||
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
|
||||
else
|
||||
r = dns_answer_add(answer, j->rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
|
||||
j = hashmap_get(z->by_key, q->keys[i]);
|
||||
LIST_FOREACH(by_key, j, j) {
|
||||
r = dns_answer_add(answer, j->rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (j) {
|
||||
LIST_FOREACH(by_key, j, j) {
|
||||
r = dns_answer_add(answer, j->rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]))) {
|
||||
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ret = answer;
|
||||
*ret_answer = answer;
|
||||
answer = NULL;
|
||||
|
||||
*ret_soa = soa;
|
||||
soa = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -37,4 +37,4 @@ void dns_zone_flush(DnsZone *z);
|
||||
int dns_zone_put(DnsZone *z, DnsResourceRecord *rr);
|
||||
void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
|
||||
|
||||
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer);
|
||||
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa);
|
||||
|
Loading…
x
Reference in New Issue
Block a user