aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/util/data
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/util/data')
-rw-r--r--external/unbound/util/data/dname.c782
-rw-r--r--external/unbound/util/data/dname.h304
-rw-r--r--external/unbound/util/data/msgencode.c841
-rw-r--r--external/unbound/util/data/msgencode.h131
-rw-r--r--external/unbound/util/data/msgparse.c1022
-rw-r--r--external/unbound/util/data/msgparse.h301
-rw-r--r--external/unbound/util/data/msgreply.c830
-rw-r--r--external/unbound/util/data/msgreply.h438
-rw-r--r--external/unbound/util/data/packed_rrset.c389
-rw-r--r--external/unbound/util/data/packed_rrset.h428
10 files changed, 5466 insertions, 0 deletions
diff --git a/external/unbound/util/data/dname.c b/external/unbound/util/data/dname.c
new file mode 100644
index 000000000..76f2e6458
--- /dev/null
+++ b/external/unbound/util/data/dname.c
@@ -0,0 +1,782 @@
+/*
+ * util/data/dname.h - domain name handling
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains domain name handling functions.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include "util/data/dname.h"
+#include "util/data/msgparse.h"
+#include "util/log.h"
+#include "util/storage/lookup3.h"
+#include "ldns/sbuffer.h"
+
+/* determine length of a dname in buffer, no compression pointers allowed */
+size_t
+query_dname_len(sldns_buffer* query)
+{
+ size_t len = 0;
+ size_t labellen;
+ while(1) {
+ if(sldns_buffer_remaining(query) < 1)
+ return 0; /* parse error, need label len */
+ labellen = sldns_buffer_read_u8(query);
+ if(labellen&0xc0)
+ return 0; /* no compression allowed in queries */
+ len += labellen + 1;
+ if(len > LDNS_MAX_DOMAINLEN)
+ return 0; /* too long */
+ if(labellen == 0)
+ return len;
+ if(sldns_buffer_remaining(query) < labellen)
+ return 0; /* parse error, need content */
+ sldns_buffer_skip(query, (ssize_t)labellen);
+ }
+}
+
+size_t
+dname_valid(uint8_t* dname, size_t maxlen)
+{
+ size_t len = 0;
+ size_t labellen;
+ labellen = *dname++;
+ while(labellen) {
+ if(labellen&0xc0)
+ return 0; /* no compression ptrs allowed */
+ len += labellen + 1;
+ if(len >= LDNS_MAX_DOMAINLEN)
+ return 0; /* too long */
+ if(len > maxlen)
+ return 0; /* does not fit in memory allocation */
+ dname += labellen;
+ labellen = *dname++;
+ }
+ len += 1;
+ if(len > maxlen)
+ return 0; /* does not fit in memory allocation */
+ return len;
+}
+
+/** compare uncompressed, noncanonical, registers are hints for speed */
+int
+query_dname_compare(register uint8_t* d1, register uint8_t* d2)
+{
+ register uint8_t lab1, lab2;
+ log_assert(d1 && d2);
+ lab1 = *d1++;
+ lab2 = *d2++;
+ while( lab1 != 0 || lab2 != 0 ) {
+ /* compare label length */
+ /* if one dname ends, it has labellength 0 */
+ if(lab1 != lab2) {
+ if(lab1 < lab2)
+ return -1;
+ return 1;
+ }
+ log_assert(lab1 == lab2 && lab1 != 0);
+ /* compare lowercased labels. */
+ while(lab1--) {
+ /* compare bytes first for speed */
+ if(*d1 != *d2 &&
+ tolower((int)*d1) != tolower((int)*d2)) {
+ if(tolower((int)*d1) < tolower((int)*d2))
+ return -1;
+ return 1;
+ }
+ d1++;
+ d2++;
+ }
+ /* next pair of labels. */
+ lab1 = *d1++;
+ lab2 = *d2++;
+ }
+ return 0;
+}
+
+void
+query_dname_tolower(uint8_t* dname)
+{
+ /* the dname is stored uncompressed */
+ uint8_t labellen;
+ labellen = *dname;
+ while(labellen) {
+ dname++;
+ while(labellen--) {
+ *dname = (uint8_t)tolower((int)*dname);
+ dname++;
+ }
+ labellen = *dname;
+ }
+}
+
+void
+pkt_dname_tolower(sldns_buffer* pkt, uint8_t* dname)
+{
+ uint8_t lablen;
+ int count = 0;
+ if(dname >= sldns_buffer_end(pkt))
+ return;
+ lablen = *dname++;
+ while(lablen) {
+ if(LABEL_IS_PTR(lablen)) {
+ if((size_t)PTR_OFFSET(lablen, *dname)
+ >= sldns_buffer_limit(pkt))
+ return;
+ dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+ lablen = *dname++;
+ if(count++ > MAX_COMPRESS_PTRS)
+ return;
+ continue;
+ }
+ if(dname+lablen >= sldns_buffer_end(pkt))
+ return;
+ while(lablen--) {
+ *dname = (uint8_t)tolower((int)*dname);
+ dname++;
+ }
+ if(dname >= sldns_buffer_end(pkt))
+ return;
+ lablen = *dname++;
+ }
+}
+
+
+size_t
+pkt_dname_len(sldns_buffer* pkt)
+{
+ size_t len = 0;
+ int ptrcount = 0;
+ uint8_t labellen;
+ size_t endpos = 0;
+
+ /* read dname and determine length */
+ /* check compression pointers, loops, out of bounds */
+ while(1) {
+ /* read next label */
+ if(sldns_buffer_remaining(pkt) < 1)
+ return 0;
+ labellen = sldns_buffer_read_u8(pkt);
+ if(LABEL_IS_PTR(labellen)) {
+ /* compression ptr */
+ uint16_t ptr;
+ if(sldns_buffer_remaining(pkt) < 1)
+ return 0;
+ ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt));
+ if(ptrcount++ > MAX_COMPRESS_PTRS)
+ return 0; /* loop! */
+ if(sldns_buffer_limit(pkt) <= ptr)
+ return 0; /* out of bounds! */
+ if(!endpos)
+ endpos = sldns_buffer_position(pkt);
+ sldns_buffer_set_position(pkt, ptr);
+ } else {
+ /* label contents */
+ if(labellen > 0x3f)
+ return 0; /* label too long */
+ len += 1 + labellen;
+ if(len > LDNS_MAX_DOMAINLEN)
+ return 0;
+ if(labellen == 0) {
+ /* end of dname */
+ break;
+ }
+ if(sldns_buffer_remaining(pkt) < labellen)
+ return 0;
+ sldns_buffer_skip(pkt, (ssize_t)labellen);
+ }
+ }
+ if(endpos)
+ sldns_buffer_set_position(pkt, endpos);
+
+ return len;
+}
+
+int
+dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2)
+{
+ uint8_t len1, len2;
+ log_assert(pkt && d1 && d2);
+ len1 = *d1++;
+ len2 = *d2++;
+ while( len1 != 0 || len2 != 0 ) {
+ /* resolve ptrs */
+ if(LABEL_IS_PTR(len1)) {
+ d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1));
+ len1 = *d1++;
+ continue;
+ }
+ if(LABEL_IS_PTR(len2)) {
+ d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2));
+ len2 = *d2++;
+ continue;
+ }
+ /* check label length */
+ log_assert(len1 <= LDNS_MAX_LABELLEN);
+ log_assert(len2 <= LDNS_MAX_LABELLEN);
+ if(len1 != len2) {
+ if(len1 < len2) return -1;
+ return 1;
+ }
+ log_assert(len1 == len2 && len1 != 0);
+ /* compare labels */
+ while(len1--) {
+ if(tolower((int)*d1++) != tolower((int)*d2++)) {
+ if(tolower((int)d1[-1]) < tolower((int)d2[-1]))
+ return -1;
+ return 1;
+ }
+ }
+ len1 = *d1++;
+ len2 = *d2++;
+ }
+ return 0;
+}
+
+hashvalue_t
+dname_query_hash(uint8_t* dname, hashvalue_t h)
+{
+ uint8_t labuf[LDNS_MAX_LABELLEN+1];
+ uint8_t lablen;
+ int i;
+
+ /* preserve case of query, make hash label by label */
+ lablen = *dname++;
+ while(lablen) {
+ log_assert(lablen <= LDNS_MAX_LABELLEN);
+ labuf[0] = lablen;
+ i=0;
+ while(lablen--)
+ labuf[++i] = (uint8_t)tolower((int)*dname++);
+ h = hashlittle(labuf, labuf[0] + 1, h);
+ lablen = *dname++;
+ }
+
+ return h;
+}
+
+hashvalue_t
+dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_t h)
+{
+ uint8_t labuf[LDNS_MAX_LABELLEN+1];
+ uint8_t lablen;
+ int i;
+
+ /* preserve case of query, make hash label by label */
+ lablen = *dname++;
+ while(lablen) {
+ if(LABEL_IS_PTR(lablen)) {
+ /* follow pointer */
+ dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+ lablen = *dname++;
+ continue;
+ }
+ log_assert(lablen <= LDNS_MAX_LABELLEN);
+ labuf[0] = lablen;
+ i=0;
+ while(lablen--)
+ labuf[++i] = (uint8_t)tolower((int)*dname++);
+ h = hashlittle(labuf, labuf[0] + 1, h);
+ lablen = *dname++;
+ }
+
+ return h;
+}
+
+void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname)
+{
+ /* copy over the dname and decompress it at the same time */
+ size_t len = 0;
+ uint8_t lablen;
+ lablen = *dname++;
+ while(lablen) {
+ if(LABEL_IS_PTR(lablen)) {
+ /* follow pointer */
+ dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+ lablen = *dname++;
+ continue;
+ }
+ log_assert(lablen <= LDNS_MAX_LABELLEN);
+ len += (size_t)lablen+1;
+ if(len >= LDNS_MAX_DOMAINLEN) {
+ *to = 0; /* end the result prematurely */
+ log_err("bad dname in dname_pkt_copy");
+ return;
+ }
+ *to++ = lablen;
+ memmove(to, dname, lablen);
+ dname += lablen;
+ to += lablen;
+ lablen = *dname++;
+ }
+ /* copy last \0 */
+ *to = 0;
+}
+
+void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname)
+{
+ uint8_t lablen;
+ if(!out) out = stdout;
+ if(!dname) return;
+
+ lablen = *dname++;
+ if(!lablen)
+ fputc('.', out);
+ while(lablen) {
+ if(LABEL_IS_PTR(lablen)) {
+ /* follow pointer */
+ if(!pkt) {
+ fputs("??compressionptr??", out);
+ return;
+ }
+ dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname));
+ lablen = *dname++;
+ continue;
+ }
+ if(lablen > LDNS_MAX_LABELLEN) {
+ fputs("??extendedlabel??", out);
+ return;
+ }
+ while(lablen--)
+ fputc((int)*dname++, out);
+ fputc('.', out);
+ lablen = *dname++;
+ }
+}
+
+int
+dname_count_labels(uint8_t* dname)
+{
+ uint8_t lablen;
+ int labs = 1;
+
+ lablen = *dname++;
+ while(lablen) {
+ labs++;
+ dname += lablen;
+ lablen = *dname++;
+ }
+ return labs;
+}
+
+int
+dname_count_size_labels(uint8_t* dname, size_t* size)
+{
+ uint8_t lablen;
+ int labs = 1;
+ size_t sz = 1;
+
+ lablen = *dname++;
+ while(lablen) {
+ labs++;
+ sz += lablen+1;
+ dname += lablen;
+ lablen = *dname++;
+ }
+ *size = sz;
+ return labs;
+}
+
+/**
+ * Compare labels in memory, lowercase while comparing.
+ * @param p1: label 1
+ * @param p2: label 2
+ * @param len: number of bytes to compare.
+ * @return: 0, -1, +1 comparison result.
+ */
+static int
+memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len)
+{
+ while(len--) {
+ if(*p1 != *p2 && tolower((int)*p1) != tolower((int)*p2)) {
+ if(tolower((int)*p1) < tolower((int)*p2))
+ return -1;
+ return 1;
+ }
+ p1++;
+ p2++;
+ }
+ return 0;
+}
+
+int
+dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
+{
+ uint8_t len1, len2;
+ int atlabel = labs1;
+ int lastmlabs;
+ int lastdiff = 0;
+ /* first skip so that we compare same label. */
+ if(labs1 > labs2) {
+ while(atlabel > labs2) {
+ len1 = *d1++;
+ d1 += len1;
+ atlabel--;
+ }
+ log_assert(atlabel == labs2);
+ } else if(labs1 < labs2) {
+ atlabel = labs2;
+ while(atlabel > labs1) {
+ len2 = *d2++;
+ d2 += len2;
+ atlabel--;
+ }
+ log_assert(atlabel == labs1);
+ }
+ lastmlabs = atlabel+1;
+ /* now at same label in d1 and d2, atlabel */
+ /* www.example.com. */
+ /* 4 3 2 1 atlabel number */
+ /* repeat until at root label (which is always the same) */
+ while(atlabel > 1) {
+ len1 = *d1++;
+ len2 = *d2++;
+ if(len1 != len2) {
+ log_assert(len1 != 0 && len2 != 0);
+ if(len1<len2)
+ lastdiff = -1;
+ else lastdiff = 1;
+ lastmlabs = atlabel;
+ d1 += len1;
+ d2 += len2;
+ } else {
+ /* memlowercmp is inlined here; or just like
+ * if((c=memlowercmp(d1, d2, len1)) != 0) {
+ * lastdiff = c;
+ * lastmlabs = atlabel; } apart from d1++,d2++ */
+ while(len1) {
+ if(*d1 != *d2 && tolower((int)*d1)
+ != tolower((int)*d2)) {
+ if(tolower((int)*d1) <
+ tolower((int)*d2)) {
+ lastdiff = -1;
+ lastmlabs = atlabel;
+ d1 += len1;
+ d2 += len1;
+ break;
+ }
+ lastdiff = 1;
+ lastmlabs = atlabel;
+ d1 += len1;
+ d2 += len1;
+ break; /* out of memlowercmp */
+ }
+ d1++;
+ d2++;
+ len1--;
+ }
+ }
+ atlabel--;
+ }
+ /* last difference atlabel number, so number of labels matching,
+ * at the right side, is one less. */
+ *mlabs = lastmlabs-1;
+ if(lastdiff == 0) {
+ /* all labels compared were equal, check if one has more
+ * labels, so that example.com. > com. */
+ if(labs1 > labs2)
+ return 1;
+ else if(labs1 < labs2)
+ return -1;
+ }
+ return lastdiff;
+}
+
+int
+dname_buffer_write(sldns_buffer* pkt, uint8_t* dname)
+{
+ uint8_t lablen;
+
+ if(sldns_buffer_remaining(pkt) < 1)
+ return 0;
+ lablen = *dname++;
+ sldns_buffer_write_u8(pkt, lablen);
+ while(lablen) {
+ if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
+ return 0;
+ sldns_buffer_write(pkt, dname, lablen);
+ dname += lablen;
+ lablen = *dname++;
+ sldns_buffer_write_u8(pkt, lablen);
+ }
+ return 1;
+}
+
+void dname_str(uint8_t* dname, char* str)
+{
+ size_t len = 0;
+ uint8_t lablen = 0;
+ char* s = str;
+ if(!dname || !*dname) {
+ *s++ = '.';
+ *s = 0;
+ return;
+ }
+ lablen = *dname++;
+ while(lablen) {
+ if(lablen > LDNS_MAX_LABELLEN) {
+ *s++ = '#';
+ *s = 0;
+ return;
+ }
+ len += lablen+1;
+ if(len >= LDNS_MAX_DOMAINLEN-1) {
+ *s++ = '&';
+ *s = 0;
+ return;
+ }
+ while(lablen--) {
+ if(isalnum((int)*dname)
+ || *dname == '-' || *dname == '_'
+ || *dname == '*')
+ *s++ = *(char*)dname++;
+ else {
+ *s++ = '?';
+ dname++;
+ }
+ }
+ *s++ = '.';
+ lablen = *dname++;
+ }
+ *s = 0;
+}
+
+int
+dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2)
+{
+ int m;
+ /* check subdomain: d1: www.example.com. and d2: example.com. */
+ if(labs2 >= labs1)
+ return 0;
+ if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) {
+ /* subdomain if all labels match */
+ return (m == labs2);
+ }
+ return 0;
+}
+
+int
+dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2)
+{
+ return dname_strict_subdomain(d1, dname_count_labels(d1), d2,
+ dname_count_labels(d2));
+}
+
+int
+dname_subdomain_c(uint8_t* d1, uint8_t* d2)
+{
+ int m;
+ /* check subdomain: d1: www.example.com. and d2: example.com. */
+ /* or d1: example.com. and d2: example.com. */
+ int labs1 = dname_count_labels(d1);
+ int labs2 = dname_count_labels(d2);
+ if(labs2 > labs1)
+ return 0;
+ if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) {
+ /* must have been example.com , www.example.com - wrong */
+ /* or otherwise different dnames */
+ return 0;
+ }
+ return (m == labs2);
+}
+
+int
+dname_is_root(uint8_t* dname)
+{
+ uint8_t len;
+ log_assert(dname);
+ len = dname[0];
+ log_assert(!LABEL_IS_PTR(len));
+ return (len == 0);
+}
+
+void
+dname_remove_label(uint8_t** dname, size_t* len)
+{
+ size_t lablen;
+ log_assert(dname && *dname && len);
+ lablen = (*dname)[0];
+ log_assert(!LABEL_IS_PTR(lablen));
+ log_assert(*len > lablen);
+ if(lablen == 0)
+ return; /* do not modify root label */
+ *len -= lablen+1;
+ *dname += lablen+1;
+}
+
+void
+dname_remove_labels(uint8_t** dname, size_t* len, int n)
+{
+ int i;
+ for(i=0; i<n; i++)
+ dname_remove_label(dname, len);
+}
+
+int
+dname_signame_label_count(uint8_t* dname)
+{
+ uint8_t lablen;
+ int count = 0;
+ if(!*dname)
+ return 0;
+ if(dname[0] == 1 && dname[1] == '*')
+ dname += 2;
+ lablen = dname[0];
+ while(lablen) {
+ count++;
+ dname += lablen;
+ dname += 1;
+ lablen = dname[0];
+ }
+ return count;
+}
+
+int
+dname_is_wild(uint8_t* dname)
+{
+ return (dname[0] == 1 && dname[1] == '*');
+}
+
+/**
+ * Compare labels in memory, lowercase while comparing.
+ * Returns canonical order for labels. If all is equal, the
+ * shortest is first.
+ *
+ * @param p1: label 1
+ * @param len1: length of label 1.
+ * @param p2: label 2
+ * @param len2: length of label 2.
+ * @return: 0, -1, +1 comparison result.
+ */
+static int
+memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
+{
+ uint8_t min = (len1<len2)?len1:len2;
+ int c = memlowercmp(p1, p2, min);
+ if(c != 0)
+ return c;
+ /* equal, see who is shortest */
+ if(len1 < len2)
+ return -1;
+ if(len1 > len2)
+ return 1;
+ return 0;
+}
+
+
+int
+dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
+{
+ /* like dname_lab_cmp, but with different label comparison,
+ * empty character sorts before \000.
+ * So ylyly is before z. */
+ uint8_t len1, len2;
+ int atlabel = labs1;
+ int lastmlabs;
+ int lastdiff = 0;
+ int c;
+ /* first skip so that we compare same label. */
+ if(labs1 > labs2) {
+ while(atlabel > labs2) {
+ len1 = *d1++;
+ d1 += len1;
+ atlabel--;
+ }
+ log_assert(atlabel == labs2);
+ } else if(labs1 < labs2) {
+ atlabel = labs2;
+ while(atlabel > labs1) {
+ len2 = *d2++;
+ d2 += len2;
+ atlabel--;
+ }
+ log_assert(atlabel == labs1);
+ }
+ lastmlabs = atlabel+1;
+ /* now at same label in d1 and d2, atlabel */
+ /* www.example.com. */
+ /* 4 3 2 1 atlabel number */
+ /* repeat until at root label (which is always the same) */
+ while(atlabel > 1) {
+ len1 = *d1++;
+ len2 = *d2++;
+
+ if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
+ if(c<0)
+ lastdiff = -1;
+ else lastdiff = 1;
+ lastmlabs = atlabel;
+ }
+
+ d1 += len1;
+ d2 += len2;
+ atlabel--;
+ }
+ /* last difference atlabel number, so number of labels matching,
+ * at the right side, is one less. */
+ *mlabs = lastmlabs-1;
+ if(lastdiff == 0) {
+ /* all labels compared were equal, check if one has more
+ * labels, so that example.com. > com. */
+ if(labs1 > labs2)
+ return 1;
+ else if(labs1 < labs2)
+ return -1;
+ }
+ return lastdiff;
+}
+
+int
+dname_canonical_compare(uint8_t* d1, uint8_t* d2)
+{
+ int labs1, labs2, m;
+ labs1 = dname_count_labels(d1);
+ labs2 = dname_count_labels(d2);
+ return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
+}
+
+uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2)
+{
+ int labs1, labs2, m;
+ size_t len = LDNS_MAX_DOMAINLEN;
+ labs1 = dname_count_labels(d1);
+ labs2 = dname_count_labels(d2);
+ (void)dname_lab_cmp(d1, labs1, d2, labs2, &m);
+ dname_remove_labels(&d1, &len, labs1-m);
+ return d1;
+}
diff --git a/external/unbound/util/data/dname.h b/external/unbound/util/data/dname.h
new file mode 100644
index 000000000..ae2fbadc1
--- /dev/null
+++ b/external/unbound/util/data/dname.h
@@ -0,0 +1,304 @@
+/*
+ * util/data/dname.h - domain name routines
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains functions to deal with domain names (dnames).
+ *
+ * Some of the functions deal with domain names as a wireformat buffer,
+ * with a length.
+ */
+
+#ifndef UTIL_DATA_DNAME_H
+#define UTIL_DATA_DNAME_H
+#include "util/storage/lruhash.h"
+struct sldns_buffer;
+
+/** max number of compression ptrs to follow */
+#define MAX_COMPRESS_PTRS 256
+
+/**
+ * Determine length of dname in buffer, no compression ptrs allowed,
+ * @param query: the ldns buffer, current position at start of dname.
+ * at end, position is at end of the dname.
+ * @return: 0 on parse failure, or length including ending 0 of dname.
+ */
+size_t query_dname_len(struct sldns_buffer* query);
+
+/**
+ * Determine if dname in memory is correct. no compression ptrs allowed.
+ * @param dname: where dname starts in memory.
+ * @param len: dname is not allowed to exceed this length (i.e. of allocation).
+ * @return length of dname if dname is ok, 0 on a parse error.
+ */
+size_t dname_valid(uint8_t* dname, size_t len);
+
+/** lowercase query dname */
+void query_dname_tolower(uint8_t* dname);
+
+/**
+ * lowercase pkt dname (follows compression pointers)
+ * @param pkt: the packet, used to follow compression pointers. Position
+ * is unchanged.
+ * @param dname: start of dname in packet.
+ */
+void pkt_dname_tolower(struct sldns_buffer* pkt, uint8_t* dname);
+
+/**
+ * Compare query dnames (uncompressed storage). The Dnames passed do not
+ * have to be lowercased, comparison routine does this.
+ *
+ * This routine is special, in that the comparison that it does corresponds
+ * with the canonical comparison needed when comparing dnames inside rdata
+ * for RR types that need canonicalization. That means that the first byte
+ * that is smaller (possibly after lowercasing) makes an RR smaller, or the
+ * shortest name makes an RR smaller.
+ *
+ * This routine does not compute the canonical order needed for NSEC
+ * processing.
+ *
+ * Dnames have to be valid format.
+ * @param d1: dname to compare
+ * @param d2: dname to compare
+ * @return: -1, 0, or +1 depending on comparison results.
+ * Sort order is first difference found. not the canonical ordering.
+ */
+int query_dname_compare(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Determine correct, compressed, dname present in packet.
+ * Checks for parse errors.
+ * @param pkt: packet to read from (from current start position).
+ * @return: 0 on parse error.
+ * At exit the position is right after the (compressed) dname.
+ * Compression pointers are followed and checked for loops.
+ * The uncompressed wireformat length is returned.
+ */
+size_t pkt_dname_len(struct sldns_buffer* pkt);
+
+/**
+ * Compare dnames in packet (compressed). Dnames must be valid.
+ * routine performs lowercasing, so the packet casing is preserved.
+ * @param pkt: packet, used to resolve compression pointers.
+ * @param d1: dname to compare
+ * @param d2: dname to compare
+ * @return: -1, 0, or +1 depending on comparison results.
+ * Sort order is first difference found. not the canonical ordering.
+ */
+int dname_pkt_compare(struct sldns_buffer* pkt, uint8_t* d1, uint8_t* d2);
+
+/**
+ * Hash dname, label by label, lowercasing, into hashvalue.
+ * Dname in query format (not compressed).
+ * @param dname: dname to hash.
+ * @param h: initial hash value.
+ * @return: result hash value.
+ */
+hashvalue_t dname_query_hash(uint8_t* dname, hashvalue_t h);
+
+/**
+ * Hash dname, label by label, lowercasing, into hashvalue.
+ * Dname in pkt format (compressed).
+ * @param pkt: packet, for resolving compression pointers.
+ * @param dname: dname to hash, pointer to the pkt buffer.
+ * Must be valid format. No loops, etc.
+ * @param h: initial hash value.
+ * @return: result hash value.
+ * Result is the same as dname_query_hash, even if compression is used.
+ */
+hashvalue_t dname_pkt_hash(struct sldns_buffer* pkt, uint8_t* dname, hashvalue_t h);
+
+/**
+ * Copy over a valid dname and decompress it.
+ * @param pkt: packet to resolve compression pointers.
+ * @param to: buffer of size from pkt_len function to hold result.
+ * @param dname: pointer into packet where dname starts.
+ */
+void dname_pkt_copy(struct sldns_buffer* pkt, uint8_t* to, uint8_t* dname);
+
+/**
+ * Copy over a valid dname to a packet.
+ * @param pkt: packet to copy to.
+ * @param dname: dname to copy.
+ * @return: 0 if not enough space in buffer.
+ */
+int dname_buffer_write(struct sldns_buffer* pkt, uint8_t* dname);
+
+/**
+ * Count the number of labels in an uncompressed dname in memory.
+ * @param dname: pointer to uncompressed dname.
+ * @return: count of labels, including root label, "com." has 2 labels.
+ */
+int dname_count_labels(uint8_t* dname);
+
+/**
+ * Count labels and dname length both, for uncompressed dname in memory.
+ * @param dname: pointer to uncompressed dname.
+ * @param size: length of dname, including root label.
+ * @return: count of labels, including root label, "com." has 2 labels.
+ */
+int dname_count_size_labels(uint8_t* dname, size_t* size);
+
+/**
+ * Compare dnames, sorted not canonical, but by label.
+ * Such that zone contents follows zone apex.
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param labs1: number of labels in first dname.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @param labs2: number of labels in second dname.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
+
+/**
+ * See if domain name d1 is a strict subdomain of d2.
+ * That is a subdomain, but not equal.
+ * @param d1: domain name, uncompressed wireformat
+ * @param labs1: number of labels in d1, including root label.
+ * @param d2: domain name, uncompressed wireformat
+ * @param labs2: number of labels in d2, including root label.
+ * @return true if d1 is a subdomain of d2, but not equal to d2.
+ */
+int dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2);
+
+/**
+ * Like dname_strict_subdomain but counts labels
+ * @param d1: domain name, uncompressed wireformat
+ * @param d2: domain name, uncompressed wireformat
+ * @return true if d1 is a subdomain of d2, but not equal to d2.
+ */
+int dname_strict_subdomain_c(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Counts labels. Tests is d1 is a subdomain of d2.
+ * @param d1: domain name, uncompressed wireformat
+ * @param d2: domain name, uncompressed wireformat
+ * @return true if d1 is a subdomain of d2.
+ */
+int dname_subdomain_c(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Debug helper. Print wireformat dname to output.
+ * @param out: like stdout or a file.
+ * @param pkt: if not NULL, the packet for resolving compression ptrs.
+ * @param dname: pointer to (start of) dname.
+ */
+void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname);
+
+/**
+ * Debug helper. Print dname to given string buffer (string buffer must
+ * be at least 255 chars + 1 for the 0, in printable form.
+ * This may lose information (? for nonprintable characters, or & if
+ * the name is too long, # for a bad label length).
+ * @param dname: uncompressed wireformat.
+ * @param str: buffer of 255+1 length.
+ */
+void dname_str(uint8_t* dname, char* str);
+
+/**
+ * Returns true if the uncompressed wireformat dname is the root "."
+ * @param dname: the dname to check
+ * @return true if ".", false if not.
+ */
+int dname_is_root(uint8_t* dname);
+
+/**
+ * Snip off first label from a dname, returning the parent zone.
+ * @param dname: from what to strip off. uncompressed wireformat.
+ * @param len: length, adjusted to become less.
+ * @return stripped off, or "." if input was ".".
+ */
+void dname_remove_label(uint8_t** dname, size_t* len);
+
+/**
+ * Snip off first N labels from a dname, returning the parent zone.
+ * @param dname: from what to strip off. uncompressed wireformat.
+ * @param len: length, adjusted to become less.
+ * @param n: number of labels to strip off (from the left).
+ * if 0, nothing happens.
+ * @return stripped off, or "." if input was ".".
+ */
+void dname_remove_labels(uint8_t** dname, size_t* len, int n);
+
+/**
+ * Count labels for the RRSIG signature label field.
+ * Like a normal labelcount, but "*" wildcard and "." root are not counted.
+ * @param dname: valid uncompressed wireformat.
+ * @return number of labels like in RRSIG; '*' and '.' are not counted.
+ */
+int dname_signame_label_count(uint8_t* dname);
+
+/**
+ * Return true if the label is a wildcard, *.example.com.
+ * @param dname: valid uncompressed wireformat.
+ * @return true if wildcard, or false.
+ */
+int dname_is_wild(uint8_t* dname);
+
+/**
+ * Compare dnames, Canonical in rfc4034 sense, but by label.
+ * Such that zone contents follows zone apex.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param labs1: number of labels in first dname.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @param labs2: number of labels in second dname.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2,
+ int* mlabs);
+
+/**
+ * Canonical dname compare. Takes care of counting labels.
+ * Per rfc 4034 canonical order.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canonical_compare(uint8_t* d1, uint8_t* d2);
+
+/**
+ * Get the shared topdomain between two names. Root "." or longer.
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @return pointer to shared topdomain. Ptr to a part of d1.
+ */
+uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2);
+
+#endif /* UTIL_DATA_DNAME_H */
diff --git a/external/unbound/util/data/msgencode.c b/external/unbound/util/data/msgencode.c
new file mode 100644
index 000000000..26b5deabe
--- /dev/null
+++ b/external/unbound/util/data/msgencode.c
@@ -0,0 +1,841 @@
+/*
+ * util/data/msgencode.c - Encode DNS messages, queries and replies.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a routines to encode DNS messages.
+ */
+
+#include "config.h"
+#include "util/data/msgencode.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/log.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+#include "ldns/sbuffer.h"
+
+/** return code that means the function ran out of memory. negative so it does
+ * not conflict with DNS rcodes. */
+#define RETVAL_OUTMEM -2
+/** return code that means the data did not fit (completely) in the packet */
+#define RETVAL_TRUNC -4
+/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
+#define RETVAL_OK 0
+
+/**
+ * Data structure to help domain name compression in outgoing messages.
+ * A tree of dnames and their offsets in the packet is kept.
+ * It is kept sorted, not canonical, but by label at least, so that after
+ * a lookup of a name you know its closest match, and the parent from that
+ * closest match. These are possible compression targets.
+ *
+ * It is a binary tree, not a rbtree or balanced tree, as the effort
+ * of keeping it balanced probably outweighs usefulness (given typical
+ * DNS packet size).
+ */
+struct compress_tree_node {
+ /** left node in tree, all smaller to this */
+ struct compress_tree_node* left;
+ /** right node in tree, all larger than this */
+ struct compress_tree_node* right;
+
+ /** the parent node - not for tree, but zone parent. One less label */
+ struct compress_tree_node* parent;
+ /** the domain name for this node. Pointer to uncompressed memory. */
+ uint8_t* dname;
+ /** number of labels in domain name, kept to help compare func. */
+ int labs;
+ /** offset in packet that points to this dname */
+ size_t offset;
+};
+
+/**
+ * Find domain name in tree, returns exact and closest match.
+ * @param tree: root of tree.
+ * @param dname: pointer to uncompressed dname.
+ * @param labs: number of labels in domain name.
+ * @param match: closest or exact match.
+ * guaranteed to be smaller or equal to the sought dname.
+ * can be null if the tree is empty.
+ * @param matchlabels: number of labels that match with closest match.
+ * can be zero is there is no match.
+ * @param insertpt: insert location for dname, if not found.
+ * @return: 0 if no exact match.
+ */
+static int
+compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
+ int labs, struct compress_tree_node** match, int* matchlabels,
+ struct compress_tree_node*** insertpt)
+{
+ int c, n, closen=0;
+ struct compress_tree_node* p = *tree;
+ struct compress_tree_node* close = 0;
+ struct compress_tree_node** prev = tree;
+ while(p) {
+ if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
+ == 0) {
+ *matchlabels = n;
+ *match = p;
+ return 1;
+ }
+ if(c<0) {
+ prev = &p->left;
+ p = p->left;
+ } else {
+ closen = n;
+ close = p; /* p->dname is smaller than dname */
+ prev = &p->right;
+ p = p->right;
+ }
+ }
+ *insertpt = prev;
+ *matchlabels = closen;
+ *match = close;
+ return 0;
+}
+
+/**
+ * Lookup a domain name in compression tree.
+ * @param tree: root of tree (not the node with '.').
+ * @param dname: pointer to uncompressed dname.
+ * @param labs: number of labels in domain name.
+ * @param insertpt: insert location for dname, if not found.
+ * @return: 0 if not found or compress treenode with best compression.
+ */
+static struct compress_tree_node*
+compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
+ int labs, struct compress_tree_node*** insertpt)
+{
+ struct compress_tree_node* p;
+ int m;
+ if(labs <= 1)
+ return 0; /* do not compress root node */
+ if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
+ /* exact match */
+ return p;
+ }
+ /* return some ancestor of p that compresses well. */
+ if(m>1) {
+ /* www.example.com. (labs=4) matched foo.example.com.(labs=4)
+ * then matchcount = 3. need to go up. */
+ while(p && p->labs > m)
+ p = p->parent;
+ return p;
+ }
+ return 0;
+}
+
+/**
+ * Create node for domain name compression tree.
+ * @param dname: pointer to uncompressed dname (stored in tree).
+ * @param labs: number of labels in dname.
+ * @param offset: offset into packet for dname.
+ * @param region: how to allocate memory for new node.
+ * @return new node or 0 on malloc failure.
+ */
+static struct compress_tree_node*
+compress_tree_newnode(uint8_t* dname, int labs, size_t offset,
+ struct regional* region)
+{
+ struct compress_tree_node* n = (struct compress_tree_node*)
+ regional_alloc(region, sizeof(struct compress_tree_node));
+ if(!n) return 0;
+ n->left = 0;
+ n->right = 0;
+ n->parent = 0;
+ n->dname = dname;
+ n->labs = labs;
+ n->offset = offset;
+ return n;
+}
+
+/**
+ * Store domain name and ancestors into compression tree.
+ * @param dname: pointer to uncompressed dname (stored in tree).
+ * @param labs: number of labels in dname.
+ * @param offset: offset into packet for dname.
+ * @param region: how to allocate memory for new node.
+ * @param closest: match from previous lookup, used to compress dname.
+ * may be NULL if no previous match.
+ * if the tree has an ancestor of dname already, this must be it.
+ * @param insertpt: where to insert the dname in tree.
+ * @return: 0 on memory error.
+ */
+static int
+compress_tree_store(uint8_t* dname, int labs, size_t offset,
+ struct regional* region, struct compress_tree_node* closest,
+ struct compress_tree_node** insertpt)
+{
+ uint8_t lablen;
+ struct compress_tree_node* newnode;
+ struct compress_tree_node* prevnode = NULL;
+ int uplabs = labs-1; /* does not store root in tree */
+ if(closest) uplabs = labs - closest->labs;
+ log_assert(uplabs >= 0);
+ /* algorithms builds up a vine of dname-labels to hang into tree */
+ while(uplabs--) {
+ if(offset > PTR_MAX_OFFSET) {
+ /* insertion failed, drop vine */
+ return 1; /* compression pointer no longer useful */
+ }
+ if(!(newnode = compress_tree_newnode(dname, labs, offset,
+ region))) {
+ /* insertion failed, drop vine */
+ return 0;
+ }
+
+ if(prevnode) {
+ /* chain nodes together, last one has one label more,
+ * so is larger than newnode, thus goes right. */
+ newnode->right = prevnode;
+ prevnode->parent = newnode;
+ }
+
+ /* next label */
+ lablen = *dname++;
+ dname += lablen;
+ offset += lablen+1;
+ prevnode = newnode;
+ labs--;
+ }
+ /* if we have a vine, hang the vine into the tree */
+ if(prevnode) {
+ *insertpt = prevnode;
+ prevnode->parent = closest;
+ }
+ return 1;
+}
+
+/** compress a domain name */
+static int
+write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
+ struct compress_tree_node* p)
+{
+ /* compress it */
+ int labcopy = labs - p->labs;
+ uint8_t lablen;
+ uint16_t ptr;
+
+ if(labs == 1) {
+ /* write root label */
+ if(sldns_buffer_remaining(pkt) < 1)
+ return 0;
+ sldns_buffer_write_u8(pkt, 0);
+ return 1;
+ }
+
+ /* copy the first couple of labels */
+ while(labcopy--) {
+ lablen = *dname++;
+ if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
+ return 0;
+ sldns_buffer_write_u8(pkt, lablen);
+ sldns_buffer_write(pkt, dname, lablen);
+ dname += lablen;
+ }
+ /* insert compression ptr */
+ if(sldns_buffer_remaining(pkt) < 2)
+ return 0;
+ ptr = PTR_CREATE(p->offset);
+ sldns_buffer_write_u16(pkt, ptr);
+ return 1;
+}
+
+/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
+static int
+compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
+ struct regional* region, struct compress_tree_node** tree,
+ size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
+{
+ struct compress_tree_node* p;
+ struct compress_tree_node** insertpt;
+ if(!*owner_ptr) {
+ /* compress first time dname */
+ if((p = compress_tree_lookup(tree, key->rk.dname,
+ owner_labs, &insertpt))) {
+ if(p->labs == owner_labs)
+ /* avoid ptr chains, since some software is
+ * not capable of decoding ptr after a ptr. */
+ *owner_ptr = htons(PTR_CREATE(p->offset));
+ if(!write_compressed_dname(pkt, key->rk.dname,
+ owner_labs, p))
+ return RETVAL_TRUNC;
+ /* check if typeclass+4 ttl + rdatalen is available */
+ if(sldns_buffer_remaining(pkt) < 4+4+2)
+ return RETVAL_TRUNC;
+ } else {
+ /* no compress */
+ if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, key->rk.dname,
+ key->rk.dname_len);
+ if(owner_pos <= PTR_MAX_OFFSET)
+ *owner_ptr = htons(PTR_CREATE(owner_pos));
+ }
+ if(!compress_tree_store(key->rk.dname, owner_labs,
+ owner_pos, region, p, insertpt))
+ return RETVAL_OUTMEM;
+ } else {
+ /* always compress 2nd-further RRs in RRset */
+ if(owner_labs == 1) {
+ if(sldns_buffer_remaining(pkt) < 1+4+4+2)
+ return RETVAL_TRUNC;
+ sldns_buffer_write_u8(pkt, 0);
+ } else {
+ if(sldns_buffer_remaining(pkt) < 2+4+4+2)
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, owner_ptr, 2);
+ }
+ }
+ return RETVAL_OK;
+}
+
+/** compress any domain name to the packet, return RETVAL_* */
+static int
+compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
+ struct regional* region, struct compress_tree_node** tree)
+{
+ struct compress_tree_node* p;
+ struct compress_tree_node** insertpt = NULL;
+ size_t pos = sldns_buffer_position(pkt);
+ if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
+ if(!write_compressed_dname(pkt, dname, labs, p))
+ return RETVAL_TRUNC;
+ } else {
+ if(!dname_buffer_write(pkt, dname))
+ return RETVAL_TRUNC;
+ }
+ if(!compress_tree_store(dname, labs, pos, region, p, insertpt))
+ return RETVAL_OUTMEM;
+ return RETVAL_OK;
+}
+
+/** return true if type needs domain name compression in rdata */
+static const sldns_rr_descriptor*
+type_rdata_compressable(struct ub_packed_rrset_key* key)
+{
+ uint16_t t = ntohs(key->rk.type);
+ if(sldns_rr_descript(t) &&
+ sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
+ return sldns_rr_descript(t);
+ return 0;
+}
+
+/** compress domain names in rdata, return RETVAL_* */
+static int
+compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
+ struct regional* region, struct compress_tree_node** tree,
+ const sldns_rr_descriptor* desc)
+{
+ int labs, r, rdf = 0;
+ size_t dname_len, len, pos = sldns_buffer_position(pkt);
+ uint8_t count = desc->_dname_count;
+
+ sldns_buffer_skip(pkt, 2); /* rdata len fill in later */
+ /* space for rdatalen checked for already */
+ rdata += 2;
+ todolen -= 2;
+ while(todolen > 0 && count) {
+ switch(desc->_wireformat[rdf]) {
+ case LDNS_RDF_TYPE_DNAME:
+ labs = dname_count_size_labels(rdata, &dname_len);
+ if((r=compress_any_dname(rdata, pkt, labs, region,
+ tree)) != RETVAL_OK)
+ return r;
+ rdata += dname_len;
+ todolen -= dname_len;
+ count--;
+ len = 0;
+ break;
+ case LDNS_RDF_TYPE_STR:
+ len = *rdata + 1;
+ break;
+ default:
+ len = get_rdf_size(desc->_wireformat[rdf]);
+ }
+ if(len) {
+ /* copy over */
+ if(sldns_buffer_remaining(pkt) < len)
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, rdata, len);
+ todolen -= len;
+ rdata += len;
+ }
+ rdf++;
+ }
+ /* copy remainder */
+ if(todolen > 0) {
+ if(sldns_buffer_remaining(pkt) < todolen)
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, rdata, todolen);
+ }
+
+ /* set rdata len */
+ sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2);
+ return RETVAL_OK;
+}
+
+/** Returns true if RR type should be included */
+static int
+rrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
+ int dnssec)
+{
+ if(dnssec)
+ return 1;
+ /* skip non DNSSEC types, except if directly queried for */
+ if(s == LDNS_SECTION_ANSWER) {
+ if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
+ return 1;
+ }
+ /* check DNSSEC-ness */
+ switch(rrtype) {
+ case LDNS_RR_TYPE_SIG:
+ case LDNS_RR_TYPE_KEY:
+ case LDNS_RR_TYPE_NXT:
+ case LDNS_RR_TYPE_DS:
+ case LDNS_RR_TYPE_RRSIG:
+ case LDNS_RR_TYPE_NSEC:
+ case LDNS_RR_TYPE_DNSKEY:
+ case LDNS_RR_TYPE_NSEC3:
+ case LDNS_RR_TYPE_NSEC3PARAMS:
+ return 0;
+ }
+ return 1;
+}
+
+/** store rrset in buffer in wireformat, return RETVAL_* */
+static int
+packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
+ uint16_t* num_rrs, time_t timenow, struct regional* region,
+ int do_data, int do_sig, struct compress_tree_node** tree,
+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
+{
+ size_t i, j, owner_pos;
+ int r, owner_labs;
+ uint16_t owner_ptr = 0;
+ struct packed_rrset_data* data = (struct packed_rrset_data*)
+ key->entry.data;
+
+ /* does this RR type belong in the answer? */
+ if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
+ return RETVAL_OK;
+
+ owner_labs = dname_count_labels(key->rk.dname);
+ owner_pos = sldns_buffer_position(pkt);
+
+ if(do_data) {
+ const sldns_rr_descriptor* c = type_rdata_compressable(key);
+ for(i=0; i<data->count; i++) {
+ /* rrset roundrobin */
+ j = (i + rr_offset) % data->count;
+ if((r=compress_owner(key, pkt, region, tree,
+ owner_pos, &owner_ptr, owner_labs))
+ != RETVAL_OK)
+ return r;
+ sldns_buffer_write(pkt, &key->rk.type, 2);
+ sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+ if(data->rr_ttl[j] < timenow)
+ sldns_buffer_write_u32(pkt, 0);
+ else sldns_buffer_write_u32(pkt,
+ data->rr_ttl[j]-timenow);
+ if(c) {
+ if((r=compress_rdata(pkt, data->rr_data[j],
+ data->rr_len[j], region, tree, c))
+ != RETVAL_OK)
+ return r;
+ } else {
+ if(sldns_buffer_remaining(pkt) < data->rr_len[j])
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, data->rr_data[j],
+ data->rr_len[j]);
+ }
+ }
+ }
+ /* insert rrsigs */
+ if(do_sig && dnssec) {
+ size_t total = data->count+data->rrsig_count;
+ for(i=data->count; i<total; i++) {
+ if(owner_ptr && owner_labs != 1) {
+ if(sldns_buffer_remaining(pkt) <
+ 2+4+4+data->rr_len[i])
+ return RETVAL_TRUNC;
+ sldns_buffer_write(pkt, &owner_ptr, 2);
+ } else {
+ if((r=compress_any_dname(key->rk.dname,
+ pkt, owner_labs, region, tree))
+ != RETVAL_OK)
+ return r;
+ if(sldns_buffer_remaining(pkt) <
+ 4+4+data->rr_len[i])
+ return RETVAL_TRUNC;
+ }
+ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
+ sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+ if(data->rr_ttl[i] < timenow)
+ sldns_buffer_write_u32(pkt, 0);
+ else sldns_buffer_write_u32(pkt,
+ data->rr_ttl[i]-timenow);
+ /* rrsig rdata cannot be compressed, perform 100+ byte
+ * memcopy. */
+ sldns_buffer_write(pkt, data->rr_data[i],
+ data->rr_len[i]);
+ }
+ }
+ /* change rrnum only after we are sure it fits */
+ if(do_data)
+ *num_rrs += data->count;
+ if(do_sig && dnssec)
+ *num_rrs += data->rrsig_count;
+
+ return RETVAL_OK;
+}
+
+/** store msg section in wireformat buffer, return RETVAL_* */
+static int
+insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
+ sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
+ struct regional* region, struct compress_tree_node** tree,
+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
+{
+ int r;
+ size_t i, setstart;
+ *num_rrs = 0;
+ if(s != LDNS_SECTION_ADDITIONAL) {
+ if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
+ dnssec = 1; /* include all types in ANY answer */
+ for(i=0; i<num_rrsets; i++) {
+ setstart = sldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 1, 1, tree,
+ s, qtype, dnssec, rr_offset))
+ != RETVAL_OK) {
+ /* Bad, but if due to size must set TC bit */
+ /* trim off the rrset neatly. */
+ sldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ } else {
+ for(i=0; i<num_rrsets; i++) {
+ setstart = sldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 1, 0, tree,
+ s, qtype, dnssec, rr_offset))
+ != RETVAL_OK) {
+ sldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ if(dnssec)
+ for(i=0; i<num_rrsets; i++) {
+ setstart = sldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 0, 1, tree,
+ s, qtype, dnssec, rr_offset))
+ != RETVAL_OK) {
+ sldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ }
+ return RETVAL_OK;
+}
+
+/** store query section in wireformat buffer, return RETVAL */
+static int
+insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
+ sldns_buffer* buffer, struct regional* region)
+{
+ if(sldns_buffer_remaining(buffer) <
+ qinfo->qname_len+sizeof(uint16_t)*2)
+ return RETVAL_TRUNC; /* buffer too small */
+ /* the query is the first name inserted into the tree */
+ if(!compress_tree_store(qinfo->qname,
+ dname_count_labels(qinfo->qname),
+ sldns_buffer_position(buffer), region, NULL, tree))
+ return RETVAL_OUTMEM;
+ if(sldns_buffer_current(buffer) == qinfo->qname)
+ sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len);
+ else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
+ sldns_buffer_write_u16(buffer, qinfo->qtype);
+ sldns_buffer_write_u16(buffer, qinfo->qclass);
+ return RETVAL_OK;
+}
+
+static int
+positive_answer(struct reply_info* rep, uint16_t qtype) {
+ size_t i;
+ if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
+ return 0;
+
+ for(i=0;i<rep->an_numrrsets; i++) {
+ if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
+ /* in case it is a wildcard with DNSSEC, there will
+ * be NSEC/NSEC3 records in the authority section
+ * that we cannot remove */
+ for(i=rep->an_numrrsets; i<rep->an_numrrsets+
+ rep->ns_numrrsets; i++) {
+ if(ntohs(rep->rrsets[i]->rk.type) ==
+ LDNS_RR_TYPE_NSEC ||
+ ntohs(rep->rrsets[i]->rk.type) ==
+ LDNS_RR_TYPE_NSEC3)
+ return 0;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
+ uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow,
+ struct regional* region, uint16_t udpsize, int dnssec)
+{
+ uint16_t ancount=0, nscount=0, arcount=0;
+ struct compress_tree_node* tree = 0;
+ int r;
+ size_t rr_offset;
+
+ sldns_buffer_clear(buffer);
+ if(udpsize < sldns_buffer_limit(buffer))
+ sldns_buffer_set_limit(buffer, udpsize);
+ if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
+ return 0;
+
+ sldns_buffer_write(buffer, &id, sizeof(uint16_t));
+ sldns_buffer_write_u16(buffer, flags);
+ sldns_buffer_write_u16(buffer, rep->qdcount);
+ /* set an, ns, ar counts to zero in case of small packets */
+ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
+
+ /* insert query section */
+ if(rep->qdcount) {
+ if((r=insert_query(qinfo, &tree, buffer, region)) !=
+ RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* create truncated message */
+ sldns_buffer_write_u16_at(buffer, 4, 0);
+ LDNS_TC_SET(sldns_buffer_begin(buffer));
+ sldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ }
+ /* roundrobin offset. using query id for random number. With ntohs
+ * for different roundrobins for sequential id client senders. */
+ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0;
+
+ /* insert answer section */
+ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
+ 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
+ dnssec, rr_offset)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* create truncated message */
+ sldns_buffer_write_u16_at(buffer, 6, ancount);
+ LDNS_TC_SET(sldns_buffer_begin(buffer));
+ sldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ sldns_buffer_write_u16_at(buffer, 6, ancount);
+
+ /* if response is positive answer, auth/add sections are not required */
+ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) {
+ /* insert auth section */
+ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
+ rep->an_numrrsets, timenow, region, &tree,
+ LDNS_SECTION_AUTHORITY, qinfo->qtype,
+ dnssec, rr_offset)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* create truncated message */
+ sldns_buffer_write_u16_at(buffer, 8, nscount);
+ LDNS_TC_SET(sldns_buffer_begin(buffer));
+ sldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ sldns_buffer_write_u16_at(buffer, 8, nscount);
+
+ /* insert add section */
+ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
+ rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
+ &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
+ dnssec, rr_offset)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* no need to set TC bit, this is the additional */
+ sldns_buffer_write_u16_at(buffer, 10, arcount);
+ sldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ sldns_buffer_write_u16_at(buffer, 10, arcount);
+ }
+ sldns_buffer_flip(buffer);
+ return 1;
+}
+
+uint16_t
+calc_edns_field_size(struct edns_data* edns)
+{
+ if(!edns || !edns->edns_present)
+ return 0;
+ /* domain root '.' + type + class + ttl + rdatalen(=0) */
+ return 1 + 2 + 2 + 4 + 2;
+}
+
+void
+attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
+{
+ size_t len;
+ if(!edns || !edns->edns_present)
+ return;
+ /* inc additional count */
+ sldns_buffer_write_u16_at(pkt, 10,
+ sldns_buffer_read_u16_at(pkt, 10) + 1);
+ len = sldns_buffer_limit(pkt);
+ sldns_buffer_clear(pkt);
+ sldns_buffer_set_position(pkt, len);
+ /* write EDNS record */
+ sldns_buffer_write_u8(pkt, 0); /* '.' label */
+ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
+ sldns_buffer_write_u16(pkt, edns->udp_size); /* class */
+ sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
+ sldns_buffer_write_u8(pkt, edns->edns_version);
+ sldns_buffer_write_u16(pkt, edns->bits);
+ sldns_buffer_write_u16(pkt, 0); /* rdatalen */
+ sldns_buffer_flip(pkt);
+}
+
+int
+reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
+ uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
+ int cached, struct regional* region, uint16_t udpsize,
+ struct edns_data* edns, int dnssec, int secure)
+{
+ uint16_t flags;
+ int attach_edns = 1;
+
+ if(!cached || rep->authoritative) {
+ /* original flags, copy RD and CD bits from query. */
+ flags = rep->flags | (qflags & (BIT_RD|BIT_CD));
+ } else {
+ /* remove AA bit, copy RD and CD bits from query. */
+ flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
+ }
+ if(secure && (dnssec || (qflags&BIT_AD)))
+ flags |= BIT_AD;
+ log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
+ if(udpsize < LDNS_HEADER_SIZE)
+ return 0;
+ if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) {
+ /* packet too small to contain edns, omit it. */
+ attach_edns = 0;
+ } else {
+ /* reserve space for edns record */
+ udpsize -= calc_edns_field_size(edns);
+ }
+
+ if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
+ udpsize, dnssec)) {
+ log_err("reply encode: out of memory");
+ return 0;
+ }
+ if(attach_edns)
+ attach_edns_record(pkt, edns);
+ return 1;
+}
+
+void
+qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
+{
+ uint16_t flags = 0; /* QUERY, NOERROR */
+ sldns_buffer_clear(pkt);
+ log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
+ sldns_buffer_skip(pkt, 2); /* id done later */
+ sldns_buffer_write_u16(pkt, flags);
+ sldns_buffer_write_u16(pkt, 1); /* query count */
+ sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
+ sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
+ sldns_buffer_write_u16(pkt, qinfo->qtype);
+ sldns_buffer_write_u16(pkt, qinfo->qclass);
+ sldns_buffer_flip(pkt);
+}
+
+void
+error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
+ uint16_t qid, uint16_t qflags, struct edns_data* edns)
+{
+ uint16_t flags;
+
+ sldns_buffer_clear(buf);
+ sldns_buffer_write(buf, &qid, sizeof(uint16_t));
+ flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/
+ flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
+ sldns_buffer_write_u16(buf, flags);
+ if(qinfo) flags = 1;
+ else flags = 0;
+ sldns_buffer_write_u16(buf, flags);
+ flags = 0;
+ sldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ sldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ sldns_buffer_write(buf, &flags, sizeof(uint16_t));
+ if(qinfo) {
+ if(sldns_buffer_current(buf) == qinfo->qname)
+ sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len);
+ else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len);
+ sldns_buffer_write_u16(buf, qinfo->qtype);
+ sldns_buffer_write_u16(buf, qinfo->qclass);
+ }
+ sldns_buffer_flip(buf);
+ if(edns) {
+ struct edns_data es = *edns;
+ es.edns_version = EDNS_ADVERTISED_VERSION;
+ es.udp_size = EDNS_ADVERTISED_SIZE;
+ es.ext_rcode = 0;
+ es.bits &= EDNS_DO;
+ if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
+ edns->udp_size)
+ return;
+ attach_edns_record(buf, &es);
+ }
+}
diff --git a/external/unbound/util/data/msgencode.h b/external/unbound/util/data/msgencode.h
new file mode 100644
index 000000000..eea129d98
--- /dev/null
+++ b/external/unbound/util/data/msgencode.h
@@ -0,0 +1,131 @@
+/*
+ * util/data/msgencode.h - encode compressed DNS messages.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains temporary data structures and routines to create
+ * compressed DNS messages.
+ */
+
+#ifndef UTIL_DATA_MSGENCODE_H
+#define UTIL_DATA_MSGENCODE_H
+struct sldns_buffer;
+struct query_info;
+struct reply_info;
+struct regional;
+struct edns_data;
+
+/**
+ * Generate answer from reply_info.
+ * @param qinf: query information that provides query section in packet.
+ * @param rep: reply to fill in.
+ * @param id: id word from the query.
+ * @param qflags: flags word from the query.
+ * @param dest: buffer to put message into; will truncate if it does not fit.
+ * @param timenow: time to subtract.
+ * @param cached: set true if a cached reply (so no AA bit).
+ * set false for the first reply.
+ * @param region: where to allocate temp variables (for compression).
+ * @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
+ * @param edns: EDNS data included in the answer, NULL for none.
+ * or if edns_present = 0, it is not included.
+ * @param dnssec: if 0 DNSSEC records are omitted from the answer.
+ * @param secure: if 1, the AD bit is set in the reply.
+ * @return: 0 on error (server failure).
+ */
+int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
+ uint16_t id, uint16_t qflags, struct sldns_buffer* dest, time_t timenow,
+ int cached, struct regional* region, uint16_t udpsize,
+ struct edns_data* edns, int dnssec, int secure);
+
+/**
+ * Regenerate the wireformat from the stored msg reply.
+ * If the buffer is too small then the message is truncated at a whole
+ * rrset and the TC bit set, or whole rrsets are left out of the additional
+ * and the TC bit is not set.
+ * @param qinfo: query info to store.
+ * @param rep: reply to store.
+ * @param id: id value to store, network order.
+ * @param flags: flags value to store, host order.
+ * @param buffer: buffer to store the packet into.
+ * @param timenow: time now, to adjust ttl values.
+ * @param region: to store temporary data in.
+ * @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP.
+ * @param dnssec: if 0 DNSSEC records are omitted from the answer.
+ * @return: nonzero is success, or
+ * 0 on error: malloc failure (no log_err has been done).
+ */
+int reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
+ uint16_t id, uint16_t flags, struct sldns_buffer* buffer, time_t timenow,
+ struct regional* region, uint16_t udpsize, int dnssec);
+
+/**
+ * Encode query packet. Assumes the buffer is large enough.
+ * @param pkt: where to store the packet.
+ * @param qinfo: query info.
+ */
+void qinfo_query_encode(struct sldns_buffer* pkt, struct query_info* qinfo);
+
+/**
+ * Estimate size of EDNS record in packet. EDNS record will be no larger.
+ * @param edns: edns data or NULL.
+ * @return octets to reserve for EDNS.
+ */
+uint16_t calc_edns_field_size(struct edns_data* edns);
+
+/**
+ * Attach EDNS record to buffer. Buffer has complete packet. There must
+ * be enough room left for the EDNS record.
+ * @param pkt: packet added to.
+ * @param edns: if NULL or present=0, nothing is added to the packet.
+ */
+void attach_edns_record(struct sldns_buffer* pkt, struct edns_data* edns);
+
+/**
+ * Encode an error. With QR and RA set.
+ *
+ * @param pkt: where to store the packet.
+ * @param r: RCODE value to encode.
+ * @param qinfo: if not NULL, the query is included.
+ * @param qid: query ID to set in packet. network order.
+ * @param qflags: original query flags (to copy RD and CD bits). host order.
+ * @param edns: if not NULL, this is the query edns info,
+ * and an edns reply is attached. Only attached if EDNS record fits reply.
+ */
+void error_encode(struct sldns_buffer* pkt, int r, struct query_info* qinfo,
+ uint16_t qid, uint16_t qflags, struct edns_data* edns);
+
+#endif /* UTIL_DATA_MSGENCODE_H */
diff --git a/external/unbound/util/data/msgparse.c b/external/unbound/util/data/msgparse.c
new file mode 100644
index 000000000..abe778a89
--- /dev/null
+++ b/external/unbound/util/data/msgparse.c
@@ -0,0 +1,1022 @@
+/*
+ * util/data/msgparse.c - parse wireformat DNS messages.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Routines for message parsing a packet buffer to a descriptive structure.
+ */
+#include "config.h"
+#include "util/data/msgparse.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/storage/lookup3.h"
+#include "util/regional.h"
+#include "ldns/rrdef.h"
+#include "ldns/sbuffer.h"
+#include "ldns/parseutil.h"
+#include "ldns/wire2str.h"
+
+/** smart comparison of (compressed, valid) dnames from packet */
+static int
+smart_compare(sldns_buffer* pkt, uint8_t* dnow,
+ uint8_t* dprfirst, uint8_t* dprlast)
+{
+ if(LABEL_IS_PTR(*dnow)) {
+ /* ptr points to a previous dname */
+ uint8_t* p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
+ if( p == dprfirst || p == dprlast )
+ return 0;
+ /* prev dname is also a ptr, both ptrs are the same. */
+ if(LABEL_IS_PTR(*dprlast) &&
+ dprlast[0] == dnow[0] && dprlast[1] == dnow[1])
+ return 0;
+ }
+ return dname_pkt_compare(pkt, dnow, dprlast);
+}
+
+/**
+ * Allocate new rrset in region, fill with data.
+ */
+static struct rrset_parse*
+new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen,
+ uint16_t type, uint16_t dclass, hashvalue_t hash,
+ uint32_t rrset_flags, sldns_pkt_section section,
+ struct regional* region)
+{
+ struct rrset_parse* p = regional_alloc(region, sizeof(*p));
+ if(!p) return NULL;
+ p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)];
+ msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p;
+ p->rrset_all_next = 0;
+ if(msg->rrset_last)
+ msg->rrset_last->rrset_all_next = p;
+ else msg->rrset_first = p;
+ msg->rrset_last = p;
+ p->hash = hash;
+ p->section = section;
+ p->dname = dname;
+ p->dname_len = dnamelen;
+ p->type = type;
+ p->rrset_class = dclass;
+ p->flags = rrset_flags;
+ p->rr_count = 0;
+ p->size = 0;
+ p->rr_first = 0;
+ p->rr_last = 0;
+ p->rrsig_count = 0;
+ p->rrsig_first = 0;
+ p->rrsig_last = 0;
+ return p;
+}
+
+/** See if next rrset is nsec at zone apex */
+static int
+nsec_at_apex(sldns_buffer* pkt)
+{
+ /* we are at ttl position in packet. */
+ size_t pos = sldns_buffer_position(pkt);
+ uint16_t rdatalen;
+ if(sldns_buffer_remaining(pkt) < 7) /* ttl+len+root */
+ return 0; /* eek! */
+ sldns_buffer_skip(pkt, 4); /* ttl */;
+ rdatalen = sldns_buffer_read_u16(pkt);
+ if(sldns_buffer_remaining(pkt) < rdatalen) {
+ sldns_buffer_set_position(pkt, pos);
+ return 0; /* parse error happens later */
+ }
+ /* must validate the nsec next domain name format */
+ if(pkt_dname_len(pkt) == 0) {
+ sldns_buffer_set_position(pkt, pos);
+ return 0; /* parse error */
+ }
+
+ /* see if SOA bit is set. */
+ if(sldns_buffer_position(pkt) < pos+4+rdatalen) {
+ /* nsec type bitmap contains items */
+ uint8_t win, blen, bits;
+ /* need: windownum, bitmap len, firstbyte */
+ if(sldns_buffer_position(pkt)+3 > pos+4+rdatalen) {
+ sldns_buffer_set_position(pkt, pos);
+ return 0; /* malformed nsec */
+ }
+ win = sldns_buffer_read_u8(pkt);
+ blen = sldns_buffer_read_u8(pkt);
+ bits = sldns_buffer_read_u8(pkt);
+ /* 0window always first window. bitlen >=1 or parse
+ error really. bit 0x2 is SOA. */
+ if(win == 0 && blen >= 1 && (bits & 0x02)) {
+ sldns_buffer_set_position(pkt, pos);
+ return 1;
+ }
+ }
+
+ sldns_buffer_set_position(pkt, pos);
+ return 0;
+}
+
+/** Calculate rrset flags */
+static uint32_t
+pkt_rrset_flags(sldns_buffer* pkt, uint16_t type, sldns_pkt_section sec)
+{
+ uint32_t f = 0;
+ if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) {
+ f |= PACKED_RRSET_NSEC_AT_APEX;
+ } else if(type == LDNS_RR_TYPE_SOA && sec == LDNS_SECTION_AUTHORITY) {
+ f |= PACKED_RRSET_SOA_NEG;
+ }
+ return f;
+}
+
+hashvalue_t
+pkt_hash_rrset(sldns_buffer* pkt, uint8_t* dname, uint16_t type,
+ uint16_t dclass, uint32_t rrset_flags)
+{
+ /* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+ /* this routine handles compressed names */
+ hashvalue_t h = 0xab;
+ h = dname_pkt_hash(pkt, dname, h);
+ h = hashlittle(&type, sizeof(type), h); /* host order */
+ h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */
+ h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
+ return h;
+}
+
+/** create partial dname hash for rrset hash */
+static hashvalue_t
+pkt_hash_rrset_first(sldns_buffer* pkt, uint8_t* dname)
+{
+ /* works together with pkt_hash_rrset_rest */
+ /* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+ /* this routine handles compressed names */
+ hashvalue_t h = 0xab;
+ h = dname_pkt_hash(pkt, dname, h);
+ return h;
+}
+
+/** create a rrset hash from a partial dname hash */
+static hashvalue_t
+pkt_hash_rrset_rest(hashvalue_t dname_h, uint16_t type, uint16_t dclass,
+ uint32_t rrset_flags)
+{
+ /* works together with pkt_hash_rrset_first */
+ /* note this MUST be identical to rrset_key_hash in packed_rrset.c */
+ hashvalue_t h;
+ h = hashlittle(&type, sizeof(type), dname_h); /* host order */
+ h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */
+ h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
+ return h;
+}
+
+/** compare rrset_parse with data */
+static int
+rrset_parse_equals(struct rrset_parse* p, sldns_buffer* pkt, hashvalue_t h,
+ uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
+ uint16_t type, uint16_t dclass)
+{
+ if(p->hash == h && p->dname_len == dnamelen && p->type == type &&
+ p->rrset_class == dclass && p->flags == rrset_flags &&
+ dname_pkt_compare(pkt, dname, p->dname) == 0)
+ return 1;
+ return 0;
+}
+
+
+struct rrset_parse*
+msgparse_hashtable_lookup(struct msg_parse* msg, sldns_buffer* pkt,
+ hashvalue_t h, uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
+ uint16_t type, uint16_t dclass)
+{
+ struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)];
+ while(p) {
+ if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen,
+ type, dclass))
+ return p;
+ p = p->rrset_bucket_next;
+ }
+ return NULL;
+}
+
+/** return type networkformat that rrsig in packet covers */
+static int
+pkt_rrsig_covered(sldns_buffer* pkt, uint8_t* here, uint16_t* type)
+{
+ size_t pos = sldns_buffer_position(pkt);
+ sldns_buffer_set_position(pkt, (size_t)(here-sldns_buffer_begin(pkt)));
+ /* ttl + len + size of small rrsig(rootlabel, no signature) */
+ if(sldns_buffer_remaining(pkt) < 4+2+19)
+ return 0;
+ sldns_buffer_skip(pkt, 4); /* ttl */
+ if(sldns_buffer_read_u16(pkt) < 19) /* too short */ {
+ sldns_buffer_set_position(pkt, pos);
+ return 0;
+ }
+ *type = sldns_buffer_read_u16(pkt);
+ sldns_buffer_set_position(pkt, pos);
+ return 1;
+}
+
+/** true if covered type equals prevtype */
+static int
+pkt_rrsig_covered_equals(sldns_buffer* pkt, uint8_t* here, uint16_t type)
+{
+ uint16_t t;
+ if(pkt_rrsig_covered(pkt, here, &t) && t == type)
+ return 1;
+ return 0;
+}
+
+void
+msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
+{
+ struct rrset_parse** p;
+ p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ];
+ while(*p) {
+ if(*p == rrset) {
+ *p = rrset->rrset_bucket_next;
+ return;
+ }
+ p = &( (*p)->rrset_bucket_next );
+ }
+}
+
+/** change section of rrset from previous to current section */
+static void
+change_section(struct msg_parse* msg, struct rrset_parse* rrset,
+ sldns_pkt_section section)
+{
+ struct rrset_parse *p, *prev;
+ /* remove from list */
+ if(section == rrset->section)
+ return;
+ p = msg->rrset_first;
+ prev = 0;
+ while(p) {
+ if(p == rrset) {
+ if(prev) prev->rrset_all_next = p->rrset_all_next;
+ else msg->rrset_first = p->rrset_all_next;
+ if(msg->rrset_last == rrset)
+ msg->rrset_last = prev;
+ break;
+ }
+ prev = p;
+ p = p->rrset_all_next;
+ }
+ /* remove from count */
+ switch(rrset->section) {
+ case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
+ case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
+ case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
+ default: log_assert(0);
+ }
+ /* insert at end of list */
+ rrset->rrset_all_next = 0;
+ if(msg->rrset_last)
+ msg->rrset_last->rrset_all_next = rrset;
+ else msg->rrset_first = rrset;
+ msg->rrset_last = rrset;
+ /* up count of new section */
+ switch(section) {
+ case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
+ case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
+ default: log_assert(0);
+ }
+ rrset->section = section;
+}
+
+/** see if rrset of type RRSIG contains sig over given type */
+static int
+rrset_has_sigover(sldns_buffer* pkt, struct rrset_parse* rrset, uint16_t type,
+ int* hasother)
+{
+ int res = 0;
+ struct rr_parse* rr = rrset->rr_first;
+ log_assert( rrset->type == LDNS_RR_TYPE_RRSIG );
+ while(rr) {
+ if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type))
+ res = 1;
+ else *hasother = 1;
+ rr = rr->next;
+ }
+ return res;
+}
+
+/** move rrsigs from sigset to dataset */
+static int
+moveover_rrsigs(sldns_buffer* pkt, struct regional* region,
+ struct rrset_parse* sigset, struct rrset_parse* dataset, int duplicate)
+{
+ struct rr_parse* sig = sigset->rr_first;
+ struct rr_parse* prev = NULL;
+ struct rr_parse* insert;
+ struct rr_parse* nextsig;
+ while(sig) {
+ nextsig = sig->next;
+ if(pkt_rrsig_covered_equals(pkt, sig->ttl_data,
+ dataset->type)) {
+ if(duplicate) {
+ /* new */
+ insert = (struct rr_parse*)regional_alloc(
+ region, sizeof(struct rr_parse));
+ if(!insert) return 0;
+ insert->outside_packet = 0;
+ insert->ttl_data = sig->ttl_data;
+ insert->size = sig->size;
+ /* prev not used */
+ } else {
+ /* remove from sigset */
+ if(prev) prev->next = sig->next;
+ else sigset->rr_first = sig->next;
+ if(sigset->rr_last == sig)
+ sigset->rr_last = prev;
+ sigset->rr_count--;
+ sigset->size -= sig->size;
+ insert = sig;
+ /* prev not changed */
+ }
+ /* add to dataset */
+ dataset->rrsig_count++;
+ insert->next = 0;
+ if(dataset->rrsig_last)
+ dataset->rrsig_last->next = insert;
+ else dataset->rrsig_first = insert;
+ dataset->rrsig_last = insert;
+ dataset->size += insert->size;
+ } else {
+ prev = sig;
+ }
+ sig = nextsig;
+ }
+ return 1;
+}
+
+/** change an rrsig rrset for use as data rrset */
+static struct rrset_parse*
+change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg,
+ sldns_buffer* pkt, uint16_t datatype, uint32_t rrset_flags,
+ int hasother, sldns_pkt_section section, struct regional* region)
+{
+ struct rrset_parse* dataset = sigset;
+ hashvalue_t hash = pkt_hash_rrset(pkt, sigset->dname, datatype,
+ sigset->rrset_class, rrset_flags);
+ log_assert( sigset->type == LDNS_RR_TYPE_RRSIG );
+ log_assert( datatype != LDNS_RR_TYPE_RRSIG );
+ if(hasother) {
+ /* need to make new rrset to hold data type */
+ dataset = new_rrset(msg, sigset->dname, sigset->dname_len,
+ datatype, sigset->rrset_class, hash, rrset_flags,
+ section, region);
+ if(!dataset)
+ return NULL;
+ switch(section) {
+ case LDNS_SECTION_ANSWER: msg->an_rrsets++; break;
+ case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
+ case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
+ default: log_assert(0);
+ }
+ if(!moveover_rrsigs(pkt, region, sigset, dataset,
+ msg->qtype == LDNS_RR_TYPE_RRSIG ||
+ (msg->qtype == LDNS_RR_TYPE_ANY &&
+ section != LDNS_SECTION_ANSWER) ))
+ return NULL;
+ return dataset;
+ }
+ /* changeover the type of the rrset to data set */
+ msgparse_bucket_remove(msg, dataset);
+ /* insert into new hash bucket */
+ dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)];
+ msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset;
+ dataset->hash = hash;
+ /* use section of data item for result */
+ change_section(msg, dataset, section);
+ dataset->type = datatype;
+ dataset->flags = rrset_flags;
+ dataset->rrsig_count += dataset->rr_count;
+ dataset->rr_count = 0;
+ /* move sigs to end of siglist */
+ if(dataset->rrsig_last)
+ dataset->rrsig_last->next = dataset->rr_first;
+ else dataset->rrsig_first = dataset->rr_first;
+ dataset->rrsig_last = dataset->rr_last;
+ dataset->rr_first = 0;
+ dataset->rr_last = 0;
+ return dataset;
+}
+
+/** Find rrset. If equal to previous it is fast. hash if not so.
+ * @param msg: the message with hash table.
+ * @param pkt: the packet in wireformat (needed for compression ptrs).
+ * @param dname: pointer to start of dname (compressed) in packet.
+ * @param dnamelen: uncompressed wirefmt length of dname.
+ * @param type: type of current rr.
+ * @param dclass: class of current rr.
+ * @param hash: hash value is returned if the rrset could not be found.
+ * @param rrset_flags: is returned if the rrset could not be found.
+ * @param prev_dname_first: dname of last seen RR. First seen dname.
+ * @param prev_dname_last: dname of last seen RR. Last seen dname.
+ * @param prev_dnamelen: dname len of last seen RR.
+ * @param prev_type: type of last seen RR.
+ * @param prev_dclass: class of last seen RR.
+ * @param rrset_prev: last seen RRset.
+ * @param section: the current section in the packet.
+ * @param region: used to allocate temporary parsing data.
+ * @return 0 on out of memory.
+ */
+static int
+find_rrset(struct msg_parse* msg, sldns_buffer* pkt, uint8_t* dname,
+ size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash,
+ uint32_t* rrset_flags,
+ uint8_t** prev_dname_first, uint8_t** prev_dname_last,
+ size_t* prev_dnamelen, uint16_t* prev_type,
+ uint16_t* prev_dclass, struct rrset_parse** rrset_prev,
+ sldns_pkt_section section, struct regional* region)
+{
+ hashvalue_t dname_h = pkt_hash_rrset_first(pkt, dname);
+ uint16_t covtype;
+ if(*rrset_prev) {
+ /* check if equal to previous item */
+ if(type == *prev_type && dclass == *prev_dclass &&
+ dnamelen == *prev_dnamelen &&
+ smart_compare(pkt, dname, *prev_dname_first,
+ *prev_dname_last) == 0 &&
+ type != LDNS_RR_TYPE_RRSIG) {
+ /* same as previous */
+ *prev_dname_last = dname;
+ return 1;
+ }
+ /* check if rrsig over previous item */
+ if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass &&
+ pkt_rrsig_covered_equals(pkt, sldns_buffer_current(pkt),
+ *prev_type) &&
+ smart_compare(pkt, dname, *prev_dname_first,
+ *prev_dname_last) == 0) {
+ /* covers previous */
+ *prev_dname_last = dname;
+ return 1;
+ }
+ }
+ /* find by hashing and lookup in hashtable */
+ *rrset_flags = pkt_rrset_flags(pkt, type, section);
+
+ /* if rrsig - try to lookup matching data set first */
+ if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt,
+ sldns_buffer_current(pkt), &covtype)) {
+ *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
+ *rrset_flags);
+ *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
+ *rrset_flags, dname, dnamelen, covtype, dclass);
+ if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) {
+ /* if NSEC try with NSEC apex bit twiddled */
+ *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
+ *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
+ *rrset_flags);
+ *rrset_prev = msgparse_hashtable_lookup(msg, pkt,
+ *hash, *rrset_flags, dname, dnamelen, covtype,
+ dclass);
+ if(!*rrset_prev) /* untwiddle if not found */
+ *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
+ }
+ if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) {
+ /* if SOA try with SOA neg flag twiddled */
+ *rrset_flags ^= PACKED_RRSET_SOA_NEG;
+ *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
+ *rrset_flags);
+ *rrset_prev = msgparse_hashtable_lookup(msg, pkt,
+ *hash, *rrset_flags, dname, dnamelen, covtype,
+ dclass);
+ if(!*rrset_prev) /* untwiddle if not found */
+ *rrset_flags ^= PACKED_RRSET_SOA_NEG;
+ }
+ if(*rrset_prev) {
+ *prev_dname_first = (*rrset_prev)->dname;
+ *prev_dname_last = dname;
+ *prev_dnamelen = dnamelen;
+ *prev_type = covtype;
+ *prev_dclass = dclass;
+ return 1;
+ }
+ }
+ if(type != LDNS_RR_TYPE_RRSIG) {
+ int hasother = 0;
+ /* find matching rrsig */
+ *hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG,
+ dclass, 0);
+ *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
+ 0, dname, dnamelen, LDNS_RR_TYPE_RRSIG,
+ dclass);
+ if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type,
+ &hasother)) {
+ /* yes! */
+ *prev_dname_first = (*rrset_prev)->dname;
+ *prev_dname_last = dname;
+ *prev_dnamelen = dnamelen;
+ *prev_type = type;
+ *prev_dclass = dclass;
+ *rrset_prev = change_rrsig_rrset(*rrset_prev, msg,
+ pkt, type, *rrset_flags, hasother, section,
+ region);
+ if(!*rrset_prev) return 0;
+ return 1;
+ }
+ }
+
+ *hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags);
+ *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags,
+ dname, dnamelen, type, dclass);
+ if(*rrset_prev)
+ *prev_dname_first = (*rrset_prev)->dname;
+ else *prev_dname_first = dname;
+ *prev_dname_last = dname;
+ *prev_dnamelen = dnamelen;
+ *prev_type = type;
+ *prev_dclass = dclass;
+ return 1;
+}
+
+/**
+ * Parse query section.
+ * @param pkt: packet, position at call must be at start of query section.
+ * at end position is after query section.
+ * @param msg: store results here.
+ * @return: 0 if OK, or rcode on error.
+ */
+static int
+parse_query_section(sldns_buffer* pkt, struct msg_parse* msg)
+{
+ if(msg->qdcount == 0)
+ return 0;
+ if(msg->qdcount > 1)
+ return LDNS_RCODE_FORMERR;
+ log_assert(msg->qdcount == 1);
+ if(sldns_buffer_remaining(pkt) <= 0)
+ return LDNS_RCODE_FORMERR;
+ msg->qname = sldns_buffer_current(pkt);
+ if((msg->qname_len = pkt_dname_len(pkt)) == 0)
+ return LDNS_RCODE_FORMERR;
+ if(sldns_buffer_remaining(pkt) < sizeof(uint16_t)*2)
+ return LDNS_RCODE_FORMERR;
+ msg->qtype = sldns_buffer_read_u16(pkt);
+ msg->qclass = sldns_buffer_read_u16(pkt);
+ return 0;
+}
+
+size_t
+get_rdf_size(sldns_rdf_type rdf)
+{
+ switch(rdf) {
+ case LDNS_RDF_TYPE_CLASS:
+ case LDNS_RDF_TYPE_ALG:
+ case LDNS_RDF_TYPE_INT8:
+ return 1;
+ break;
+ case LDNS_RDF_TYPE_INT16:
+ case LDNS_RDF_TYPE_TYPE:
+ case LDNS_RDF_TYPE_CERT_ALG:
+ return 2;
+ break;
+ case LDNS_RDF_TYPE_INT32:
+ case LDNS_RDF_TYPE_TIME:
+ case LDNS_RDF_TYPE_A:
+ case LDNS_RDF_TYPE_PERIOD:
+ return 4;
+ break;
+ case LDNS_RDF_TYPE_TSIGTIME:
+ return 6;
+ break;
+ case LDNS_RDF_TYPE_AAAA:
+ return 16;
+ break;
+ default:
+ log_assert(0); /* add type above */
+ /* only types that appear before a domain *
+ * name are needed. rest is simply copied. */
+ }
+ return 0;
+}
+
+/** calculate the size of one rr */
+static int
+calc_size(sldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
+{
+ const sldns_rr_descriptor* desc;
+ uint16_t pkt_len; /* length of rr inside the packet */
+ rr->size = sizeof(uint16_t); /* the rdatalen */
+ sldns_buffer_skip(pkt, 4); /* skip ttl */
+ pkt_len = sldns_buffer_read_u16(pkt);
+ if(sldns_buffer_remaining(pkt) < pkt_len)
+ return 0;
+ desc = sldns_rr_descript(type);
+ if(pkt_len > 0 && desc && desc->_dname_count > 0) {
+ int count = (int)desc->_dname_count;
+ int rdf = 0;
+ size_t len;
+ size_t oldpos;
+ /* skip first part. */
+ while(pkt_len > 0 && count) {
+ switch(desc->_wireformat[rdf]) {
+ case LDNS_RDF_TYPE_DNAME:
+ /* decompress every domain name */
+ oldpos = sldns_buffer_position(pkt);
+ if((len = pkt_dname_len(pkt)) == 0)
+ return 0; /* malformed dname */
+ if(sldns_buffer_position(pkt)-oldpos > pkt_len)
+ return 0; /* dname exceeds rdata */
+ pkt_len -= sldns_buffer_position(pkt)-oldpos;
+ rr->size += len;
+ count--;
+ len = 0;
+ break;
+ case LDNS_RDF_TYPE_STR:
+ if(pkt_len < 1) {
+ /* NOTREACHED, due to 'while(>0)' */
+ return 0; /* len byte exceeds rdata */
+ }
+ len = sldns_buffer_current(pkt)[0] + 1;
+ break;
+ default:
+ len = get_rdf_size(desc->_wireformat[rdf]);
+ }
+ if(len) {
+ if(pkt_len < len)
+ return 0; /* exceeds rdata */
+ pkt_len -= len;
+ sldns_buffer_skip(pkt, (ssize_t)len);
+ rr->size += len;
+ }
+ rdf++;
+ }
+ }
+ /* remaining rdata */
+ rr->size += pkt_len;
+ sldns_buffer_skip(pkt, (ssize_t)pkt_len);
+ return 1;
+}
+
+/** skip rr ttl and rdata */
+static int
+skip_ttl_rdata(sldns_buffer* pkt)
+{
+ uint16_t rdatalen;
+ if(sldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
+ return 0;
+ sldns_buffer_skip(pkt, 4); /* ttl */
+ rdatalen = sldns_buffer_read_u16(pkt);
+ if(sldns_buffer_remaining(pkt) < rdatalen)
+ return 0;
+ sldns_buffer_skip(pkt, (ssize_t)rdatalen);
+ return 1;
+}
+
+/** see if RRSIG is a duplicate of another */
+static int
+sig_is_double(sldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata)
+{
+ uint16_t rlen, siglen;
+ size_t pos = sldns_buffer_position(pkt);
+ struct rr_parse* sig;
+ if(sldns_buffer_remaining(pkt) < 6)
+ return 0;
+ sldns_buffer_skip(pkt, 4); /* ttl */
+ rlen = sldns_buffer_read_u16(pkt);
+ if(sldns_buffer_remaining(pkt) < rlen) {
+ sldns_buffer_set_position(pkt, pos);
+ return 0;
+ }
+ sldns_buffer_set_position(pkt, pos);
+
+ sig = rrset->rrsig_first;
+ while(sig) {
+ /* check if rdatalen is same */
+ memmove(&siglen, sig->ttl_data+4, sizeof(siglen));
+ siglen = ntohs(siglen);
+ /* checks if data in packet is exactly the same, this means
+ * also dname in rdata is the same, but rrsig is not allowed
+ * to have compressed dnames anyway. If it is compressed anyway
+ * it will lead to duplicate rrs for qtype=RRSIG. (or ANY).
+ *
+ * Cannot use sig->size because size of the other one is not
+ * calculated yet.
+ */
+ if(siglen == rlen) {
+ if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6,
+ siglen) == 0) {
+ /* same! */
+ return 1;
+ }
+ }
+ sig = sig->next;
+ }
+ return 0;
+}
+
+/** Add rr (from packet here) to rrset, skips rr */
+static int
+add_rr_to_rrset(struct rrset_parse* rrset, sldns_buffer* pkt,
+ struct msg_parse* msg, struct regional* region,
+ sldns_pkt_section section, uint16_t type)
+{
+ struct rr_parse* rr;
+ /* check section of rrset. */
+ if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG &&
+ rrset->type != LDNS_RR_TYPE_RRSIG) {
+ /* silently drop it - we drop the last part, since
+ * trust in rr data depends on the section it is in.
+ * the less trustworthy part is discarded.
+ * also the last part is more likely to be incomplete.
+ * RFC 2181: must put RRset only once in response. */
+ /*
+ verbose(VERB_QUERY, "Packet contains rrset data in "
+ "multiple sections, dropped last part.");
+ log_buf(VERB_QUERY, "packet was", pkt);
+ */
+ /* forwards */
+ if(!skip_ttl_rdata(pkt))
+ return LDNS_RCODE_FORMERR;
+ return 0;
+ }
+
+ if( (msg->qtype == LDNS_RR_TYPE_RRSIG ||
+ msg->qtype == LDNS_RR_TYPE_ANY)
+ && sig_is_double(pkt, rrset, sldns_buffer_current(pkt))) {
+ if(!skip_ttl_rdata(pkt))
+ return LDNS_RCODE_FORMERR;
+ return 0;
+ }
+
+ /* create rr */
+ if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr))))
+ return LDNS_RCODE_SERVFAIL;
+ rr->outside_packet = 0;
+ rr->ttl_data = sldns_buffer_current(pkt);
+ rr->next = 0;
+ if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) {
+ if(rrset->rrsig_last)
+ rrset->rrsig_last->next = rr;
+ else rrset->rrsig_first = rr;
+ rrset->rrsig_last = rr;
+ rrset->rrsig_count++;
+ } else {
+ if(rrset->rr_last)
+ rrset->rr_last->next = rr;
+ else rrset->rr_first = rr;
+ rrset->rr_last = rr;
+ rrset->rr_count++;
+ }
+
+ /* calc decompressed size */
+ if(!calc_size(pkt, type, rr))
+ return LDNS_RCODE_FORMERR;
+ rrset->size += rr->size;
+
+ return 0;
+}
+
+/**
+ * Parse packet RR section, for answer, authority and additional sections.
+ * @param pkt: packet, position at call must be at start of section.
+ * at end position is after section.
+ * @param msg: store results here.
+ * @param region: how to alloc results.
+ * @param section: section enum.
+ * @param num_rrs: how many rrs are in the section.
+ * @param num_rrsets: returns number of rrsets in the section.
+ * @return: 0 if OK, or rcode on error.
+ */
+static int
+parse_section(sldns_buffer* pkt, struct msg_parse* msg,
+ struct regional* region, sldns_pkt_section section,
+ uint16_t num_rrs, size_t* num_rrsets)
+{
+ uint16_t i;
+ uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL;
+ size_t dnamelen, prev_dnamelen = 0;
+ uint16_t type, prev_type = 0;
+ uint16_t dclass, prev_dclass = 0;
+ uint32_t rrset_flags = 0;
+ hashvalue_t hash = 0;
+ struct rrset_parse* rrset = NULL;
+ int r;
+
+ if(num_rrs == 0)
+ return 0;
+ if(sldns_buffer_remaining(pkt) <= 0)
+ return LDNS_RCODE_FORMERR;
+ for(i=0; i<num_rrs; i++) {
+ /* parse this RR. */
+ dname = sldns_buffer_current(pkt);
+ if((dnamelen = pkt_dname_len(pkt)) == 0)
+ return LDNS_RCODE_FORMERR;
+ if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */
+ return LDNS_RCODE_FORMERR;
+ type = sldns_buffer_read_u16(pkt);
+ sldns_buffer_read(pkt, &dclass, sizeof(dclass));
+
+ if(0) { /* debug show what is being parsed. */
+ if(type == LDNS_RR_TYPE_RRSIG) {
+ uint16_t t;
+ if(pkt_rrsig_covered(pkt,
+ sldns_buffer_current(pkt), &t))
+ fprintf(stderr, "parse of %s(%d) [%s(%d)]",
+ sldns_rr_descript(type)?
+ sldns_rr_descript(type)->_name: "??",
+ (int)type,
+ sldns_rr_descript(t)?
+ sldns_rr_descript(t)->_name: "??",
+ (int)t);
+ } else
+ fprintf(stderr, "parse of %s(%d)",
+ sldns_rr_descript(type)?
+ sldns_rr_descript(type)->_name: "??",
+ (int)type);
+ fprintf(stderr, " %s(%d) ",
+ sldns_lookup_by_id(sldns_rr_classes,
+ (int)ntohs(dclass))?sldns_lookup_by_id(
+ sldns_rr_classes, (int)ntohs(dclass))->name:
+ "??", (int)ntohs(dclass));
+ dname_print(stderr, pkt, dname);
+ fprintf(stderr, "\n");
+ }
+
+ /* see if it is part of an existing RR set */
+ if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash,
+ &rrset_flags, &prev_dname_f, &prev_dname_l,
+ &prev_dnamelen, &prev_type, &prev_dclass, &rrset,
+ section, region))
+ return LDNS_RCODE_SERVFAIL;
+ if(!rrset) {
+ /* it is a new RR set. hash&flags already calculated.*/
+ (*num_rrsets)++;
+ rrset = new_rrset(msg, dname, dnamelen, type, dclass,
+ hash, rrset_flags, section, region);
+ if(!rrset)
+ return LDNS_RCODE_SERVFAIL;
+ }
+ else if(0) {
+ fprintf(stderr, "is part of existing: ");
+ dname_print(stderr, pkt, rrset->dname);
+ fprintf(stderr, " type %s(%d)\n",
+ sldns_rr_descript(rrset->type)?
+ sldns_rr_descript(rrset->type)->_name: "??",
+ (int)rrset->type);
+ }
+ /* add to rrset. */
+ if((r=add_rr_to_rrset(rrset, pkt, msg, region, section,
+ type)) != 0)
+ return r;
+ }
+ return 0;
+}
+
+int
+parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
+{
+ int ret;
+ if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE)
+ return LDNS_RCODE_FORMERR;
+ /* read the header */
+ sldns_buffer_read(pkt, &msg->id, sizeof(uint16_t));
+ msg->flags = sldns_buffer_read_u16(pkt);
+ msg->qdcount = sldns_buffer_read_u16(pkt);
+ msg->ancount = sldns_buffer_read_u16(pkt);
+ msg->nscount = sldns_buffer_read_u16(pkt);
+ msg->arcount = sldns_buffer_read_u16(pkt);
+ if(msg->qdcount > 1)
+ return LDNS_RCODE_FORMERR;
+ if((ret = parse_query_section(pkt, msg)) != 0)
+ return ret;
+ if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER,
+ msg->ancount, &msg->an_rrsets)) != 0)
+ return ret;
+ if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY,
+ msg->nscount, &msg->ns_rrsets)) != 0)
+ return ret;
+ if(sldns_buffer_remaining(pkt) == 0 && msg->arcount == 1) {
+ /* BIND accepts leniently that an EDNS record is missing.
+ * so, we do too. */
+ } else if((ret = parse_section(pkt, msg, region,
+ LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0)
+ return ret;
+ /* if(sldns_buffer_remaining(pkt) > 0) { */
+ /* there is spurious data at end of packet. ignore */
+ /* } */
+ msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets;
+ return 0;
+}
+
+int
+parse_extract_edns(struct msg_parse* msg, struct edns_data* edns)
+{
+ struct rrset_parse* rrset = msg->rrset_first;
+ struct rrset_parse* prev = 0;
+ struct rrset_parse* found = 0;
+ struct rrset_parse* found_prev = 0;
+ /* since the class encodes the UDP size, we cannot use hash table to
+ * find the EDNS OPT record. Scan the packet. */
+ while(rrset) {
+ if(rrset->type == LDNS_RR_TYPE_OPT) {
+ /* only one OPT RR allowed. */
+ if(found) return LDNS_RCODE_FORMERR;
+ /* found it! */
+ found_prev = prev;
+ found = rrset;
+ }
+ prev = rrset;
+ rrset = rrset->rrset_all_next;
+ }
+ if(!found) {
+ memset(edns, 0, sizeof(*edns));
+ edns->udp_size = 512;
+ return 0;
+ }
+ /* check the found RRset */
+ /* most lenient check possible. ignore dname, use last opt */
+ if(found->section != LDNS_SECTION_ADDITIONAL)
+ return LDNS_RCODE_FORMERR;
+ if(found->rr_count == 0)
+ return LDNS_RCODE_FORMERR;
+ if(0) { /* strict checking of dname and RRcount */
+ if(found->dname_len != 1 || !found->dname
+ || found->dname[0] != 0) return LDNS_RCODE_FORMERR;
+ if(found->rr_count != 1) return LDNS_RCODE_FORMERR;
+ }
+ log_assert(found->rr_first && found->rr_last);
+
+ /* remove from packet */
+ if(found_prev) found_prev->rrset_all_next = found->rrset_all_next;
+ else msg->rrset_first = found->rrset_all_next;
+ if(found == msg->rrset_last)
+ msg->rrset_last = found_prev;
+ msg->arcount --;
+ msg->ar_rrsets --;
+ msg->rrset_count --;
+
+ /* take the data ! */
+ edns->edns_present = 1;
+ edns->ext_rcode = found->rr_last->ttl_data[0];
+ edns->edns_version = found->rr_last->ttl_data[1];
+ edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]);
+ edns->udp_size = ntohs(found->rrset_class);
+ /* ignore rdata and rrsigs */
+ return 0;
+}
+
+int
+parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns)
+{
+ log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
+ log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0);
+ log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0);
+ /* check edns section is present */
+ if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
+ return LDNS_RCODE_FORMERR;
+ }
+ if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) {
+ memset(edns, 0, sizeof(*edns));
+ edns->udp_size = 512;
+ return 0;
+ }
+ /* domain name must be the root of length 1. */
+ if(pkt_dname_len(pkt) != 1)
+ return LDNS_RCODE_FORMERR;
+ if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */
+ return LDNS_RCODE_FORMERR;
+ if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT)
+ return LDNS_RCODE_FORMERR;
+ edns->edns_present = 1;
+ edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */
+ edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */
+ edns->edns_version = sldns_buffer_read_u8(pkt);
+ edns->bits = sldns_buffer_read_u16(pkt);
+ /* ignore rdata and rrsigs */
+ return 0;
+}
diff --git a/external/unbound/util/data/msgparse.h b/external/unbound/util/data/msgparse.h
new file mode 100644
index 000000000..221a45aad
--- /dev/null
+++ b/external/unbound/util/data/msgparse.h
@@ -0,0 +1,301 @@
+/*
+ * util/data/msgparse.h - parse wireformat DNS messages.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Contains message parsing data structures.
+ * These point back into the packet buffer.
+ *
+ * During parsing RRSIGS are put together with the rrsets they (claim to) sign.
+ * This process works as follows:
+ * o if RRSIG follows the data rrset, it is added to the rrset rrsig list.
+ * o if no matching data rrset is found, the RRSIG becomes a new rrset.
+ * o If the data rrset later follows the RRSIG
+ * o See if the RRSIG rrset contains multiple types, and needs to
+ * have the rrsig(s) for that data type split off.
+ * o Put the data rr as data type in the rrset and rrsig in list.
+ * o RRSIGs are allowed to move to a different section. The section of
+ * the data item is used for the final rrset.
+ * o multiple signatures over an RRset are possible.
+ *
+ * For queries of qtype=RRSIG, some special handling is needed, to avoid
+ * splitting the RRSIG in the answer section.
+ * o duplicate, not split, RRSIGs from the answer section, if qtype=RRSIG.
+ * o check for doubles in the rrsig list when adding an RRSIG to data,
+ * so that a data rrset is signed by RRSIGs with different rdata.
+ * when qtype=RRSIG.
+ * This will move the RRSIG from the answer section to sign the data further
+ * in the packet (if possible). If then after that, more RRSIGs are found
+ * that sign the data as well, doubles are removed.
+ */
+
+#ifndef UTIL_DATA_MSGPARSE_H
+#define UTIL_DATA_MSGPARSE_H
+#include "util/storage/lruhash.h"
+#include "ldns/pkthdr.h"
+#include "ldns/rrdef.h"
+struct sldns_buffer;
+struct rrset_parse;
+struct rr_parse;
+struct regional;
+
+/** number of buckets in parse rrset hash table. Must be power of 2. */
+#define PARSE_TABLE_SIZE 32
+/** Maximum TTL that is allowed. */
+extern time_t MAX_TTL;
+/** Minimum TTL that is allowed. */
+extern time_t MIN_TTL;
+/** Negative cache time (for entries without any RRs.) */
+#define NORR_TTL 5 /* seconds */
+
+/**
+ * Data stored in scratch pad memory during parsing.
+ * Stores the data that will enter into the msgreply and packet result.
+ */
+struct msg_parse {
+ /** id from message, network format. */
+ uint16_t id;
+ /** flags from message, host format. */
+ uint16_t flags;
+ /** count of RRs, host format */
+ uint16_t qdcount;
+ /** count of RRs, host format */
+ uint16_t ancount;
+ /** count of RRs, host format */
+ uint16_t nscount;
+ /** count of RRs, host format */
+ uint16_t arcount;
+ /** count of RRsets per section. */
+ size_t an_rrsets;
+ /** count of RRsets per section. */
+ size_t ns_rrsets;
+ /** count of RRsets per section. */
+ size_t ar_rrsets;
+ /** total number of rrsets found. */
+ size_t rrset_count;
+
+ /** query dname (pointer to start location in packet, NULL if none */
+ uint8_t* qname;
+ /** length of query dname in octets, 0 if none */
+ size_t qname_len;
+ /** query type, host order. 0 if qdcount=0 */
+ uint16_t qtype;
+ /** query class, host order. 0 if qdcount=0 */
+ uint16_t qclass;
+
+ /**
+ * Hash table array used during parsing to lookup rrset types.
+ * Based on name, type, class. Same hash value as in rrset cache.
+ */
+ struct rrset_parse* hashtable[PARSE_TABLE_SIZE];
+
+ /** linked list of rrsets that have been found (in order). */
+ struct rrset_parse* rrset_first;
+ /** last element of rrset list. */
+ struct rrset_parse* rrset_last;
+};
+
+/**
+ * Data stored for an rrset during parsing.
+ */
+struct rrset_parse {
+ /** next in hash bucket */
+ struct rrset_parse* rrset_bucket_next;
+ /** next in list of all rrsets */
+ struct rrset_parse* rrset_all_next;
+ /** hash value of rrset */
+ hashvalue_t hash;
+ /** which section was it found in: one of
+ * LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL
+ */
+ sldns_pkt_section section;
+ /** start of (possibly compressed) dname in packet */
+ uint8_t* dname;
+ /** length of the dname uncompressed wireformat */
+ size_t dname_len;
+ /** type, host order. */
+ uint16_t type;
+ /** class, network order. var name so that it is not a c++ keyword. */
+ uint16_t rrset_class;
+ /** the flags for the rrset, like for packedrrset */
+ uint32_t flags;
+ /** number of RRs in the rr list */
+ size_t rr_count;
+ /** sum of RR rdata sizes */
+ size_t size;
+ /** linked list of RRs in this rrset. */
+ struct rr_parse* rr_first;
+ /** last in list of RRs in this rrset. */
+ struct rr_parse* rr_last;
+ /** number of RRSIGs over this rrset. */
+ size_t rrsig_count;
+ /** linked list of RRsig RRs over this rrset. */
+ struct rr_parse* rrsig_first;
+ /** last in list of RRSIG RRs over this rrset. */
+ struct rr_parse* rrsig_last;
+};
+
+/**
+ * Data stored for an RR during parsing.
+ */
+struct rr_parse {
+ /**
+ * Pointer to the RR. Points to start of TTL value in the packet.
+ * Rdata length and rdata follow it.
+ * its dname, type and class are the same and stored for the rrset.
+ */
+ uint8_t* ttl_data;
+ /** true if ttl_data is not part of the packet, but elsewhere in mem.
+ * Set for generated CNAMEs for DNAMEs. */
+ int outside_packet;
+ /** the length of the rdata if allocated (with no dname compression)*/
+ size_t size;
+ /** next in list of RRs. */
+ struct rr_parse* next;
+};
+
+/** Check if label length is first octet of a compression pointer, pass u8. */
+#define LABEL_IS_PTR(x) ( ((x)&0xc0) == 0xc0 )
+/** Calculate destination offset of a compression pointer. pass first and
+ * second octets of the compression pointer. */
+#define PTR_OFFSET(x, y) ( ((x)&0x3f)<<8 | (y) )
+/** create a compression pointer to the given offset. */
+#define PTR_CREATE(offset) ((uint16_t)(0xc000 | (offset)))
+
+/** error codes, extended with EDNS, so > 15. */
+#define EDNS_RCODE_BADVERS 16 /** bad EDNS version */
+/** largest valid compression offset */
+#define PTR_MAX_OFFSET 0x3fff
+
+/**
+ * EDNS data storage
+ * EDNS rdata is ignored.
+ */
+struct edns_data {
+ /** if EDNS OPT record was present */
+ int edns_present;
+ /** Extended RCODE */
+ uint8_t ext_rcode;
+ /** The EDNS version number */
+ uint8_t edns_version;
+ /** the EDNS bits field from ttl (host order): Z */
+ uint16_t bits;
+ /** UDP reassembly size. */
+ uint16_t udp_size;
+};
+
+/**
+ * Obtain size in the packet of an rr type, that is before dname type.
+ * Do TYPE_DNAME, and type STR, yourself. Gives size for most regular types.
+ * @param rdf: the rdf type from the descriptor.
+ * @return: size in octets. 0 on failure.
+ */
+size_t get_rdf_size(sldns_rdf_type rdf);
+
+/**
+ * Parse the packet.
+ * @param pkt: packet, position at call must be at start of packet.
+ * at end position is after packet.
+ * @param msg: where to store results.
+ * @param region: how to alloc results.
+ * @return: 0 if OK, or rcode on error.
+ */
+int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg,
+ struct regional* region);
+
+/**
+ * After parsing the packet, extract EDNS data from packet.
+ * If not present this is noted in the data structure.
+ * If a parse error happens, an error code is returned.
+ *
+ * Quirks:
+ * o ignores OPT rdata.
+ * o ignores OPT owner name.
+ * o ignores extra OPT records, except the last one in the packet.
+ *
+ * @param msg: parsed message structure. Modified on exit, if EDNS was present
+ * it is removed from the additional section.
+ * @param edns: the edns data is stored here. Does not have to be initialised.
+ * @return: 0 on success. or an RCODE on an error.
+ * RCODE formerr if OPT in wrong section, and so on.
+ */
+int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns);
+
+/**
+ * If EDNS data follows a query section, extract it and initialize edns struct.
+ * @param pkt: the packet. position at start must be right after the query
+ * section. At end, right after EDNS data or no movement if failed.
+ * @param edns: the edns data allocated by the caller. Does not have to be
+ * initialised.
+ * @return: 0 on success, or an RCODE on error.
+ * RCODE formerr if OPT is badly formatted and so on.
+ */
+int parse_edns_from_pkt(struct sldns_buffer* pkt, struct edns_data* edns);
+
+/**
+ * Calculate hash value for rrset in packet.
+ * @param pkt: the packet.
+ * @param dname: pointer to uncompressed dname, or compressed dname in packet.
+ * @param type: rrset type in host order.
+ * @param dclass: rrset class in network order.
+ * @param rrset_flags: rrset flags (same as packed_rrset flags).
+ * @return hash value
+ */
+hashvalue_t pkt_hash_rrset(struct sldns_buffer* pkt, uint8_t* dname, uint16_t type,
+ uint16_t dclass, uint32_t rrset_flags);
+
+/**
+ * Lookup in msg hashtable to find a rrset.
+ * @param msg: with the hashtable.
+ * @param pkt: packet for compressed names.
+ * @param h: hash value
+ * @param rrset_flags: flags of rrset sought for.
+ * @param dname: name of rrset sought for.
+ * @param dnamelen: len of dname.
+ * @param type: rrset type, host order.
+ * @param dclass: rrset class, network order.
+ * @return NULL or the rrset_parse if found.
+ */
+struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg,
+ struct sldns_buffer* pkt, hashvalue_t h, uint32_t rrset_flags,
+ uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Remove rrset from hash table.
+ * @param msg: with hashtable.
+ * @param rrset: with hash value and id info.
+ */
+void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset);
+
+#endif /* UTIL_DATA_MSGPARSE_H */
diff --git a/external/unbound/util/data/msgreply.c b/external/unbound/util/data/msgreply.c
new file mode 100644
index 000000000..126e7bef4
--- /dev/null
+++ b/external/unbound/util/data/msgreply.c
@@ -0,0 +1,830 @@
+/*
+ * util/data/msgreply.c - store message and reply data.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data structure to store a message and its reply.
+ */
+
+#include "config.h"
+#include "util/data/msgreply.h"
+#include "util/storage/lookup3.h"
+#include "util/log.h"
+#include "util/alloc.h"
+#include "util/netevent.h"
+#include "util/net_help.h"
+#include "util/data/dname.h"
+#include "util/regional.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgencode.h"
+#include "ldns/sbuffer.h"
+#include "ldns/wire2str.h"
+
+/** MAX TTL default for messages and rrsets */
+time_t MAX_TTL = 3600 * 24 * 10; /* ten days */
+/** MIN TTL default for messages and rrsets */
+time_t MIN_TTL = 0;
+
+/** allocate qinfo, return 0 on error */
+static int
+parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg,
+ struct query_info* qinf, struct regional* region)
+{
+ if(msg->qname) {
+ if(region)
+ qinf->qname = (uint8_t*)regional_alloc(region,
+ msg->qname_len);
+ else qinf->qname = (uint8_t*)malloc(msg->qname_len);
+ if(!qinf->qname) return 0;
+ dname_pkt_copy(pkt, qinf->qname, msg->qname);
+ } else qinf->qname = 0;
+ qinf->qname_len = msg->qname_len;
+ qinf->qtype = msg->qtype;
+ qinf->qclass = msg->qclass;
+ return 1;
+}
+
+/** constructor for replyinfo */
+struct reply_info*
+construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
+ time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar,
+ size_t total, enum sec_status sec)
+{
+ struct reply_info* rep;
+ /* rrset_count-1 because the first ref is part of the struct. */
+ size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) +
+ sizeof(struct ub_packed_rrset_key*) * total;
+ if(region)
+ rep = (struct reply_info*)regional_alloc(region, s);
+ else rep = (struct reply_info*)malloc(s +
+ sizeof(struct rrset_ref) * (total));
+ if(!rep)
+ return NULL;
+ rep->flags = flags;
+ rep->qdcount = qd;
+ rep->ttl = ttl;
+ rep->prefetch_ttl = prettl;
+ rep->an_numrrsets = an;
+ rep->ns_numrrsets = ns;
+ rep->ar_numrrsets = ar;
+ rep->rrset_count = total;
+ rep->security = sec;
+ rep->authoritative = 0;
+ /* array starts after the refs */
+ if(region)
+ rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[0]);
+ else rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[total]);
+ /* zero the arrays to assist cleanup in case of malloc failure */
+ memset( rep->rrsets, 0, sizeof(struct ub_packed_rrset_key*) * total);
+ if(!region)
+ memset( &rep->ref[0], 0, sizeof(struct rrset_ref) * total);
+ return rep;
+}
+
+/** allocate replyinfo, return 0 on error */
+static int
+parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep,
+ struct regional* region)
+{
+ *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0,
+ 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets,
+ msg->rrset_count, sec_status_unchecked);
+ if(!*rep)
+ return 0;
+ return 1;
+}
+
+/** allocate (special) rrset keys, return 0 on error */
+static int
+repinfo_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc,
+ struct regional* region)
+{
+ size_t i;
+ for(i=0; i<rep->rrset_count; i++) {
+ if(region) {
+ rep->rrsets[i] = (struct ub_packed_rrset_key*)
+ regional_alloc(region,
+ sizeof(struct ub_packed_rrset_key));
+ if(rep->rrsets[i]) {
+ memset(rep->rrsets[i], 0,
+ sizeof(struct ub_packed_rrset_key));
+ rep->rrsets[i]->entry.key = rep->rrsets[i];
+ }
+ }
+ else rep->rrsets[i] = alloc_special_obtain(alloc);
+ if(!rep->rrsets[i])
+ return 0;
+ rep->rrsets[i]->entry.data = NULL;
+ }
+ return 1;
+}
+
+/** do the rdata copy */
+static int
+rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
+ struct rr_parse* rr, time_t* rr_ttl, uint16_t type)
+{
+ uint16_t pkt_len;
+ const sldns_rr_descriptor* desc;
+
+ *rr_ttl = sldns_read_uint32(rr->ttl_data);
+ /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
+ if(*rr_ttl & 0x80000000U)
+ *rr_ttl = 0;
+ if(*rr_ttl < MIN_TTL)
+ *rr_ttl = MIN_TTL;
+ if(*rr_ttl < data->ttl)
+ data->ttl = *rr_ttl;
+
+ if(rr->outside_packet) {
+ /* uncompressed already, only needs copy */
+ memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size);
+ return 1;
+ }
+
+ sldns_buffer_set_position(pkt, (size_t)
+ (rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t)));
+ /* insert decompressed size into rdata len stored in memory */
+ /* -2 because rdatalen bytes are not included. */
+ pkt_len = htons(rr->size - 2);
+ memmove(to, &pkt_len, sizeof(uint16_t));
+ to += 2;
+ /* read packet rdata len */
+ pkt_len = sldns_buffer_read_u16(pkt);
+ if(sldns_buffer_remaining(pkt) < pkt_len)
+ return 0;
+ desc = sldns_rr_descript(type);
+ if(pkt_len > 0 && desc && desc->_dname_count > 0) {
+ int count = (int)desc->_dname_count;
+ int rdf = 0;
+ size_t len;
+ size_t oldpos;
+ /* decompress dnames. */
+ while(pkt_len > 0 && count) {
+ switch(desc->_wireformat[rdf]) {
+ case LDNS_RDF_TYPE_DNAME:
+ oldpos = sldns_buffer_position(pkt);
+ dname_pkt_copy(pkt, to,
+ sldns_buffer_current(pkt));
+ to += pkt_dname_len(pkt);
+ pkt_len -= sldns_buffer_position(pkt)-oldpos;
+ count--;
+ len = 0;
+ break;
+ case LDNS_RDF_TYPE_STR:
+ len = sldns_buffer_current(pkt)[0] + 1;
+ break;
+ default:
+ len = get_rdf_size(desc->_wireformat[rdf]);
+ break;
+ }
+ if(len) {
+ memmove(to, sldns_buffer_current(pkt), len);
+ to += len;
+ sldns_buffer_skip(pkt, (ssize_t)len);
+ log_assert(len <= pkt_len);
+ pkt_len -= len;
+ }
+ rdf++;
+ }
+ }
+ /* copy remaining rdata */
+ if(pkt_len > 0)
+ memmove(to, sldns_buffer_current(pkt), pkt_len);
+
+ return 1;
+}
+
+/** copy over the data into packed rrset */
+static int
+parse_rr_copy(sldns_buffer* pkt, struct rrset_parse* pset,
+ struct packed_rrset_data* data)
+{
+ size_t i;
+ struct rr_parse* rr = pset->rr_first;
+ uint8_t* nextrdata;
+ size_t total = pset->rr_count + pset->rrsig_count;
+ data->ttl = MAX_TTL;
+ data->count = pset->rr_count;
+ data->rrsig_count = pset->rrsig_count;
+ data->trust = rrset_trust_none;
+ data->security = sec_status_unchecked;
+ /* layout: struct - rr_len - rr_data - rr_ttl - rdata - rrsig */
+ data->rr_len = (size_t*)((uint8_t*)data +
+ sizeof(struct packed_rrset_data));
+ data->rr_data = (uint8_t**)&(data->rr_len[total]);
+ data->rr_ttl = (time_t*)&(data->rr_data[total]);
+ nextrdata = (uint8_t*)&(data->rr_ttl[total]);
+ for(i=0; i<data->count; i++) {
+ data->rr_len[i] = rr->size;
+ data->rr_data[i] = nextrdata;
+ nextrdata += rr->size;
+ if(!rdata_copy(pkt, data, data->rr_data[i], rr,
+ &data->rr_ttl[i], pset->type))
+ return 0;
+ rr = rr->next;
+ }
+ /* if rrsig, its rdata is at nextrdata */
+ rr = pset->rrsig_first;
+ for(i=data->count; i<total; i++) {
+ data->rr_len[i] = rr->size;
+ data->rr_data[i] = nextrdata;
+ nextrdata += rr->size;
+ if(!rdata_copy(pkt, data, data->rr_data[i], rr,
+ &data->rr_ttl[i], LDNS_RR_TYPE_RRSIG))
+ return 0;
+ rr = rr->next;
+ }
+ return 1;
+}
+
+/** create rrset return 0 on failure */
+static int
+parse_create_rrset(sldns_buffer* pkt, struct rrset_parse* pset,
+ struct packed_rrset_data** data, struct regional* region)
+{
+ /* allocate */
+ size_t s = sizeof(struct packed_rrset_data) +
+ (pset->rr_count + pset->rrsig_count) *
+ (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)) +
+ pset->size;
+ if(region)
+ *data = regional_alloc(region, s);
+ else *data = malloc(s);
+ if(!*data)
+ return 0;
+ /* copy & decompress */
+ if(!parse_rr_copy(pkt, pset, *data)) {
+ if(!region) free(*data);
+ return 0;
+ }
+ return 1;
+}
+
+/** get trust value for rrset */
+static enum rrset_trust
+get_rrset_trust(struct msg_parse* msg, struct rrset_parse* rrset)
+{
+ uint16_t AA = msg->flags & BIT_AA;
+ if(rrset->section == LDNS_SECTION_ANSWER) {
+ if(AA) {
+ /* RFC2181 says remainder of CNAME chain is nonauth*/
+ if(msg->rrset_first &&
+ msg->rrset_first->section==LDNS_SECTION_ANSWER
+ && msg->rrset_first->type==LDNS_RR_TYPE_CNAME){
+ if(rrset == msg->rrset_first)
+ return rrset_trust_ans_AA;
+ else return rrset_trust_ans_noAA;
+ }
+ if(msg->rrset_first &&
+ msg->rrset_first->section==LDNS_SECTION_ANSWER
+ && msg->rrset_first->type==LDNS_RR_TYPE_DNAME){
+ if(rrset == msg->rrset_first ||
+ rrset == msg->rrset_first->rrset_all_next)
+ return rrset_trust_ans_AA;
+ else return rrset_trust_ans_noAA;
+ }
+ return rrset_trust_ans_AA;
+ }
+ else return rrset_trust_ans_noAA;
+ } else if(rrset->section == LDNS_SECTION_AUTHORITY) {
+ if(AA) return rrset_trust_auth_AA;
+ else return rrset_trust_auth_noAA;
+ } else {
+ /* addit section */
+ if(AA) return rrset_trust_add_AA;
+ else return rrset_trust_add_noAA;
+ }
+ /* NOTREACHED */
+ return rrset_trust_none;
+}
+
+int
+parse_copy_decompress_rrset(sldns_buffer* pkt, struct msg_parse* msg,
+ struct rrset_parse *pset, struct regional* region,
+ struct ub_packed_rrset_key* pk)
+{
+ struct packed_rrset_data* data;
+ pk->rk.flags = pset->flags;
+ pk->rk.dname_len = pset->dname_len;
+ if(region)
+ pk->rk.dname = (uint8_t*)regional_alloc(
+ region, pset->dname_len);
+ else pk->rk.dname =
+ (uint8_t*)malloc(pset->dname_len);
+ if(!pk->rk.dname)
+ return 0;
+ /** copy & decompress dname */
+ dname_pkt_copy(pkt, pk->rk.dname, pset->dname);
+ /** copy over type and class */
+ pk->rk.type = htons(pset->type);
+ pk->rk.rrset_class = pset->rrset_class;
+ /** read data part. */
+ if(!parse_create_rrset(pkt, pset, &data, region))
+ return 0;
+ pk->entry.data = (void*)data;
+ pk->entry.key = (void*)pk;
+ pk->entry.hash = pset->hash;
+ data->trust = get_rrset_trust(msg, pset);
+ return 1;
+}
+
+/**
+ * Copy and decompress rrs
+ * @param pkt: the packet for compression pointer resolution.
+ * @param msg: the parsed message
+ * @param rep: reply info to put rrs into.
+ * @param region: if not NULL, used for allocation.
+ * @return 0 on failure.
+ */
+static int
+parse_copy_decompress(sldns_buffer* pkt, struct msg_parse* msg,
+ struct reply_info* rep, struct regional* region)
+{
+ size_t i;
+ struct rrset_parse *pset = msg->rrset_first;
+ struct packed_rrset_data* data;
+ log_assert(rep);
+ rep->ttl = MAX_TTL;
+ rep->security = sec_status_unchecked;
+ if(rep->rrset_count == 0)
+ rep->ttl = NORR_TTL;
+
+ for(i=0; i<rep->rrset_count; i++) {
+ if(!parse_copy_decompress_rrset(pkt, msg, pset, region,
+ rep->rrsets[i]))
+ return 0;
+ data = (struct packed_rrset_data*)rep->rrsets[i]->entry.data;
+ if(data->ttl < rep->ttl)
+ rep->ttl = data->ttl;
+
+ pset = pset->rrset_all_next;
+ }
+ rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl);
+ return 1;
+}
+
+int
+parse_create_msg(sldns_buffer* pkt, struct msg_parse* msg,
+ struct alloc_cache* alloc, struct query_info* qinf,
+ struct reply_info** rep, struct regional* region)
+{
+ log_assert(pkt && msg);
+ if(!parse_create_qinfo(pkt, msg, qinf, region))
+ return 0;
+ if(!parse_create_repinfo(msg, rep, region))
+ return 0;
+ if(!repinfo_alloc_rrset_keys(*rep, alloc, region))
+ return 0;
+ if(!parse_copy_decompress(pkt, msg, *rep, region))
+ return 0;
+ return 1;
+}
+
+int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc,
+ struct query_info* qinf, struct reply_info** rep,
+ struct regional* region, struct edns_data* edns)
+{
+ /* use scratch pad region-allocator during parsing. */
+ struct msg_parse* msg;
+ int ret;
+
+ qinf->qname = NULL;
+ *rep = NULL;
+ if(!(msg = regional_alloc(region, sizeof(*msg)))) {
+ return LDNS_RCODE_SERVFAIL;
+ }
+ memset(msg, 0, sizeof(*msg));
+
+ sldns_buffer_set_position(pkt, 0);
+ if((ret = parse_packet(pkt, msg, region)) != 0) {
+ return ret;
+ }
+ if((ret = parse_extract_edns(msg, edns)) != 0)
+ return ret;
+
+ /* parse OK, allocate return structures */
+ /* this also performs dname decompression */
+ if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) {
+ query_info_clear(qinf);
+ reply_info_parsedelete(*rep, alloc);
+ *rep = NULL;
+ return LDNS_RCODE_SERVFAIL;
+ }
+ return 0;
+}
+
+/** helper compare function to sort in lock order */
+static int
+reply_info_sortref_cmp(const void* a, const void* b)
+{
+ struct rrset_ref* x = (struct rrset_ref*)a;
+ struct rrset_ref* y = (struct rrset_ref*)b;
+ if(x->key < y->key) return -1;
+ if(x->key > y->key) return 1;
+ return 0;
+}
+
+void
+reply_info_sortref(struct reply_info* rep)
+{
+ qsort(&rep->ref[0], rep->rrset_count, sizeof(struct rrset_ref),
+ reply_info_sortref_cmp);
+}
+
+void
+reply_info_set_ttls(struct reply_info* rep, time_t timenow)
+{
+ size_t i, j;
+ rep->ttl += timenow;
+ rep->prefetch_ttl += timenow;
+ for(i=0; i<rep->rrset_count; i++) {
+ struct packed_rrset_data* data = (struct packed_rrset_data*)
+ rep->ref[i].key->entry.data;
+ if(i>0 && rep->ref[i].key == rep->ref[i-1].key)
+ continue;
+ data->ttl += timenow;
+ for(j=0; j<data->count + data->rrsig_count; j++) {
+ data->rr_ttl[j] += timenow;
+ }
+ }
+}
+
+void
+reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc)
+{
+ size_t i;
+ if(!rep)
+ return;
+ /* no need to lock, since not shared in hashtables. */
+ for(i=0; i<rep->rrset_count; i++) {
+ ub_packed_rrset_parsedelete(rep->rrsets[i], alloc);
+ }
+ free(rep);
+}
+
+int
+query_info_parse(struct query_info* m, sldns_buffer* query)
+{
+ uint8_t* q = sldns_buffer_begin(query);
+ /* minimum size: header + \0 + qtype + qclass */
+ if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5)
+ return 0;
+ if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY ||
+ LDNS_QDCOUNT(q) != 1 || sldns_buffer_position(query) != 0)
+ return 0;
+ sldns_buffer_skip(query, LDNS_HEADER_SIZE);
+ m->qname = sldns_buffer_current(query);
+ if((m->qname_len = query_dname_len(query)) == 0)
+ return 0; /* parse error */
+ if(sldns_buffer_remaining(query) < 4)
+ return 0; /* need qtype, qclass */
+ m->qtype = sldns_buffer_read_u16(query);
+ m->qclass = sldns_buffer_read_u16(query);
+ return 1;
+}
+
+/** tiny subroutine for msgreply_compare */
+#define COMPARE_IT(x, y) \
+ if( (x) < (y) ) return -1; \
+ else if( (x) > (y) ) return +1; \
+ log_assert( (x) == (y) );
+
+int
+query_info_compare(void* m1, void* m2)
+{
+ struct query_info* msg1 = (struct query_info*)m1;
+ struct query_info* msg2 = (struct query_info*)m2;
+ int mc;
+ /* from most different to least different for speed */
+ COMPARE_IT(msg1->qtype, msg2->qtype);
+ if((mc = query_dname_compare(msg1->qname, msg2->qname)) != 0)
+ return mc;
+ log_assert(msg1->qname_len == msg2->qname_len);
+ COMPARE_IT(msg1->qclass, msg2->qclass);
+ return 0;
+#undef COMPARE_IT
+}
+
+void
+query_info_clear(struct query_info* m)
+{
+ free(m->qname);
+ m->qname = NULL;
+}
+
+size_t
+msgreply_sizefunc(void* k, void* d)
+{
+ struct msgreply_entry* q = (struct msgreply_entry*)k;
+ struct reply_info* r = (struct reply_info*)d;
+ size_t s = sizeof(struct msgreply_entry) + sizeof(struct reply_info)
+ + q->key.qname_len + lock_get_mem(&q->entry.lock)
+ - sizeof(struct rrset_ref);
+ s += r->rrset_count * sizeof(struct rrset_ref);
+ s += r->rrset_count * sizeof(struct ub_packed_rrset_key*);
+ return s;
+}
+
+void
+query_entry_delete(void *k, void* ATTR_UNUSED(arg))
+{
+ struct msgreply_entry* q = (struct msgreply_entry*)k;
+ lock_rw_destroy(&q->entry.lock);
+ query_info_clear(&q->key);
+ free(q);
+}
+
+void
+reply_info_delete(void* d, void* ATTR_UNUSED(arg))
+{
+ struct reply_info* r = (struct reply_info*)d;
+ free(r);
+}
+
+hashvalue_t
+query_info_hash(struct query_info *q)
+{
+ hashvalue_t h = 0xab;
+ h = hashlittle(&q->qtype, sizeof(q->qtype), h);
+ h = hashlittle(&q->qclass, sizeof(q->qclass), h);
+ h = dname_query_hash(q->qname, h);
+ return h;
+}
+
+struct msgreply_entry*
+query_info_entrysetup(struct query_info* q, struct reply_info* r,
+ hashvalue_t h)
+{
+ struct msgreply_entry* e = (struct msgreply_entry*)malloc(
+ sizeof(struct msgreply_entry));
+ if(!e) return NULL;
+ memcpy(&e->key, q, sizeof(*q));
+ e->entry.hash = h;
+ e->entry.key = e;
+ e->entry.data = r;
+ lock_rw_init(&e->entry.lock);
+ lock_protect(&e->entry.lock, &e->key, sizeof(e->key));
+ lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash) +
+ sizeof(e->entry.key) + sizeof(e->entry.data));
+ lock_protect(&e->entry.lock, e->key.qname, e->key.qname_len);
+ q->qname = NULL;
+ return e;
+}
+
+/** copy rrsets from replyinfo to dest replyinfo */
+static int
+repinfo_copy_rrsets(struct reply_info* dest, struct reply_info* from,
+ struct regional* region)
+{
+ size_t i, s;
+ struct packed_rrset_data* fd, *dd;
+ struct ub_packed_rrset_key* fk, *dk;
+ for(i=0; i<dest->rrset_count; i++) {
+ fk = from->rrsets[i];
+ dk = dest->rrsets[i];
+ fd = (struct packed_rrset_data*)fk->entry.data;
+ dk->entry.hash = fk->entry.hash;
+ dk->rk = fk->rk;
+ if(region) {
+ dk->id = fk->id;
+ dk->rk.dname = (uint8_t*)regional_alloc_init(region,
+ fk->rk.dname, fk->rk.dname_len);
+ } else
+ dk->rk.dname = (uint8_t*)memdup(fk->rk.dname,
+ fk->rk.dname_len);
+ if(!dk->rk.dname)
+ return 0;
+ s = packed_rrset_sizeof(fd);
+ if(region)
+ dd = (struct packed_rrset_data*)regional_alloc_init(
+ region, fd, s);
+ else dd = (struct packed_rrset_data*)memdup(fd, s);
+ if(!dd)
+ return 0;
+ packed_rrset_ptr_fixup(dd);
+ dk->entry.data = (void*)dd;
+ }
+ return 1;
+}
+
+struct reply_info*
+reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc,
+ struct regional* region)
+{
+ struct reply_info* cp;
+ cp = construct_reply_info_base(region, rep->flags, rep->qdcount,
+ rep->ttl, rep->prefetch_ttl, rep->an_numrrsets,
+ rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count,
+ rep->security);
+ if(!cp)
+ return NULL;
+ /* allocate ub_key structures special or not */
+ if(!repinfo_alloc_rrset_keys(cp, alloc, region)) {
+ if(!region)
+ reply_info_parsedelete(cp, alloc);
+ return NULL;
+ }
+ if(!repinfo_copy_rrsets(cp, rep, region)) {
+ if(!region)
+ reply_info_parsedelete(cp, alloc);
+ return NULL;
+ }
+ return cp;
+}
+
+uint8_t*
+reply_find_final_cname_target(struct query_info* qinfo, struct reply_info* rep)
+{
+ uint8_t* sname = qinfo->qname;
+ size_t snamelen = qinfo->qname_len;
+ size_t i;
+ for(i=0; i<rep->an_numrrsets; i++) {
+ struct ub_packed_rrset_key* s = rep->rrsets[i];
+ /* follow CNAME chain (if any) */
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+ ntohs(s->rk.rrset_class) == qinfo->qclass &&
+ snamelen == s->rk.dname_len &&
+ query_dname_compare(sname, s->rk.dname) == 0) {
+ get_cname_target(s, &sname, &snamelen);
+ }
+ }
+ if(sname != qinfo->qname)
+ return sname;
+ return NULL;
+}
+
+struct ub_packed_rrset_key*
+reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep)
+{
+ uint8_t* sname = qinfo->qname;
+ size_t snamelen = qinfo->qname_len;
+ size_t i;
+ for(i=0; i<rep->an_numrrsets; i++) {
+ struct ub_packed_rrset_key* s = rep->rrsets[i];
+ /* first match type, for query of qtype cname */
+ if(ntohs(s->rk.type) == qinfo->qtype &&
+ ntohs(s->rk.rrset_class) == qinfo->qclass &&
+ snamelen == s->rk.dname_len &&
+ query_dname_compare(sname, s->rk.dname) == 0) {
+ return s;
+ }
+ /* follow CNAME chain (if any) */
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+ ntohs(s->rk.rrset_class) == qinfo->qclass &&
+ snamelen == s->rk.dname_len &&
+ query_dname_compare(sname, s->rk.dname) == 0) {
+ get_cname_target(s, &sname, &snamelen);
+ }
+ }
+ return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+ size_t i;
+ for(i=0; i<rep->an_numrrsets; i++) {
+ struct ub_packed_rrset_key* s = rep->rrsets[i];
+ if(ntohs(s->rk.type) == type &&
+ ntohs(s->rk.rrset_class) == dclass &&
+ namelen == s->rk.dname_len &&
+ query_dname_compare(name, s->rk.dname) == 0) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+ size_t i;
+ for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
+ struct ub_packed_rrset_key* s = rep->rrsets[i];
+ if(ntohs(s->rk.type) == type &&
+ ntohs(s->rk.rrset_class) == dclass &&
+ namelen == s->rk.dname_len &&
+ query_dname_compare(name, s->rk.dname) == 0) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
+{
+ size_t i;
+ for(i=0; i<rep->rrset_count; i++) {
+ struct ub_packed_rrset_key* s = rep->rrsets[i];
+ if(ntohs(s->rk.type) == type &&
+ ntohs(s->rk.rrset_class) == dclass &&
+ namelen == s->rk.dname_len &&
+ query_dname_compare(name, s->rk.dname) == 0) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+void
+log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep)
+{
+ /* not particularly fast but flexible, make wireformat and print */
+ sldns_buffer* buf = sldns_buffer_new(65535);
+ struct regional* region = regional_create();
+ if(!reply_info_encode(qinfo, rep, 0, rep->flags, buf, 0,
+ region, 65535, 1)) {
+ log_info("%s: log_dns_msg: out of memory", str);
+ } else {
+ char* str = sldns_wire2str_pkt(sldns_buffer_begin(buf),
+ sldns_buffer_limit(buf));
+ if(!str) {
+ log_info("%s: log_dns_msg: ldns tostr failed", str);
+ } else {
+ log_info("%s %s",
+ str, (char*)sldns_buffer_begin(buf));
+ }
+ free(str);
+ }
+ sldns_buffer_free(buf);
+ regional_destroy(region);
+}
+
+void
+log_query_info(enum verbosity_value v, const char* str,
+ struct query_info* qinf)
+{
+ log_nametypeclass(v, str, qinf->qname, qinf->qtype, qinf->qclass);
+}
+
+int
+reply_check_cname_chain(struct reply_info* rep)
+{
+ /* check only answer section rrs for matching cname chain.
+ * the cache may return changed rdata, but owner names are untouched.*/
+ size_t i;
+ uint8_t* sname = rep->rrsets[0]->rk.dname;
+ size_t snamelen = rep->rrsets[0]->rk.dname_len;
+ for(i=0; i<rep->an_numrrsets; i++) {
+ uint16_t t = ntohs(rep->rrsets[i]->rk.type);
+ if(t == LDNS_RR_TYPE_DNAME)
+ continue; /* skip dnames; note TTL 0 not cached */
+ /* verify that owner matches current sname */
+ if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){
+ /* cname chain broken */
+ return 0;
+ }
+ /* if this is a cname; move on */
+ if(t == LDNS_RR_TYPE_CNAME) {
+ get_cname_target(rep->rrsets[i], &sname, &snamelen);
+ }
+ }
+ return 1;
+}
+
+int
+reply_all_rrsets_secure(struct reply_info* rep)
+{
+ size_t i;
+ for(i=0; i<rep->rrset_count; i++) {
+ if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+ ->security != sec_status_secure )
+ return 0;
+ }
+ return 1;
+}
diff --git a/external/unbound/util/data/msgreply.h b/external/unbound/util/data/msgreply.h
new file mode 100644
index 000000000..ccbd0d748
--- /dev/null
+++ b/external/unbound/util/data/msgreply.h
@@ -0,0 +1,438 @@
+/*
+ * util/data/msgreply.h - store message and reply data.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a data structure to store a message and its reply.
+ */
+
+#ifndef UTIL_DATA_MSGREPLY_H
+#define UTIL_DATA_MSGREPLY_H
+#include "util/storage/lruhash.h"
+#include "util/data/packed_rrset.h"
+struct sldns_buffer;
+struct comm_reply;
+struct alloc_cache;
+struct iovec;
+struct regional;
+struct edns_data;
+struct msg_parse;
+struct rrset_parse;
+
+/** calculate the prefetch TTL as 90% of original. Calculation
+ * without numerical overflow (uin32_t) */
+#define PREFETCH_TTL_CALC(ttl) ((ttl) - (ttl)/10)
+
+/**
+ * Structure to store query information that makes answers to queries
+ * different.
+ */
+struct query_info {
+ /**
+ * Salient data on the query: qname, in wireformat.
+ * can be allocated or a pointer to outside buffer.
+ * User has to keep track on the status of this.
+ */
+ uint8_t* qname;
+ /** length of qname (including last 0 octet) */
+ size_t qname_len;
+ /** qtype, host byte order */
+ uint16_t qtype;
+ /** qclass, host byte order */
+ uint16_t qclass;
+};
+
+/**
+ * Information to reference an rrset
+ */
+struct rrset_ref {
+ /** the key with lock, and ptr to packed data. */
+ struct ub_packed_rrset_key* key;
+ /** id needed */
+ rrset_id_t id;
+};
+
+/**
+ * Structure to store DNS query and the reply packet.
+ * To use it, copy over the flags from reply and modify using flags from
+ * the query (RD,CD if not AA). prepend ID.
+ *
+ * Memory layout is:
+ * o struct
+ * o rrset_ref array
+ * o packed_rrset_key* array.
+ *
+ * Memory layout is sometimes not packed, when the message is synthesized,
+ * for easy of the generation. It is allocated packed when it is copied
+ * from the region allocation to the malloc allocation.
+ */
+struct reply_info {
+ /** the flags for the answer, host byte order. */
+ uint16_t flags;
+
+ /**
+ * This flag informs unbound the answer is authoritative and
+ * the AA flag should be preserved.
+ */
+ uint8_t authoritative;
+
+ /**
+ * Number of RRs in the query section.
+ * If qdcount is not 0, then it is 1, and the data that appears
+ * in the reply is the same as the query_info.
+ * Host byte order.
+ */
+ uint8_t qdcount;
+
+ /** 32 bit padding to pad struct member alignment to 64 bits. */
+ uint32_t padding;
+
+ /**
+ * TTL of the entire reply (for negative caching).
+ * only for use when there are 0 RRsets in this message.
+ * if there are RRsets, check those instead.
+ */
+ time_t ttl;
+
+ /**
+ * TTL for prefetch. After it has expired, a prefetch is suitable.
+ * Smaller than the TTL, otherwise the prefetch would not happen.
+ */
+ time_t prefetch_ttl;
+
+ /**
+ * The security status from DNSSEC validation of this message.
+ */
+ enum sec_status security;
+
+ /**
+ * Number of RRsets in each section.
+ * The answer section. Add up the RRs in every RRset to calculate
+ * the number of RRs, and the count for the dns packet.
+ * The number of RRs in RRsets can change due to RRset updates.
+ */
+ size_t an_numrrsets;
+
+ /** Count of authority section RRsets */
+ size_t ns_numrrsets;
+ /** Count of additional section RRsets */
+ size_t ar_numrrsets;
+
+ /** number of RRsets: an_numrrsets + ns_numrrsets + ar_numrrsets */
+ size_t rrset_count;
+
+ /**
+ * List of pointers (only) to the rrsets in the order in which
+ * they appear in the reply message.
+ * Number of elements is ancount+nscount+arcount RRsets.
+ * This is a pointer to that array.
+ * Use the accessor function for access.
+ */
+ struct ub_packed_rrset_key** rrsets;
+
+ /**
+ * Packed array of ids (see counts) and pointers to packed_rrset_key.
+ * The number equals ancount+nscount+arcount RRsets.
+ * These are sorted in ascending pointer, the locking order. So
+ * this list can be locked (and id, ttl checked), to see if
+ * all the data is available and recent enough.
+ *
+ * This is defined as an array of size 1, so that the compiler
+ * associates the identifier with this position in the structure.
+ * Array bound overflow on this array then gives access to the further
+ * elements of the array, which are allocated after the main structure.
+ *
+ * It could be more pure to define as array of size 0, ref[0].
+ * But ref[1] may be less confusing for compilers.
+ * Use the accessor function for access.
+ */
+ struct rrset_ref ref[1];
+};
+
+/**
+ * Structure to keep hash table entry for message replies.
+ */
+struct msgreply_entry {
+ /** the hash table key */
+ struct query_info key;
+ /** the hash table entry, data is struct reply_info* */
+ struct lruhash_entry entry;
+};
+
+/**
+ * Constructor for replyinfo.
+ * @param region: where to allocate the results, pass NULL to use malloc.
+ * @param flags: flags for the replyinfo.
+ * @param qd: qd count
+ * @param ttl: TTL of replyinfo
+ * @param prettl: prefetch ttl
+ * @param an: an count
+ * @param ns: ns count
+ * @param ar: ar count
+ * @param total: total rrset count (presumably an+ns+ar).
+ * @param sec: security status of the reply info.
+ * @return the reply_info base struct with the array for putting the rrsets
+ * in. The array has been zeroed. Returns NULL on malloc failure.
+ */
+struct reply_info*
+construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
+ time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar,
+ size_t total, enum sec_status sec);
+
+/**
+ * Parse wire query into a queryinfo structure, return 0 on parse error.
+ * initialises the (prealloced) queryinfo structure as well.
+ * This query structure contains a pointer back info the buffer!
+ * This pointer avoids memory allocation. allocqname does memory allocation.
+ * @param m: the prealloced queryinfo structure to put query into.
+ * must be unused, or _clear()ed.
+ * @param query: the wireformat packet query. starts with ID.
+ * @return: 0 on format error.
+ */
+int query_info_parse(struct query_info* m, struct sldns_buffer* query);
+
+/**
+ * Parse query reply.
+ * Fills in preallocated query_info structure (with ptr into buffer).
+ * Allocates reply_info and packed_rrsets. These are not yet added to any
+ * caches or anything, this is only parsing. Returns formerror on qdcount > 1.
+ * @param pkt: the packet buffer. Must be positioned after the query section.
+ * @param alloc: creates packed rrset key structures.
+ * @param rep: allocated reply_info is returned (only on no error).
+ * @param qinf: query_info is returned (only on no error).
+ * @param region: where to store temporary data (for parsing).
+ * @param edns: where to store edns information, does not need to be inited.
+ * @return: zero is OK, or DNS error code in case of error
+ * o FORMERR for parse errors.
+ * o SERVFAIL for memory allocation errors.
+ */
+int reply_info_parse(struct sldns_buffer* pkt, struct alloc_cache* alloc,
+ struct query_info* qinf, struct reply_info** rep,
+ struct regional* region, struct edns_data* edns);
+
+/**
+ * Allocate and decompress parsed message and rrsets.
+ * @param pkt: for name decompression.
+ * @param msg: parsed message in scratch region.
+ * @param alloc: alloc cache for special rrset key structures.
+ * Not used if region!=NULL, it can be NULL in that case.
+ * @param qinf: where to store query info.
+ * qinf itself is allocated by the caller.
+ * @param rep: reply info is allocated and returned.
+ * @param region: if this parameter is NULL then malloc and the alloc is used.
+ * otherwise, everything is allocated in this region.
+ * In a region, no special rrset key structures are needed (not shared),
+ * and no rrset_ref array in the reply is built up.
+ * @return 0 if allocation failed.
+ */
+int parse_create_msg(struct sldns_buffer* pkt, struct msg_parse* msg,
+ struct alloc_cache* alloc, struct query_info* qinf,
+ struct reply_info** rep, struct regional* region);
+
+/**
+ * Sorts the ref array.
+ * @param rep: reply info. rrsets must be filled in.
+ */
+void reply_info_sortref(struct reply_info* rep);
+
+/**
+ * Set TTLs inside the replyinfo to absolute values.
+ * @param rep: reply info. rrsets must be filled in.
+ * Also refs must be filled in.
+ * @param timenow: the current time.
+ */
+void reply_info_set_ttls(struct reply_info* rep, time_t timenow);
+
+/**
+ * Delete reply_info and packed_rrsets (while they are not yet added to the
+ * hashtables.). Returns rrsets to the alloc cache.
+ * @param rep: reply_info to delete.
+ * @param alloc: where to return rrset structures to.
+ */
+void reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc);
+
+/**
+ * Compare two queryinfo structures, on query and type, class.
+ * It is _not_ sorted in canonical ordering.
+ * @param m1: struct query_info* , void* here to ease use as function pointer.
+ * @param m2: struct query_info* , void* here to ease use as function pointer.
+ * @return: 0 = same, -1 m1 is smaller, +1 m1 is larger.
+ */
+int query_info_compare(void* m1, void* m2);
+
+/** clear out query info structure */
+void query_info_clear(struct query_info* m);
+
+/** calculate size of struct query_info + reply_info */
+size_t msgreply_sizefunc(void* k, void* d);
+
+/** delete msgreply_entry key structure */
+void query_entry_delete(void *q, void* arg);
+
+/** delete reply_info data structure */
+void reply_info_delete(void* d, void* arg);
+
+/** calculate hash value of query_info, lowercases the qname */
+hashvalue_t query_info_hash(struct query_info *q);
+
+/**
+ * Setup query info entry
+ * @param q: query info to copy. Emptied as if clear is called.
+ * @param r: reply to init data.
+ * @param h: hash value.
+ * @return: newly allocated message reply cache item.
+ */
+struct msgreply_entry* query_info_entrysetup(struct query_info* q,
+ struct reply_info* r, hashvalue_t h);
+
+/**
+ * Copy reply_info and all rrsets in it and allocate.
+ * @param rep: what to copy, probably inside region, no ref[] array in it.
+ * @param alloc: how to allocate rrset keys.
+ * Not used if region!=NULL, it can be NULL in that case.
+ * @param region: if this parameter is NULL then malloc and the alloc is used.
+ * otherwise, everything is allocated in this region.
+ * In a region, no special rrset key structures are needed (not shared),
+ * and no rrset_ref array in the reply is built up.
+ * @return new reply info or NULL on memory error.
+ */
+struct reply_info* reply_info_copy(struct reply_info* rep,
+ struct alloc_cache* alloc, struct regional* region);
+
+/**
+ * Copy a parsed rrset into given key, decompressing and allocating rdata.
+ * @param pkt: packet for decompression
+ * @param msg: the parser message (for flags for trust).
+ * @param pset: the parsed rrset to copy.
+ * @param region: if NULL - malloc, else data is allocated in this region.
+ * @param pk: a freshly obtained rrsetkey structure. No dname is set yet,
+ * will be set on return.
+ * Note that TTL will still be relative on return.
+ * @return false on alloc failure.
+ */
+int parse_copy_decompress_rrset(struct sldns_buffer* pkt, struct msg_parse* msg,
+ struct rrset_parse *pset, struct regional* region,
+ struct ub_packed_rrset_key* pk);
+
+/**
+ * Find final cname target in reply, the one matching qinfo. Follows CNAMEs.
+ * @param qinfo: what to start with.
+ * @param rep: looks in answer section of this message.
+ * @return: pointer dname, or NULL if not found.
+ */
+uint8_t* reply_find_final_cname_target(struct query_info* qinfo,
+ struct reply_info* rep);
+
+/**
+ * Check if cname chain in cached reply is still valid.
+ * @param rep: reply to check.
+ * @return: true if valid, false if invalid.
+ */
+int reply_check_cname_chain(struct reply_info* rep);
+
+/**
+ * Check security status of all RRs in the message.
+ * @param rep: reply to check
+ * @return: true if all RRs are secure. False if not.
+ * True if there are zero RRs.
+ */
+int reply_all_rrsets_secure(struct reply_info* rep);
+
+/**
+ * Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the
+ * result may have a different owner name.
+ * @param qinfo: what to look for.
+ * @param rep: looks in answer section of this message.
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo,
+ struct reply_info* rep);
+
+/**
+ * Find rrset in reply, inside the answer section. Does not follow CNAMEs.
+ * @param rep: looks in answer section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Find rrset in reply, inside the authority section. Does not follow CNAMEs.
+ * @param rep: looks in authority section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Find rrset in reply, inside any section. Does not follow CNAMEs.
+ * @param rep: looks in answer,authority and additional section of this message.
+ * @param name: what to look for.
+ * @param namelen: length of name.
+ * @param type: looks for (host order).
+ * @param dclass: looks for (host order).
+ * @return: pointer to rrset, or NULL if not found.
+ */
+struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep,
+ uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
+
+/**
+ * Debug send the query info and reply info to the log in readable form.
+ * @param str: descriptive string printed with packet content.
+ * @param qinfo: query section.
+ * @param rep: rest of message.
+ */
+void log_dns_msg(const char* str, struct query_info* qinfo,
+ struct reply_info* rep);
+
+/**
+ * Print string with neat domain name, type, class from query info.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param qinf: query info structure with name, type and class.
+ */
+void log_query_info(enum verbosity_value v, const char* str,
+ struct query_info* qinf);
+
+#endif /* UTIL_DATA_MSGREPLY_H */
diff --git a/external/unbound/util/data/packed_rrset.c b/external/unbound/util/data/packed_rrset.c
new file mode 100644
index 000000000..807468576
--- /dev/null
+++ b/external/unbound/util/data/packed_rrset.c
@@ -0,0 +1,389 @@
+/*
+ * util/data/packed_rrset.c - data storage for a set of resource records.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the data storage for RRsets.
+ */
+
+#include "config.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/storage/lookup3.h"
+#include "util/log.h"
+#include "util/alloc.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+#include "ldns/rrdef.h"
+#include "ldns/sbuffer.h"
+#include "ldns/wire2str.h"
+
+void
+ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
+ struct alloc_cache* alloc)
+{
+ if(!pkey)
+ return;
+ if(pkey->entry.data)
+ free(pkey->entry.data);
+ pkey->entry.data = NULL;
+ if(pkey->rk.dname)
+ free(pkey->rk.dname);
+ pkey->rk.dname = NULL;
+ pkey->id = 0;
+ alloc_special_release(alloc, pkey);
+}
+
+size_t
+ub_rrset_sizefunc(void* key, void* data)
+{
+ struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
+ struct packed_rrset_data* d = (struct packed_rrset_data*)data;
+ size_t s = sizeof(struct ub_packed_rrset_key) + k->rk.dname_len;
+ s += packed_rrset_sizeof(d) + lock_get_mem(&k->entry.lock);
+ return s;
+}
+
+size_t
+packed_rrset_sizeof(struct packed_rrset_data* d)
+{
+ size_t s;
+ if(d->rrsig_count > 0) {
+ s = ((uint8_t*)d->rr_data[d->count+d->rrsig_count-1] -
+ (uint8_t*)d) + d->rr_len[d->count+d->rrsig_count-1];
+ } else {
+ log_assert(d->count > 0);
+ s = ((uint8_t*)d->rr_data[d->count-1] - (uint8_t*)d) +
+ d->rr_len[d->count-1];
+ }
+ return s;
+}
+
+int
+ub_rrset_compare(void* k1, void* k2)
+{
+ struct ub_packed_rrset_key* key1 = (struct ub_packed_rrset_key*)k1;
+ struct ub_packed_rrset_key* key2 = (struct ub_packed_rrset_key*)k2;
+ int c;
+ if(key1 == key2)
+ return 0;
+ if(key1->rk.type != key2->rk.type) {
+ if(key1->rk.type < key2->rk.type)
+ return -1;
+ return 1;
+ }
+ if(key1->rk.dname_len != key2->rk.dname_len) {
+ if(key1->rk.dname_len < key2->rk.dname_len)
+ return -1;
+ return 1;
+ }
+ if((c=query_dname_compare(key1->rk.dname, key2->rk.dname)) != 0)
+ return c;
+ if(key1->rk.rrset_class != key2->rk.rrset_class) {
+ if(key1->rk.rrset_class < key2->rk.rrset_class)
+ return -1;
+ return 1;
+ }
+ if(key1->rk.flags != key2->rk.flags) {
+ if(key1->rk.flags < key2->rk.flags)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+
+void
+ub_rrset_key_delete(void* key, void* userdata)
+{
+ struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
+ struct alloc_cache* a = (struct alloc_cache*)userdata;
+ k->id = 0;
+ free(k->rk.dname);
+ k->rk.dname = NULL;
+ alloc_special_release(a, k);
+}
+
+void
+rrset_data_delete(void* data, void* ATTR_UNUSED(userdata))
+{
+ struct packed_rrset_data* d = (struct packed_rrset_data*)data;
+ free(d);
+}
+
+int
+rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2)
+{
+ size_t i;
+ size_t total;
+ if(d1->count != d2->count || d1->rrsig_count != d2->rrsig_count)
+ return 0;
+ total = d1->count + d1->rrsig_count;
+ for(i=0; i<total; i++) {
+ if(d1->rr_len[i] != d2->rr_len[i])
+ return 0;
+ if(memcmp(d1->rr_data[i], d2->rr_data[i], d1->rr_len[i]) != 0)
+ return 0;
+ }
+ return 1;
+}
+
+hashvalue_t
+rrset_key_hash(struct packed_rrset_key* key)
+{
+ /* type is hashed in host order */
+ uint16_t t = ntohs(key->type);
+ /* Note this MUST be identical to pkt_hash_rrset in msgparse.c */
+ /* this routine does not have a compressed name */
+ hashvalue_t h = 0xab;
+ h = dname_query_hash(key->dname, h);
+ h = hashlittle(&t, sizeof(t), h);
+ h = hashlittle(&key->rrset_class, sizeof(uint16_t), h);
+ h = hashlittle(&key->flags, sizeof(uint32_t), h);
+ return h;
+}
+
+void
+packed_rrset_ptr_fixup(struct packed_rrset_data* data)
+{
+ size_t i;
+ size_t total = data->count + data->rrsig_count;
+ uint8_t* nextrdata;
+ /* fixup pointers in packed rrset data */
+ data->rr_len = (size_t*)((uint8_t*)data +
+ sizeof(struct packed_rrset_data));
+ data->rr_data = (uint8_t**)&(data->rr_len[total]);
+ data->rr_ttl = (time_t*)&(data->rr_data[total]);
+ nextrdata = (uint8_t*)&(data->rr_ttl[total]);
+ for(i=0; i<total; i++) {
+ data->rr_data[i] = nextrdata;
+ nextrdata += data->rr_len[i];
+ }
+}
+
+void
+get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname,
+ size_t* dname_len)
+{
+ struct packed_rrset_data* d;
+ size_t len;
+ if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME &&
+ ntohs(rrset->rk.type) != LDNS_RR_TYPE_DNAME)
+ return;
+ d = (struct packed_rrset_data*)rrset->entry.data;
+ if(d->count < 1)
+ return;
+ if(d->rr_len[0] < 3) /* at least rdatalen + 0byte root label */
+ return;
+ len = sldns_read_uint16(d->rr_data[0]);
+ if(len != d->rr_len[0] - sizeof(uint16_t))
+ return;
+ if(dname_valid(d->rr_data[0]+sizeof(uint16_t), len) != len)
+ return;
+ *dname = d->rr_data[0]+sizeof(uint16_t);
+ *dname_len = len;
+}
+
+void
+packed_rrset_ttl_add(struct packed_rrset_data* data, time_t add)
+{
+ size_t i;
+ size_t total = data->count + data->rrsig_count;
+ data->ttl += add;
+ for(i=0; i<total; i++)
+ data->rr_ttl[i] += add;
+}
+
+const char*
+rrset_trust_to_string(enum rrset_trust s)
+{
+ switch(s) {
+ case rrset_trust_none: return "rrset_trust_none";
+ case rrset_trust_add_noAA: return "rrset_trust_add_noAA";
+ case rrset_trust_auth_noAA: return "rrset_trust_auth_noAA";
+ case rrset_trust_add_AA: return "rrset_trust_add_AA";
+ case rrset_trust_nonauth_ans_AA:return "rrset_trust_nonauth_ans_AA";
+ case rrset_trust_ans_noAA: return "rrset_trust_ans_noAA";
+ case rrset_trust_glue: return "rrset_trust_glue";
+ case rrset_trust_auth_AA: return "rrset_trust_auth_AA";
+ case rrset_trust_ans_AA: return "rrset_trust_ans_AA";
+ case rrset_trust_sec_noglue: return "rrset_trust_sec_noglue";
+ case rrset_trust_prim_noglue: return "rrset_trust_prim_noglue";
+ case rrset_trust_validated: return "rrset_trust_validated";
+ case rrset_trust_ultimate: return "rrset_trust_ultimate";
+ }
+ return "unknown_rrset_trust_value";
+}
+
+const char*
+sec_status_to_string(enum sec_status s)
+{
+ switch(s) {
+ case sec_status_unchecked: return "sec_status_unchecked";
+ case sec_status_bogus: return "sec_status_bogus";
+ case sec_status_indeterminate: return "sec_status_indeterminate";
+ case sec_status_insecure: return "sec_status_insecure";
+ case sec_status_secure: return "sec_status_secure";
+ }
+ return "unknown_sec_status_value";
+}
+
+void log_rrset_key(enum verbosity_value v, const char* str,
+ struct ub_packed_rrset_key* rrset)
+{
+ if(verbosity >= v)
+ log_nametypeclass(v, str, rrset->rk.dname,
+ ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
+}
+
+int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
+ time_t now, char* dest, size_t dest_len)
+{
+ struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+ entry.data;
+ uint8_t rr[65535];
+ size_t rlen = rrset->rk.dname_len + 2 + 2 + 4 + d->rr_len[i];
+ log_assert(dest_len > 0 && dest);
+ if(rlen > dest_len) {
+ dest[0] = 0;
+ return 0;
+ }
+ memmove(rr, rrset->rk.dname, rrset->rk.dname_len);
+ if(i < d->count)
+ memmove(rr+rrset->rk.dname_len, &rrset->rk.type, 2);
+ else sldns_write_uint16(rr+rrset->rk.dname_len, LDNS_RR_TYPE_RRSIG);
+ memmove(rr+rrset->rk.dname_len+2, &rrset->rk.rrset_class, 2);
+ sldns_write_uint32(rr+rrset->rk.dname_len+4,
+ (uint32_t)(d->rr_ttl[i]-now));
+ memmove(rr+rrset->rk.dname_len+8, d->rr_data[i], d->rr_len[i]);
+ if(sldns_wire2str_rr_buf(rr, rlen, dest, dest_len) == -1) {
+ log_info("rrbuf failure %d %s", (int)d->rr_len[i], dest);
+ dest[0] = 0;
+ return 0;
+ }
+ return 1;
+}
+
+void log_packed_rrset(enum verbosity_value v, const char* str,
+ struct ub_packed_rrset_key* rrset)
+{
+ struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+ entry.data;
+ char buf[65535];
+ size_t i;
+ if(verbosity < v)
+ return;
+ for(i=0; i<d->count+d->rrsig_count; i++) {
+ if(!packed_rr_to_string(rrset, i, 0, buf, sizeof(buf))) {
+ log_info("%s: rr %d wire2str-error", str, (int)i);
+ } else {
+ log_info("%s: %s", str, buf);
+ }
+ }
+}
+
+time_t
+ub_packed_rrset_ttl(struct ub_packed_rrset_key* key)
+{
+ struct packed_rrset_data* d = (struct packed_rrset_data*)key->
+ entry.data;
+ return d->ttl;
+}
+
+struct ub_packed_rrset_key*
+packed_rrset_copy_region(struct ub_packed_rrset_key* key,
+ struct regional* region, time_t now)
+{
+ struct ub_packed_rrset_key* ck = regional_alloc(region,
+ sizeof(struct ub_packed_rrset_key));
+ struct packed_rrset_data* d;
+ struct packed_rrset_data* data = (struct packed_rrset_data*)
+ key->entry.data;
+ size_t dsize, i;
+ if(!ck)
+ return NULL;
+ ck->id = key->id;
+ memset(&ck->entry, 0, sizeof(ck->entry));
+ ck->entry.hash = key->entry.hash;
+ ck->entry.key = ck;
+ ck->rk = key->rk;
+ ck->rk.dname = regional_alloc_init(region, key->rk.dname,
+ key->rk.dname_len);
+ if(!ck->rk.dname)
+ return NULL;
+ dsize = packed_rrset_sizeof(data);
+ d = (struct packed_rrset_data*)regional_alloc_init(region, data, dsize);
+ if(!d)
+ return NULL;
+ ck->entry.data = d;
+ packed_rrset_ptr_fixup(d);
+ /* make TTLs relative - once per rrset */
+ for(i=0; i<d->count + d->rrsig_count; i++) {
+ if(d->rr_ttl[i] < now)
+ d->rr_ttl[i] = 0;
+ else d->rr_ttl[i] -= now;
+ }
+ if(d->ttl < now)
+ d->ttl = 0;
+ else d->ttl -= now;
+ return ck;
+}
+
+struct ub_packed_rrset_key*
+packed_rrset_copy_alloc(struct ub_packed_rrset_key* key,
+ struct alloc_cache* alloc, time_t now)
+{
+ struct packed_rrset_data* fd, *dd;
+ struct ub_packed_rrset_key* dk = alloc_special_obtain(alloc);
+ if(!dk) return NULL;
+ fd = (struct packed_rrset_data*)key->entry.data;
+ dk->entry.hash = key->entry.hash;
+ dk->rk = key->rk;
+ dk->rk.dname = (uint8_t*)memdup(key->rk.dname, key->rk.dname_len);
+ if(!dk->rk.dname) {
+ alloc_special_release(alloc, dk);
+ return NULL;
+ }
+ dd = (struct packed_rrset_data*)memdup(fd, packed_rrset_sizeof(fd));
+ if(!dd) {
+ free(dk->rk.dname);
+ alloc_special_release(alloc, dk);
+ return NULL;
+ }
+ packed_rrset_ptr_fixup(dd);
+ dk->entry.data = (void*)dd;
+ packed_rrset_ttl_add(dd, now);
+ return dk;
+}
diff --git a/external/unbound/util/data/packed_rrset.h b/external/unbound/util/data/packed_rrset.h
new file mode 100644
index 000000000..5d7990a2b
--- /dev/null
+++ b/external/unbound/util/data/packed_rrset.h
@@ -0,0 +1,428 @@
+/*
+ * util/data/packed_rrset.h - data storage for a set of resource records.
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the data storage for RRsets.
+ */
+
+#ifndef UTIL_DATA_PACKED_RRSET_H
+#define UTIL_DATA_PACKED_RRSET_H
+#include "util/storage/lruhash.h"
+struct alloc_cache;
+struct regional;
+
+/** type used to uniquely identify rrsets. Cannot be reused without
+ * clearing the cache. */
+typedef uint64_t rrset_id_t;
+
+/** this rrset is NSEC and is at zone apex (at child side of zonecut) */
+#define PACKED_RRSET_NSEC_AT_APEX 0x1
+/** this rrset is A/AAAA and is in-zone-glue (from parent side of zonecut) */
+#define PACKED_RRSET_PARENT_SIDE 0x2
+/** this rrset is SOA and has the negative ttl (from nxdomain or nodata),
+ * this is set on SOA rrsets in the authority section, to keep its TTL separate
+ * from the SOA in the answer section from a direct SOA query or ANY query. */
+#define PACKED_RRSET_SOA_NEG 0x4
+
+/**
+ * The identifying information for an RRset.
+ */
+struct packed_rrset_key {
+ /**
+ * The domain name. If not null (for id=0) it is allocated, and
+ * contains the wireformat domain name.
+ * This dname is not canonicalized.
+ */
+ uint8_t* dname;
+ /**
+ * Length of the domain name, including last 0 root octet.
+ */
+ size_t dname_len;
+ /**
+ * Flags. 32bit to be easy for hashing:
+ * o PACKED_RRSET_NSEC_AT_APEX
+ * o PACKED_RRSET_PARENT_SIDE
+ * o PACKED_RRSET_SOA_NEG
+ */
+ uint32_t flags;
+ /** the rrset type in network format */
+ uint16_t type;
+ /** the rrset class in network format */
+ uint16_t rrset_class;
+};
+
+/**
+ * This structure contains an RRset. A set of resource records that
+ * share the same domain name, type and class.
+ *
+ * Due to memory management and threading, the key structure cannot be
+ * deleted, although the data can be. The id can be set to 0 to store and the
+ * structure can be recycled with a new id.
+ */
+struct ub_packed_rrset_key {
+ /**
+ * entry into hashtable. Note the lock is never destroyed,
+ * even when this key is retired to the cache.
+ * the data pointer (if not null) points to a struct packed_rrset.
+ */
+ struct lruhash_entry entry;
+ /**
+ * the ID of this rrset. unique, based on threadid + sequenceno.
+ * ids are not reused, except after flushing the cache.
+ * zero is an unused entry, and never a valid id.
+ * Check this value after getting entry.lock.
+ * The other values in this struct may only be altered after changing
+ * the id (which needs a writelock on entry.lock).
+ */
+ rrset_id_t id;
+ /** key data: dname, type and class */
+ struct packed_rrset_key rk;
+};
+
+/**
+ * RRset trustworthiness. Bigger value is more trust. RFC 2181.
+ * The rrset_trust_add_noAA, rrset_trust_auth_noAA, rrset_trust_add_AA,
+ * are mentioned as the same trustworthiness in 2181, but split up here
+ * for ease of processing.
+ *
+ * rrset_trust_nonauth_ans_AA, rrset_trust_ans_noAA
+ * are also mentioned as the same trustworthiness in 2181, but split up here
+ * for ease of processing.
+ *
+ * Added trust_none for a sane initial value, smaller than anything else.
+ * Added validated and ultimate trust for keys and rrsig validated content.
+ */
+enum rrset_trust {
+ /** initial value for trust */
+ rrset_trust_none = 0,
+ /** Additional information from non-authoritative answers */
+ rrset_trust_add_noAA,
+ /** Data from the authority section of a non-authoritative answer */
+ rrset_trust_auth_noAA,
+ /** Additional information from an authoritative answer */
+ rrset_trust_add_AA,
+ /** non-authoritative data from the answer section of authoritative
+ * answers */
+ rrset_trust_nonauth_ans_AA,
+ /** Data from the answer section of a non-authoritative answer */
+ rrset_trust_ans_noAA,
+ /** Glue from a primary zone, or glue from a zone transfer */
+ rrset_trust_glue,
+ /** Data from the authority section of an authoritative answer */
+ rrset_trust_auth_AA,
+ /** The authoritative data included in the answer section of an
+ * authoritative reply */
+ rrset_trust_ans_AA,
+ /** Data from a zone transfer, other than glue */
+ rrset_trust_sec_noglue,
+ /** Data from a primary zone file, other than glue data */
+ rrset_trust_prim_noglue,
+ /** DNSSEC(rfc4034) validated with trusted keys */
+ rrset_trust_validated,
+ /** ultimately trusted, no more trust is possible;
+ * trusted keys from the unbound configuration setup. */
+ rrset_trust_ultimate
+};
+
+/**
+ * Security status from validation for data.
+ * The order is significant; more secure, more proven later.
+ */
+enum sec_status {
+ /** UNCHECKED means that object has yet to be validated. */
+ sec_status_unchecked = 0,
+ /** BOGUS means that the object (RRset or message) failed to validate
+ * (according to local policy), but should have validated. */
+ sec_status_bogus,
+ /** INDETERMINATE means that the object is insecure, but not
+ * authoritatively so. Generally this means that the RRset is not
+ * below a configured trust anchor. */
+ sec_status_indeterminate,
+ /** INSECURE means that the object is authoritatively known to be
+ * insecure. Generally this means that this RRset is below a trust
+ * anchor, but also below a verified, insecure delegation. */
+ sec_status_insecure,
+ /** SECURE means that the object (RRset or message) validated
+ * according to local policy. */
+ sec_status_secure
+};
+
+/**
+ * RRset data.
+ *
+ * The data is packed, stored contiguously in memory.
+ * memory layout:
+ * o base struct
+ * o rr_len size_t array
+ * o rr_data uint8_t* array
+ * o rr_ttl time_t array (after size_t and ptrs because those may be
+ * 64bit and this array before those would make them unaligned).
+ * Since the stuff before is 32/64bit, rr_ttl is 32 bit aligned.
+ * o rr_data rdata wireformats
+ * o rrsig_data rdata wireformat(s)
+ *
+ * Rdata is stored in wireformat. The dname is stored in wireformat.
+ * TTLs are stored as absolute values (and could be expired).
+ *
+ * RRSIGs are stored in the arrays after the regular rrs.
+ *
+ * You need the packed_rrset_key to know dname, type, class of the
+ * resource records in this RRset. (if signed the rrsig gives the type too).
+ *
+ * On the wire an RR is:
+ * name, type, class, ttl, rdlength, rdata.
+ * So we need to send the following per RR:
+ * key.dname, ttl, rr_data[i].
+ * since key.dname ends with type and class.
+ * and rr_data starts with the rdlength.
+ * the ttl value to send changes due to time.
+ */
+struct packed_rrset_data {
+ /** TTL (in seconds like time()) of the rrset.
+ * Same for all RRs see rfc2181(5.2). */
+ time_t ttl;
+ /** number of rrs. */
+ size_t count;
+ /** number of rrsigs, if 0 no rrsigs */
+ size_t rrsig_count;
+ /** the trustworthiness of the rrset data */
+ enum rrset_trust trust;
+ /** security status of the rrset data */
+ enum sec_status security;
+ /** length of every rr's rdata, rr_len[i] is size of rr_data[i]. */
+ size_t* rr_len;
+ /** ttl of every rr. rr_ttl[i] ttl of rr i. */
+ time_t *rr_ttl;
+ /**
+ * Array of pointers to every rr's rdata.
+ * The rr_data[i] rdata is stored in uncompressed wireformat.
+ * The first uint16_t of rr_data[i] is network format rdlength.
+ *
+ * rr_data[count] to rr_data[count+rrsig_count] contain the rrsig data.
+ */
+ uint8_t** rr_data;
+};
+
+/**
+ * An RRset can be represented using both key and data together.
+ * Split into key and data structures to simplify implementation of
+ * caching schemes.
+ */
+struct packed_rrset {
+ /** domain name, type and class */
+ struct packed_rrset_key* k;
+ /** ttl, count and rdatas (and rrsig) */
+ struct packed_rrset_data* d;
+};
+
+/**
+ * list of packed rrsets
+ */
+struct packed_rrset_list {
+ /** next in list */
+ struct packed_rrset_list* next;
+ /** rrset key and data */
+ struct packed_rrset rrset;
+};
+
+/**
+ * Delete packed rrset key and data, not entered in hashtables yet.
+ * Used during parsing.
+ * @param pkey: rrset key structure with locks, key and data pointers.
+ * @param alloc: where to return the unfree-able key structure.
+ */
+void ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
+ struct alloc_cache* alloc);
+
+/**
+ * Memory size of rrset data. RRset data must be filled in correctly.
+ * @param data: data to examine.
+ * @return size in bytes.
+ */
+size_t packed_rrset_sizeof(struct packed_rrset_data* data);
+
+/**
+ * Get TTL of rrset. RRset data must be filled in correctly.
+ * @param key: rrset key, with data to examine.
+ * @return ttl value.
+ */
+time_t ub_packed_rrset_ttl(struct ub_packed_rrset_key* key);
+
+/**
+ * Calculate memory size of rrset entry. For hash table usage.
+ * @param key: struct ub_packed_rrset_key*.
+ * @param data: struct packed_rrset_data*.
+ * @return size in bytes.
+ */
+size_t ub_rrset_sizefunc(void* key, void* data);
+
+/**
+ * compares two rrset keys.
+ * @param k1: struct ub_packed_rrset_key*.
+ * @param k2: struct ub_packed_rrset_key*.
+ * @return 0 if equal.
+ */
+int ub_rrset_compare(void* k1, void* k2);
+
+/**
+ * compare two rrset data structures.
+ * Compared rdata and rrsigdata, not the trust or ttl value.
+ * @param d1: data to compare.
+ * @param d2: data to compare.
+ * @return 1 if equal.
+ */
+int rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2);
+
+/**
+ * Old key to be deleted. RRset keys are recycled via alloc.
+ * The id is set to 0. So that other threads, after acquiring a lock always
+ * get the correct value, in this case the 0 deleted-special value.
+ * @param key: struct ub_packed_rrset_key*.
+ * @param userdata: alloc structure to use for recycling.
+ */
+void ub_rrset_key_delete(void* key, void* userdata);
+
+/**
+ * Old data to be deleted.
+ * @param data: what to delete.
+ * @param userdata: user data ptr.
+ */
+void rrset_data_delete(void* data, void* userdata);
+
+/**
+ * Calculate hash value for a packed rrset key.
+ * @param key: the rrset key with name, type, class, flags.
+ * @return hash value.
+ */
+hashvalue_t rrset_key_hash(struct packed_rrset_key* key);
+
+/**
+ * Fixup pointers in fixed data packed_rrset_data blob.
+ * After a memcpy of the data for example. Will set internal pointers right.
+ * @param data: rrset data structure. Otherwise correctly filled in.
+ */
+void packed_rrset_ptr_fixup(struct packed_rrset_data* data);
+
+/**
+ * Fixup TTLs in fixed data packed_rrset_data blob.
+ * @param data: rrset data structure. Otherwise correctly filled in.
+ * @param add: how many seconds to add, pass time(0) for example.
+ */
+void packed_rrset_ttl_add(struct packed_rrset_data* data, time_t add);
+
+/**
+ * Utility procedure to extract CNAME target name from its rdata.
+ * Failsafes; it will change passed dname to a valid dname or do nothing.
+ * @param rrset: the rrset structure. Must be a CNAME.
+ * Only first RR is used (multiple RRs are technically illegal anyway).
+ * Also works on type DNAME. Returns target name.
+ * @param dname: this pointer is updated to point into the cname rdata.
+ * If a failsafe fails, nothing happens to the pointer (such as the
+ * rdata was not a valid dname, not a CNAME, ...).
+ * @param dname_len: length of dname is returned.
+ */
+void get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname,
+ size_t* dname_len);
+
+/**
+ * Get a printable string for a rrset trust value
+ * @param s: rrset trust value
+ * @return printable string.
+ */
+const char* rrset_trust_to_string(enum rrset_trust s);
+
+/**
+ * Get a printable string for a security status value
+ * @param s: security status
+ * @return printable string.
+ */
+const char* sec_status_to_string(enum sec_status s);
+
+/**
+ * Print string with neat domain name, type, class from rrset.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param rrset: structure with name, type and class.
+ */
+void log_rrset_key(enum verbosity_value v, const char* str,
+ struct ub_packed_rrset_key* rrset);
+
+/**
+ * Convert RR from RRset to string.
+ * @param rrset: structure with data.
+ * @param i: index of rr or RRSIG.
+ * @param now: time that is subtracted from ttl before printout. Can be 0.
+ * @param dest: destination string buffer. Must be nonNULL.
+ * @param dest_len: length of dest buffer (>0).
+ * @return false on failure.
+ */
+int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
+ time_t now, char* dest, size_t dest_len);
+
+/**
+ * Print the string with prefix, one rr per line.
+ * @param v: at what verbosity level to print this.
+ * @param str: string of message.
+ * @param rrset: with name, and rdata, and rrsigs.
+ */
+void log_packed_rrset(enum verbosity_value v, const char* str,
+ struct ub_packed_rrset_key* rrset);
+
+/**
+ * Allocate rrset in region - no more locks needed
+ * @param key: a (just from rrset cache looked up) rrset key + valid,
+ * packed data record.
+ * @param region: where to alloc the copy
+ * @param now: adjust the TTLs to be relative (subtract from all TTLs).
+ * @return new region-alloced rrset key or NULL on alloc failure.
+ */
+struct ub_packed_rrset_key* packed_rrset_copy_region(
+ struct ub_packed_rrset_key* key, struct regional* region,
+ time_t now);
+
+/**
+ * Allocate rrset with malloc (from region or you are holding the lock).
+ * @param key: key with data entry.
+ * @param alloc: alloc_cache to create rrset_keys
+ * @param now: adjust the TTLs to be absolute (add to all TTLs).
+ * @return new region-alloced rrset key or NULL on alloc failure.
+ */
+struct ub_packed_rrset_key* packed_rrset_copy_alloc(
+ struct ub_packed_rrset_key* key, struct alloc_cache* alloc,
+ time_t now);
+
+#endif /* UTIL_DATA_PACKED_RRSET_H */