1238106Sdes/* 2238106Sdes * util/data/msgencode.c - Encode DNS messages, queries and replies. 3238106Sdes * 4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved. 5238106Sdes * 6238106Sdes * This software is open source. 7238106Sdes * 8238106Sdes * Redistribution and use in source and binary forms, with or without 9238106Sdes * modification, are permitted provided that the following conditions 10238106Sdes * are met: 11238106Sdes * 12238106Sdes * Redistributions of source code must retain the above copyright notice, 13238106Sdes * this list of conditions and the following disclaimer. 14238106Sdes * 15238106Sdes * Redistributions in binary form must reproduce the above copyright notice, 16238106Sdes * this list of conditions and the following disclaimer in the documentation 17238106Sdes * and/or other materials provided with the distribution. 18238106Sdes * 19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 20238106Sdes * be used to endorse or promote products derived from this software without 21238106Sdes * specific prior written permission. 22238106Sdes * 23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34238106Sdes */ 35238106Sdes 36238106Sdes/** 37238106Sdes * \file 38238106Sdes * 39238106Sdes * This file contains a routines to encode DNS messages. 40238106Sdes */ 41238106Sdes 42238106Sdes#include "config.h" 43238106Sdes#include "util/data/msgencode.h" 44238106Sdes#include "util/data/msgreply.h" 45238106Sdes#include "util/data/msgparse.h" 46238106Sdes#include "util/data/dname.h" 47238106Sdes#include "util/log.h" 48238106Sdes#include "util/regional.h" 49238106Sdes#include "util/net_help.h" 50269257Sdes#include "ldns/sbuffer.h" 51238106Sdes 52238106Sdes/** return code that means the function ran out of memory. negative so it does 53238106Sdes * not conflict with DNS rcodes. */ 54238106Sdes#define RETVAL_OUTMEM -2 55238106Sdes/** return code that means the data did not fit (completely) in the packet */ 56238106Sdes#define RETVAL_TRUNC -4 57238106Sdes/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */ 58238106Sdes#define RETVAL_OK 0 59238106Sdes 60238106Sdes/** 61238106Sdes * Data structure to help domain name compression in outgoing messages. 62238106Sdes * A tree of dnames and their offsets in the packet is kept. 63238106Sdes * It is kept sorted, not canonical, but by label at least, so that after 64238106Sdes * a lookup of a name you know its closest match, and the parent from that 65238106Sdes * closest match. These are possible compression targets. 66238106Sdes * 67238106Sdes * It is a binary tree, not a rbtree or balanced tree, as the effort 68238106Sdes * of keeping it balanced probably outweighs usefulness (given typical 69238106Sdes * DNS packet size). 70238106Sdes */ 71238106Sdesstruct compress_tree_node { 72238106Sdes /** left node in tree, all smaller to this */ 73238106Sdes struct compress_tree_node* left; 74238106Sdes /** right node in tree, all larger than this */ 75238106Sdes struct compress_tree_node* right; 76238106Sdes 77238106Sdes /** the parent node - not for tree, but zone parent. One less label */ 78238106Sdes struct compress_tree_node* parent; 79238106Sdes /** the domain name for this node. Pointer to uncompressed memory. */ 80238106Sdes uint8_t* dname; 81238106Sdes /** number of labels in domain name, kept to help compare func. */ 82238106Sdes int labs; 83238106Sdes /** offset in packet that points to this dname */ 84238106Sdes size_t offset; 85238106Sdes}; 86238106Sdes 87238106Sdes/** 88238106Sdes * Find domain name in tree, returns exact and closest match. 89238106Sdes * @param tree: root of tree. 90238106Sdes * @param dname: pointer to uncompressed dname. 91238106Sdes * @param labs: number of labels in domain name. 92238106Sdes * @param match: closest or exact match. 93238106Sdes * guaranteed to be smaller or equal to the sought dname. 94238106Sdes * can be null if the tree is empty. 95238106Sdes * @param matchlabels: number of labels that match with closest match. 96238106Sdes * can be zero is there is no match. 97238106Sdes * @param insertpt: insert location for dname, if not found. 98238106Sdes * @return: 0 if no exact match. 99238106Sdes */ 100238106Sdesstatic int 101238106Sdescompress_tree_search(struct compress_tree_node** tree, uint8_t* dname, 102238106Sdes int labs, struct compress_tree_node** match, int* matchlabels, 103238106Sdes struct compress_tree_node*** insertpt) 104238106Sdes{ 105238106Sdes int c, n, closen=0; 106238106Sdes struct compress_tree_node* p = *tree; 107238106Sdes struct compress_tree_node* close = 0; 108238106Sdes struct compress_tree_node** prev = tree; 109238106Sdes while(p) { 110238106Sdes if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n)) 111238106Sdes == 0) { 112238106Sdes *matchlabels = n; 113238106Sdes *match = p; 114238106Sdes return 1; 115238106Sdes } 116238106Sdes if(c<0) { 117238106Sdes prev = &p->left; 118238106Sdes p = p->left; 119238106Sdes } else { 120238106Sdes closen = n; 121238106Sdes close = p; /* p->dname is smaller than dname */ 122238106Sdes prev = &p->right; 123238106Sdes p = p->right; 124238106Sdes } 125238106Sdes } 126238106Sdes *insertpt = prev; 127238106Sdes *matchlabels = closen; 128238106Sdes *match = close; 129238106Sdes return 0; 130238106Sdes} 131238106Sdes 132238106Sdes/** 133238106Sdes * Lookup a domain name in compression tree. 134238106Sdes * @param tree: root of tree (not the node with '.'). 135238106Sdes * @param dname: pointer to uncompressed dname. 136238106Sdes * @param labs: number of labels in domain name. 137238106Sdes * @param insertpt: insert location for dname, if not found. 138238106Sdes * @return: 0 if not found or compress treenode with best compression. 139238106Sdes */ 140238106Sdesstatic struct compress_tree_node* 141238106Sdescompress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname, 142238106Sdes int labs, struct compress_tree_node*** insertpt) 143238106Sdes{ 144238106Sdes struct compress_tree_node* p; 145238106Sdes int m; 146238106Sdes if(labs <= 1) 147238106Sdes return 0; /* do not compress root node */ 148238106Sdes if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) { 149238106Sdes /* exact match */ 150238106Sdes return p; 151238106Sdes } 152238106Sdes /* return some ancestor of p that compresses well. */ 153238106Sdes if(m>1) { 154238106Sdes /* www.example.com. (labs=4) matched foo.example.com.(labs=4) 155238106Sdes * then matchcount = 3. need to go up. */ 156238106Sdes while(p && p->labs > m) 157238106Sdes p = p->parent; 158238106Sdes return p; 159238106Sdes } 160238106Sdes return 0; 161238106Sdes} 162238106Sdes 163238106Sdes/** 164238106Sdes * Create node for domain name compression tree. 165238106Sdes * @param dname: pointer to uncompressed dname (stored in tree). 166238106Sdes * @param labs: number of labels in dname. 167238106Sdes * @param offset: offset into packet for dname. 168238106Sdes * @param region: how to allocate memory for new node. 169238106Sdes * @return new node or 0 on malloc failure. 170238106Sdes */ 171238106Sdesstatic struct compress_tree_node* 172238106Sdescompress_tree_newnode(uint8_t* dname, int labs, size_t offset, 173238106Sdes struct regional* region) 174238106Sdes{ 175238106Sdes struct compress_tree_node* n = (struct compress_tree_node*) 176238106Sdes regional_alloc(region, sizeof(struct compress_tree_node)); 177238106Sdes if(!n) return 0; 178238106Sdes n->left = 0; 179238106Sdes n->right = 0; 180238106Sdes n->parent = 0; 181238106Sdes n->dname = dname; 182238106Sdes n->labs = labs; 183238106Sdes n->offset = offset; 184238106Sdes return n; 185238106Sdes} 186238106Sdes 187238106Sdes/** 188238106Sdes * Store domain name and ancestors into compression tree. 189238106Sdes * @param dname: pointer to uncompressed dname (stored in tree). 190238106Sdes * @param labs: number of labels in dname. 191238106Sdes * @param offset: offset into packet for dname. 192238106Sdes * @param region: how to allocate memory for new node. 193238106Sdes * @param closest: match from previous lookup, used to compress dname. 194238106Sdes * may be NULL if no previous match. 195238106Sdes * if the tree has an ancestor of dname already, this must be it. 196238106Sdes * @param insertpt: where to insert the dname in tree. 197238106Sdes * @return: 0 on memory error. 198238106Sdes */ 199238106Sdesstatic int 200238106Sdescompress_tree_store(uint8_t* dname, int labs, size_t offset, 201238106Sdes struct regional* region, struct compress_tree_node* closest, 202238106Sdes struct compress_tree_node** insertpt) 203238106Sdes{ 204238106Sdes uint8_t lablen; 205238106Sdes struct compress_tree_node* newnode; 206238106Sdes struct compress_tree_node* prevnode = NULL; 207238106Sdes int uplabs = labs-1; /* does not store root in tree */ 208238106Sdes if(closest) uplabs = labs - closest->labs; 209238106Sdes log_assert(uplabs >= 0); 210238106Sdes /* algorithms builds up a vine of dname-labels to hang into tree */ 211238106Sdes while(uplabs--) { 212238106Sdes if(offset > PTR_MAX_OFFSET) { 213238106Sdes /* insertion failed, drop vine */ 214238106Sdes return 1; /* compression pointer no longer useful */ 215238106Sdes } 216238106Sdes if(!(newnode = compress_tree_newnode(dname, labs, offset, 217238106Sdes region))) { 218238106Sdes /* insertion failed, drop vine */ 219238106Sdes return 0; 220238106Sdes } 221238106Sdes 222238106Sdes if(prevnode) { 223238106Sdes /* chain nodes together, last one has one label more, 224238106Sdes * so is larger than newnode, thus goes right. */ 225238106Sdes newnode->right = prevnode; 226238106Sdes prevnode->parent = newnode; 227238106Sdes } 228238106Sdes 229238106Sdes /* next label */ 230238106Sdes lablen = *dname++; 231238106Sdes dname += lablen; 232238106Sdes offset += lablen+1; 233238106Sdes prevnode = newnode; 234238106Sdes labs--; 235238106Sdes } 236238106Sdes /* if we have a vine, hang the vine into the tree */ 237238106Sdes if(prevnode) { 238238106Sdes *insertpt = prevnode; 239238106Sdes prevnode->parent = closest; 240238106Sdes } 241238106Sdes return 1; 242238106Sdes} 243238106Sdes 244238106Sdes/** compress a domain name */ 245238106Sdesstatic int 246269257Sdeswrite_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs, 247238106Sdes struct compress_tree_node* p) 248238106Sdes{ 249238106Sdes /* compress it */ 250238106Sdes int labcopy = labs - p->labs; 251238106Sdes uint8_t lablen; 252238106Sdes uint16_t ptr; 253238106Sdes 254238106Sdes if(labs == 1) { 255238106Sdes /* write root label */ 256269257Sdes if(sldns_buffer_remaining(pkt) < 1) 257238106Sdes return 0; 258269257Sdes sldns_buffer_write_u8(pkt, 0); 259238106Sdes return 1; 260238106Sdes } 261238106Sdes 262238106Sdes /* copy the first couple of labels */ 263238106Sdes while(labcopy--) { 264238106Sdes lablen = *dname++; 265269257Sdes if(sldns_buffer_remaining(pkt) < (size_t)lablen+1) 266238106Sdes return 0; 267269257Sdes sldns_buffer_write_u8(pkt, lablen); 268269257Sdes sldns_buffer_write(pkt, dname, lablen); 269238106Sdes dname += lablen; 270238106Sdes } 271238106Sdes /* insert compression ptr */ 272269257Sdes if(sldns_buffer_remaining(pkt) < 2) 273238106Sdes return 0; 274238106Sdes ptr = PTR_CREATE(p->offset); 275269257Sdes sldns_buffer_write_u16(pkt, ptr); 276238106Sdes return 1; 277238106Sdes} 278238106Sdes 279238106Sdes/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ 280238106Sdesstatic int 281269257Sdescompress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, 282238106Sdes struct regional* region, struct compress_tree_node** tree, 283238106Sdes size_t owner_pos, uint16_t* owner_ptr, int owner_labs) 284238106Sdes{ 285238106Sdes struct compress_tree_node* p; 286238106Sdes struct compress_tree_node** insertpt; 287238106Sdes if(!*owner_ptr) { 288238106Sdes /* compress first time dname */ 289238106Sdes if((p = compress_tree_lookup(tree, key->rk.dname, 290238106Sdes owner_labs, &insertpt))) { 291238106Sdes if(p->labs == owner_labs) 292238106Sdes /* avoid ptr chains, since some software is 293238106Sdes * not capable of decoding ptr after a ptr. */ 294238106Sdes *owner_ptr = htons(PTR_CREATE(p->offset)); 295238106Sdes if(!write_compressed_dname(pkt, key->rk.dname, 296238106Sdes owner_labs, p)) 297238106Sdes return RETVAL_TRUNC; 298238106Sdes /* check if typeclass+4 ttl + rdatalen is available */ 299269257Sdes if(sldns_buffer_remaining(pkt) < 4+4+2) 300238106Sdes return RETVAL_TRUNC; 301238106Sdes } else { 302238106Sdes /* no compress */ 303269257Sdes if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2) 304238106Sdes return RETVAL_TRUNC; 305269257Sdes sldns_buffer_write(pkt, key->rk.dname, 306238106Sdes key->rk.dname_len); 307238106Sdes if(owner_pos <= PTR_MAX_OFFSET) 308238106Sdes *owner_ptr = htons(PTR_CREATE(owner_pos)); 309238106Sdes } 310238106Sdes if(!compress_tree_store(key->rk.dname, owner_labs, 311238106Sdes owner_pos, region, p, insertpt)) 312238106Sdes return RETVAL_OUTMEM; 313238106Sdes } else { 314238106Sdes /* always compress 2nd-further RRs in RRset */ 315238106Sdes if(owner_labs == 1) { 316269257Sdes if(sldns_buffer_remaining(pkt) < 1+4+4+2) 317238106Sdes return RETVAL_TRUNC; 318269257Sdes sldns_buffer_write_u8(pkt, 0); 319238106Sdes } else { 320269257Sdes if(sldns_buffer_remaining(pkt) < 2+4+4+2) 321238106Sdes return RETVAL_TRUNC; 322269257Sdes sldns_buffer_write(pkt, owner_ptr, 2); 323238106Sdes } 324238106Sdes } 325238106Sdes return RETVAL_OK; 326238106Sdes} 327238106Sdes 328238106Sdes/** compress any domain name to the packet, return RETVAL_* */ 329238106Sdesstatic int 330269257Sdescompress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, 331238106Sdes struct regional* region, struct compress_tree_node** tree) 332238106Sdes{ 333238106Sdes struct compress_tree_node* p; 334238106Sdes struct compress_tree_node** insertpt = NULL; 335269257Sdes size_t pos = sldns_buffer_position(pkt); 336238106Sdes if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) { 337238106Sdes if(!write_compressed_dname(pkt, dname, labs, p)) 338238106Sdes return RETVAL_TRUNC; 339238106Sdes } else { 340238106Sdes if(!dname_buffer_write(pkt, dname)) 341238106Sdes return RETVAL_TRUNC; 342238106Sdes } 343238106Sdes if(!compress_tree_store(dname, labs, pos, region, p, insertpt)) 344238106Sdes return RETVAL_OUTMEM; 345238106Sdes return RETVAL_OK; 346238106Sdes} 347238106Sdes 348238106Sdes/** return true if type needs domain name compression in rdata */ 349269257Sdesstatic const sldns_rr_descriptor* 350238106Sdestype_rdata_compressable(struct ub_packed_rrset_key* key) 351238106Sdes{ 352238106Sdes uint16_t t = ntohs(key->rk.type); 353269257Sdes if(sldns_rr_descript(t) && 354269257Sdes sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS) 355269257Sdes return sldns_rr_descript(t); 356238106Sdes return 0; 357238106Sdes} 358238106Sdes 359238106Sdes/** compress domain names in rdata, return RETVAL_* */ 360238106Sdesstatic int 361269257Sdescompress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, 362238106Sdes struct regional* region, struct compress_tree_node** tree, 363269257Sdes const sldns_rr_descriptor* desc) 364238106Sdes{ 365238106Sdes int labs, r, rdf = 0; 366269257Sdes size_t dname_len, len, pos = sldns_buffer_position(pkt); 367238106Sdes uint8_t count = desc->_dname_count; 368238106Sdes 369269257Sdes sldns_buffer_skip(pkt, 2); /* rdata len fill in later */ 370238106Sdes /* space for rdatalen checked for already */ 371238106Sdes rdata += 2; 372238106Sdes todolen -= 2; 373238106Sdes while(todolen > 0 && count) { 374238106Sdes switch(desc->_wireformat[rdf]) { 375238106Sdes case LDNS_RDF_TYPE_DNAME: 376238106Sdes labs = dname_count_size_labels(rdata, &dname_len); 377238106Sdes if((r=compress_any_dname(rdata, pkt, labs, region, 378238106Sdes tree)) != RETVAL_OK) 379238106Sdes return r; 380238106Sdes rdata += dname_len; 381238106Sdes todolen -= dname_len; 382238106Sdes count--; 383238106Sdes len = 0; 384238106Sdes break; 385238106Sdes case LDNS_RDF_TYPE_STR: 386238106Sdes len = *rdata + 1; 387238106Sdes break; 388238106Sdes default: 389238106Sdes len = get_rdf_size(desc->_wireformat[rdf]); 390238106Sdes } 391238106Sdes if(len) { 392238106Sdes /* copy over */ 393269257Sdes if(sldns_buffer_remaining(pkt) < len) 394238106Sdes return RETVAL_TRUNC; 395269257Sdes sldns_buffer_write(pkt, rdata, len); 396238106Sdes todolen -= len; 397238106Sdes rdata += len; 398238106Sdes } 399238106Sdes rdf++; 400238106Sdes } 401238106Sdes /* copy remainder */ 402238106Sdes if(todolen > 0) { 403269257Sdes if(sldns_buffer_remaining(pkt) < todolen) 404238106Sdes return RETVAL_TRUNC; 405269257Sdes sldns_buffer_write(pkt, rdata, todolen); 406238106Sdes } 407238106Sdes 408238106Sdes /* set rdata len */ 409269257Sdes sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2); 410238106Sdes return RETVAL_OK; 411238106Sdes} 412238106Sdes 413238106Sdes/** Returns true if RR type should be included */ 414238106Sdesstatic int 415269257Sdesrrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype, 416238106Sdes int dnssec) 417238106Sdes{ 418238106Sdes if(dnssec) 419238106Sdes return 1; 420238106Sdes /* skip non DNSSEC types, except if directly queried for */ 421238106Sdes if(s == LDNS_SECTION_ANSWER) { 422238106Sdes if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype) 423238106Sdes return 1; 424238106Sdes } 425238106Sdes /* check DNSSEC-ness */ 426238106Sdes switch(rrtype) { 427238106Sdes case LDNS_RR_TYPE_SIG: 428238106Sdes case LDNS_RR_TYPE_KEY: 429238106Sdes case LDNS_RR_TYPE_NXT: 430238106Sdes case LDNS_RR_TYPE_DS: 431238106Sdes case LDNS_RR_TYPE_RRSIG: 432238106Sdes case LDNS_RR_TYPE_NSEC: 433238106Sdes case LDNS_RR_TYPE_DNSKEY: 434238106Sdes case LDNS_RR_TYPE_NSEC3: 435238106Sdes case LDNS_RR_TYPE_NSEC3PARAMS: 436238106Sdes return 0; 437238106Sdes } 438238106Sdes return 1; 439238106Sdes} 440238106Sdes 441238106Sdes/** store rrset in buffer in wireformat, return RETVAL_* */ 442238106Sdesstatic int 443269257Sdespacked_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, 444269257Sdes uint16_t* num_rrs, time_t timenow, struct regional* region, 445238106Sdes int do_data, int do_sig, struct compress_tree_node** tree, 446269257Sdes sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) 447238106Sdes{ 448238106Sdes size_t i, j, owner_pos; 449238106Sdes int r, owner_labs; 450238106Sdes uint16_t owner_ptr = 0; 451238106Sdes struct packed_rrset_data* data = (struct packed_rrset_data*) 452238106Sdes key->entry.data; 453238106Sdes 454238106Sdes /* does this RR type belong in the answer? */ 455238106Sdes if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec)) 456238106Sdes return RETVAL_OK; 457238106Sdes 458238106Sdes owner_labs = dname_count_labels(key->rk.dname); 459269257Sdes owner_pos = sldns_buffer_position(pkt); 460238106Sdes 461238106Sdes if(do_data) { 462269257Sdes const sldns_rr_descriptor* c = type_rdata_compressable(key); 463238106Sdes for(i=0; i<data->count; i++) { 464238106Sdes /* rrset roundrobin */ 465238106Sdes j = (i + rr_offset) % data->count; 466238106Sdes if((r=compress_owner(key, pkt, region, tree, 467238106Sdes owner_pos, &owner_ptr, owner_labs)) 468238106Sdes != RETVAL_OK) 469238106Sdes return r; 470269257Sdes sldns_buffer_write(pkt, &key->rk.type, 2); 471269257Sdes sldns_buffer_write(pkt, &key->rk.rrset_class, 2); 472238106Sdes if(data->rr_ttl[j] < timenow) 473269257Sdes sldns_buffer_write_u32(pkt, 0); 474269257Sdes else sldns_buffer_write_u32(pkt, 475238106Sdes data->rr_ttl[j]-timenow); 476238106Sdes if(c) { 477238106Sdes if((r=compress_rdata(pkt, data->rr_data[j], 478238106Sdes data->rr_len[j], region, tree, c)) 479238106Sdes != RETVAL_OK) 480238106Sdes return r; 481238106Sdes } else { 482269257Sdes if(sldns_buffer_remaining(pkt) < data->rr_len[j]) 483238106Sdes return RETVAL_TRUNC; 484269257Sdes sldns_buffer_write(pkt, data->rr_data[j], 485238106Sdes data->rr_len[j]); 486238106Sdes } 487238106Sdes } 488238106Sdes } 489238106Sdes /* insert rrsigs */ 490238106Sdes if(do_sig && dnssec) { 491238106Sdes size_t total = data->count+data->rrsig_count; 492238106Sdes for(i=data->count; i<total; i++) { 493238106Sdes if(owner_ptr && owner_labs != 1) { 494269257Sdes if(sldns_buffer_remaining(pkt) < 495238106Sdes 2+4+4+data->rr_len[i]) 496238106Sdes return RETVAL_TRUNC; 497269257Sdes sldns_buffer_write(pkt, &owner_ptr, 2); 498238106Sdes } else { 499238106Sdes if((r=compress_any_dname(key->rk.dname, 500238106Sdes pkt, owner_labs, region, tree)) 501238106Sdes != RETVAL_OK) 502238106Sdes return r; 503269257Sdes if(sldns_buffer_remaining(pkt) < 504238106Sdes 4+4+data->rr_len[i]) 505238106Sdes return RETVAL_TRUNC; 506238106Sdes } 507269257Sdes sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG); 508269257Sdes sldns_buffer_write(pkt, &key->rk.rrset_class, 2); 509238106Sdes if(data->rr_ttl[i] < timenow) 510269257Sdes sldns_buffer_write_u32(pkt, 0); 511269257Sdes else sldns_buffer_write_u32(pkt, 512238106Sdes data->rr_ttl[i]-timenow); 513238106Sdes /* rrsig rdata cannot be compressed, perform 100+ byte 514238106Sdes * memcopy. */ 515269257Sdes sldns_buffer_write(pkt, data->rr_data[i], 516238106Sdes data->rr_len[i]); 517238106Sdes } 518238106Sdes } 519238106Sdes /* change rrnum only after we are sure it fits */ 520238106Sdes if(do_data) 521238106Sdes *num_rrs += data->count; 522238106Sdes if(do_sig && dnssec) 523238106Sdes *num_rrs += data->rrsig_count; 524238106Sdes 525238106Sdes return RETVAL_OK; 526238106Sdes} 527238106Sdes 528238106Sdes/** store msg section in wireformat buffer, return RETVAL_* */ 529238106Sdesstatic int 530238106Sdesinsert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, 531269257Sdes sldns_buffer* pkt, size_t rrsets_before, time_t timenow, 532238106Sdes struct regional* region, struct compress_tree_node** tree, 533269257Sdes sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) 534238106Sdes{ 535238106Sdes int r; 536238106Sdes size_t i, setstart; 537238106Sdes *num_rrs = 0; 538238106Sdes if(s != LDNS_SECTION_ADDITIONAL) { 539238106Sdes if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY) 540238106Sdes dnssec = 1; /* include all types in ANY answer */ 541238106Sdes for(i=0; i<num_rrsets; i++) { 542269257Sdes setstart = sldns_buffer_position(pkt); 543238106Sdes if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 544238106Sdes pkt, num_rrs, timenow, region, 1, 1, tree, 545238106Sdes s, qtype, dnssec, rr_offset)) 546238106Sdes != RETVAL_OK) { 547238106Sdes /* Bad, but if due to size must set TC bit */ 548238106Sdes /* trim off the rrset neatly. */ 549269257Sdes sldns_buffer_set_position(pkt, setstart); 550238106Sdes return r; 551238106Sdes } 552238106Sdes } 553238106Sdes } else { 554238106Sdes for(i=0; i<num_rrsets; i++) { 555269257Sdes setstart = sldns_buffer_position(pkt); 556238106Sdes if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 557238106Sdes pkt, num_rrs, timenow, region, 1, 0, tree, 558238106Sdes s, qtype, dnssec, rr_offset)) 559238106Sdes != RETVAL_OK) { 560269257Sdes sldns_buffer_set_position(pkt, setstart); 561238106Sdes return r; 562238106Sdes } 563238106Sdes } 564238106Sdes if(dnssec) 565238106Sdes for(i=0; i<num_rrsets; i++) { 566269257Sdes setstart = sldns_buffer_position(pkt); 567238106Sdes if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], 568238106Sdes pkt, num_rrs, timenow, region, 0, 1, tree, 569238106Sdes s, qtype, dnssec, rr_offset)) 570238106Sdes != RETVAL_OK) { 571269257Sdes sldns_buffer_set_position(pkt, setstart); 572238106Sdes return r; 573238106Sdes } 574238106Sdes } 575238106Sdes } 576238106Sdes return RETVAL_OK; 577238106Sdes} 578238106Sdes 579238106Sdes/** store query section in wireformat buffer, return RETVAL */ 580238106Sdesstatic int 581238106Sdesinsert_query(struct query_info* qinfo, struct compress_tree_node** tree, 582269257Sdes sldns_buffer* buffer, struct regional* region) 583238106Sdes{ 584269257Sdes if(sldns_buffer_remaining(buffer) < 585238106Sdes qinfo->qname_len+sizeof(uint16_t)*2) 586238106Sdes return RETVAL_TRUNC; /* buffer too small */ 587238106Sdes /* the query is the first name inserted into the tree */ 588238106Sdes if(!compress_tree_store(qinfo->qname, 589238106Sdes dname_count_labels(qinfo->qname), 590269257Sdes sldns_buffer_position(buffer), region, NULL, tree)) 591238106Sdes return RETVAL_OUTMEM; 592269257Sdes if(sldns_buffer_current(buffer) == qinfo->qname) 593269257Sdes sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); 594269257Sdes else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); 595269257Sdes sldns_buffer_write_u16(buffer, qinfo->qtype); 596269257Sdes sldns_buffer_write_u16(buffer, qinfo->qclass); 597238106Sdes return RETVAL_OK; 598238106Sdes} 599238106Sdes 600238106Sdesstatic int 601238106Sdespositive_answer(struct reply_info* rep, uint16_t qtype) { 602238106Sdes size_t i; 603238106Sdes if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR) 604238106Sdes return 0; 605238106Sdes 606238106Sdes for(i=0;i<rep->an_numrrsets; i++) { 607238106Sdes if(ntohs(rep->rrsets[i]->rk.type) == qtype) { 608238106Sdes /* in case it is a wildcard with DNSSEC, there will 609238106Sdes * be NSEC/NSEC3 records in the authority section 610238106Sdes * that we cannot remove */ 611238106Sdes for(i=rep->an_numrrsets; i<rep->an_numrrsets+ 612238106Sdes rep->ns_numrrsets; i++) { 613238106Sdes if(ntohs(rep->rrsets[i]->rk.type) == 614238106Sdes LDNS_RR_TYPE_NSEC || 615238106Sdes ntohs(rep->rrsets[i]->rk.type) == 616238106Sdes LDNS_RR_TYPE_NSEC3) 617238106Sdes return 0; 618238106Sdes } 619238106Sdes return 1; 620238106Sdes } 621238106Sdes } 622238106Sdes return 0; 623238106Sdes} 624238106Sdes 625238106Sdesint 626238106Sdesreply_info_encode(struct query_info* qinfo, struct reply_info* rep, 627269257Sdes uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, 628238106Sdes struct regional* region, uint16_t udpsize, int dnssec) 629238106Sdes{ 630238106Sdes uint16_t ancount=0, nscount=0, arcount=0; 631238106Sdes struct compress_tree_node* tree = 0; 632238106Sdes int r; 633238106Sdes size_t rr_offset; 634238106Sdes 635269257Sdes sldns_buffer_clear(buffer); 636269257Sdes if(udpsize < sldns_buffer_limit(buffer)) 637269257Sdes sldns_buffer_set_limit(buffer, udpsize); 638269257Sdes if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) 639238106Sdes return 0; 640238106Sdes 641269257Sdes sldns_buffer_write(buffer, &id, sizeof(uint16_t)); 642269257Sdes sldns_buffer_write_u16(buffer, flags); 643269257Sdes sldns_buffer_write_u16(buffer, rep->qdcount); 644238106Sdes /* set an, ns, ar counts to zero in case of small packets */ 645269257Sdes sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); 646238106Sdes 647238106Sdes /* insert query section */ 648238106Sdes if(rep->qdcount) { 649238106Sdes if((r=insert_query(qinfo, &tree, buffer, region)) != 650238106Sdes RETVAL_OK) { 651238106Sdes if(r == RETVAL_TRUNC) { 652238106Sdes /* create truncated message */ 653269257Sdes sldns_buffer_write_u16_at(buffer, 4, 0); 654269257Sdes LDNS_TC_SET(sldns_buffer_begin(buffer)); 655269257Sdes sldns_buffer_flip(buffer); 656238106Sdes return 1; 657238106Sdes } 658238106Sdes return 0; 659238106Sdes } 660238106Sdes } 661269257Sdes /* roundrobin offset. using query id for random number. With ntohs 662269257Sdes * for different roundrobins for sequential id client senders. */ 663269257Sdes rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; 664238106Sdes 665238106Sdes /* insert answer section */ 666238106Sdes if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 667238106Sdes 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, 668238106Sdes dnssec, rr_offset)) != RETVAL_OK) { 669238106Sdes if(r == RETVAL_TRUNC) { 670238106Sdes /* create truncated message */ 671269257Sdes sldns_buffer_write_u16_at(buffer, 6, ancount); 672269257Sdes LDNS_TC_SET(sldns_buffer_begin(buffer)); 673269257Sdes sldns_buffer_flip(buffer); 674238106Sdes return 1; 675238106Sdes } 676238106Sdes return 0; 677238106Sdes } 678269257Sdes sldns_buffer_write_u16_at(buffer, 6, ancount); 679238106Sdes 680238106Sdes /* if response is positive answer, auth/add sections are not required */ 681238106Sdes if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { 682238106Sdes /* insert auth section */ 683238106Sdes if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, 684238106Sdes rep->an_numrrsets, timenow, region, &tree, 685238106Sdes LDNS_SECTION_AUTHORITY, qinfo->qtype, 686238106Sdes dnssec, rr_offset)) != RETVAL_OK) { 687238106Sdes if(r == RETVAL_TRUNC) { 688238106Sdes /* create truncated message */ 689269257Sdes sldns_buffer_write_u16_at(buffer, 8, nscount); 690269257Sdes LDNS_TC_SET(sldns_buffer_begin(buffer)); 691269257Sdes sldns_buffer_flip(buffer); 692238106Sdes return 1; 693238106Sdes } 694238106Sdes return 0; 695238106Sdes } 696269257Sdes sldns_buffer_write_u16_at(buffer, 8, nscount); 697238106Sdes 698238106Sdes /* insert add section */ 699238106Sdes if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, 700238106Sdes rep->an_numrrsets + rep->ns_numrrsets, timenow, region, 701238106Sdes &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, 702238106Sdes dnssec, rr_offset)) != RETVAL_OK) { 703238106Sdes if(r == RETVAL_TRUNC) { 704238106Sdes /* no need to set TC bit, this is the additional */ 705269257Sdes sldns_buffer_write_u16_at(buffer, 10, arcount); 706269257Sdes sldns_buffer_flip(buffer); 707238106Sdes return 1; 708238106Sdes } 709238106Sdes return 0; 710238106Sdes } 711269257Sdes sldns_buffer_write_u16_at(buffer, 10, arcount); 712238106Sdes } 713269257Sdes sldns_buffer_flip(buffer); 714238106Sdes return 1; 715238106Sdes} 716238106Sdes 717238106Sdesuint16_t 718238106Sdescalc_edns_field_size(struct edns_data* edns) 719238106Sdes{ 720238106Sdes if(!edns || !edns->edns_present) 721238106Sdes return 0; 722238106Sdes /* domain root '.' + type + class + ttl + rdatalen(=0) */ 723238106Sdes return 1 + 2 + 2 + 4 + 2; 724238106Sdes} 725238106Sdes 726238106Sdesvoid 727269257Sdesattach_edns_record(sldns_buffer* pkt, struct edns_data* edns) 728238106Sdes{ 729238106Sdes size_t len; 730238106Sdes if(!edns || !edns->edns_present) 731238106Sdes return; 732238106Sdes /* inc additional count */ 733269257Sdes sldns_buffer_write_u16_at(pkt, 10, 734269257Sdes sldns_buffer_read_u16_at(pkt, 10) + 1); 735269257Sdes len = sldns_buffer_limit(pkt); 736269257Sdes sldns_buffer_clear(pkt); 737269257Sdes sldns_buffer_set_position(pkt, len); 738238106Sdes /* write EDNS record */ 739269257Sdes sldns_buffer_write_u8(pkt, 0); /* '.' label */ 740269257Sdes sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */ 741269257Sdes sldns_buffer_write_u16(pkt, edns->udp_size); /* class */ 742269257Sdes sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */ 743269257Sdes sldns_buffer_write_u8(pkt, edns->edns_version); 744269257Sdes sldns_buffer_write_u16(pkt, edns->bits); 745269257Sdes sldns_buffer_write_u16(pkt, 0); /* rdatalen */ 746269257Sdes sldns_buffer_flip(pkt); 747238106Sdes} 748238106Sdes 749238106Sdesint 750238106Sdesreply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 751269257Sdes uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow, 752238106Sdes int cached, struct regional* region, uint16_t udpsize, 753238106Sdes struct edns_data* edns, int dnssec, int secure) 754238106Sdes{ 755238106Sdes uint16_t flags; 756238106Sdes int attach_edns = 1; 757238106Sdes 758238106Sdes if(!cached || rep->authoritative) { 759238106Sdes /* original flags, copy RD and CD bits from query. */ 760238106Sdes flags = rep->flags | (qflags & (BIT_RD|BIT_CD)); 761238106Sdes } else { 762238106Sdes /* remove AA bit, copy RD and CD bits from query. */ 763238106Sdes flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD)); 764238106Sdes } 765238106Sdes if(secure && (dnssec || (qflags&BIT_AD))) 766238106Sdes flags |= BIT_AD; 767238106Sdes log_assert(flags & BIT_QR); /* QR bit must be on in our replies */ 768238106Sdes if(udpsize < LDNS_HEADER_SIZE) 769238106Sdes return 0; 770238106Sdes if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) { 771238106Sdes /* packet too small to contain edns, omit it. */ 772238106Sdes attach_edns = 0; 773238106Sdes } else { 774238106Sdes /* reserve space for edns record */ 775238106Sdes udpsize -= calc_edns_field_size(edns); 776238106Sdes } 777238106Sdes 778238106Sdes if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, 779238106Sdes udpsize, dnssec)) { 780238106Sdes log_err("reply encode: out of memory"); 781238106Sdes return 0; 782238106Sdes } 783238106Sdes if(attach_edns) 784238106Sdes attach_edns_record(pkt, edns); 785238106Sdes return 1; 786238106Sdes} 787238106Sdes 788238106Sdesvoid 789269257Sdesqinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo) 790238106Sdes{ 791238106Sdes uint16_t flags = 0; /* QUERY, NOERROR */ 792269257Sdes sldns_buffer_clear(pkt); 793269257Sdes log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/); 794269257Sdes sldns_buffer_skip(pkt, 2); /* id done later */ 795269257Sdes sldns_buffer_write_u16(pkt, flags); 796269257Sdes sldns_buffer_write_u16(pkt, 1); /* query count */ 797269257Sdes sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */ 798269257Sdes sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len); 799269257Sdes sldns_buffer_write_u16(pkt, qinfo->qtype); 800269257Sdes sldns_buffer_write_u16(pkt, qinfo->qclass); 801269257Sdes sldns_buffer_flip(pkt); 802238106Sdes} 803238106Sdes 804238106Sdesvoid 805269257Sdeserror_encode(sldns_buffer* buf, int r, struct query_info* qinfo, 806238106Sdes uint16_t qid, uint16_t qflags, struct edns_data* edns) 807238106Sdes{ 808238106Sdes uint16_t flags; 809238106Sdes 810269257Sdes sldns_buffer_clear(buf); 811269257Sdes sldns_buffer_write(buf, &qid, sizeof(uint16_t)); 812238106Sdes flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ 813238106Sdes flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */ 814269257Sdes sldns_buffer_write_u16(buf, flags); 815238106Sdes if(qinfo) flags = 1; 816238106Sdes else flags = 0; 817269257Sdes sldns_buffer_write_u16(buf, flags); 818238106Sdes flags = 0; 819269257Sdes sldns_buffer_write(buf, &flags, sizeof(uint16_t)); 820269257Sdes sldns_buffer_write(buf, &flags, sizeof(uint16_t)); 821269257Sdes sldns_buffer_write(buf, &flags, sizeof(uint16_t)); 822238106Sdes if(qinfo) { 823269257Sdes if(sldns_buffer_current(buf) == qinfo->qname) 824269257Sdes sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len); 825269257Sdes else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len); 826269257Sdes sldns_buffer_write_u16(buf, qinfo->qtype); 827269257Sdes sldns_buffer_write_u16(buf, qinfo->qclass); 828238106Sdes } 829269257Sdes sldns_buffer_flip(buf); 830238106Sdes if(edns) { 831238106Sdes struct edns_data es = *edns; 832238106Sdes es.edns_version = EDNS_ADVERTISED_VERSION; 833238106Sdes es.udp_size = EDNS_ADVERTISED_SIZE; 834238106Sdes es.ext_rcode = 0; 835238106Sdes es.bits &= EDNS_DO; 836269257Sdes if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > 837238106Sdes edns->udp_size) 838238106Sdes return; 839238106Sdes attach_edns_record(buf, &es); 840238106Sdes } 841238106Sdes} 842