1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-09-08 13:44:50 +03:00

resolved: follow CNAMES for DNS stub replies

Clients expect us to follow CNAMEs for them, hence do so. On the first
iteration start putting together a packet, and then keep adding data we
acquire through CNAMEs to it, until we finally send it off.

Fixes: #3826
This commit is contained in:
Lennart Poettering
2017-02-08 19:12:55 +01:00
parent f9e0eefc7c
commit e8d23f92b5
3 changed files with 84 additions and 51 deletions

View File

@@ -403,6 +403,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
sd_bus_track_unref(q->bus_track); sd_bus_track_unref(q->bus_track);
dns_packet_unref(q->request_dns_packet); dns_packet_unref(q->request_dns_packet);
dns_packet_unref(q->reply_dns_packet);
if (q->request_dns_stream) { if (q->request_dns_stream) {
/* Detach the stream from our query, in case something else keeps a reference to it. */ /* Detach the stream from our query, in case something else keeps a reference to it. */

View File

@@ -71,7 +71,6 @@ struct DnsQuery {
* family */ * family */
bool suppress_unroutable_family; bool suppress_unroutable_family;
/* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */ /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
bool clamp_ttl; bool clamp_ttl;
@@ -102,6 +101,7 @@ struct DnsQuery {
/* DNS stub information */ /* DNS stub information */
DnsPacket *request_dns_packet; DnsPacket *request_dns_packet;
DnsStream *request_dns_stream; DnsStream *request_dns_stream;
DnsPacket *reply_dns_packet;
/* Completion callback */ /* Completion callback */
void (*complete)(DnsQuery* q); void (*complete)(DnsQuery* q);

View File

@@ -29,49 +29,33 @@ static int manager_dns_stub_udp_fd(Manager *m);
static int manager_dns_stub_tcp_fd(Manager *m); static int manager_dns_stub_tcp_fd(Manager *m);
static int dns_stub_make_reply_packet( static int dns_stub_make_reply_packet(
uint16_t id, DnsPacket **p,
int rcode,
DnsQuestion *q, DnsQuestion *q,
DnsAnswer *answer, DnsAnswer *answer) {
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 */
DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsResourceRecord *rr; DnsResourceRecord *rr;
unsigned c = 0; unsigned c = 0;
int r; int r;
assert(p);
/* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
* roundtrips aren't expensive. */ * roundtrips aren't expensive. */
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); if (!*p) {
if (r < 0) r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0);
return r; if (r < 0)
return r;
/* If the client didn't do EDNS, clamp the rcode to 4 bit */ r = dns_packet_append_question(*p, q);
if (!add_opt && rcode > 0xF) if (r < 0)
rcode = DNS_RCODE_SERVFAIL; return r;
DNS_PACKET_HEADER(p)->id = id; DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( }
1 /* qr */,
0 /* opcode */,
0 /* aa */,
0 /* tc */,
1 /* rd */,
1 /* ra */,
ad /* ad */,
0 /* cd */,
rcode));
r = dns_packet_append_question(p, q);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
DNS_ANSWER_FOREACH(rr, answer) { DNS_ANSWER_FOREACH(rr, answer) {
r = dns_question_matches_rr(q, rr, NULL); r = dns_question_matches_rr(q, rr, NULL);
if (r < 0) if (r < 0)
return r; return r;
@@ -86,13 +70,46 @@ static int dns_stub_make_reply_packet(
continue; continue;
add: add:
r = dns_packet_append_rr(p, rr, NULL, NULL); r = dns_packet_append_rr(*p, rr, NULL, NULL);
if (r < 0) if (r < 0)
return r; return r;
c++; c++;
} }
DNS_PACKET_HEADER(p)->ancount = htobe16(c);
DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
return 0;
}
static int dns_stub_finish_reply_packet(
DnsPacket *p,
uint16_t id,
int rcode,
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? */
int r;
assert(p);
/* If the client didn't do EDNS, clamp the rcode to 4 bit */
if (!add_opt && rcode > 0xF)
rcode = DNS_RCODE_SERVFAIL;
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 */,
ad /* ad */,
0 /* cd */,
rcode));
if (add_opt) { if (add_opt) {
r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
@@ -100,9 +117,6 @@ static int dns_stub_make_reply_packet(
return r; return r;
} }
*ret = p;
p = NULL;
return 0; return 0;
} }
@@ -155,7 +169,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
assert(m); assert(m);
assert(p); assert(p);
r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply); r = dns_stub_make_reply_packet(&reply, p->question, 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), false);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m"); return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -170,26 +188,40 @@ static void dns_stub_query_complete(DnsQuery *q) {
switch (q->state) { switch (q->state) {
case DNS_TRANSACTION_SUCCESS: { case DNS_TRANSACTION_SUCCESS:
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
r = dns_stub_make_reply_packet( r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer);
DNS_PACKET_ID(q->request_dns_packet),
q->answer_rcode,
q->question_idna,
q->answer,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
&reply);
if (r < 0) { if (r < 0) {
log_debug_errno(r, "Failed to build reply packet: %m"); log_debug_errno(r, "Failed to build reply packet: %m");
break; break;
} }
(void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply); r = dns_query_process_cname(q);
if (r == -ELOOP) {
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
break;
}
if (r < 0) {
log_debug_errno(r, "Failed to process CNAME: %m");
break;
}
if (r == DNS_QUERY_RESTARTED)
return;
r = dns_stub_finish_reply_packet(
q->reply_dns_packet,
DNS_PACKET_ID(q->request_dns_packet),
q->answer_rcode,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated);
if (r < 0) {
log_debug_errno(r, "Failed to finish reply packet: %m");
break;
}
(void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
break; break;
}
case DNS_TRANSACTION_RCODE_FAILURE: case DNS_TRANSACTION_RCODE_FAILURE:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode); (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
@@ -301,7 +333,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
goto fail; goto fail;
} }
r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME); r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to generate query object: %m"); log_error_errno(r, "Failed to generate query object: %m");
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);