From: Wouter Wijngaards Date: Fri, 1 Jun 2007 12:25:38 +0000 (+0000) Subject: neater code layout. X-Git-Tag: release-0.4~110 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3feea769688d428b4f66acaf74764c199386d747;p=thirdparty%2Funbound.git neater code layout. git-svn-id: file:///svn/unbound/trunk@356 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 78ef5d0a0..f4f493bcd 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -55,6 +55,7 @@ #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 diff --git a/doc/Changelog b/doc/Changelog index 75a352889..57b1c8a02 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - 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. diff --git a/iterator/iterator.c b/iterator/iterator.c index e4d6e84a2..49eb818fe 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -53,6 +53,7 @@ #include "util/net_help.h" #include "util/region-allocator.h" #include "util/data/dname.h" +#include "util/data/msgencode.h" /** iterator init */ static int diff --git a/services/outside_network.c b/services/outside_network.c index 22b764744..cf5ae88f4 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -45,6 +45,7 @@ #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" diff --git a/testcode/fake_event.c b/testcode/fake_event.c index ee60f1166..a30640c47 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -50,6 +50,7 @@ #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" diff --git a/testcode/unitmsgparse.c b/testcode/unitmsgparse.c index 27f3790e2..4060c1ea6 100644 --- a/testcode/unitmsgparse.c +++ b/testcode/unitmsgparse.c @@ -43,6 +43,7 @@ #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" diff --git a/util/data/msgencode.c b/util/data/msgencode.c new file mode 100644 index 000000000..ce1802ffb --- /dev/null +++ b/util/data/msgencode.c @@ -0,0 +1,687 @@ +/* + * 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; icount; 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; irr_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; irrsets[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; irrsets[rrsets_before+i], + pkt, num_rrs, timenow, region, 1, 0, tree)) + != RETVAL_OK) { + ldns_buffer_set_position(pkt, setstart); + return r; + } + } + for(i=0; irrsets[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); +} diff --git a/util/data/msgencode.h b/util/data/msgencode.h new file mode 100644 index 000000000..de49404f5 --- /dev/null +++ b/util/data/msgencode.h @@ -0,0 +1,114 @@ +/* + * 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 */ diff --git a/util/data/msgreply.c b/util/data/msgreply.c index d49b3e895..3c904eaaa 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -50,14 +50,6 @@ #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, @@ -515,620 +507,6 @@ query_info_hash(struct query_info *q) 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; icount; 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; irr_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; irrsets[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; irrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 1, 0, tree)) - != RETVAL_OK) { - ldns_buffer_set_position(pkt, setstart); - return r; - } - } - for(i=0; irrsets[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) @@ -1148,19 +526,3 @@ query_info_entrysetup(struct query_info* q, struct reply_info* r, 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); -} diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 38d49bf8a..f3e21aeee 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -246,54 +246,6 @@ 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); -/** - * 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. @@ -304,20 +256,4 @@ void qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo); 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 */