#include "services/outbound_list.h"
#include "services/cache/rrset.h"
#include "util/data/msgparse.h"
+#include "util/data/msgencode.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
- normalize incoming messages. Like unbound-java, with CNAME chain
checked, DNAME checked, CNAME's synthesized, glue checked.
- sanitize incoming messages.
+ - split msgreply encode functions into own file msgencode.c.
31 May 2007: Wouter
- querytargets state.
#include "util/net_help.h"
#include "util/region-allocator.h"
#include "util/data/dname.h"
+#include "util/data/msgencode.h"
/** iterator init */
static int
#include "services/cache/infra.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
+#include "util/data/msgencode.h"
#include "util/netevent.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/net_help.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
+#include "util/data/msgencode.h"
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "testcode/replay.h"
#include "testcode/unitmain.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
+#include "util/data/msgencode.h"
#include "util/alloc.h"
#include "util/region-allocator.h"
#include "util/net_help.h"
--- /dev/null
+/*
+ * 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 REGENTS 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/region-allocator.h"
+#include "util/net_help.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.
+ * @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)
+{
+ int c, n, closen=0;
+ struct compress_tree_node* p = tree;
+ struct compress_tree_node* close = 0;
+ while(p) {
+ if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
+ == 0) {
+ *matchlabels = n;
+ *match = p;
+ return 1;
+ }
+ if(c<0) p = p->left;
+ else {
+ closen = n;
+ close = p; /* p->dname is smaller than dname */
+ p = p->right;
+ }
+ }
+ *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.
+ * @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* p;
+ int m;
+ if(labs <= 1)
+ return 0; /* do not compress root node */
+ if(compress_tree_search(tree, dname, labs, &p, &m)) {
+ /* 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;
+}
+
+/**
+ * Insert node into domain name compression tree.
+ * @param tree: root of tree (may be modified)
+ * @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_insert(struct compress_tree_node** tree, uint8_t* dname,
+ int labs, size_t offset, region_type* region)
+{
+ int c, m;
+ struct compress_tree_node* p, **prev;
+ struct compress_tree_node* n = (struct compress_tree_node*)
+ region_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;
+
+ /* find spot to insert it into */
+ prev = tree;
+ p = *tree;
+ while(p) {
+ c = dname_lab_cmp(dname, labs, p->dname, p->labs, &m);
+ log_assert(c != 0); /* may not already be in tree */
+ if(c==0) return p; /* insert only once */
+ if(c<0) {
+ prev = &p->left;
+ p = p->left;
+ } else {
+ prev = &p->right;
+ p = p->right;
+ }
+ }
+ *prev = n;
+ return n;
+}
+
+/**
+ * Store domain name and ancestors into compression tree.
+ * @param tree: root of tree (may be modified)
+ * @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.
+ * @return: 0 on memory error.
+ */
+static int
+compress_tree_store(struct compress_tree_node** tree, uint8_t* dname,
+ int labs, size_t offset, region_type* region,
+ struct compress_tree_node* closest)
+{
+ uint8_t lablen;
+ struct compress_tree_node** lastparentptr = 0;
+ struct compress_tree_node* newnode;
+ int uplabs = labs-1; /* does not store root in tree */
+ if(closest) uplabs = labs - closest->labs;
+ log_assert(uplabs >= 0);
+ while(uplabs--) {
+ if(offset > PTR_MAX_OFFSET) {
+ if(lastparentptr)
+ *lastparentptr = closest;
+ return 1; /* compression pointer no longer useful */
+ }
+ /* store dname, labs, offset */
+ if(!(newnode = compress_tree_insert(tree, dname, labs, offset,
+ region))) {
+ if(lastparentptr)
+ *lastparentptr = closest;
+ return 0;
+ }
+ if(lastparentptr)
+ *lastparentptr = newnode;
+ lastparentptr = &newnode->parent;
+
+ /* next label */
+ lablen = *dname++;
+ dname += lablen;
+ offset += lablen+1;
+ labs--;
+ }
+ if(lastparentptr)
+ *lastparentptr = closest;
+ return 1;
+}
+
+/** compress a domain name */
+static int
+write_compressed_dname(ldns_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(ldns_buffer_remaining(pkt) < 1)
+ return 0;
+ ldns_buffer_write_u8(pkt, 0);
+ return 1;
+ }
+
+ /* copy the first couple of labels */
+ while(labcopy--) {
+ lablen = *dname++;
+ if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
+ return 0;
+ ldns_buffer_write_u8(pkt, lablen);
+ ldns_buffer_write(pkt, dname, lablen);
+ dname += lablen;
+ }
+ /* insert compression ptr */
+ if(ldns_buffer_remaining(pkt) < 2)
+ return 0;
+ ptr = PTR_CREATE(p->offset);
+ ldns_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, ldns_buffer* pkt,
+ region_type* region, struct compress_tree_node** tree,
+ size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
+{
+ struct compress_tree_node* p;
+ if(!*owner_ptr) {
+ /* compress first time dname */
+ if((p = compress_tree_lookup(*tree, key->rk.dname,
+ owner_labs))) {
+ 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(ldns_buffer_remaining(pkt) < 4+4+2)
+ return RETVAL_TRUNC;
+ } else {
+ /* no compress */
+ if(ldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
+ return RETVAL_TRUNC;
+ ldns_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(tree, key->rk.dname,
+ owner_labs, owner_pos, region, p))
+ return RETVAL_OUTMEM;
+ } else {
+ /* always compress 2nd-further RRs in RRset */
+ if(owner_labs == 1) {
+ if(ldns_buffer_remaining(pkt) < 1+4+4+2)
+ return RETVAL_TRUNC;
+ ldns_buffer_write_u8(pkt, 0);
+ } else {
+ if(ldns_buffer_remaining(pkt) < 2+4+4+2)
+ return RETVAL_TRUNC;
+ ldns_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, ldns_buffer* pkt, int labs,
+ region_type* region, struct compress_tree_node** tree)
+{
+ struct compress_tree_node* p;
+ size_t pos = ldns_buffer_position(pkt);
+ if((p = compress_tree_lookup(*tree, dname, labs))) {
+ 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(tree, dname, labs, pos, region, p))
+ return RETVAL_OUTMEM;
+ return RETVAL_OK;
+}
+
+/** return true if type needs domain name compression in rdata */
+static const ldns_rr_descriptor*
+type_rdata_compressable(struct ub_packed_rrset_key* key)
+{
+ uint16_t t = ntohs(key->rk.type);
+ if(ldns_rr_descript(t) &&
+ ldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
+ return ldns_rr_descript(t);
+ return 0;
+}
+
+/** compress domain names in rdata, return RETVAL_* */
+static int
+compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen,
+ region_type* region, struct compress_tree_node** tree,
+ const ldns_rr_descriptor* desc)
+{
+ int labs, r, rdf = 0;
+ size_t dname_len, len, pos = ldns_buffer_position(pkt);
+ uint8_t count = desc->_dname_count;
+
+ ldns_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(ldns_buffer_remaining(pkt) < len)
+ return RETVAL_TRUNC;
+ ldns_buffer_write(pkt, rdata, len);
+ todolen -= len;
+ rdata += len;
+ }
+ rdf++;
+ }
+ /* copy remainder */
+ if(todolen > 0) {
+ if(ldns_buffer_remaining(pkt) < todolen)
+ return RETVAL_TRUNC;
+ ldns_buffer_write(pkt, rdata, todolen);
+ }
+
+ /* set rdata len */
+ ldns_buffer_write_u16_at(pkt, pos, ldns_buffer_position(pkt)-pos-2);
+ return RETVAL_OK;
+}
+
+/** store rrset in buffer in wireformat, return RETVAL_* */
+static int
+packed_rrset_encode(struct ub_packed_rrset_key* key, ldns_buffer* pkt,
+ uint16_t* num_rrs, uint32_t timenow, region_type* region,
+ int do_data, int do_sig, struct compress_tree_node** tree)
+{
+ size_t i, owner_pos;
+ int r, owner_labs;
+ uint16_t owner_ptr = 0;
+ struct packed_rrset_data* data = (struct packed_rrset_data*)
+ key->entry.data;
+
+ owner_labs = dname_count_labels(key->rk.dname);
+ owner_pos = ldns_buffer_position(pkt);
+
+ if(do_data) {
+ const ldns_rr_descriptor* c = type_rdata_compressable(key);
+ for(i=0; i<data->count; i++) {
+ if((r=compress_owner(key, pkt, region, tree,
+ owner_pos, &owner_ptr, owner_labs))
+ != RETVAL_OK)
+ return r;
+ ldns_buffer_write(pkt, &key->rk.type, 2);
+ ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+ ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
+ if(c) {
+ if((r=compress_rdata(pkt, data->rr_data[i],
+ data->rr_len[i], region, tree, c))
+ != RETVAL_OK)
+ return r;
+ } else {
+ if(ldns_buffer_remaining(pkt) < data->rr_len[i])
+ return RETVAL_TRUNC;
+ ldns_buffer_write(pkt, data->rr_data[i],
+ data->rr_len[i]);
+ }
+ }
+ }
+ /* insert rrsigs */
+ if(do_sig) {
+ size_t total = data->count+data->rrsig_count;
+ for(i=data->count; i<total; i++) {
+ if(owner_ptr && owner_labs != 1) {
+ if(ldns_buffer_remaining(pkt) <
+ 2+4+4+data->rr_len[i])
+ return RETVAL_TRUNC;
+ ldns_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(ldns_buffer_remaining(pkt) <
+ 4+4+data->rr_len[i])
+ return RETVAL_TRUNC;
+ }
+ ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
+ ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
+ ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
+ /* rrsig rdata cannot be compressed, perform 100+ byte
+ * memcopy. */
+ ldns_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)
+ *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,
+ ldns_buffer* pkt, size_t rrsets_before, uint32_t timenow,
+ region_type* region, int addit, struct compress_tree_node** tree)
+{
+ int r;
+ size_t i, setstart;
+ *num_rrs = 0;
+ if(!addit) {
+ for(i=0; i<num_rrsets; i++) {
+ setstart = ldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 1, 1, tree))
+ != RETVAL_OK) {
+ /* Bad, but if due to size must set TC bit */
+ /* trim off the rrset neatly. */
+ ldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ } else {
+ for(i=0; i<num_rrsets; i++) {
+ setstart = ldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 1, 0, tree))
+ != RETVAL_OK) {
+ ldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ for(i=0; i<num_rrsets; i++) {
+ setstart = ldns_buffer_position(pkt);
+ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
+ pkt, num_rrs, timenow, region, 0, 1, tree))
+ != RETVAL_OK) {
+ ldns_buffer_set_position(pkt, setstart);
+ return r;
+ }
+ }
+ }
+ return RETVAL_OK;
+}
+
+int reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
+ uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow,
+ region_type* region, uint16_t udpsize)
+{
+ uint16_t ancount=0, nscount=0, arcount=0;
+ struct compress_tree_node* tree = 0;
+ int r;
+
+ ldns_buffer_clear(buffer);
+ if(udpsize < ldns_buffer_limit(buffer))
+ ldns_buffer_set_limit(buffer, udpsize);
+ if(ldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
+ return 0;
+
+ ldns_buffer_write(buffer, &id, sizeof(uint16_t));
+ ldns_buffer_write_u16(buffer, flags);
+ ldns_buffer_write_u16(buffer, rep->qdcount);
+ /* set an, ns, ar counts to zero in case of small packets */
+ ldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
+
+ /* insert query section */
+ if(rep->qdcount) {
+ if(ldns_buffer_remaining(buffer) <
+ qinfo->qname_len+sizeof(uint16_t)*2)
+ return 0; /* buffer too small */
+ if(!compress_tree_store(&tree, qinfo->qname,
+ dname_count_labels(qinfo->qname),
+ ldns_buffer_position(buffer), region, NULL))
+ return 0;
+ ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
+ ldns_buffer_write_u16(buffer, qinfo->qtype);
+ ldns_buffer_write_u16(buffer, qinfo->qclass);
+ }
+
+ /* insert answer section */
+ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
+ 0, timenow, region, 0, &tree)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* create truncated message */
+ ldns_buffer_write_u16_at(buffer, 6, ancount);
+ LDNS_TC_SET(ldns_buffer_begin(buffer));
+ ldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ ldns_buffer_write_u16_at(buffer, 6, ancount);
+
+ /* insert auth section */
+ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
+ rep->an_numrrsets, timenow, region, 0, &tree)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* create truncated message */
+ ldns_buffer_write_u16_at(buffer, 8, nscount);
+ LDNS_TC_SET(ldns_buffer_begin(buffer));
+ ldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ ldns_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,
+ 1, &tree)) != RETVAL_OK) {
+ if(r == RETVAL_TRUNC) {
+ /* no need to set TC bit, this is the additional */
+ ldns_buffer_write_u16_at(buffer, 10, arcount);
+ ldns_buffer_flip(buffer);
+ return 1;
+ }
+ return 0;
+ }
+ ldns_buffer_write_u16_at(buffer, 10, arcount);
+ ldns_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(ldns_buffer* pkt, struct edns_data* edns)
+{
+ size_t len;
+ if(!edns || !edns->edns_present)
+ return;
+ /* inc additional count */
+ ldns_buffer_write_u16_at(pkt, 10,
+ ldns_buffer_read_u16_at(pkt, 10) + 1);
+ len = ldns_buffer_limit(pkt);
+ ldns_buffer_clear(pkt);
+ ldns_buffer_set_position(pkt, len);
+ /* write EDNS record */
+ ldns_buffer_write_u8(pkt, 0); /* '.' label */
+ ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
+ ldns_buffer_write_u16(pkt, edns->udp_size); /* class */
+ ldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
+ ldns_buffer_write_u8(pkt, edns->edns_version);
+ ldns_buffer_write_u16(pkt, edns->bits);
+ ldns_buffer_write_u16(pkt, 0); /* rdatalen */
+ ldns_buffer_flip(pkt);
+}
+
+int
+reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
+ uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
+ int cached, struct region* region, uint16_t udpsize,
+ struct edns_data* edns)
+{
+ uint16_t flags;
+
+ if(!cached) {
+ /* original flags, copy RD bit from query. */
+ flags = rep->flags | (qflags & BIT_RD);
+ } else {
+ /* remove AA bit, copy RD and CD bits from query. */
+ flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
+ }
+ log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
+ if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns))
+ return 0; /* packet too small to contain edns... */
+ udpsize -= calc_edns_field_size(edns);
+ if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
+ udpsize)) {
+ log_err("reply encode: out of memory");
+ return 0;
+ }
+ attach_edns_record(pkt, edns);
+ return 1;
+}
+
+void
+qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo)
+{
+ uint16_t flags = 0; /* QUERY, NOERROR */
+ ldns_buffer_clear(pkt);
+ log_assert(ldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
+ ldns_buffer_skip(pkt, 2); /* id done later */
+ ldns_buffer_write_u16(pkt, flags);
+ ldns_buffer_write_u16(pkt, 1); /* query count */
+ ldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
+ ldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
+ ldns_buffer_write_u16(pkt, qinfo->qtype);
+ ldns_buffer_write_u16(pkt, qinfo->qclass);
+ ldns_buffer_flip(pkt);
+}
--- /dev/null
+/*
+ * 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 REGENTS 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 query_info;
+struct reply_info;
+struct region;
+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.
+ * @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, ldns_buffer* dest, uint32_t timenow,
+ int cached, struct region* region, uint16_t udpsize,
+ struct edns_data* edns);
+
+/**
+ * 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.
+ * @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, ldns_buffer* buffer, uint32_t timenow,
+ struct region* region, uint16_t udpsize);
+
+/**
+ * Encode query packet. Assumes the buffer is large enough.
+ * @param pkt: where to store the packet.
+ * @param qinfo: query info.
+ */
+void qinfo_query_encode(ldns_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(ldns_buffer* pkt, struct edns_data* edns);
+
+
+#endif /* UTIL_DATA_MSGENCODE_H */
#include "util/region-allocator.h"
#include "util/data/msgparse.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
-
/** allocate qinfo, return 0 on error. */
static int
parse_create_qinfo(ldns_buffer* pkt, struct msg_parse* msg,
return h;
}
-/**
- * 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.
- * @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)
-{
- int c, n, closen=0;
- struct compress_tree_node* p = tree;
- struct compress_tree_node* close = 0;
- while(p) {
- if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
- == 0) {
- *matchlabels = n;
- *match = p;
- return 1;
- }
- if(c<0) p = p->left;
- else {
- closen = n;
- close = p; /* p->dname is smaller than dname */
- p = p->right;
- }
- }
- *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.
- * @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* p;
- int m;
- if(labs <= 1)
- return 0; /* do not compress root node */
- if(compress_tree_search(tree, dname, labs, &p, &m)) {
- /* 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;
-}
-
-/**
- * Insert node into domain name compression tree.
- * @param tree: root of tree (may be modified)
- * @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_insert(struct compress_tree_node** tree, uint8_t* dname,
- int labs, size_t offset, region_type* region)
-{
- int c, m;
- struct compress_tree_node* p, **prev;
- struct compress_tree_node* n = (struct compress_tree_node*)
- region_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;
-
- /* find spot to insert it into */
- prev = tree;
- p = *tree;
- while(p) {
- c = dname_lab_cmp(dname, labs, p->dname, p->labs, &m);
- log_assert(c != 0); /* may not already be in tree */
- if(c==0) return p; /* insert only once */
- if(c<0) {
- prev = &p->left;
- p = p->left;
- } else {
- prev = &p->right;
- p = p->right;
- }
- }
- *prev = n;
- return n;
-}
-
-/**
- * Store domain name and ancestors into compression tree.
- * @param tree: root of tree (may be modified)
- * @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.
- * @return: 0 on memory error.
- */
-static int
-compress_tree_store(struct compress_tree_node** tree, uint8_t* dname,
- int labs, size_t offset, region_type* region,
- struct compress_tree_node* closest)
-{
- uint8_t lablen;
- struct compress_tree_node** lastparentptr = 0;
- struct compress_tree_node* newnode;
- int uplabs = labs-1; /* does not store root in tree */
- if(closest) uplabs = labs - closest->labs;
- log_assert(uplabs >= 0);
- while(uplabs--) {
- if(offset > PTR_MAX_OFFSET) {
- if(lastparentptr)
- *lastparentptr = closest;
- return 1; /* compression pointer no longer useful */
- }
- /* store dname, labs, offset */
- if(!(newnode = compress_tree_insert(tree, dname, labs, offset,
- region))) {
- if(lastparentptr)
- *lastparentptr = closest;
- return 0;
- }
- if(lastparentptr)
- *lastparentptr = newnode;
- lastparentptr = &newnode->parent;
-
- /* next label */
- lablen = *dname++;
- dname += lablen;
- offset += lablen+1;
- labs--;
- }
- if(lastparentptr)
- *lastparentptr = closest;
- return 1;
-}
-
-/** compress a domain name */
-static int
-write_compressed_dname(ldns_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(ldns_buffer_remaining(pkt) < 1)
- return 0;
- ldns_buffer_write_u8(pkt, 0);
- return 1;
- }
-
- /* copy the first couple of labels */
- while(labcopy--) {
- lablen = *dname++;
- if(ldns_buffer_remaining(pkt) < (size_t)lablen+1)
- return 0;
- ldns_buffer_write_u8(pkt, lablen);
- ldns_buffer_write(pkt, dname, lablen);
- dname += lablen;
- }
- /* insert compression ptr */
- if(ldns_buffer_remaining(pkt) < 2)
- return 0;
- ptr = PTR_CREATE(p->offset);
- ldns_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, ldns_buffer* pkt,
- region_type* region, struct compress_tree_node** tree,
- size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
-{
- struct compress_tree_node* p;
- if(!*owner_ptr) {
- /* compress first time dname */
- if((p = compress_tree_lookup(*tree, key->rk.dname,
- owner_labs))) {
- 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(ldns_buffer_remaining(pkt) < 4+4+2)
- return RETVAL_TRUNC;
- } else {
- /* no compress */
- if(ldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
- return RETVAL_TRUNC;
- ldns_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(tree, key->rk.dname,
- owner_labs, owner_pos, region, p))
- return RETVAL_OUTMEM;
- } else {
- /* always compress 2nd-further RRs in RRset */
- if(owner_labs == 1) {
- if(ldns_buffer_remaining(pkt) < 1+4+4+2)
- return RETVAL_TRUNC;
- ldns_buffer_write_u8(pkt, 0);
- } else {
- if(ldns_buffer_remaining(pkt) < 2+4+4+2)
- return RETVAL_TRUNC;
- ldns_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, ldns_buffer* pkt, int labs,
- region_type* region, struct compress_tree_node** tree)
-{
- struct compress_tree_node* p;
- size_t pos = ldns_buffer_position(pkt);
- if((p = compress_tree_lookup(*tree, dname, labs))) {
- 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(tree, dname, labs, pos, region, p))
- return RETVAL_OUTMEM;
- return RETVAL_OK;
-}
-
-/** return true if type needs domain name compression in rdata */
-static const ldns_rr_descriptor*
-type_rdata_compressable(struct ub_packed_rrset_key* key)
-{
- uint16_t t = ntohs(key->rk.type);
- if(ldns_rr_descript(t) &&
- ldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
- return ldns_rr_descript(t);
- return 0;
-}
-
-/** compress domain names in rdata, return RETVAL_* */
-static int
-compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen,
- region_type* region, struct compress_tree_node** tree,
- const ldns_rr_descriptor* desc)
-{
- int labs, r, rdf = 0;
- size_t dname_len, len, pos = ldns_buffer_position(pkt);
- uint8_t count = desc->_dname_count;
-
- ldns_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(ldns_buffer_remaining(pkt) < len)
- return RETVAL_TRUNC;
- ldns_buffer_write(pkt, rdata, len);
- todolen -= len;
- rdata += len;
- }
- rdf++;
- }
- /* copy remainder */
- if(todolen > 0) {
- if(ldns_buffer_remaining(pkt) < todolen)
- return RETVAL_TRUNC;
- ldns_buffer_write(pkt, rdata, todolen);
- }
-
- /* set rdata len */
- ldns_buffer_write_u16_at(pkt, pos, ldns_buffer_position(pkt)-pos-2);
- return RETVAL_OK;
-}
-
-/** store rrset in buffer in wireformat, return RETVAL_* */
-static int
-packed_rrset_encode(struct ub_packed_rrset_key* key, ldns_buffer* pkt,
- uint16_t* num_rrs, uint32_t timenow, region_type* region,
- int do_data, int do_sig, struct compress_tree_node** tree)
-{
- size_t i, owner_pos;
- int r, owner_labs;
- uint16_t owner_ptr = 0;
- struct packed_rrset_data* data = (struct packed_rrset_data*)
- key->entry.data;
-
- owner_labs = dname_count_labels(key->rk.dname);
- owner_pos = ldns_buffer_position(pkt);
-
- if(do_data) {
- const ldns_rr_descriptor* c = type_rdata_compressable(key);
- for(i=0; i<data->count; i++) {
- if((r=compress_owner(key, pkt, region, tree,
- owner_pos, &owner_ptr, owner_labs))
- != RETVAL_OK)
- return r;
- ldns_buffer_write(pkt, &key->rk.type, 2);
- ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
- ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
- if(c) {
- if((r=compress_rdata(pkt, data->rr_data[i],
- data->rr_len[i], region, tree, c))
- != RETVAL_OK)
- return r;
- } else {
- if(ldns_buffer_remaining(pkt) < data->rr_len[i])
- return RETVAL_TRUNC;
- ldns_buffer_write(pkt, data->rr_data[i],
- data->rr_len[i]);
- }
- }
- }
- /* insert rrsigs */
- if(do_sig) {
- size_t total = data->count+data->rrsig_count;
- for(i=data->count; i<total; i++) {
- if(owner_ptr && owner_labs != 1) {
- if(ldns_buffer_remaining(pkt) <
- 2+4+4+data->rr_len[i])
- return RETVAL_TRUNC;
- ldns_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(ldns_buffer_remaining(pkt) <
- 4+4+data->rr_len[i])
- return RETVAL_TRUNC;
- }
- ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
- ldns_buffer_write(pkt, &key->rk.rrset_class, 2);
- ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow);
- /* rrsig rdata cannot be compressed, perform 100+ byte
- * memcopy. */
- ldns_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)
- *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,
- ldns_buffer* pkt, size_t rrsets_before, uint32_t timenow,
- region_type* region, int addit, struct compress_tree_node** tree)
-{
- int r;
- size_t i, setstart;
- *num_rrs = 0;
- if(!addit) {
- for(i=0; i<num_rrsets; i++) {
- setstart = ldns_buffer_position(pkt);
- if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
- pkt, num_rrs, timenow, region, 1, 1, tree))
- != RETVAL_OK) {
- /* Bad, but if due to size must set TC bit */
- /* trim off the rrset neatly. */
- ldns_buffer_set_position(pkt, setstart);
- return r;
- }
- }
- } else {
- for(i=0; i<num_rrsets; i++) {
- setstart = ldns_buffer_position(pkt);
- if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
- pkt, num_rrs, timenow, region, 1, 0, tree))
- != RETVAL_OK) {
- ldns_buffer_set_position(pkt, setstart);
- return r;
- }
- }
- for(i=0; i<num_rrsets; i++) {
- setstart = ldns_buffer_position(pkt);
- if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
- pkt, num_rrs, timenow, region, 0, 1, tree))
- != RETVAL_OK) {
- ldns_buffer_set_position(pkt, setstart);
- return r;
- }
- }
- }
- return RETVAL_OK;
-}
-
-int reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
- uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow,
- region_type* region, uint16_t udpsize)
-{
- uint16_t ancount=0, nscount=0, arcount=0;
- struct compress_tree_node* tree = 0;
- int r;
-
- ldns_buffer_clear(buffer);
- if(udpsize < ldns_buffer_limit(buffer))
- ldns_buffer_set_limit(buffer, udpsize);
- if(ldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
- return 0;
-
- ldns_buffer_write(buffer, &id, sizeof(uint16_t));
- ldns_buffer_write_u16(buffer, flags);
- ldns_buffer_write_u16(buffer, rep->qdcount);
- /* set an, ns, ar counts to zero in case of small packets */
- ldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
-
- /* insert query section */
- if(rep->qdcount) {
- if(ldns_buffer_remaining(buffer) <
- qinfo->qname_len+sizeof(uint16_t)*2)
- return 0; /* buffer too small */
- if(!compress_tree_store(&tree, qinfo->qname,
- dname_count_labels(qinfo->qname),
- ldns_buffer_position(buffer), region, NULL))
- return 0;
- ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len);
- ldns_buffer_write_u16(buffer, qinfo->qtype);
- ldns_buffer_write_u16(buffer, qinfo->qclass);
- }
-
- /* insert answer section */
- if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
- 0, timenow, region, 0, &tree)) != RETVAL_OK) {
- if(r == RETVAL_TRUNC) {
- /* create truncated message */
- ldns_buffer_write_u16_at(buffer, 6, ancount);
- LDNS_TC_SET(ldns_buffer_begin(buffer));
- ldns_buffer_flip(buffer);
- return 1;
- }
- return 0;
- }
- ldns_buffer_write_u16_at(buffer, 6, ancount);
-
- /* insert auth section */
- if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
- rep->an_numrrsets, timenow, region, 0, &tree)) != RETVAL_OK) {
- if(r == RETVAL_TRUNC) {
- /* create truncated message */
- ldns_buffer_write_u16_at(buffer, 8, nscount);
- LDNS_TC_SET(ldns_buffer_begin(buffer));
- ldns_buffer_flip(buffer);
- return 1;
- }
- return 0;
- }
- ldns_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,
- 1, &tree)) != RETVAL_OK) {
- if(r == RETVAL_TRUNC) {
- /* no need to set TC bit, this is the additional */
- ldns_buffer_write_u16_at(buffer, 10, arcount);
- ldns_buffer_flip(buffer);
- return 1;
- }
- return 0;
- }
- ldns_buffer_write_u16_at(buffer, 10, arcount);
- ldns_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(ldns_buffer* pkt, struct edns_data* edns)
-{
- size_t len;
- if(!edns || !edns->edns_present)
- return;
- /* inc additional count */
- ldns_buffer_write_u16_at(pkt, 10,
- ldns_buffer_read_u16_at(pkt, 10) + 1);
- len = ldns_buffer_limit(pkt);
- ldns_buffer_clear(pkt);
- ldns_buffer_set_position(pkt, len);
- /* write EDNS record */
- ldns_buffer_write_u8(pkt, 0); /* '.' label */
- ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
- ldns_buffer_write_u16(pkt, edns->udp_size); /* class */
- ldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
- ldns_buffer_write_u8(pkt, edns->edns_version);
- ldns_buffer_write_u16(pkt, edns->bits);
- ldns_buffer_write_u16(pkt, 0); /* rdatalen */
- ldns_buffer_flip(pkt);
-}
-
-int
-reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
- uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
- int cached, struct region* region, uint16_t udpsize,
- struct edns_data* edns)
-{
- uint16_t flags;
-
- if(!cached) {
- /* original flags, copy RD bit from query. */
- flags = rep->flags | (qflags & BIT_RD);
- } else {
- /* remove AA bit, copy RD and CD bits from query. */
- flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
- }
- log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
- if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns))
- return 0; /* packet too small to contain edns... */
- udpsize -= calc_edns_field_size(edns);
- if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
- udpsize)) {
- log_err("reply encode: out of memory");
- return 0;
- }
- attach_edns_record(pkt, edns);
- return 1;
-}
-
struct msgreply_entry*
query_info_entrysetup(struct query_info* q, struct reply_info* r,
hashvalue_t h)
q->qname = NULL;
return e;
}
-
-void
-qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo)
-{
- uint16_t flags = 0; /* QUERY, NOERROR */
- ldns_buffer_clear(pkt);
- log_assert(ldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
- ldns_buffer_skip(pkt, 2); /* id done later */
- ldns_buffer_write_u16(pkt, flags);
- ldns_buffer_write_u16(pkt, 1); /* query count */
- ldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
- ldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len);
- ldns_buffer_write_u16(pkt, qinfo->qtype);
- ldns_buffer_write_u16(pkt, qinfo->qclass);
- ldns_buffer_flip(pkt);
-}
/** calculate hash value of query_info, lowercases the qname. */
hashvalue_t query_info_hash(struct query_info *q);
-/**
- * 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.
- * @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, ldns_buffer* dest, uint32_t timenow,
- int cached, struct region* region, uint16_t udpsize,
- struct edns_data* edns);
-
-/**
- * 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.
- * @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, ldns_buffer* buffer, uint32_t timenow,
- struct region* region, uint16_t udpsize);
-
-/**
- * Encode query packet. Assumes the buffer is large enough.
- * @param pkt: where to store the packet.
- * @param qinfo: query info.
- */
-void qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo);
-
/**
* Setup query info entry
* @param q: query info to copy. Emptied as if clear is called.
struct msgreply_entry* query_info_entrysetup(struct query_info* q,
struct reply_info* r, hashvalue_t h);
-/**
- * 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(ldns_buffer* pkt, struct edns_data* edns);
-
-
#endif /* UTIL_DATA_MSGREPLY_H */