/** perf test a packet */ static void perftestpkt(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out, const char* hex) { struct query_info qi; struct reply_info* rep = 0; int ret; uint16_t id; uint16_t flags; time_t timenow = 0; struct regional* region = regional_create(); struct edns_data edns; hex_to_buf(pkt, hex); memmove(&id, sldns_buffer_begin(pkt), sizeof(id)); if(sldns_buffer_limit(pkt) < 2) flags = 0; else memmove(&flags, sldns_buffer_at(pkt, 2), sizeof(flags)); flags = ntohs(flags); ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns); if(ret != 0) { char rbuf[16]; sldns_wire2str_rcode_buf(ret, rbuf, sizeof(rbuf)); if(vbmp) printf("parse code %d: %s\n", ret, rbuf); 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); }
int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, struct query_info* qinf, struct reply_info** rep, struct regional* region, struct edns_data* edns) { /* use scratch pad region-allocator during parsing. */ struct msg_parse* msg; int ret; qinf->qname = NULL; *rep = NULL; if(!(msg = regional_alloc(region, sizeof(*msg)))) { return LDNS_RCODE_SERVFAIL; } memset(msg, 0, sizeof(*msg)); sldns_buffer_set_position(pkt, 0); if((ret = parse_packet(pkt, msg, region)) != 0) { return ret; } if((ret = parse_extract_edns(msg, edns)) != 0) return ret; /* parse OK, allocate return structures */ /* this also performs dname decompression */ if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) { query_info_clear(qinf); reply_info_parsedelete(*rep, alloc); *rep = NULL; return LDNS_RCODE_SERVFAIL; } return 0; }
/** verify and test an entry - every rr in the message */ static void verifytest_entry(struct entry* e, struct alloc_cache* alloc, struct regional* region, ldns_buffer* pkt, struct ub_packed_rrset_key* dnskey, struct module_env* env, struct val_env* ve) { struct query_info qinfo; struct reply_info* rep = NULL; size_t i; regional_free_all(region); if(vsig) { printf("verifying pkt:\n"); ldns_pkt_print(stdout, e->reply_list->reply); printf("\n"); } entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep); for(i=0; i<rep->rrset_count; i++) { verifytest_rrset(env, ve, rep->rrsets[i], dnskey, &qinfo); } reply_info_parsedelete(rep, alloc); query_info_clear(&qinfo); }
int dns_cache_store(struct module_env* env, struct query_info* msgqinf, struct reply_info* msgrep, int is_referral, time_t leeway, int pside, struct regional* region, uint32_t flags) { struct reply_info* rep = NULL; /* alloc, malloc properly (not in region, like msg is) */ rep = reply_info_copy(msgrep, env->alloc, NULL); if(!rep) return 0; /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. * the env->now is added to message and RRsets in this routine. */ /* the leeway is used to invalidate other rrsets earlier */ if(is_referral) { /* store rrsets */ struct rrset_ref ref; size_t i; for(i=0; i<rep->rrset_count; i++) { packed_rrset_ttl_add((struct packed_rrset_data*) rep->rrsets[i]->entry.data, *env->now); ref.key = rep->rrsets[i]; ref.id = rep->rrsets[i]->id; /*ignore ret: it was in the cache, ref updated */ /* no leeway for typeNS */ (void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, *env->now + ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS && !pside) ? 0:leeway)); } free(rep); return 1; } else { /* store msg, and rrsets */ struct query_info qinf; hashvalue_type h; qinf = *msgqinf; qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len); if(!qinf.qname) { reply_info_parsedelete(rep, env->alloc); return 0; } /* fixup flags to be sensible for a reply based on the cache */ /* this module means that RA is available. It is an answer QR. * Not AA from cache. Not CD in cache (depends on client bit). */ rep->flags |= (BIT_RA | BIT_QR); rep->flags &= ~(BIT_AA | BIT_CD); h = query_info_hash(&qinf, (uint16_t)flags); dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep, flags, region); /* qname is used inside query_info_entrysetup, and set to * NULL. If it has not been used, free it. free(0) is safe. */ free(qinf.qname); } return 1; }
struct reply_info* reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region) { struct reply_info* cp; cp = construct_reply_info_base(region, rep->flags, rep->qdcount, rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count, rep->security); if(!cp) return NULL; /* allocate ub_key structures special or not */ if(!repinfo_alloc_rrset_keys(cp, alloc, region)) { if(!region) reply_info_parsedelete(cp, alloc); return NULL; } if(!repinfo_copy_rrsets(cp, rep, region)) { if(!region) reply_info_parsedelete(cp, alloc); return NULL; } return cp; }
/** Test hash algo - NSEC3 hash it and compare result */ static void nsec3_hash_test_entry(struct entry* e, rbtree_type* ct, struct alloc_cache* alloc, struct regional* region, sldns_buffer* buf) { struct query_info qinfo; struct reply_info* rep = NULL; struct ub_packed_rrset_key* answer, *nsec3; struct nsec3_cached_hash* hash = NULL; int ret; uint8_t* qname; if(vsig) { char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt, e->reply_list->reply_len); printf("verifying NSEC3 hash:\n%s\n", s?s:"outofmemory"); free(s); } entry_to_repinfo(e, alloc, region, buf, &qinfo, &rep); nsec3 = find_rrset_type(rep, LDNS_RR_TYPE_NSEC3); answer = find_rrset_type(rep, LDNS_RR_TYPE_AAAA); qname = regional_alloc_init(region, qinfo.qname, qinfo.qname_len); /* check test is OK */ unit_assert(nsec3 && answer && qname); ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname, qinfo.qname_len, &hash); if(ret != 1) { printf("Bad nsec3_hash_name retcode %d\n", ret); unit_assert(ret == 1); } unit_assert(hash->dname && hash->hash && hash->hash_len && hash->b32 && hash->b32_len); unit_assert(hash->b32_len == (size_t)answer->rk.dname[0]); /* does not do lowercasing. */ unit_assert(memcmp(hash->b32, answer->rk.dname+1, hash->b32_len) == 0); reply_info_parsedelete(rep, alloc); query_info_clear(&qinfo); }
/** DS sig test an entry - get DNSKEY and DS in entry and verify */ static void dstest_entry(struct entry* e, struct alloc_cache* alloc, struct regional* region, sldns_buffer* pkt, struct module_env* env) { struct query_info qinfo; struct reply_info* rep = NULL; struct ub_packed_rrset_key* ds, *dnskey; int ret; regional_free_all(region); if(vsig) { char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt, e->reply_list->reply_len); printf("verifying DS-DNSKEY match:\n%s\n", s?s:"outofmemory"); free(s); } entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep); ds = find_rrset_type(rep, LDNS_RR_TYPE_DS); dnskey = find_rrset_type(rep, LDNS_RR_TYPE_DNSKEY); /* check test is OK */ unit_assert(ds && dnskey); ret = ds_digest_match_dnskey(env, dnskey, 0, ds, 0); if(strncmp((char*)qinfo.qname, "\003yes", 4) == 0) { if(vsig) { printf("result(yes)= %s\n", ret?"yes":"no"); } unit_assert(ret); } else if (strncmp((char*)qinfo.qname, "\002no", 3) == 0) { if(vsig) { printf("result(no)= %s\n", ret?"yes":"no"); } unit_assert(!ret); verbose(VERB_QUERY, "DS fail: OK; matched unit test"); } else { fatal_exit("Bad qname in DS unit test, yes or no"); } reply_info_parsedelete(rep, alloc); query_info_clear(&qinfo); }
/** extract DNSKEY rrset from answer and convert it */ static struct ub_packed_rrset_key* extract_keys(struct entry* e, struct alloc_cache* alloc, struct regional* region, ldns_buffer* pkt) { struct ub_packed_rrset_key* dnskey = NULL; struct query_info qinfo; struct reply_info* rep = NULL; size_t i; entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep); for(i=0; i<rep->an_numrrsets; i++) { if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DNSKEY) { dnskey = rep->rrsets[i]; rep->rrsets[i] = NULL; break; } } unit_assert(dnskey); reply_info_parsedelete(rep, alloc); query_info_clear(&qinfo); return dnskey; }
/** test a packet */ static void testpkt(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_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, sldns_buffer_begin(pkt), sizeof(id)); if(sldns_buffer_limit(pkt) < 2) flags = 0; else memmove(&flags, sldns_buffer_at(pkt, 2), sizeof(flags)); flags = ntohs(flags); ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns); if(ret != 0) { char rbuf[16]; sldns_wire2str_rcode_buf(ret, rbuf, sizeof(rbuf)); if(vbmp) printf("parse code %d: %s\n", ret, rbuf); 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)sldns_buffer_limit(pkt), (unsigned)sldns_buffer_limit(out)); if(!check_nosameness) test_buffers(pkt, out); if(check_rrsigs) check_the_rrsigs(&qi, rep); if(sldns_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(sldns_buffer_begin(out)) != LDNS_QDCOUNT(sldns_buffer_begin(pkt)) || LDNS_ANCOUNT(sldns_buffer_begin(out)) != LDNS_ANCOUNT(sldns_buffer_begin(pkt)) || LDNS_NSCOUNT(sldns_buffer_begin(out)) != LDNS_NSCOUNT(sldns_buffer_begin(pkt))) unit_assert( LDNS_TC_WIRE(sldns_buffer_begin(out))); /* must set TC bit if shortened */ unit_assert(sldns_buffer_limit(out) <= lim); } } query_info_clear(&qi); reply_info_parsedelete(rep, alloc); regional_destroy(region); }