1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

Merge pull request #6988 from poettering/dns-stub-truncate

rework how resolved's dns stub deals with truncation
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2017-10-05 12:35:24 +02:00 committed by GitHub
commit 70b491d190
10 changed files with 75 additions and 49 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -86,27 +86,21 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
key = p->question->keys[0];
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return r;
}
if (r < 0)
return log_debug_errno(r, "Failed to lookup key: %m");
if (r == 0)
return 0;
r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &reply);
if (r < 0) {
log_debug_errno(r, "Failed to build reply packet: %m");
return r;
}
if (r < 0)
return log_debug_errno(r, "Failed to build reply packet: %m");
if (!ratelimit_test(&s->ratelimit))
return 0;
r = dns_scope_emit_udp(s, -1, reply);
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return r;
}
if (r < 0)
return log_debug_errno(r, "Failed to send reply packet: %m");
return 0;
}

View File

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

View File

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