summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2017-10-04 12:35:48 +0200
committerLennart Poettering <lennart@poettering.net>2017-10-05 12:22:43 +0200
commit51027656951988babd4724def5d934e4817fdd1f (patch)
tree7fd87c0a020f1359124f1d54e0c73efc268cedec
parent9886b6b13cfe4348f9bd2ab62bb1d821fdac7ab7 (diff)
downloadsystemd-51027656951988babd4724def5d934e4817fdd1f.tar.gz
resolved: rework how we handle truncation in the stub resolver
When we a reply message gets longer than the client supports we need to truncate the response and set the TC bit, and we already do that. However, we are not supposed to send incomplete RRs in that case, but instead truncate right at a record boundary. Do that. This fixes the "Message parser reports malformed message packet." warning the venerable "host" tool outputs when a very large response is requested. See: #6520
-rw-r--r--src/resolve/resolve-tool.c2
-rw-r--r--src/resolve/resolved-dns-packet.c26
-rw-r--r--src/resolve/resolved-dns-packet.h14
-rw-r--r--src/resolve/resolved-dns-scope.c4
-rw-r--r--src/resolve/resolved-dns-stream.c2
-rw-r--r--src/resolve/resolved-dns-stub.c48
-rw-r--r--src/resolve/resolved-manager.c2
-rw-r--r--src/resolve/test-dns-packet.c4
-rw-r--r--src/resolve/test-resolved-packet.c4
9 files changed, 69 insertions, 37 deletions
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index c62058917f..195555c3c0 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -357,7 +357,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
int r;
char ifname[IF_NAMESIZE] = "";
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
+ r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
return log_oom();
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index bf879475d4..e2f227bfc6 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -43,11 +43,20 @@ static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0)
#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0)
-int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize) {
+int dns_packet_new(
+ DnsPacket **ret,
+ DnsProtocol protocol,
+ size_t min_alloc_dsize,
+ size_t max_size) {
+
DnsPacket *p;
size_t a;
assert(ret);
+ assert(max_size >= DNS_PACKET_HEADER_SIZE);
+
+ if (max_size > DNS_PACKET_SIZE_MAX)
+ max_size = DNS_PACKET_SIZE_MAX;
/* The caller may not check what is going to be truly allocated, so do not allow to
* allocate a DNS packet bigger than DNS_PACKET_SIZE_MAX.
@@ -70,8 +79,8 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize
a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket));
/* make sure we never allocate more than useful */
- if (a > DNS_PACKET_SIZE_MAX)
- a = DNS_PACKET_SIZE_MAX;
+ if (a > max_size)
+ a = max_size;
p = malloc0(ALIGN(sizeof(DnsPacket)) + a);
if (!p)
@@ -79,6 +88,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize
p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
p->allocated = a;
+ p->max_size = max_size;
p->protocol = protocol;
p->opt_start = p->opt_size = (size_t) -1;
p->n_ref = 1;
@@ -144,7 +154,7 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc
assert(ret);
- r = dns_packet_new(&p, protocol, min_alloc_dsize);
+ r = dns_packet_new(&p, protocol, min_alloc_dsize, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
@@ -313,11 +323,13 @@ static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start
assert(p);
if (p->size + add > p->allocated) {
- size_t a;
+ size_t a, ms;
a = PAGE_ALIGN((p->size + add) * 2);
- if (a > DNS_PACKET_SIZE_MAX)
- a = DNS_PACKET_SIZE_MAX;
+
+ ms = dns_packet_size_max(p);
+ if (a > ms)
+ a = ms;
if (p->size + add > a)
return -EMSGSIZE;
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index d4c6b3c9cb..b873c0f745 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -73,7 +73,7 @@ struct DnsPacketHeader {
struct DnsPacket {
int n_ref;
DnsProtocol protocol;
- size_t size, allocated, rindex;
+ size_t size, allocated, rindex, max_size;
void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
Hashmap *names; /* For name compression */
size_t opt_start, opt_size;
@@ -187,7 +187,7 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
(unsigned) DNS_PACKET_ARCOUNT(p);
}
-int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize);
+int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, size_t max_size);
int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, bool dnssec_checking_disabled);
void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated);
@@ -303,3 +303,13 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family,
return f;
}
}
+
+static inline size_t dns_packet_size_max(DnsPacket *p) {
+ assert(p);
+
+ /* Why not insist on a fully initialized max_size during DnsPacket construction? Well, this way it's easy to
+ * allocate a transient, throw-away DnsPacket on the stack by simple zero initialization, without having to
+ * deal with explicit field initialization. */
+
+ return p->max_size != 0 ? p->max_size : DNS_PACKET_SIZE_MAX;
+}
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index b01ee6c9cc..ca54158898 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -632,7 +632,7 @@ int dns_scope_make_reply_packet(
dns_answer_isempty(soa))
return -EINVAL;
- r = dns_packet_new(&p, s->protocol, 0);
+ r = dns_packet_new(&p, s->protocol, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
@@ -818,7 +818,7 @@ static int dns_scope_make_conflict_packet(
assert(rr);
assert(ret);
- r = dns_packet_new(&p, s->protocol, 0);
+ r = dns_packet_new(&p, s->protocol, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 4a94fa9916..5892534687 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -260,7 +260,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
ssize_t ss;
if (!s->read_packet) {
- r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size));
+ r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size), DNS_PACKET_SIZE_MAX);
if (r < 0)
return dns_stream_complete(s, -r);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 292e94daa3..5bc79a313e 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -30,9 +30,12 @@ static int manager_dns_stub_tcp_fd(Manager *m);
static int dns_stub_make_reply_packet(
DnsPacket **p,
+ size_t max_size,
DnsQuestion *q,
- DnsAnswer *answer) {
+ DnsAnswer *answer,
+ bool *ret_truncated) {
+ bool truncated = false;
DnsResourceRecord *rr;
unsigned c = 0;
int r;
@@ -43,7 +46,7 @@ static int dns_stub_make_reply_packet(
* roundtrips aren't expensive. */
if (!*p) {
- r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0);
+ r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
if (r < 0)
return r;
@@ -71,12 +74,21 @@ static int dns_stub_make_reply_packet(
continue;
add:
r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
+ if (r == -EMSGSIZE) {
+ truncated = true;
+ break;
+ }
if (r < 0)
return r;
c++;
}
+ if (ret_truncated)
+ *ret_truncated = truncated;
+ else if (truncated)
+ return -EMSGSIZE;
+
DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
return 0;
@@ -86,6 +98,7 @@ static int dns_stub_finish_reply_packet(
DnsPacket *p,
uint16_t id,
int rcode,
+ bool tc, /* set the Truncated bit? */
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? */
@@ -110,14 +123,14 @@ static int dns_stub_finish_reply_packet(
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 */,
+ 1 /* qr */,
+ 0 /* opcode */,
+ 0 /* aa */,
+ tc /* tc */,
+ 1 /* rd */,
+ 1 /* ra */,
ad /* ad */,
- 0 /* cd */,
+ 0 /* cd */,
rcode));
if (add_opt) {
@@ -149,12 +162,6 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
else {
int fd;
- /* Truncate the message to the right size */
- if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) {
- dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX);
- DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC);
- }
-
fd = manager_dns_stub_udp_fd(m);
if (fd < 0)
return log_debug_errno(fd, "Failed to get reply socket: %m");
@@ -178,11 +185,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
assert(m);
assert(p);
- r = dns_stub_make_reply_packet(&reply, p->question, NULL);
+ r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, 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), authenticated);
+ r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -197,9 +204,10 @@ static void dns_stub_query_complete(DnsQuery *q) {
switch (q->state) {
- case DNS_TRANSACTION_SUCCESS:
+ case DNS_TRANSACTION_SUCCESS: {
+ bool truncated;
- r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer);
+ r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated);
if (r < 0) {
log_debug_errno(r, "Failed to build reply packet: %m");
break;
@@ -221,6 +229,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
q->reply_dns_packet,
DNS_PACKET_ID(q->request_dns_packet),
q->answer_rcode,
+ truncated,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
dns_query_fully_authenticated(q));
@@ -231,6 +240,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
(void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
break;
+ }
case DNS_TRANSACTION_RCODE_FAILURE:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 1270ca2b25..58fe572d3b 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -723,7 +723,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
if (ms < 0)
return ms;
- r = dns_packet_new(&p, protocol, ms);
+ r = dns_packet_new(&p, protocol, ms, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
index 8cbe492526..00dde9b6bd 100644
--- a/src/resolve/test-dns-packet.c
+++ b/src/resolve/test-dns-packet.c
@@ -75,7 +75,7 @@ static void test_packet_from_file(const char* filename, bool canonical) {
assert_se(packet_size > 0);
assert_se(offset + 8 + packet_size <= data_size);
- assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0);
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0);
@@ -90,7 +90,7 @@ static void test_packet_from_file(const char* filename, bool canonical) {
assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0);
- assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0);
+ assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0);
diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c
index cf886b1bf2..ab11fbcd32 100644
--- a/src/resolve/test-resolved-packet.c
+++ b/src/resolve/test-resolved-packet.c
@@ -27,7 +27,7 @@ static void test_dns_packet_new(void) {
for (i = 0; i <= DNS_PACKET_SIZE_MAX; i++) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i) == 0);
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i, DNS_PACKET_SIZE_MAX) == 0);
log_debug("dns_packet_new: %zu → %zu", i, p->allocated);
assert_se(p->allocated >= MIN(DNS_PACKET_SIZE_MAX, i));
@@ -36,7 +36,7 @@ static void test_dns_packet_new(void) {
i = MIN(i * 2, DNS_PACKET_SIZE_MAX - 10);
}
- assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1) == -EFBIG);
+ assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1, DNS_PACKET_SIZE_MAX) == -EFBIG);
}
int main(int argc, char **argv) {