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:
@@ -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. */
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user