Ejemplo n.º 1
0
/**
 * 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++;
	}
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/** 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;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 9
0
/**
 * 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);
}
Ejemplo n.º 11
0
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 ++;
	}
}
Ejemplo n.º 12
0
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);
}
Ejemplo n.º 13
0
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);
}
Ejemplo n.º 14
0
Archivo: lua.c Proyecto: benlaurie/ldns
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;
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
/** 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);
}
Ejemplo n.º 18
0
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;
}
Ejemplo n.º 19
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;
}
Ejemplo n.º 20
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;
}
Ejemplo n.º 21
0
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;
}
Ejemplo n.º 22
0
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);
}
Ejemplo n.º 23
0
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);
	}
}
Ejemplo n.º 24
0
/** 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;
}
Ejemplo n.º 25
0
/** 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);
}
Ejemplo n.º 27
0
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;
}
Ejemplo n.º 29
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;
}
Ejemplo n.º 30
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)
{
	/* 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;
}