size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) { size_t i, hash_len; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, nm, nmlen); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); hash_len = nsec3_hash_algo_size_supported(algo); if(hash_len == 0) { log_err("nsec3 hash of unknown algo %d", algo); return 0; } if(hash_len > max) return 0; if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)res)) return 0; for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, res, hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)res)) return 0; } return hash_len; }
/** * Inser canonical owner name into buffer. * @param buf: buffer to insert into at current position. * @param k: rrset with its owner name. * @param sig: signature with signer name and label count. * must be length checked, at least 18 bytes long. * @param can_owner: position in buffer returned for future use. * @param can_owner_len: length of canonical owner name. */ static void insert_can_owner(sldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, uint8_t** can_owner, size_t* can_owner_len) { int rrsig_labels = (int)sig[3]; int fqdn_labels = dname_signame_label_count(k->rk.dname); *can_owner = sldns_buffer_current(buf); if(rrsig_labels == fqdn_labels) { /* no change */ sldns_buffer_write(buf, k->rk.dname, k->rk.dname_len); query_dname_tolower(*can_owner); *can_owner_len = k->rk.dname_len; return; } log_assert(rrsig_labels < fqdn_labels); /* *. | fqdn(rightmost rrsig_labels) */ if(rrsig_labels < fqdn_labels) { int i; uint8_t* nm = k->rk.dname; size_t len = k->rk.dname_len; /* so skip fqdn_labels-rrsig_labels */ for(i=0; i<fqdn_labels-rrsig_labels; i++) { dname_remove_label(&nm, &len); } *can_owner_len = len+2; sldns_buffer_write(buf, (uint8_t*)"\001*", 2); sldns_buffer_write(buf, nm, len); query_dname_tolower(*can_owner); } }
/** * Create a DS digest for a DNSKEY entry. * * @param env: module environment. Uses scratch space. * @param dnskey_rrset: DNSKEY rrset. * @param dnskey_idx: index of RR in rrset. * @param ds_rrset: DS rrset * @param ds_idx: index of RR in DS rrset. * @param digest: digest is returned in here (must be correctly sized). * @return false on error. */ static int ds_create_dnskey_digest(struct module_env* env, struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx, struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, uint8_t* digest) { sldns_buffer* b = env->scratch_buffer; uint8_t* dnskey_rdata; size_t dnskey_len; rrset_get_rdata(dnskey_rrset, dnskey_idx, &dnskey_rdata, &dnskey_len); /* create digest source material in buffer * digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); * DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */ sldns_buffer_clear(b); sldns_buffer_write(b, dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len); query_dname_tolower(sldns_buffer_begin(b)); sldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/ sldns_buffer_flip(b); return secalgo_ds_digest(ds_get_digest_algo(ds_rrset, ds_idx), (unsigned char*)sldns_buffer_begin(b), sldns_buffer_limit(b), (unsigned char*)digest); }
size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) { size_t i, hash_len; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, nm, nmlen); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); switch(algo) { #if defined(HAVE_EVP_SHA1) || defined(HAVE_NSS) case NSEC3_HASH_SHA1: #ifdef HAVE_SSL hash_len = SHA_DIGEST_LENGTH; #else hash_len = SHA1_LENGTH; #endif if(hash_len > max) return 0; # ifdef HAVE_SSL (void)SHA1((unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, res, hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); # ifdef HAVE_SSL (void)SHA1( (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif } break; #endif /* HAVE_EVP_SHA1 or NSS */ default: log_err("nsec3 hash of unknown algo %d", algo); return 0; } return hash_len; }
/** compress domain names in rdata, return RETVAL_* */ static int compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, struct regional* region, struct compress_tree_node** tree, const sldns_rr_descriptor* desc) { int labs, r, rdf = 0; size_t dname_len, len, pos = sldns_buffer_position(pkt); uint8_t count = desc->_dname_count; sldns_buffer_skip(pkt, 2); /* rdata len fill in later */ /* space for rdatalen checked for already */ rdata += 2; todolen -= 2; while(todolen > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: labs = dname_count_size_labels(rdata, &dname_len); if((r=compress_any_dname(rdata, pkt, labs, region, tree)) != RETVAL_OK) return r; rdata += dname_len; todolen -= dname_len; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = *rdata + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { /* copy over */ if(sldns_buffer_remaining(pkt) < len) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, len); todolen -= len; rdata += len; } rdf++; } /* copy remainder */ if(todolen > 0) { if(sldns_buffer_remaining(pkt) < todolen) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, todolen); } /* set rdata len */ sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2); return RETVAL_OK; }
void qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo) { uint16_t flags = 0; /* QUERY, NOERROR */ sldns_buffer_clear(pkt); log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/); sldns_buffer_skip(pkt, 2); /* id done later */ sldns_buffer_write_u16(pkt, flags); sldns_buffer_write_u16(pkt, 1); /* query count */ sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */ sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len); sldns_buffer_write_u16(pkt, qinfo->qtype); sldns_buffer_write_u16(pkt, qinfo->qclass); sldns_buffer_flip(pkt); }
/** compress a domain name */ static int write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs, struct compress_tree_node* p) { /* compress it */ int labcopy = labs - p->labs; uint8_t lablen; uint16_t ptr; if(labs == 1) { /* write root label */ if(sldns_buffer_remaining(pkt) < 1) return 0; sldns_buffer_write_u8(pkt, 0); return 1; } /* copy the first couple of labels */ while(labcopy--) { lablen = *dname++; if(sldns_buffer_remaining(pkt) < (size_t)lablen+1) return 0; sldns_buffer_write_u8(pkt, lablen); sldns_buffer_write(pkt, dname, lablen); dname += lablen; } /* insert compression ptr */ if(sldns_buffer_remaining(pkt) < 2) return 0; ptr = PTR_CREATE(p->offset); sldns_buffer_write_u16(pkt, ptr); return 1; }
/** * Fill CH class answer into buffer. Keeps query. * @param pkt: buffer * @param str: string to put into text record (<255). * @param edns: edns reply information. * @param worker: worker with scratch region. */ static void chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns, struct worker* worker) { size_t len = strlen(str); unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt)); unsigned int cd = LDNS_CD_WIRE(sldns_buffer_begin(pkt)); if(len>255) len=255; /* cap size of TXT record */ sldns_buffer_clear(pkt); sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */ sldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA)); if(rd) LDNS_RD_SET(sldns_buffer_begin(pkt)); if(cd) LDNS_CD_SET(sldns_buffer_begin(pkt)); sldns_buffer_write_u16(pkt, 1); /* qdcount */ sldns_buffer_write_u16(pkt, 1); /* ancount */ sldns_buffer_write_u16(pkt, 0); /* nscount */ sldns_buffer_write_u16(pkt, 0); /* arcount */ (void)query_dname_len(pkt); /* skip qname */ sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */ sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */ sldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT); sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH); sldns_buffer_write_u32(pkt, 0); /* TTL */ sldns_buffer_write_u16(pkt, sizeof(uint8_t) + len); sldns_buffer_write_u8(pkt, len); sldns_buffer_write(pkt, str, len); sldns_buffer_flip(pkt); edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) edns->opt_list = NULL; attach_edns_record(pkt, edns); }
static int testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env, char* key, struct sldns_buffer* result_buffer) { struct testframe_moddata* d = (struct testframe_moddata*) cachedb_env->backend_data; (void)env; verbose(VERB_ALGO, "testframe_lookup of %s", key); lock_basic_lock(&d->lock); if(d->stored_key && strcmp(d->stored_key, key) == 0) { if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) { lock_basic_unlock(&d->lock); return 0; /* too large */ } verbose(VERB_ALGO, "testframe_lookup found %d bytes", (int)d->stored_datalen); sldns_buffer_clear(result_buffer); sldns_buffer_write(result_buffer, d->stored_data, d->stored_datalen); sldns_buffer_flip(result_buffer); lock_basic_unlock(&d->lock); return 1; } lock_basic_unlock(&d->lock); return 0; }
void attach_edns_record(sldns_buffer* pkt, struct edns_data* edns) { size_t len; size_t rdatapos; struct edns_option* opt; if(!edns || !edns->edns_present) return; /* inc additional count */ sldns_buffer_write_u16_at(pkt, 10, sldns_buffer_read_u16_at(pkt, 10) + 1); len = sldns_buffer_limit(pkt); sldns_buffer_clear(pkt); sldns_buffer_set_position(pkt, len); /* write EDNS record */ sldns_buffer_write_u8(pkt, 0); /* '.' label */ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */ sldns_buffer_write_u16(pkt, edns->udp_size); /* class */ sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */ sldns_buffer_write_u8(pkt, edns->edns_version); sldns_buffer_write_u16(pkt, edns->bits); rdatapos = sldns_buffer_position(pkt); sldns_buffer_write_u16(pkt, 0); /* rdatalen */ /* write rdata */ for(opt=edns->opt_list; opt; opt=opt->next) { sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) sldns_buffer_write(pkt, opt->opt_data, opt->opt_len); } if(edns->opt_list) sldns_buffer_write_u16_at(pkt, rdatapos, sldns_buffer_position(pkt)-rdatapos-2); sldns_buffer_flip(pkt); }
/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ static int compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, struct regional* region, struct compress_tree_node** tree, size_t owner_pos, uint16_t* owner_ptr, int owner_labs) { struct compress_tree_node* p; struct compress_tree_node** insertpt = NULL; if(!*owner_ptr) { /* compress first time dname */ if((p = compress_tree_lookup(tree, key->rk.dname, owner_labs, &insertpt))) { if(p->labs == owner_labs) /* avoid ptr chains, since some software is * not capable of decoding ptr after a ptr. */ *owner_ptr = htons(PTR_CREATE(p->offset)); if(!write_compressed_dname(pkt, key->rk.dname, owner_labs, p)) return RETVAL_TRUNC; /* check if typeclass+4 ttl + rdatalen is available */ if(sldns_buffer_remaining(pkt) < 4+4+2) return RETVAL_TRUNC; } else { /* no compress */ if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2) return RETVAL_TRUNC; sldns_buffer_write(pkt, key->rk.dname, key->rk.dname_len); if(owner_pos <= PTR_MAX_OFFSET) *owner_ptr = htons(PTR_CREATE(owner_pos)); } if(!compress_tree_store(key->rk.dname, owner_labs, owner_pos, region, p, insertpt)) return RETVAL_OUTMEM; } else { /* always compress 2nd-further RRs in RRset */ if(owner_labs == 1) { if(sldns_buffer_remaining(pkt) < 1+4+4+2) return RETVAL_TRUNC; sldns_buffer_write_u8(pkt, 0); } else { if(sldns_buffer_remaining(pkt) < 2+4+4+2) return RETVAL_TRUNC; sldns_buffer_write(pkt, owner_ptr, 2); } } return RETVAL_OK; }
void qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo) { uint16_t flags = 0; /* QUERY, NOERROR */ const uint8_t* qname = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname : qinfo->qname; size_t qname_len = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len; sldns_buffer_clear(pkt); log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/); sldns_buffer_skip(pkt, 2); /* id done later */ sldns_buffer_write_u16(pkt, flags); sldns_buffer_write_u16(pkt, 1); /* query count */ sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */ sldns_buffer_write(pkt, qname, qname_len); sldns_buffer_write_u16(pkt, qinfo->qtype); sldns_buffer_write_u16(pkt, qinfo->qclass); sldns_buffer_flip(pkt); }
void error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, uint16_t qid, uint16_t qflags, struct edns_data* edns) { uint16_t flags; sldns_buffer_clear(buf); sldns_buffer_write(buf, &qid, sizeof(uint16_t)); flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */ sldns_buffer_write_u16(buf, flags); if(qinfo) flags = 1; else flags = 0; sldns_buffer_write_u16(buf, flags); flags = 0; sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); if(qinfo) { const uint8_t* qname = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname : qinfo->qname; size_t qname_len = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len; if(sldns_buffer_current(buf) == qname) sldns_buffer_skip(buf, (ssize_t)qname_len); else sldns_buffer_write(buf, qname, qname_len); sldns_buffer_write_u16(buf, qinfo->qtype); sldns_buffer_write_u16(buf, qinfo->qclass); } sldns_buffer_flip(buf); if(edns) { struct edns_data es = *edns; es.edns_version = EDNS_ADVERTISED_VERSION; es.udp_size = EDNS_ADVERTISED_SIZE; es.ext_rcode = 0; es.bits &= EDNS_DO; if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > edns->udp_size) return; attach_edns_record(buf, &es); } }
void sldns_buffer_copy(sldns_buffer* result, sldns_buffer* from) { size_t tocopy = sldns_buffer_limit(from); if(tocopy > sldns_buffer_capacity(result)) tocopy = sldns_buffer_capacity(result); sldns_buffer_clear(result); sldns_buffer_write(result, sldns_buffer_begin(from), tocopy); sldns_buffer_flip(result); }
/** entry to packet buffer with wireformat */ static void entry_to_buf(struct entry* e, sldns_buffer* pkt) { unit_assert(e->reply_list); if(e->reply_list->reply_from_hex) { sldns_buffer_copy(pkt, e->reply_list->reply_from_hex); } else { sldns_buffer_clear(pkt); sldns_buffer_write(pkt, e->reply_list->reply_pkt, e->reply_list->reply_len); sldns_buffer_flip(pkt); } }
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet, sldns_buffer* packet, struct sockaddr_storage* addr, socklen_t addrlen, int timeout, comm_point_callback_t* callback, void* callback_arg, int ATTR_UNUSED(ssl_upstream)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); log_assert(pend); pend->buffer = sldns_buffer_new(sldns_buffer_capacity(packet)); log_assert(pend->buffer); sldns_buffer_write(pend->buffer, sldns_buffer_begin(packet), sldns_buffer_limit(packet)); sldns_buffer_flip(pend->buffer); memcpy(&pend->addr, addr, addrlen); pend->addrlen = addrlen; pend->callback = callback; pend->cb_arg = callback_arg; pend->timeout = timeout; pend->transport = transport_tcp; pend->pkt = NULL; pend->zone = NULL; pend->runtime = runtime; pend->serviced = 0; pend->pkt_len = sldns_buffer_limit(packet); pend->pkt = memdup(sldns_buffer_begin(packet), pend->pkt_len); if(!pend->pkt) fatal_exit("out of memory"); log_pkt("pending tcp pkt: ", pend->pkt, pend->pkt_len); /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && (runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->pkt_len, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); log_info("testbound: do STEP %d %s", runtime->now->time_step, repevt_string(runtime->now->evt_type)); advance_moment(runtime); /* still create the pending, because we need it to callback */ } log_info("testbound: created fake pending"); /* add to list */ pend->next = runtime->pending_list; runtime->pending_list = pend; return (struct waiting_tcp*)pend; }
/** perform hash of name */ static int nsec3_calc_hash(struct regional* region, sldns_buffer* buf, struct nsec3_cached_hash* c) { int algo = nsec3_get_algo(c->nsec3, c->rr); size_t iter = nsec3_get_iter(c->nsec3, c->rr); uint8_t* salt; size_t saltlen, i; if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen)) return -1; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, c->dname, c->dname_len); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); c->hash_len = nsec3_hash_algo_size_supported(algo); if(c->hash_len == 0) { log_err("nsec3 hash of unknown algo %d", algo); return -1; } c->hash = (uint8_t*)regional_alloc(region, c->hash_len); if(!c->hash) return 0; (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)c->hash); for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, c->hash, c->hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)c->hash); } return 1; }
/** * 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, sldns_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; if(d->count > RR_COUNT_MAX) return 0; /* integer overflow protection */ 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); } sldns_buffer_clear(buf); sldns_buffer_write(buf, sig, siglen); /* canonicalize signer name */ query_dname_tolower(sldns_buffer_begin(buf)+18); RBTREE_FOR(walk, struct canon_rr*, (*sortree)) { /* see if there is enough space left in the buffer */ if(sldns_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) sldns_buffer_write(buf, can_owner, can_owner_len); else insert_can_owner(buf, k, sig, &can_owner, &can_owner_len); sldns_buffer_write(buf, &k->rk.type, 2); sldns_buffer_write(buf, &k->rk.rrset_class, 2); sldns_buffer_write(buf, sig+4, 4); sldns_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]); } sldns_buffer_flip(buf); return 1; }
int dname_buffer_write(sldns_buffer* pkt, uint8_t* dname) { uint8_t lablen; if(sldns_buffer_remaining(pkt) < 1) return 0; lablen = *dname++; sldns_buffer_write_u8(pkt, lablen); while(lablen) { if(sldns_buffer_remaining(pkt) < (size_t)lablen+1) return 0; sldns_buffer_write(pkt, dname, lablen); dname += lablen; lablen = *dname++; sldns_buffer_write_u8(pkt, lablen); } return 1; }
/** store query section in wireformat buffer, return RETVAL */ static int insert_query(struct query_info* qinfo, struct compress_tree_node** tree, sldns_buffer* buffer, struct regional* region) { if(sldns_buffer_remaining(buffer) < qinfo->qname_len+sizeof(uint16_t)*2) return RETVAL_TRUNC; /* buffer too small */ /* the query is the first name inserted into the tree */ if(!compress_tree_store(qinfo->qname, dname_count_labels(qinfo->qname), sldns_buffer_position(buffer), region, NULL, tree)) return RETVAL_OUTMEM; if(sldns_buffer_current(buffer) == qinfo->qname) sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); sldns_buffer_write_u16(buffer, qinfo->qtype); sldns_buffer_write_u16(buffer, qinfo->qclass); return RETVAL_OK; }
/** * Fill buffer with reply from the entry. */ static void fill_buffer_with_reply(sldns_buffer* buffer, struct entry* entry, uint8_t* q, size_t qlen) { uint8_t* c; size_t clen; log_assert(entry && entry->reply_list); sldns_buffer_clear(buffer); if(entry->reply_list->reply_from_hex) { c = sldns_buffer_begin(entry->reply_list->reply_from_hex); clen = sldns_buffer_limit(entry->reply_list->reply_from_hex); if(!c) fatal_exit("out of memory"); } else { c = entry->reply_list->reply_pkt; clen = entry->reply_list->reply_len; } if(c) { if(q) adjust_packet(entry, &c, &clen, q, qlen); sldns_buffer_write(buffer, c, clen); if(q) free(c); } sldns_buffer_flip(buffer); }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* "prepend" any local alias records in the answer section if this * response is supposed to be authoritative. Currently it should * be a single CNAME record (sanity-checked in worker_handle_request()) * but it can be extended if and when we support more variations of * aliases. */ if(qinfo->local_alias && (flags & BIT_AA)) { struct reply_info arep; time_t timezero = 0; /* to use the 'authoritative' TTL */ memset(&arep, 0, sizeof(arep)); arep.flags = rep->flags; arep.an_numrrsets = 1; arep.rrset_count = 1; arep.rrsets = &qinfo->local_alias->rrset; if((r=insert_section(&arep, 1, &ancount, buffer, 0, timezero, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }
/** process answer from bg worker */ static int process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len, ub_callback_t* cb, void** cbarg, int* err, struct ub_result** res) { struct ctx_query* q; if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) { log_err("error: bad data from bg worker %d", (int)context_serial_getcmd(msg, len)); return 0; } lock_basic_lock(&ctx->cfglock); q = context_deserialize_answer(ctx, msg, len, err); if(!q) { lock_basic_unlock(&ctx->cfglock); /* probably simply the lookup that failed, i.e. * response returned before cancel was sent out, so noerror */ return 1; } log_assert(q->async); /* grab cb while locked */ if(q->cancelled) { *cb = NULL; *cbarg = NULL; } else { *cb = q->cb; *cbarg = q->cb_arg; } if(*err) { *res = NULL; ub_resolve_free(q->res); } else { /* parse the message, extract rcode, fill result */ sldns_buffer* buf = sldns_buffer_new(q->msg_len); struct regional* region = regional_create(); *res = q->res; (*res)->rcode = LDNS_RCODE_SERVFAIL; if(region && buf) { sldns_buffer_clear(buf); sldns_buffer_write(buf, q->msg, q->msg_len); sldns_buffer_flip(buf); libworker_enter_result(*res, buf, region, q->msg_security); } (*res)->answer_packet = q->msg; (*res)->answer_len = (int)q->msg_len; q->msg = NULL; sldns_buffer_free(buf); regional_destroy(region); } q->res = NULL; /* delete the q from list */ (void)rbtree_delete(&ctx->queries, q->node.key); ctx->num_async--; context_query_delete(q); lock_basic_unlock(&ctx->cfglock); if(*cb) return 2; ub_resolve_free(*res); 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, sldns_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; sldns_buffer_clear(buf); while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { if(rdlen == 1 && sldns_buffer_position(buf) == 1 && isspace((unsigned char)*sldns_buffer_begin(buf))) { /* starting whitespace is removed */ sldns_buffer_clear(buf); continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '"') { /* remove " from the string */ if(contnum == 0) { quoted = 1; comments = 0; } sldns_buffer_skip(buf, -1); if(contnum > 0 && quoted) { if(sldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } sldns_buffer_write(buf, " DNSKEY ", 8); quoted = 0; comments = 1; } else if(contnum > 0) comments = !comments; continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == ';') { if(contnum < 5) { sldns_buffer_write_u8(buf, 0); log_err("line %d, bad key", *line); return 0; } sldns_buffer_skip(buf, -1); sldns_buffer_write_u8(buf, 0); str = strdup((char*)sldns_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); sldns_buffer_clear(buf); contnum = 0; quoted = 0; comments = 1; continue; } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '}') { if(contnum > 0) { sldns_buffer_write_u8(buf, 0); log_err("line %d, bad key before }", *line); return 0; } return 1; } else if(rdlen == 1 && isspace((unsigned char)sldns_buffer_current(buf)[-1])) { /* leave whitespace here */ } else { /* not space or whatnot, so actual content */ contnum ++; if(contnum == 1 && !quoted) { if(sldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } sldns_buffer_write(buf, " DNSKEY ", 8); } } } log_err("line %d, EOF before }", *line); return 0; }
/** perform hash of name */ static int nsec3_calc_hash(struct regional* region, sldns_buffer* buf, struct nsec3_cached_hash* c) { int algo = nsec3_get_algo(c->nsec3, c->rr); size_t iter = nsec3_get_iter(c->nsec3, c->rr); uint8_t* salt; size_t saltlen, i; if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen)) return -1; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, c->dname, c->dname_len); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); switch(algo) { #if defined(HAVE_EVP_SHA1) || defined(HAVE_NSS) case NSEC3_HASH_SHA1: #ifdef HAVE_SSL c->hash_len = SHA_DIGEST_LENGTH; #else c->hash_len = SHA1_LENGTH; #endif c->hash = (uint8_t*)regional_alloc(region, c->hash_len); if(!c->hash) return 0; # ifdef HAVE_SSL (void)SHA1((unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)c->hash); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)c->hash, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, c->hash, c->hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); # ifdef HAVE_SSL (void)SHA1( (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)c->hash); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)c->hash, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif } break; #endif /* HAVE_EVP_SHA1 or NSS */ default: log_err("nsec3 hash of unknown algo %d", algo); return -1; } return 1; }
struct serviced_query* outnet_serviced_query(struct outside_network* outnet, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, comm_point_callback_t* callback, void* callback_arg, sldns_buffer* ATTR_UNUSED(buff)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); char z[256]; log_assert(pend); log_nametypeclass(VERB_OPS, "pending serviced query", qname, qtype, qclass); dname_str(zone, z); verbose(VERB_OPS, "pending serviced query zone %s flags%s%s%s%s", z, (flags&BIT_RD)?" RD":"", (flags&BIT_CD)?" CD":"", (flags&~(BIT_RD|BIT_CD))?" MORE":"", (dnssec)?" DO":""); /* create packet with EDNS */ pend->buffer = sldns_buffer_new(512); log_assert(pend->buffer); sldns_buffer_write_u16(pend->buffer, 0); /* id */ sldns_buffer_write_u16(pend->buffer, flags); sldns_buffer_write_u16(pend->buffer, 1); /* qdcount */ sldns_buffer_write_u16(pend->buffer, 0); /* ancount */ sldns_buffer_write_u16(pend->buffer, 0); /* nscount */ sldns_buffer_write_u16(pend->buffer, 0); /* arcount */ sldns_buffer_write(pend->buffer, qname, qnamelen); sldns_buffer_write_u16(pend->buffer, qtype); sldns_buffer_write_u16(pend->buffer, qclass); sldns_buffer_flip(pend->buffer); if(1) { /* add edns */ struct edns_data edns; edns.edns_present = 1; edns.ext_rcode = 0; edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; edns.bits = 0; if(dnssec) edns.bits = EDNS_DO; attach_edns_record(pend->buffer, &edns); } memcpy(&pend->addr, addr, addrlen); pend->addrlen = addrlen; pend->zone = memdup(zone, zonelen); pend->zonelen = zonelen; pend->qtype = (int)qtype; log_assert(pend->zone); pend->callback = callback; pend->cb_arg = callback_arg; pend->timeout = UDP_AUTH_QUERY_TIMEOUT; pend->transport = transport_udp; /* pretend UDP */ pend->pkt = NULL; pend->runtime = runtime; pend->serviced = 1; pend->pkt_len = sldns_buffer_limit(pend->buffer); pend->pkt = memdup(sldns_buffer_begin(pend->buffer), pend->pkt_len); if(!pend->pkt) fatal_exit("out of memory"); /*log_pkt("pending serviced query: ", pend->pkt, pend->pkt_len);*/ /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && (runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->pkt_len, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); log_info("testbound: do STEP %d %s", runtime->now->time_step, repevt_string(runtime->now->evt_type)); advance_moment(runtime); /* still create the pending, because we need it to callback */ } log_info("testbound: created fake pending"); /* add to list */ pend->next = runtime->pending_list; runtime->pending_list = pend; return (struct serviced_query*)pend; }
/** test pkt_dname_len */ static void dname_test_pkt_dname_len(sldns_buffer* buff) { unit_show_func("util/data/dname.c", "pkt_dname_len"); sldns_buffer_clear(buff); sldns_buffer_write(buff, "\000", 1); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 1 ); unit_assert( sldns_buffer_position(buff) == 1); sldns_buffer_clear(buff); sldns_buffer_write(buff, "\003org\000", 5); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 5 ); unit_assert( sldns_buffer_position(buff) == 5); sldns_buffer_clear(buff); sldns_buffer_write(buff, "\002os\007example\003org\000", 16); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 16 ); unit_assert( sldns_buffer_position(buff) == 16); /* invalid compression pointer: to self */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\300\000os\007example\003org\000", 17); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 0 ); /* valid compression pointer */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\003com\000\040\300\000", 8); sldns_buffer_flip(buff); sldns_buffer_set_position(buff, 6); unit_assert( pkt_dname_len(buff) == 5 ); unit_assert( sldns_buffer_position(buff) == 8); /* unknown label type */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\002os\107example\003org\000", 16); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 0 ); /* label too long */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\002os\047example\003org\000", 16); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 0 ); /* label exceeds packet */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\002os\007example\007org\004", 16); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 0 ); /* name very long */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\020a1cdef5555544444" "\020a2cdef5555544444" "\020a3cdef5555544444" "\020a4cdef5555544444" "\020a5cdef5555544444" "\020a6cdef5555544444" "\020a7cdef5555544444" "\020a8cdef5555544444" "\020a9cdef5555544444" "\020aAcdef5555544444" "\020aBcdef5555544444" "\020aCcdef5555544444" "\020aDcdef5555544444" "\020aEcdef5555544444" /* 238 up to here */ "\007aabbccd" /* 246 up to here */ "\007example\000" /* 255 to here */ , 255); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 255 ); unit_assert( sldns_buffer_position(buff) == 255); /* name too long */ sldns_buffer_clear(buff); sldns_buffer_write(buff, "\020a1cdef5555544444" "\020a2cdef5555544444" "\020a3cdef5555544444" "\020a4cdef5555544444" "\020a5cdef5555544444" "\020a6cdef5555544444" "\020a7cdef5555544444" "\020a8cdef5555544444" "\020a9cdef5555544444" "\020aAcdef5555544444" "\020aBcdef5555544444" "\020aCcdef5555544444" "\020aXcdef5555544444" "\020aXcdef5555544444" "\020aXcdef5555544444" "\020aDcdef5555544444" "\020aEcdef5555544444" /* 238 up to here */ "\007aabbccd" /* 246 up to here */ "\007example\000" /* 255 to here */ , 255); sldns_buffer_flip(buff); unit_assert( pkt_dname_len(buff) == 0 ); }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }
/** store rrset in buffer in wireformat, return RETVAL_* */ static int packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, uint16_t* num_rrs, time_t timenow, struct regional* region, int do_data, int do_sig, struct compress_tree_node** tree, sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) { size_t i, j, owner_pos; int r, owner_labs; uint16_t owner_ptr = 0; struct packed_rrset_data* data = (struct packed_rrset_data*) key->entry.data; /* does this RR type belong in the answer? */ if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec)) return RETVAL_OK; owner_labs = dname_count_labels(key->rk.dname); owner_pos = sldns_buffer_position(pkt); if(do_data) { const sldns_rr_descriptor* c = type_rdata_compressable(key); for(i=0; i<data->count; i++) { /* rrset roundrobin */ j = (i + rr_offset) % data->count; if((r=compress_owner(key, pkt, region, tree, owner_pos, &owner_ptr, owner_labs)) != RETVAL_OK) return r; sldns_buffer_write(pkt, &key->rk.type, 2); sldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[j] < timenow) sldns_buffer_write_u32(pkt, 0); else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-timenow); if(c) { if((r=compress_rdata(pkt, data->rr_data[j], data->rr_len[j], region, tree, c)) != RETVAL_OK) return r; } else { if(sldns_buffer_remaining(pkt) < data->rr_len[j]) return RETVAL_TRUNC; sldns_buffer_write(pkt, data->rr_data[j], data->rr_len[j]); } } } /* insert rrsigs */ if(do_sig && dnssec) { size_t total = data->count+data->rrsig_count; for(i=data->count; i<total; i++) { if(owner_ptr && owner_labs != 1) { if(sldns_buffer_remaining(pkt) < 2+4+4+data->rr_len[i]) return RETVAL_TRUNC; sldns_buffer_write(pkt, &owner_ptr, 2); } else { if((r=compress_any_dname(key->rk.dname, pkt, owner_labs, region, tree)) != RETVAL_OK) return r; if(sldns_buffer_remaining(pkt) < 4+4+data->rr_len[i]) return RETVAL_TRUNC; } sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG); sldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[i] < timenow) sldns_buffer_write_u32(pkt, 0); else sldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow); /* rrsig rdata cannot be compressed, perform 100+ byte * memcopy. */ sldns_buffer_write(pkt, data->rr_data[i], data->rr_len[i]); } } /* change rrnum only after we are sure it fits */ if(do_data) *num_rrs += data->count; if(do_sig && dnssec) *num_rrs += data->rrsig_count; return RETVAL_OK; }