/** 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 domain names in rdata, return RETVAL_* */ static int compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen, struct regional* 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; }
size_t pkt_dname_len(ldns_buffer* pkt) { size_t len = 0; int ptrcount = 0; uint8_t labellen; size_t endpos = 0; /* read dname and determine length */ /* check compression pointers, loops, out of bounds */ while(1) { /* read next label */ if(ldns_buffer_remaining(pkt) < 1) return 0; labellen = ldns_buffer_read_u8(pkt); if(LABEL_IS_PTR(labellen)) { /* compression ptr */ uint16_t ptr; if(ldns_buffer_remaining(pkt) < 1) return 0; ptr = PTR_OFFSET(labellen, ldns_buffer_read_u8(pkt)); if(ptrcount++ > MAX_COMPRESS_PTRS) return 0; /* loop! */ if(ldns_buffer_limit(pkt) <= ptr) return 0; /* out of bounds! */ if(!endpos) endpos = ldns_buffer_position(pkt); ldns_buffer_set_position(pkt, ptr); } else { /* label contents */ if(labellen > 0x3f) return 0; /* label too long */ len += 1 + labellen; if(len > LDNS_MAX_DOMAINLEN) return 0; if(labellen == 0) { /* end of dname */ break; } if(ldns_buffer_remaining(pkt) < labellen) return 0; ldns_buffer_skip(pkt, (ssize_t)labellen); } } if(endpos) ldns_buffer_set_position(pkt, endpos); return len; }
uint8_t* context_serialize_answer(struct ctx_query* q, int err, ldns_buffer* pkt, uint32_t* len) { /* answer format * o uint32 cmd * o uint32 id * o uint32 error_code * o uint32 msg_security * o uint32 length of why_bogus string (+1 for eos); 0 absent. * o why_bogus_string * o the remainder is the answer msg from resolver lookup. * remainder can be length 0. */ size_t pkt_len = pkt?ldns_buffer_remaining(pkt):0; size_t wlen = (pkt&&q->res->why_bogus)?strlen(q->res->why_bogus)+1:0; uint8_t* p; *len = sizeof(uint32_t)*5 + pkt_len + wlen; p = (uint8_t*)malloc(*len); if(!p) return NULL; ldns_write_uint32(p, UB_LIBCMD_ANSWER); ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum); ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err); ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security); ldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)wlen); if(wlen > 0) memmove(p+5*sizeof(uint32_t), q->res->why_bogus, wlen); if(pkt_len > 0) memmove(p+5*sizeof(uint32_t)+wlen, ldns_buffer_begin(pkt), pkt_len); return p; }
/** analyse pkt */ static void analyze(ldns_buffer* pkt) { uint16_t i, f, qd, an, ns, ar; int rrnum = 0; printf("packet length %d\n", (int)ldns_buffer_limit(pkt)); if(ldns_buffer_limit(pkt) < 12) return; i = ldns_buffer_read_u16(pkt); printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i); f = ldns_buffer_read_u16(pkt); printf("flags: 0x%x\n", (unsigned)f); qd = ldns_buffer_read_u16(pkt); printf("qdcount: %d\n", (int)qd); an = ldns_buffer_read_u16(pkt); printf("ancount: %d\n", (int)an); ns = ldns_buffer_read_u16(pkt); printf("nscount: %d\n", (int)ns); ar = ldns_buffer_read_u16(pkt); printf("arcount: %d\n", (int)ar); printf(";-- query section\n"); while(ldns_buffer_remaining(pkt) > 0) { if(rrnum == (int)qd) printf(";-- answer section\n"); if(rrnum == (int)qd+(int)an) printf(";-- authority section\n"); if(rrnum == (int)qd+(int)an+(int)ns) printf(";-- additional section\n"); printf("rr %d ", rrnum); analyze_rr(pkt, rrnum < (int)qd); rrnum++; } }
/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ static int compress_owner(struct ub_packed_rrset_key* key, ldns_buffer* pkt, struct regional* region, struct compress_tree_node** tree, size_t owner_pos, uint16_t* owner_ptr, int owner_labs) { struct compress_tree_node* p; struct compress_tree_node** insertpt; if(!*owner_ptr) { /* compress first time dname */ if((p = compress_tree_lookup(tree, key->rk.dname, owner_labs, &insertpt))) { if(p->labs == owner_labs) /* avoid ptr chains, since some software is * not capable of decoding ptr after a ptr. */ *owner_ptr = htons(PTR_CREATE(p->offset)); if(!write_compressed_dname(pkt, key->rk.dname, owner_labs, p)) return RETVAL_TRUNC; /* check if typeclass+4 ttl + rdatalen is available */ if(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(key->rk.dname, owner_labs, owner_pos, region, p, insertpt)) return RETVAL_OUTMEM; } else { /* always compress 2nd-further RRs in RRset */ if(owner_labs == 1) { if(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; }
int dname_buffer_write(ldns_buffer* pkt, uint8_t* dname) { uint8_t lablen; if(ldns_buffer_remaining(pkt) < 1) return 0; lablen = *dname++; ldns_buffer_write_u8(pkt, lablen); while(lablen) { if(ldns_buffer_remaining(pkt) < (size_t)lablen+1) return 0; ldns_buffer_write(pkt, dname, lablen); dname += lablen; lablen = *dname++; ldns_buffer_write_u8(pkt, lablen); } return 1; }
/* determine length of a dname in buffer, no compression pointers allowed */ size_t query_dname_len(ldns_buffer* query) { size_t len = 0; size_t labellen; while(1) { if(ldns_buffer_remaining(query) < 1) return 0; /* parse error, need label len */ labellen = ldns_buffer_read_u8(query); if(labellen&0xc0) return 0; /* no compression allowed in queries */ len += labellen + 1; if(len > LDNS_MAX_DOMAINLEN) return 0; /* too long */ if(labellen == 0) return len; if(ldns_buffer_remaining(query) < labellen) return 0; /* parse error, need content */ ldns_buffer_skip(query, (ssize_t)labellen); } }
/** * Create canonical form of rrset in the scratch buffer. * @param region: temporary region. * @param buf: the buffer to use. * @param k: the rrset to insert. * @param sig: RRSIG rdata to include. * @param siglen: RRSIG rdata len excluding signature field, but inclusive * signer name length. * @param sortree: if NULL is passed a new sorted rrset tree is built. * Otherwise it is reused. * @return false on alloc error. */ static int rrset_canonical(struct regional* region, ldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen, struct rbtree_t** sortree) { struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; uint8_t* can_owner = NULL; size_t can_owner_len = 0; struct canon_rr* walk; struct canon_rr* rrs; if(!*sortree) { *sortree = (struct rbtree_t*)regional_alloc(region, sizeof(rbtree_t)); if(!*sortree) return 0; rrs = regional_alloc(region, sizeof(struct canon_rr)*d->count); if(!rrs) { *sortree = NULL; return 0; } rbtree_init(*sortree, &canonical_tree_compare); canonical_sort(k, d, *sortree, rrs); } ldns_buffer_clear(buf); ldns_buffer_write(buf, sig, siglen); /* canonicalize signer name */ query_dname_tolower(ldns_buffer_begin(buf)+18); RBTREE_FOR(walk, struct canon_rr*, (*sortree)) { /* see if there is enough space left in the buffer */ if(ldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4 + d->rr_len[walk->rr_idx]) { log_err("verify: failed to canonicalize, " "rrset too big"); return 0; } /* determine canonical owner name */ if(can_owner) ldns_buffer_write(buf, can_owner, can_owner_len); else insert_can_owner(buf, k, sig, &can_owner, &can_owner_len); ldns_buffer_write(buf, &k->rk.type, 2); ldns_buffer_write(buf, &k->rk.rrset_class, 2); ldns_buffer_write(buf, sig+4, 4); ldns_buffer_write(buf, d->rr_data[walk->rr_idx], d->rr_len[walk->rr_idx]); canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]); } ldns_buffer_flip(buf); return 1; }
int ldns_buffer_printf(ldns_buffer *buffer, const char *format, ...) { va_list args; int written = 0; size_t remaining; if (ldns_buffer_status_ok(buffer)) { ldns_buffer_invariant(buffer); assert(buffer->_limit == buffer->_capacity); remaining = ldns_buffer_remaining(buffer); va_start(args, format); written = vsnprintf((char *) ldns_buffer_current(buffer), remaining, format, args); va_end(args); if (written == -1) { buffer->_status = LDNS_STATUS_INTERNAL_ERR; return -1; } else if ((size_t) written >= remaining) { if (!ldns_buffer_reserve(buffer, (size_t) written + 1)) { buffer->_status = LDNS_STATUS_MEM_ERR; return -1; } va_start(args, format); written = vsnprintf((char *) ldns_buffer_current(buffer), ldns_buffer_remaining(buffer), format, args); va_end(args); if (written == -1) { buffer->_status = LDNS_STATUS_INTERNAL_ERR; return -1; } } buffer->_position += written; } return written; }
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); }
/** store query section in wireformat buffer, return RETVAL */ static int insert_query(struct query_info* qinfo, struct compress_tree_node** tree, ldns_buffer* buffer, struct regional* region) { if(ldns_buffer_remaining(buffer) < qinfo->qname_len+sizeof(uint16_t)*2) return RETVAL_TRUNC; /* buffer too small */ /* the query is the first name inserted into the tree */ if(!compress_tree_store(qinfo->qname, dname_count_labels(qinfo->qname), ldns_buffer_position(buffer), region, NULL, tree)) return RETVAL_OUTMEM; if(ldns_buffer_current(buffer) == qinfo->qname) ldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); else ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); ldns_buffer_write_u16(buffer, qinfo->qtype); ldns_buffer_write_u16(buffer, qinfo->qclass); return RETVAL_OK; }
int query_info_parse(struct query_info* m, ldns_buffer* query) { uint8_t* q = ldns_buffer_begin(query); /* minimum size: header + \0 + qtype + qclass */ if(ldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) return 0; if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || LDNS_QDCOUNT(q) != 1 || ldns_buffer_position(query) != 0) return 0; ldns_buffer_skip(query, LDNS_HEADER_SIZE); m->qname = ldns_buffer_current(query); if((m->qname_len = query_dname_len(query)) == 0) return 0; /* parse error */ if(ldns_buffer_remaining(query) < 4) return 0; /* need qtype, qclass */ m->qtype = ldns_buffer_read_u16(query); m->qclass = ldns_buffer_read_u16(query); return 1; }
/** add result to the bg worker result queue */ static void add_bg_result(struct libworker* w, struct ctx_query* q, ldns_buffer* pkt, int err, char* reason) { uint8_t* msg = NULL; uint32_t len = 0; /* serialize and delete unneeded q */ if(w->is_bg_thread) { lock_basic_lock(&w->ctx->cfglock); if(reason) q->res->why_bogus = strdup(reason); if(pkt) { q->msg_len = ldns_buffer_remaining(pkt); q->msg = memdup(ldns_buffer_begin(pkt), q->msg_len); if(!q->msg) msg = context_serialize_answer(q, UB_NOMEM, NULL, &len); else msg = context_serialize_answer(q, err, NULL, &len); } else msg = context_serialize_answer(q, err, NULL, &len); lock_basic_unlock(&w->ctx->cfglock); } else { if(reason) q->res->why_bogus = strdup(reason); msg = context_serialize_answer(q, err, pkt, &len); (void)rbtree_delete(&w->ctx->queries, q->node.key); w->ctx->num_async--; context_query_delete(q); } if(!msg) { log_err("out of memory for async answer"); return; } if(!tube_queue_item(w->ctx->rr_pipe, msg, len)) { log_err("out of memory for async answer"); return; } }
/** do the rdata copy */ static int rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type) { uint16_t pkt_len; const ldns_rr_descriptor* desc; *rr_ttl = ldns_read_uint32(rr->ttl_data); /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */ if(*rr_ttl & 0x80000000U) *rr_ttl = 0; if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; if(rr->outside_packet) { /* uncompressed already, only needs copy */ memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size); return 1; } ldns_buffer_set_position(pkt, (size_t) (rr->ttl_data - ldns_buffer_begin(pkt) + sizeof(uint32_t))); /* insert decompressed size into rdata len stored in memory */ /* -2 because rdatalen bytes are not included. */ pkt_len = htons(rr->size - 2); memmove(to, &pkt_len, sizeof(uint16_t)); to += 2; /* read packet rdata len */ pkt_len = ldns_buffer_read_u16(pkt); if(ldns_buffer_remaining(pkt) < pkt_len) return 0; desc = ldns_rr_descript(type); if(pkt_len > 0 && desc && desc->_dname_count > 0) { int count = (int)desc->_dname_count; int rdf = 0; size_t len; size_t oldpos; /* decompress dnames. */ while(pkt_len > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: oldpos = ldns_buffer_position(pkt); dname_pkt_copy(pkt, to, ldns_buffer_current(pkt)); to += pkt_dname_len(pkt); pkt_len -= ldns_buffer_position(pkt)-oldpos; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = ldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); break; } if(len) { memmove(to, ldns_buffer_current(pkt), len); to += len; ldns_buffer_skip(pkt, (ssize_t)len); log_assert(len <= pkt_len); pkt_len -= len; } rdf++; } } /* copy remaining rdata */ if(pkt_len > 0) memmove(to, ldns_buffer_current(pkt), pkt_len); return 1; }
/** * read contents of trusted-keys{ ... ; clauses and insert keys into storage. * @param anchors: where to store keys * @param buf: buffer to use * @param line: line number in file * @param in: file to read from. * @return 0 on error. */ static int process_bind_contents(struct val_anchors* anchors, ldns_buffer* buf, int* line, FILE* in) { /* loop over contents, collate strings before ; */ /* contents is (numbered): 0 1 2 3 4 5 6 7 8 */ /* name. 257 3 5 base64 base64 */ /* quoted value: 0 "111" 0 0 0 0 0 0 0 */ /* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */ int contnum = 0; int quoted = 0; int comments = 1; int rdlen; char* str = 0; ldns_buffer_clear(buf); while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { if(rdlen == 1 && ldns_buffer_position(buf) == 1 && isspace((int)*ldns_buffer_begin(buf))) { /* starting whitespace is removed */ ldns_buffer_clear(buf); continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '"') { /* remove " from the string */ if(contnum == 0) { quoted = 1; comments = 0; } ldns_buffer_skip(buf, -1); if(contnum > 0 && quoted) { if(ldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } ldns_buffer_write(buf, " DNSKEY ", 8); quoted = 0; comments = 1; } else if(contnum > 0) comments = !comments; continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == ';') { if(contnum < 5) { ldns_buffer_write_u8(buf, 0); log_err("line %d, bad key", *line); return 0; } ldns_buffer_skip(buf, -1); ldns_buffer_write_u8(buf, 0); str = strdup((char*)ldns_buffer_begin(buf)); if(!str) { log_err("line %d, allocation failure", *line); return 0; } if(!anchor_store_str(anchors, buf, str)) { log_err("line %d, bad key", *line); free(str); return 0; } free(str); ldns_buffer_clear(buf); contnum = 0; quoted = 0; comments = 1; continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '}') { if(contnum > 0) { ldns_buffer_write_u8(buf, 0); log_err("line %d, bad key before }", *line); return 0; } return 1; } else if(rdlen == 1 && isspace((int)ldns_buffer_current(buf)[-1])) { /* leave whitespace here */ } else { /* not space or whatnot, so actual content */ contnum ++; if(contnum == 1 && !quoted) { if(ldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } ldns_buffer_write(buf, " DNSKEY ", 8); } } } log_err("line %d, EOF before }", *line); return 0; }
/** * Read a keyword skipping bind comments; spaces, specials, restkeywords. * The file is split into the following tokens: * * special characters, on their own, rdlen=1, { } doublequote ; * * whitespace becomes a single ' ' or tab. Newlines become spaces. * * other words ('keywords') * * comments are skipped if desired * / / C++ style comment to end of line * # to end of line * / * C style comment * / * @param in: file to read from. * @param buf: buffer, what is read is stored after current buffer position. * Space is left in the buffer to write a terminating 0. * @param line: line number is increased per line, for error reports. * @param comments: if 0, comments are not possible and become text. * if 1, comments are skipped entirely. * In BIND files, this is when reading quoted strings, for example * " base 64 text with / / in there " * @return the number of character written to the buffer. * 0 on end of file. */ static int readkeyword_bindfile(FILE* in, ldns_buffer* buf, int* line, int comments) { int c; int numdone = 0; while((c = getc(in)) != EOF ) { if(comments && c == '#') { /* # blabla */ skip_to_eol(in); (*line)++; continue; } else if(comments && c=='/' && numdone>0 && /* /_/ bla*/ ldns_buffer_read_u8_at(buf, ldns_buffer_position(buf)-1) == '/') { ldns_buffer_skip(buf, -1); numdone--; skip_to_eol(in); (*line)++; continue; } else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */ ldns_buffer_read_u8_at(buf, ldns_buffer_position(buf)-1) == '/') { ldns_buffer_skip(buf, -1); numdone--; /* skip to end of comment */ while(c != EOF && (c=getc(in)) != EOF ) { if(c == '*') { if((c=getc(in)) == '/') break; } if(c == '\n') (*line)++; } continue; } /* not a comment, complete the keyword */ if(numdone > 0) { /* check same type */ if(isspace(c)) { ungetc(c, in); return numdone; } if(is_bind_special(c)) { ungetc(c, in); return numdone; } } if(c == '\n') { c = ' '; (*line)++; } /* space for 1 char + 0 string terminator */ if(ldns_buffer_remaining(buf) < 2) { fatal_exit("trusted-keys, %d, string too long", *line); } ldns_buffer_write_u8(buf, (uint8_t)c); numdone++; if(isspace(c)) { /* collate whitespace into ' ' */ while((c = getc(in)) != EOF ) { if(c == '\n') (*line)++; if(!isspace(c)) { ungetc(c, in); break; } } return numdone; } if(is_bind_special(c)) return numdone; } return numdone; }
/** 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, struct regional* region, int do_data, int do_sig, struct compress_tree_node** tree, ldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) { size_t i, j, owner_pos; int r, owner_labs; uint16_t owner_ptr = 0; struct packed_rrset_data* data = (struct packed_rrset_data*) key->entry.data; /* does this RR type belong in the answer? */ if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec)) return RETVAL_OK; owner_labs = dname_count_labels(key->rk.dname); owner_pos = ldns_buffer_position(pkt); if(do_data) { const ldns_rr_descriptor* c = type_rdata_compressable(key); for(i=0; i<data->count; i++) { /* rrset roundrobin */ j = (i + rr_offset) % data->count; if((r=compress_owner(key, pkt, region, tree, owner_pos, &owner_ptr, owner_labs)) != RETVAL_OK) return r; ldns_buffer_write(pkt, &key->rk.type, 2); ldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[j] < timenow) ldns_buffer_write_u32(pkt, 0); else ldns_buffer_write_u32(pkt, data->rr_ttl[j]-timenow); if(c) { if((r=compress_rdata(pkt, data->rr_data[j], data->rr_len[j], region, tree, c)) != RETVAL_OK) return r; } else { if(ldns_buffer_remaining(pkt) < data->rr_len[j]) return RETVAL_TRUNC; ldns_buffer_write(pkt, data->rr_data[j], data->rr_len[j]); } } } /* insert rrsigs */ if(do_sig && dnssec) { size_t total = data->count+data->rrsig_count; for(i=data->count; i<total; i++) { if(owner_ptr && owner_labs != 1) { if(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); if(data->rr_ttl[i] < timenow) ldns_buffer_write_u32(pkt, 0); else 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 && dnssec) *num_rrs += data->rrsig_count; 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, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; 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((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ ldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(ldns_buffer_begin(buffer)); ldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number */ rr_offset = RRSET_ROUNDROBIN?id:0; /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ 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); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ 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, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ ldns_buffer_write_u16_at(buffer, 10, arcount); ldns_buffer_flip(buffer); return 1; } return 0; } ldns_buffer_write_u16_at(buffer, 10, arcount); } else { ldns_buffer_write_u16_at(buffer, 8, nscount); ldns_buffer_write_u16_at(buffer, 10, arcount); } ldns_buffer_flip(buffer); return 1; }