]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: add code to generate the wire format for a single RR
authorLennart Poettering <lennart@poettering.net>
Wed, 2 Dec 2015 19:58:51 +0000 (20:58 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 2 Dec 2015 21:50:11 +0000 (22:50 +0100)
This adds dns_resource_record_to_wire_format() that generates the raw
wire-format of a single DnsResourceRecord object, and caches it in the
object, optionally in DNSSEC canonical form. This call is used later to
generate the RR serialization of RRs to verify.

This adds four new fields to DnsResourceRecord objects:

- wire_format points to the buffer with the wire-format version of the
  RR
- wire_format_size stores the size of that buffer
- wire_format_rdata_offset specifies the index into the buffer where the
  RDATA of the RR begins (i.e. the size of the key part of the RR).
- wire_format_canonical is a boolean that stores whether the cached wire
  format is in DNSSEC canonical form or not.

Note that this patch adds a mode where a DnsPacket is allocated on the
stack (instead of on the heap), so that it is cheaper to reuse the
DnsPacket object for generating this wire format. After all we reuse the
DnsPacket object for this, since it comes with all the dynamic memory
management, and serialization calls we need anyway.

src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c

index 06464324d20ae604922b0a0fdb7f2dc65087fa4a..75f31b80f65a969707085ddf623a104918584df9 100644 (file)
@@ -108,6 +108,8 @@ DnsPacket *dns_packet_ref(DnsPacket *p) {
         if (!p)
                 return NULL;
 
+        assert(!p->on_stack);
+
         assert(p->n_ref > 0);
         p->n_ref++;
         return p;
@@ -126,7 +128,9 @@ static void dns_packet_free(DnsPacket *p) {
         hashmap_free(p->names);
 
         free(p->_data);
-        free(p);
+
+        if (!p->on_stack)
+                free(p);
 }
 
 DnsPacket *dns_packet_unref(DnsPacket *p) {
@@ -380,7 +384,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
 }
 
 int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
-        void *w;
+        uint8_t *w;
         int r;
 
         assert(p);
@@ -389,12 +393,29 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start
         if (l > DNS_LABEL_MAX)
                 return -E2BIG;
 
-        r = dns_packet_extend(p, 1 + l, &w, start);
+        r = dns_packet_extend(p, 1 + l, (void**) &w, start);
         if (r < 0)
                 return r;
 
-        ((uint8_t*) w)[0] = (uint8_t) l;
-        memcpy(((uint8_t*) w) + 1, d, l);
+        *(w++) = (uint8_t) l;
+
+        if (p->canonical_form) {
+                size_t i;
+
+                /* Generate in canonical form, as defined by DNSSEC
+                 * RFC 4034, Section 6.2, i.e. all lower-case. */
+
+                for (i = 0; i < l; i++) {
+                        if (d[i] >= 'A' && d[i] <= 'Z')
+                                w[i] = (uint8_t) (d[i] - 'A' + 'a');
+                        else
+                                w[i] = (uint8_t) d[i];
+                }
+        } else
+                /* Otherwise, just copy the string unaltered. This is
+                 * essential for DNS-SD, where the casing of labels
+                 * matters and needs to be retained. */
+                memcpy(w, d, l);
 
         return 0;
 }
@@ -647,8 +668,8 @@ fail:
         return r;
 }
 
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) {
-        size_t saved_size, rdlength_offset, end, rdlength;
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
+        size_t saved_size, rdlength_offset, end, rdlength, rds;
         int r;
 
         assert(p);
@@ -669,6 +690,8 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
         if (r < 0)
                 goto fail;
 
+        rds = p->size - saved_size;
+
         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
@@ -947,6 +970,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
         if (start)
                 *start = saved_size;
 
+        if (rdata_start)
+                *rdata_start = rds;
+
         return 0;
 
 fail:
index b236333f61389f2ba39001807e0e67e7de63d9d9..853c94fe761764d9c04c2538aec1d5e5f2b6068e 100644 (file)
@@ -88,8 +88,10 @@ struct DnsPacket {
         uint16_t sender_port, destination_port;
         uint32_t ttl;
 
-        bool extracted;
-        bool refuse_compression;
+        bool on_stack:1;
+        bool extracted:1;
+        bool refuse_compression:1;
+        bool canonical_form:1;
 };
 
 static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
@@ -162,7 +164,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
 int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
 int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, size_t *start);
 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
-int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start);
+int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
 int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
 
 void dns_packet_truncate(DnsPacket *p, size_t sz);
index 09fe9725a22883cc27ddc2a84f7f8abfc3fe871e..1224eeaf25e785aaec09ece0f609e7cddc98d933 100644 (file)
@@ -416,6 +416,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
                         free(rr->generic.data);
                 }
 
+                free(rr->wire_format);
                 dns_resource_key_unref(rr->key);
         }
 
@@ -994,6 +995,51 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
         return 0;
 }
 
+int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
+
+        DnsPacket packet = {
+                .n_ref = 1,
+                .protocol = DNS_PROTOCOL_DNS,
+                .on_stack = true,
+                .refuse_compression = true,
+                .canonical_form = canonical,
+        };
+
+        size_t start, rds;
+        int r;
+
+        assert(rr);
+
+        /* Generates the RR in wire-format, optionally in the
+         * canonical form as discussed in the DNSSEC RFC 4034, Section
+         * 6.2. We allocate a throw-away DnsPacket object on the stack
+         * here, because we need some book-keeping for memory
+         * management, and can reuse the DnsPacket serializer, that
+         * can generate the canonical form, too, but also knows label
+         * compression and suchlike. */
+
+        if (rr->wire_format && rr->wire_format_canonical == canonical)
+                return 0;
+
+        r = dns_packet_append_rr(&packet, rr, &start, &rds);
+        if (r < 0)
+                return r;
+
+        assert(start == 0);
+        assert(packet._data);
+
+        free(rr->wire_format);
+        rr->wire_format = packet._data;
+        rr->wire_format_size = packet.size;
+        rr->wire_format_rdata_offset = rds;
+        rr->wire_format_canonical = canonical;
+
+        packet._data = NULL;
+        dns_packet_unref(&packet);
+
+        return 0;
+}
+
 const char *dns_class_to_string(uint16_t class) {
 
         switch (class) {
index ac4256b882c91abbcd1bd8606f43da82b0d03e28..c51419cb944c29f5c8c1ff701deed87830f75f36 100644 (file)
@@ -57,7 +57,11 @@ struct DnsResourceRecord {
         unsigned n_ref;
         DnsResourceKey *key;
         uint32_t ttl;
-        bool unparseable;
+        bool unparseable:1;
+        bool wire_format_canonical:1;
+        void *wire_format;
+        size_t wire_format_size;
+        size_t wire_format_rdata_offset;
         union {
                 struct {
                         void *data;
@@ -209,6 +213,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
 int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
 
+int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
+
 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
 
index 09e6872d26204bfba4c492774193c12bdb668db7..846280e8b81d2fa21e5bc2418d21ee230d445731 100644 (file)
@@ -549,7 +549,7 @@ static int dns_scope_make_reply_packet(
 
         if (answer) {
                 for (i = 0; i < answer->n_rrs; i++) {
-                        r = dns_packet_append_rr(p, answer->items[i].rr, NULL);
+                        r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL);
                         if (r < 0)
                                 return r;
                 }
@@ -559,7 +559,7 @@ static int dns_scope_make_reply_packet(
 
         if (soa) {
                 for (i = 0; i < soa->n_rrs; i++) {
-                        r = dns_packet_append_rr(p, soa->items[i].rr, NULL);
+                        r = dns_packet_append_rr(p, soa->items[i].rr, NULL, NULL);
                         if (r < 0)
                                 return r;
                 }
@@ -733,7 +733,7 @@ static int dns_scope_make_conflict_packet(
         if (r < 0)
                 return r;
 
-        r = dns_packet_append_rr(p, rr, NULL);
+        r = dns_packet_append_rr(p, rr, NULL, NULL);
         if (r < 0)
                 return r;