/** * Add new RR. It converts ldns RR to wire format. * @param anchors: anchor storage. * @param buffer: parsing buffer. * @param rr: the rr (allocated by caller). * @return NULL on error, else the trust anchor. */ static struct trust_anchor* anchor_store_new_rr(struct val_anchors* anchors, ldns_buffer* buffer, ldns_rr* rr) { struct trust_anchor* ta; ldns_rdf* owner = ldns_rr_owner(rr); ldns_status status; ldns_buffer_clear(buffer); ldns_buffer_skip(buffer, 2); /* skip rdatalen */ status = ldns_rr_rdata2buffer_wire(buffer, rr); if(status != LDNS_STATUS_OK) { log_err("error converting trustanchor to wireformat: %s", ldns_get_errorstr_by_id(status)); return NULL; } ldns_buffer_flip(buffer); ldns_buffer_write_u16_at(buffer, 0, ldns_buffer_limit(buffer) - 2); if(!(ta=anchor_store_new_key(anchors, ldns_rdf_data(owner), ldns_rr_get_type(rr), ldns_rr_get_class(rr), ldns_buffer_begin(buffer), ldns_buffer_limit(buffer)))) { return NULL; } log_nametypeclass(VERB_QUERY, "adding trusted key", ldns_rdf_data(owner), ldns_rr_get_type(rr), ldns_rr_get_class(rr)); return ta; }
/** 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++; } }
size_t nsec3_get_hashed(ldns_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 */ ldns_buffer_clear(buf); ldns_buffer_write(buf, nm, nmlen); query_dname_tolower(ldns_buffer_begin(buf)); ldns_buffer_write(buf, salt, saltlen); ldns_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*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf)); # endif for(i=0; i<iter; i++) { ldns_buffer_clear(buf); ldns_buffer_write(buf, res, hash_len); ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); # ifdef HAVE_SSL (void)SHA1( (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_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; }
/** check request sanity. * @param pkt: the wire packet to examine for sanity. * @param worker: parameters for checking. * @return error code, 0 OK, or -1 discard. */ static int worker_check_request(ldns_buffer* pkt, struct worker* worker) { if(ldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) { verbose(VERB_QUERY, "request too short, discarded"); return -1; } if(ldns_buffer_limit(pkt) > NORMAL_UDP_SIZE && worker->daemon->cfg->harden_large_queries) { verbose(VERB_QUERY, "request too large, discarded"); return -1; } if(LDNS_QR_WIRE(ldns_buffer_begin(pkt))) { verbose(VERB_QUERY, "request has QR bit on, discarded"); return -1; } if(LDNS_TC_WIRE(ldns_buffer_begin(pkt))) { LDNS_TC_CLR(ldns_buffer_begin(pkt)); verbose(VERB_QUERY, "request bad, has TC bit on"); return LDNS_RCODE_FORMERR; } if(LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) { verbose(VERB_QUERY, "request unknown opcode %d", LDNS_OPCODE_WIRE(ldns_buffer_begin(pkt))); return LDNS_RCODE_NOTIMPL; } if(LDNS_QDCOUNT(ldns_buffer_begin(pkt)) != 1) { verbose(VERB_QUERY, "request wrong nr qd=%d", LDNS_QDCOUNT(ldns_buffer_begin(pkt))); return LDNS_RCODE_FORMERR; } if(LDNS_ANCOUNT(ldns_buffer_begin(pkt)) != 0) { verbose(VERB_QUERY, "request wrong nr an=%d", LDNS_ANCOUNT(ldns_buffer_begin(pkt))); return LDNS_RCODE_FORMERR; } if(LDNS_NSCOUNT(ldns_buffer_begin(pkt)) != 0) { verbose(VERB_QUERY, "request wrong nr ns=%d", LDNS_NSCOUNT(ldns_buffer_begin(pkt))); return LDNS_RCODE_FORMERR; } if(LDNS_ARCOUNT(ldns_buffer_begin(pkt)) > 1) { verbose(VERB_QUERY, "request wrong nr ar=%d", LDNS_ARCOUNT(ldns_buffer_begin(pkt))); return LDNS_RCODE_FORMERR; } return 0; }
/** read qinfo from next three words */ static char* load_qinfo(char* str, struct query_info* qinfo, ldns_buffer* buf, struct regional* region) { /* s is part of the buf */ char* s = str; ldns_rr* rr; ldns_status status; /* skip three words */ s = strchr(str, ' '); if(s) s = strchr(s+1, ' '); if(s) s = strchr(s+1, ' '); if(!s) { log_warn("error line too short, %s", str); return NULL; } s[0] = 0; s++; /* parse them */ status = ldns_rr_new_question_frm_str(&rr, str, NULL, NULL); if(status != LDNS_STATUS_OK) { log_warn("error cannot parse: %s %s", ldns_get_errorstr_by_id(status), str); return NULL; } qinfo->qtype = ldns_rr_get_type(rr); qinfo->qclass = ldns_rr_get_class(rr); ldns_buffer_clear(buf); status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr)); ldns_rr_free(rr); if(status != LDNS_STATUS_OK) { log_warn("error cannot dname2wire: %s", ldns_get_errorstr_by_id(status)); return NULL; } ldns_buffer_flip(buf); qinfo->qname_len = ldns_buffer_limit(buf); qinfo->qname = (uint8_t*)regional_alloc_init(region, ldns_buffer_begin(buf), ldns_buffer_limit(buf)); if(!qinfo->qname) { log_warn("error out of memory"); return NULL; } return s; }
void pkt_dname_tolower(ldns_buffer* pkt, uint8_t* dname) { uint8_t lablen; int count = 0; if(dname >= ldns_buffer_end(pkt)) return; lablen = *dname++; while(lablen) { if(LABEL_IS_PTR(lablen)) { if((size_t)PTR_OFFSET(lablen, *dname) >= ldns_buffer_limit(pkt)) return; dname = ldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); lablen = *dname++; if(count++ > MAX_COMPRESS_PTRS) return; continue; } if(dname+lablen >= ldns_buffer_end(pkt)) return; while(lablen--) { *dname = (uint8_t)tolower((int)*dname); dname++; } if(dname >= ldns_buffer_end(pkt)) return; lablen = *dname++; } }
/** performance test message encoding */ static void perf_encode(struct query_info* qi, struct reply_info* rep, uint16_t id, uint16_t flags, ldns_buffer* out, uint32_t timenow, struct edns_data* edns) { static int num = 0; int ret; size_t max = 10000; size_t i; struct timeval start, end; double dt; struct regional* r2 = regional_create(); if(gettimeofday(&start, NULL) < 0) fatal_exit("gettimeofday: %s", strerror(errno)); /* encode a couple times */ for(i=0; i<max; i++) { ret = reply_info_encode(qi, rep, id, flags, out, timenow, r2, 65535, (int)(edns->bits & EDNS_DO) ); unit_assert(ret != 0); /* udp packets should fit */ attach_edns_record(out, edns); regional_free_all(r2); } if(gettimeofday(&end, NULL) < 0) fatal_exit("gettimeofday: %s", strerror(errno)); /* time in millisec */ dt = (double)(end.tv_sec - start.tv_sec)*1000. + ((double)end.tv_usec - (double)start.tv_usec)/1000.; printf("[%d] did %u in %g msec for %f encode/sec size %d\n", num++, (unsigned)max, dt, (double)max / (dt/1000.), (int)ldns_buffer_limit(out)); regional_destroy(r2); }
/** perf test a packet */ static void perftestpkt(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out, const char* hex) { struct query_info qi; struct reply_info* rep = 0; int ret; uint16_t id; uint16_t flags; uint32_t timenow = 0; struct regional* region = regional_create(); struct edns_data edns; hex_to_buf(pkt, hex); memmove(&id, ldns_buffer_begin(pkt), sizeof(id)); if(ldns_buffer_limit(pkt) < 2) flags = 0; else memmove(&flags, ldns_buffer_at(pkt, 2), sizeof(flags)); flags = ntohs(flags); ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns); if(ret != 0) { if(vbmp) printf("parse code %d: %s\n", ret, ldns_lookup_by_id(ldns_rcodes, ret)->name); if(ret == LDNS_RCODE_FORMERR) checkformerr(pkt); unit_assert(ret != LDNS_RCODE_SERVFAIL); } else { perf_encode(&qi, rep, id, flags, out, timenow, &edns); } query_info_clear(&qi); reply_info_parsedelete(rep, alloc); regional_destroy(region); }
/** * 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) { ldns_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. */ ldns_buffer_clear(b); ldns_buffer_write(b, dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len); query_dname_tolower(ldns_buffer_begin(b)); ldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/ ldns_buffer_flip(b); return secalgo_ds_digest(ds_get_digest_algo(ds_rrset, ds_idx), (unsigned char*)ldns_buffer_begin(b), ldns_buffer_limit(b), (unsigned char*)digest); }
/** * Fill buffer with reply from the entry. */ static void fill_buffer_with_reply(ldns_buffer* buffer, struct entry* entry, ldns_pkt* q) { ldns_status status; ldns_pkt* answer_pkt = NULL; log_assert(entry && entry->reply_list); ldns_buffer_clear(buffer); if(entry->reply_list->reply_from_hex) { status = ldns_buffer2pkt_wire(&answer_pkt, entry->reply_list->reply_from_hex); if(status != LDNS_STATUS_OK) { log_err("testbound: hex packet unparsable, used asis."); ldns_buffer_write(buffer, ldns_buffer_begin(entry->reply_list->reply_from_hex), ldns_buffer_limit(entry->reply_list->reply_from_hex)); } } else { answer_pkt = ldns_pkt_clone(entry->reply_list->reply); } if(answer_pkt) { if(q) adjust_packet(entry, answer_pkt, q); status = ldns_pkt2buffer_wire(buffer, answer_pkt); if(status != LDNS_STATUS_OK) fatal_exit("ldns: cannot pkt2buffer_wire parsed pkt"); } ldns_pkt_free(answer_pkt); ldns_buffer_flip(buffer); }
void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf) { if(stats->extended && ldns_buffer_limit(buf) != 0) { int r = (int)LDNS_RCODE_WIRE( ldns_buffer_begin(buf) ); stats->ans_rcode[r] ++; if(r == 0 && LDNS_ANCOUNT( ldns_buffer_begin(buf) ) == 0) stats->ans_rcode_nodata ++; } }
int ldns_bgetc(ldns_buffer *buffer) { if (!ldns_buffer_available_at(buffer, buffer->_position, sizeof(uint8_t))) { ldns_buffer_set_position(buffer, ldns_buffer_limit(buffer)); /* ldns_buffer_rewind(buffer);*/ return EOF; } return (int)ldns_buffer_read_u8(buffer); }
void ldns_buffer_copy(ldns_buffer* result, ldns_buffer* from) { size_t tocopy = ldns_buffer_limit(from); if(tocopy > ldns_buffer_capacity(result)) tocopy = ldns_buffer_capacity(result); ldns_buffer_clear(result); ldns_buffer_write(result, ldns_buffer_begin(from), tocopy); ldns_buffer_flip(result); }
static int l_buf_info(lua_State *L) { ldns_buffer *b = (ldns_buffer *)lua_touserdata(L, 1); if (!b) { return 0; } printf("capacity %d; position %d; limit %d\n", ldns_buffer_capacity(b), ldns_buffer_position(b), ldns_buffer_limit(b)); return 0; }
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, struct sockaddr_storage* addr, socklen_t addrlen, int timeout, comm_point_callback_t* callback, void* callback_arg) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); ldns_status status; log_assert(pend); pend->buffer = ldns_buffer_new(ldns_buffer_capacity(packet)); log_assert(pend->buffer); ldns_buffer_write(pend->buffer, ldns_buffer_begin(packet), ldns_buffer_limit(packet)); ldns_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->runtime = runtime; pend->serviced = 0; status = ldns_buffer2pkt_wire(&pend->pkt, packet); if(status != LDNS_STATUS_OK) { log_err("ldns error parsing tcp output packet: %s", ldns_get_errorstr_by_id(status)); fatal_exit("Sending unparseable DNS packets to servers!"); } log_pkt("pending tcp pkt: ", pend->pkt); /* 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->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; }
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; }
/** fillup fg results */ static void libworker_fillup_fg(struct ctx_query* q, int rcode, ldns_buffer* buf, enum sec_status s, char* why_bogus) { if(why_bogus) q->res->why_bogus = strdup(why_bogus); if(rcode != 0) { q->res->rcode = rcode; q->msg_security = s; return; } q->res->rcode = LDNS_RCODE_SERVFAIL; q->msg_security = 0; q->msg = memdup(ldns_buffer_begin(buf), ldns_buffer_limit(buf)); q->msg_len = ldns_buffer_limit(buf); if(!q->msg) { return; /* the error is in the rcode */ } /* canonname and results */ q->msg_security = s; libworker_enter_result(q->res, buf, q->w->env->scratch, s); }
int nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* rrset, int rr, ldns_buffer* buf) { uint8_t* next, *owner; size_t nextlen; int len; if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) return 0; /* malformed RR proves nothing */ /* check the owner name is a hashed value . apex * base32 encoded values must have equal length. * hash_value and next hash value must have equal length. */ if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0|| (size_t)*rrset->rk.dname != hash->b32_len || query_dname_compare(rrset->rk.dname+1+ (size_t)*rrset->rk.dname, zone) != 0) return 0; /* bad lengths or owner name */ /* This is the "normal case: owner < next and owner < hash < next */ if(label_compare_lower(rrset->rk.dname+1, hash->b32, hash->b32_len) < 0 && memcmp(hash->hash, next, nextlen) < 0) return 1; /* convert owner name from text to binary */ ldns_buffer_clear(buf); owner = ldns_buffer_begin(buf); len = ldns_b32_pton_extended_hex((char*)rrset->rk.dname+1, hash->b32_len, owner, ldns_buffer_limit(buf)); if(len<1) return 0; /* bad owner name in some way */ if((size_t)len != hash->hash_len || (size_t)len != nextlen) return 0; /* wrong length */ /* this is the end of zone case: next <= owner && * (hash > owner || hash < next) * this also covers the only-apex case of next==owner. */ if(memcmp(next, owner, nextlen) <= 0 && ( memcmp(hash->hash, owner, nextlen) > 0 || memcmp(hash->hash, next, nextlen) < 0)) { return 1; } return 0; }
/** Match q edns data to p raw edns data */ static int match_ednsdata(ldns_pkt* q, struct reply_packet* p) { size_t qdlen, pdlen; uint8_t *qd, *pd; if(!ldns_pkt_edns(q) || !ldns_pkt_edns_data(q)) { verbose(3, "No EDNS data\n"); return 0; } qdlen = ldns_rdf_size(ldns_pkt_edns_data(q)); pdlen = ldns_buffer_limit(p->raw_ednsdata); qd = ldns_rdf_data(ldns_pkt_edns_data(q)); pd = ldns_buffer_begin(p->raw_ednsdata); if( qdlen == pdlen && 0 == memcmp(qd, pd, qdlen) ) return 1; verbose(3, "EDNS data does not match.\n"); verbose_hex(3, qd, qdlen, "q:"); verbose_hex(3, pd, pdlen, "p:"); return 0; }
/** perform b32 encoding of hash */ static int nsec3_calc_b32(struct regional* region, ldns_buffer* buf, struct nsec3_cached_hash* c) { int r; ldns_buffer_clear(buf); r = ldns_b32_ntop_extended_hex(c->hash, c->hash_len, (char*)ldns_buffer_begin(buf), ldns_buffer_limit(buf)); if(r < 1) { log_err("b32_ntop_extended_hex: error in encoding: %d", r); return 0; } c->b32_len = (size_t)r; c->b32 = regional_alloc_init(region, ldns_buffer_begin(buf), c->b32_len); if(!c->b32) return 0; return 1; }
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; }
void attach_edns_record(ldns_buffer* pkt, struct edns_data* edns) { size_t len; if(!edns || !edns->edns_present) return; /* inc additional count */ ldns_buffer_write_u16_at(pkt, 10, ldns_buffer_read_u16_at(pkt, 10) + 1); len = ldns_buffer_limit(pkt); ldns_buffer_clear(pkt); ldns_buffer_set_position(pkt, len); /* write EDNS record */ ldns_buffer_write_u8(pkt, 0); /* '.' label */ ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */ ldns_buffer_write_u16(pkt, edns->udp_size); /* class */ ldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */ ldns_buffer_write_u8(pkt, edns->edns_version); ldns_buffer_write_u16(pkt, edns->bits); ldns_buffer_write_u16(pkt, 0); /* rdatalen */ ldns_buffer_flip(pkt); }
void error_encode(ldns_buffer* buf, int r, struct query_info* qinfo, uint16_t qid, uint16_t qflags, struct edns_data* edns) { uint16_t flags; ldns_buffer_clear(buf); ldns_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 */ ldns_buffer_write_u16(buf, flags); if(qinfo) flags = 1; else flags = 0; ldns_buffer_write_u16(buf, flags); flags = 0; ldns_buffer_write(buf, &flags, sizeof(uint16_t)); ldns_buffer_write(buf, &flags, sizeof(uint16_t)); ldns_buffer_write(buf, &flags, sizeof(uint16_t)); if(qinfo) { if(ldns_buffer_current(buf) == qinfo->qname) ldns_buffer_skip(buf, (ssize_t)qinfo->qname_len); else ldns_buffer_write(buf, qinfo->qname, qinfo->qname_len); ldns_buffer_write_u16(buf, qinfo->qtype); ldns_buffer_write_u16(buf, qinfo->qclass); } ldns_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(ldns_buffer_limit(buf) + calc_edns_field_size(&es) > edns->udp_size) return; attach_edns_record(buf, &es); } }
/** perform hash of name */ static int nsec3_calc_hash(struct regional* region, ldns_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 */ ldns_buffer_clear(buf); ldns_buffer_write(buf, c->dname, c->dname_len); query_dname_tolower(ldns_buffer_begin(buf)); ldns_buffer_write(buf, salt, saltlen); ldns_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*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)c->hash); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)c->hash, (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf)); # endif for(i=0; i<iter; i++) { ldns_buffer_clear(buf); ldns_buffer_write(buf, c->hash, c->hash_len); ldns_buffer_write(buf, salt, saltlen); ldns_buffer_flip(buf); # ifdef HAVE_SSL (void)SHA1( (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_buffer_limit(buf), (unsigned char*)c->hash); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)c->hash, (unsigned char*)ldns_buffer_begin(buf), (unsigned long)ldns_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; }
/** load an RR into rrset */ static int load_rr(SSL* ssl, ldns_buffer* buf, struct regional* region, struct ub_packed_rrset_key* rk, struct packed_rrset_data* d, unsigned int i, int is_rrsig, int* go_on, uint32_t now) { ldns_rr* rr; ldns_status status; /* read the line */ if(!ssl_read_buf(ssl, buf)) return 0; if(strncmp((char*)ldns_buffer_begin(buf), "BADRR\n", 6) == 0) { *go_on = 0; return 1; } status = ldns_rr_new_frm_str(&rr, (char*)ldns_buffer_begin(buf), LDNS_DEFAULT_TTL, NULL, NULL); if(status != LDNS_STATUS_OK) { log_warn("error cannot parse rr: %s: %s", ldns_get_errorstr_by_id(status), (char*)ldns_buffer_begin(buf)); return 0; } if(is_rrsig && ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) { log_warn("error expected rrsig but got %s", (char*)ldns_buffer_begin(buf)); return 0; } /* convert ldns rr into packed_rr */ d->rr_ttl[i] = ldns_rr_ttl(rr) + now; ldns_buffer_clear(buf); ldns_buffer_skip(buf, 2); status = ldns_rr_rdata2buffer_wire(buf, rr); if(status != LDNS_STATUS_OK) { log_warn("error cannot rr2wire: %s", ldns_get_errorstr_by_id(status)); ldns_rr_free(rr); return 0; } ldns_buffer_flip(buf); ldns_buffer_write_u16_at(buf, 0, ldns_buffer_limit(buf) - 2); d->rr_len[i] = ldns_buffer_limit(buf); d->rr_data[i] = (uint8_t*)regional_alloc_init(region, ldns_buffer_begin(buf), ldns_buffer_limit(buf)); if(!d->rr_data[i]) { ldns_rr_free(rr); log_warn("error out of memory"); return 0; } /* if first entry, fill the key structure */ if(i==0) { rk->rk.type = htons(ldns_rr_get_type(rr)); rk->rk.rrset_class = htons(ldns_rr_get_class(rr)); ldns_buffer_clear(buf); status = ldns_dname2buffer_wire(buf, ldns_rr_owner(rr)); if(status != LDNS_STATUS_OK) { log_warn("error cannot dname2buffer: %s", ldns_get_errorstr_by_id(status)); ldns_rr_free(rr); return 0; } ldns_buffer_flip(buf); rk->rk.dname_len = ldns_buffer_limit(buf); rk->rk.dname = regional_alloc_init(region, ldns_buffer_begin(buf), ldns_buffer_limit(buf)); if(!rk->rk.dname) { log_warn("error out of memory"); ldns_rr_free(rr); return 0; } } ldns_rr_free(rr); return 1; }
/** test a packet */ static void testpkt(ldns_buffer* pkt, struct alloc_cache* alloc, ldns_buffer* out, const char* hex) { struct query_info qi; struct reply_info* rep = 0; int ret; uint16_t id; uint16_t flags; uint32_t timenow = 0; struct regional* region = regional_create(); struct edns_data edns; hex_to_buf(pkt, hex); memmove(&id, ldns_buffer_begin(pkt), sizeof(id)); if(ldns_buffer_limit(pkt) < 2) flags = 0; else memmove(&flags, ldns_buffer_at(pkt, 2), sizeof(flags)); flags = ntohs(flags); ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns); if(ret != 0) { if(vbmp) printf("parse code %d: %s\n", ret, ldns_lookup_by_id(ldns_rcodes, ret)->name); if(ret == LDNS_RCODE_FORMERR) { unit_assert(!check_formerr_gone); checkformerr(pkt); } unit_assert(ret != LDNS_RCODE_SERVFAIL); } else if(!check_formerr_gone) { const size_t lim = 512; ret = reply_info_encode(&qi, rep, id, flags, out, timenow, region, 65535, (int)(edns.bits & EDNS_DO) ); unit_assert(ret != 0); /* udp packets should fit */ attach_edns_record(out, &edns); if(vbmp) printf("inlen %u outlen %u\n", (unsigned)ldns_buffer_limit(pkt), (unsigned)ldns_buffer_limit(out)); test_buffers(pkt, out); if(check_rrsigs) check_the_rrsigs(&qi, rep); if(ldns_buffer_limit(out) > lim) { ret = reply_info_encode(&qi, rep, id, flags, out, timenow, region, lim - calc_edns_field_size(&edns), (int)(edns.bits & EDNS_DO)); unit_assert(ret != 0); /* should fit, but with TC */ attach_edns_record(out, &edns); if( LDNS_QDCOUNT(ldns_buffer_begin(out)) != LDNS_QDCOUNT(ldns_buffer_begin(pkt)) || LDNS_ANCOUNT(ldns_buffer_begin(out)) != LDNS_ANCOUNT(ldns_buffer_begin(pkt)) || LDNS_NSCOUNT(ldns_buffer_begin(out)) != LDNS_NSCOUNT(ldns_buffer_begin(pkt))) unit_assert( LDNS_TC_WIRE(ldns_buffer_begin(out))); /* must set TC bit if shortened */ unit_assert(ldns_buffer_limit(out) <= lim); } } query_info_clear(&qi); reply_info_parsedelete(rep, alloc); regional_destroy(region); }
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; }
/** see if buffers contain the same packet */ static int test_buffers(ldns_buffer* pkt, ldns_buffer* out) { ldns_pkt* p1=0, *p2=0; ldns_status s1, s2; /* check binary same */ if(ldns_buffer_limit(pkt) == ldns_buffer_limit(out) && memcmp(ldns_buffer_begin(pkt), ldns_buffer_begin(out), ldns_buffer_limit(pkt)) == 0) { if(vbmp) printf("binary the same (length=%u)\n", (unsigned)ldns_buffer_limit(pkt)); return 1; } if(vbmp) { size_t sz = 16; size_t count; size_t lim = ldns_buffer_limit(out); if(ldns_buffer_limit(pkt) < lim) lim = ldns_buffer_limit(pkt); for(count=0; count<lim; count+=sz) { size_t rem = sz; if(lim-count < sz) rem = lim-count; if(memcmp(ldns_buffer_at(pkt, count), ldns_buffer_at(out, count), rem) == 0) { log_info("same %d %d", (int)count, (int)rem); log_hex("same: ", ldns_buffer_at(pkt, count), rem); } else { log_info("diff %d %d", (int)count, (int)rem); log_hex("difp: ", ldns_buffer_at(pkt, count), rem); log_hex("difo: ", ldns_buffer_at(out, count), rem); } } } /* check if it 'means the same' */ s1 = ldns_buffer2pkt_wire(&p1, pkt); s2 = ldns_buffer2pkt_wire(&p2, out); if(vbmp) { log_buf(0, "orig in hex", pkt); log_buf(0, "unbound out in hex", out); printf("\npacket from unbound (%d):\n", (int)ldns_buffer_limit(out)); ldns_pkt_print(stdout, p2); printf("\npacket original (%d):\n", (int)ldns_buffer_limit(pkt)); ldns_pkt_print(stdout, p1); printf("\n"); } if(s1 != s2) { /* oops! */ printf("input ldns parse: %s, output ldns parse: %s.\n", ldns_get_errorstr_by_id(s1), ldns_get_errorstr_by_id(s2)); unit_assert(0); } /* compare packets */ unit_assert(match_all(p1, p2)); ldns_pkt_free(p1); ldns_pkt_free(p2); return 0; }
/** * Check a canonical sig+rrset and signature against a dnskey * @param buf: buffer with data to verify, the first rrsig part and the * canonicalized rrset. * @param algo: DNSKEY algorithm. * @param sigblock: signature rdata field from RRSIG * @param sigblock_len: length of sigblock data. * @param key: public key data from DNSKEY RR. * @param keylen: length of keydata. * @param reason: bogus reason in more detail. * @return secure if verification succeeded, bogus on crypto failure, * unchecked on format errors and alloc failures. */ enum sec_status verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, unsigned int sigblock_len, unsigned char* key, unsigned int keylen, char** reason) { const EVP_MD *digest_type; EVP_MD_CTX ctx; int res, dofree = 0; EVP_PKEY *evp_key = NULL; if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) { verbose(VERB_QUERY, "verify: failed to setup key"); *reason = "use of key for crypto failed"; EVP_PKEY_free(evp_key); return sec_status_bogus; } /* if it is a DSA signature in bind format, convert to DER format */ if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) && sigblock_len == 1+2*SHA_DIGEST_LENGTH) { if(!setup_dsa_sig(&sigblock, &sigblock_len)) { verbose(VERB_QUERY, "verify: failed to setup DSA sig"); *reason = "use of key for DSA crypto failed"; EVP_PKEY_free(evp_key); return sec_status_bogus; } dofree = 1; } #ifdef USE_ECDSA else if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) { /* EVP uses ASN prefix on sig, which is not in the wire data */ if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) { verbose(VERB_QUERY, "verify: failed to setup ECDSA sig"); *reason = "use of signature for ECDSA crypto failed"; EVP_PKEY_free(evp_key); return sec_status_bogus; } dofree = 1; } #endif /* USE_ECDSA */ /* do the signature cryptography work */ EVP_MD_CTX_init(&ctx); if(EVP_VerifyInit(&ctx, digest_type) == 0) { verbose(VERB_QUERY, "verify: EVP_VerifyInit failed"); EVP_PKEY_free(evp_key); if(dofree) free(sigblock); return sec_status_unchecked; } if(EVP_VerifyUpdate(&ctx, (unsigned char*)ldns_buffer_begin(buf), (unsigned int)ldns_buffer_limit(buf)) == 0) { verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed"); EVP_PKEY_free(evp_key); if(dofree) free(sigblock); return sec_status_unchecked; } res = EVP_VerifyFinal(&ctx, sigblock, sigblock_len, evp_key); if(EVP_MD_CTX_cleanup(&ctx) == 0) { verbose(VERB_QUERY, "verify: EVP_MD_CTX_cleanup failed"); EVP_PKEY_free(evp_key); if(dofree) free(sigblock); return sec_status_unchecked; } EVP_PKEY_free(evp_key); if(dofree) free(sigblock); if(res == 1) { return sec_status_secure; } else if(res == 0) { verbose(VERB_QUERY, "verify: signature mismatch"); *reason = "signature crypto failed"; return sec_status_bogus; } log_crypto_error("verify:", ERR_get_error()); return sec_status_unchecked; }
/** * Check a canonical sig+rrset and signature against a dnskey * @param buf: buffer with data to verify, the first rrsig part and the * canonicalized rrset. * @param algo: DNSKEY algorithm. * @param sigblock: signature rdata field from RRSIG * @param sigblock_len: length of sigblock data. * @param key: public key data from DNSKEY RR. * @param keylen: length of keydata. * @param reason: bogus reason in more detail. * @return secure if verification succeeded, bogus on crypto failure, * unchecked on format errors and alloc failures. */ enum sec_status verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, unsigned int sigblock_len, unsigned char* key, unsigned int keylen, char** reason) { /* uses libNSS */ /* large enough for the different hashes */ unsigned char hash[HASH_LENGTH_MAX]; unsigned char hash2[HASH_LENGTH_MAX*2]; HASH_HashType htype = 0; SECKEYPublicKey* pubkey = NULL; SECItem secsig = {siBuffer, sigblock, sigblock_len}; SECItem sechash = {siBuffer, hash, 0}; SECStatus res; unsigned char* prefix = NULL; /* prefix for hash, RFC3110, RFC5702 */ size_t prefixlen = 0; int err; if(!nss_setup_key_digest(algo, &pubkey, &htype, key, keylen, &prefix, &prefixlen)) { verbose(VERB_QUERY, "verify: failed to setup key"); *reason = "use of key for crypto failed"; SECKEY_DestroyPublicKey(pubkey); return sec_status_bogus; } /* need to convert DSA, ECDSA signatures? */ if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3)) { if(sigblock_len == 1+2*SHA1_LENGTH) { secsig.data ++; secsig.len --; } else { SECItem* p = DSAU_DecodeDerSig(&secsig); if(!p) { verbose(VERB_QUERY, "verify: failed DER decode"); *reason = "signature DER decode failed"; SECKEY_DestroyPublicKey(pubkey); return sec_status_bogus; } if(SECITEM_CopyItem(pubkey->arena, &secsig, p)) { log_err("alloc failure in DER decode"); SECKEY_DestroyPublicKey(pubkey); SECITEM_FreeItem(p, PR_TRUE); return sec_status_unchecked; } SECITEM_FreeItem(p, PR_TRUE); } } /* do the signature cryptography work */ /* hash the data */ sechash.len = HASH_ResultLen(htype); if(sechash.len > sizeof(hash)) { verbose(VERB_QUERY, "verify: hash too large for buffer"); SECKEY_DestroyPublicKey(pubkey); return sec_status_unchecked; } if(HASH_HashBuf(htype, hash, (unsigned char*)ldns_buffer_begin(buf), (unsigned int)ldns_buffer_limit(buf)) != SECSuccess) { verbose(VERB_QUERY, "verify: HASH_HashBuf failed"); SECKEY_DestroyPublicKey(pubkey); return sec_status_unchecked; } if(prefix) { int hashlen = sechash.len; if(prefixlen+hashlen > sizeof(hash2)) { verbose(VERB_QUERY, "verify: hashprefix too large"); SECKEY_DestroyPublicKey(pubkey); return sec_status_unchecked; } sechash.data = hash2; sechash.len = prefixlen+hashlen; memcpy(sechash.data, prefix, prefixlen); memmove(sechash.data+prefixlen, hash, hashlen); } /* verify the signature */ res = PK11_Verify(pubkey, &secsig, &sechash, NULL /*wincx*/); SECKEY_DestroyPublicKey(pubkey); if(res == SECSuccess) { return sec_status_secure; } err = PORT_GetError(); if(err != SEC_ERROR_BAD_SIGNATURE) { /* failed to verify */ verbose(VERB_QUERY, "verify: PK11_Verify failed: %s", PORT_ErrorToString(err)); /* if it is not supported, like ECC is removed, we get, * SEC_ERROR_NO_MODULE */ if(err == SEC_ERROR_NO_MODULE) return sec_status_unchecked; /* but other errors are commonly returned * for a bad signature from NSS. Thus we return bogus, * not unchecked */ *reason = "signature crypto failed"; return sec_status_bogus; } verbose(VERB_QUERY, "verify: signature mismatch: %s", PORT_ErrorToString(err)); *reason = "signature crypto failed"; return sec_status_bogus; }