/** match all of the packet */
static int
match_all(ldns_pkt* q, ldns_pkt* p)
{
	if(ldns_pkt_get_opcode(q) != ldns_pkt_get_opcode(p))
	{ verbose(3, "allmatch: opcode different"); return 0;}
	if(ldns_pkt_get_rcode(q) != ldns_pkt_get_rcode(p))
	{ verbose(3, "allmatch: rcode different"); return 0;}
	if(ldns_pkt_id(q) != ldns_pkt_id(p))
	{ verbose(3, "allmatch: id different"); return 0;}
	if(cmp_bool(ldns_pkt_qr(q), ldns_pkt_qr(p)) != 0)
	{ verbose(3, "allmatch: qr different"); return 0;}
	if(cmp_bool(ldns_pkt_aa(q), ldns_pkt_aa(p)) != 0)
	{ verbose(3, "allmatch: aa different"); return 0;}
	if(cmp_bool(ldns_pkt_tc(q), ldns_pkt_tc(p)) != 0)
	{ verbose(3, "allmatch: tc different"); return 0;}
	if(cmp_bool(ldns_pkt_rd(q), ldns_pkt_rd(p)) != 0)
	{ verbose(3, "allmatch: rd different"); return 0;}
	if(cmp_bool(ldns_pkt_cd(q), ldns_pkt_cd(p)) != 0)
	{ verbose(3, "allmatch: cd different"); return 0;}
	if(cmp_bool(ldns_pkt_ra(q), ldns_pkt_ra(p)) != 0)
	{ verbose(3, "allmatch: ra different"); return 0;}
	if(cmp_bool(ldns_pkt_ad(q), ldns_pkt_ad(p)) != 0)
	{ verbose(3, "allmatch: ad different"); return 0;}
	if(ldns_pkt_qdcount(q) != ldns_pkt_qdcount(p))
	{ verbose(3, "allmatch: qdcount different"); return 0;}
	if(ldns_pkt_ancount(q) != ldns_pkt_ancount(p))
	{ verbose(3, "allmatch: ancount different"); return 0;}
	if(ldns_pkt_nscount(q) != ldns_pkt_nscount(p))
	{ verbose(3, "allmatch: nscount different"); return 0;}
	if(ldns_pkt_arcount(q) != ldns_pkt_arcount(p))
	{ verbose(3, "allmatch: arcount different"); return 0;}
	if(!match_list(ldns_pkt_question(q), ldns_pkt_question(p)))
	{ verbose(3, "allmatch: qd section different"); return 0;}
	if(!match_list(ldns_pkt_answer(q), ldns_pkt_answer(p)))
	{ verbose(3, "allmatch: an section different"); return 0;}
	if(!match_list(ldns_pkt_authority(q), ldns_pkt_authority(p)))
	{ verbose(3, "allmatch: ns section different"); return 0;}
	if(!match_list(ldns_pkt_additional(q), ldns_pkt_additional(p)))
	{ verbose(3, "allmatch: ar section different"); return 0;}
	if(!match_edns(q, p))
	{ verbose(3, "edns different."); return 0;}
	return 1;
}
Пример #2
0
/* rejects packets that arrive with OPCODE != QUERY, or QDCOUNT != 1 */
void query_only(evldns_server_request *srq, void *user_data, ldns_rdf *qname, ldns_rr_type qtype, ldns_rr_class qclass)
{
	ldns_pkt *req = srq->request;

	if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) {
		srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL);
	}

	if (ldns_pkt_qdcount(req) != 1) {
		srq->response = evldns_response(req, LDNS_RCODE_FORMERR);
	}
}
Пример #3
0
void dnspkt_proc (const char *bytes, uint16_t len, struct timeval ts, host_t *src, host_t *dst) {
    
    ldns_pkt *pkt;
    if (ldns_wire2pkt(&pkt, bytes, len) != LDNS_STATUS_OK) {
        return;
    }
    
    if (ldns_pkt_get_opcode(pkt) != LDNS_PACKET_QUERY) goto done;
    
    uint16_t i;
    ldns_rr_list *rrlist;
    
    if (ldns_pkt_qr(pkt) == 0) {
        rrlist = ldns_pkt_question(pkt);
        for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
            dnspkt_printqry(ldns_rr_list_rr(rrlist, i), ts, src, dst);
        }
    } else {
        dnspkt_printresp(pkt, ts, src, dst);
        if (ldns_pkt_aa(pkt) == 0) goto done;
        if (ldns_pkt_tc(pkt) == 1) goto done;
        if (ldns_pkt_rd(pkt) == 1) goto done;
        if (ldns_pkt_get_rcode(pkt) != LDNS_RCODE_NOERROR) goto done;
        rrlist = ldns_pkt_answer(pkt);
        for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
            dnspkt_printrr(ldns_rr_list_rr(rrlist, i), ts, src, dst, 0);
        }
        rrlist = ldns_pkt_authority(pkt);
        for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
            dnspkt_printrr(ldns_rr_list_rr(rrlist, i), ts, src, dst, 1);
        }
        rrlist = ldns_pkt_additional(pkt);
        for (i = 0; i < ldns_rr_list_rr_count(rrlist); i++) {
            dnspkt_printrr(ldns_rr_list_rr(rrlist, i), ts, src, dst, 2);
        }
    }
    
done:
    ldns_pkt_free(pkt);
    
}
Пример #4
0
void query_check(evldns_server_request *srq,
				 ATTR_UNUSED(void *user_data),
				 ATTR_UNUSED(ldns_rdf *qname),
				 ldns_rr_type qtype,
				 ldns_rr_class qclass)
{
	ldns_pkt *req = srq->request;

	/* only QUERY is supported */
	if (ldns_pkt_get_opcode(req) != LDNS_PACKET_QUERY) {
		srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL);
		return;
	}

	/* QDCOUNT == 1, NB: QR == 1 now handled upstream */
	if (ldns_pkt_qdcount(req) != 1) {
		srq->response = evldns_response(req, LDNS_RCODE_FORMERR);
		return;
	}

	/* Unexpected QCLASS */
	if (qclass != LDNS_RR_CLASS_IN) {
		srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL);
		return;
	}

	/* Unexpected QTYPE */
	if (qtype == LDNS_RR_TYPE_AXFR || qtype == LDNS_RR_TYPE_IXFR) {
		srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL);
		return;
	}

	/* Not going to handle QTYPE == ANY either */
	if (qtype == LDNS_RR_TYPE_ANY) {
		srq->response = evldns_response(req, LDNS_RCODE_NOTIMPL);
		return;
	}
}
Пример #5
0
/*
 * Copies the packet header data to the buffer in wire format
 */
static ldns_status
ldns_hdr2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet)
{
	uint8_t flags;
	uint16_t arcount;
	
	if (ldns_buffer_reserve(buffer, 12)) {
		ldns_buffer_write_u16(buffer, ldns_pkt_id(packet));
		
		flags = ldns_pkt_qr(packet) << 7
		        | ldns_pkt_get_opcode(packet) << 3
		        | ldns_pkt_aa(packet) << 2
		        | ldns_pkt_tc(packet) << 1 | ldns_pkt_rd(packet);
		ldns_buffer_write_u8(buffer, flags);
		
		flags = ldns_pkt_ra(packet) << 7
		        /*| ldns_pkt_z(packet) << 6*/
		        | ldns_pkt_ad(packet) << 5
		        | ldns_pkt_cd(packet) << 4 | ldns_pkt_get_rcode(packet);
		ldns_buffer_write_u8(buffer, flags);
		
		ldns_buffer_write_u16(buffer, ldns_pkt_qdcount(packet));
		ldns_buffer_write_u16(buffer, ldns_pkt_ancount(packet));
		ldns_buffer_write_u16(buffer, ldns_pkt_nscount(packet));
		/* add EDNS0 and TSIG to additional if they are there */
		arcount = ldns_pkt_arcount(packet);
		if (ldns_pkt_tsig(packet)) {
			arcount++;
		}
		if (ldns_pkt_edns(packet)) {
			arcount++;
		}
		ldns_buffer_write_u16(buffer, arcount);
	}
	
	return ldns_buffer_status(buffer);
}
Пример #6
0
/**
 * Process query.
 *
 */
query_state
query_process(query_type* q, void* engine)
{
    ldns_status status = LDNS_STATUS_OK;
    ldns_pkt* pkt = NULL;
    ldns_rr* rr = NULL;
    ldns_pkt_rcode rcode = LDNS_RCODE_NOERROR;
    ldns_pkt_opcode opcode = LDNS_PACKET_QUERY;
    ldns_rr_type qtype = LDNS_RR_TYPE_SOA;
    engine_type* e = (engine_type*) engine;
    ods_log_assert(e);
    ods_log_assert(q);
    ods_log_assert(q->buffer);
    if (!e || !q || !q->buffer) {
        ods_log_error("[%s] drop query: assertion error", query_str);
        return QUERY_DISCARDED; /* should not happen */
    }
    if (buffer_limit(q->buffer) < BUFFER_PKT_HEADER_SIZE) {
        ods_log_debug("[%s] drop query: packet too small", query_str);
        return QUERY_DISCARDED; /* too small */
    }
    if (buffer_pkt_qr(q->buffer)) {
        ods_log_debug("[%s] drop query: qr bit set", query_str);
        return QUERY_DISCARDED; /* not a query */
    }
    /* parse packet */
    status = ldns_wire2pkt(&pkt, buffer_current(q->buffer),
        buffer_remaining(q->buffer));
    if (status != LDNS_STATUS_OK) {
        ods_log_debug("[%s] got bad packet: %s", query_str,
            ldns_get_errorstr_by_id(status));
        return query_formerr(q);
    }
    rr = ldns_rr_list_rr(ldns_pkt_question(pkt), 0);
    lock_basic_lock(&e->zonelist->zl_lock);
    /* we can just lookup the zone, because we will only handle SOA queries,
       zone transfers, updates and notifies */
    q->zone = zonelist_lookup_zone_by_dname(e->zonelist, ldns_rr_owner(rr),
        ldns_rr_get_class(rr));
    /* don't answer for zones that are just added */
    if (q->zone && q->zone->zl_status == ZONE_ZL_ADDED) {
        ods_log_warning("[%s] zone %s just added, don't answer for now",
            query_str, q->zone->name);
        q->zone = NULL;
    }
    lock_basic_unlock(&e->zonelist->zl_lock);
    if (!q->zone) {
        ods_log_debug("[%s] zone not found", query_str);
        return query_servfail(q);
    }
    /* see if it is tsig signed */
    if (!query_find_tsig(q)) {
        return query_formerr(q);
    }
    /* else: valid tsig, or no tsig present */
    ods_log_debug("[%s] tsig %s", query_str, tsig_status2str(q->tsig_rr->status));
    rcode = query_process_tsig(q);
    if (rcode != LDNS_RCODE_NOERROR) {
        return query_error(q, rcode);
    }
    /* process edns */
    rcode = query_process_edns(q);
    if (rcode != LDNS_RCODE_NOERROR) {
        /* We should not return FORMERR, but BADVERS (=16).
         * BADVERS is created with Ext. RCODE, followed by RCODE.
         * Ext. RCODE is set to 1, RCODE must be 0 (getting 0x10 = 16).
         * Thus RCODE = NOERROR = NSD_RC_OK. */
        return query_error(q, LDNS_RCODE_NOERROR);
    }

    /* handle incoming request */
    opcode = ldns_pkt_get_opcode(pkt);
    qtype = ldns_rr_get_type(rr);
    ldns_pkt_free(pkt);

    switch (opcode) {
        case LDNS_PACKET_NOTIFY:
            return query_process_notify(q, qtype, engine);
        case LDNS_PACKET_QUERY:
            return query_process_query(q, qtype, engine);
        case LDNS_PACKET_UPDATE:
            return query_process_update(q);
        default:
            break;
    }
    return query_notimpl(q);
}
Пример #7
0
/* finds entry in list, or returns NULL */
struct entry* 
find_match(struct entry* entries, ldns_pkt* query_pkt,
	enum transport_type transport)
{
	struct entry* p = entries;
	ldns_pkt* reply = NULL;
	for(p=entries; p; p=p->next) {
		verbose(3, "comparepkt: ");
		reply = p->reply_list->reply;
		if(p->match_opcode && ldns_pkt_get_opcode(query_pkt) != 
			ldns_pkt_get_opcode(reply)) {
			verbose(3, "bad opcode\n");
			continue;
		}
		if(p->match_qtype && get_qtype(query_pkt) != get_qtype(reply)) {
			verbose(3, "bad qtype\n");
			continue;
		}
		if(p->match_qname) {
			if(!get_owner(query_pkt) || !get_owner(reply) ||
				ldns_dname_compare(
				get_owner(query_pkt), get_owner(reply)) != 0) {
				verbose(3, "bad qname\n");
				continue;
			}
		}
		if(p->match_subdomain) {
			if(!get_owner(query_pkt) || !get_owner(reply) ||
				(ldns_dname_compare(get_owner(query_pkt), 
				get_owner(reply)) != 0 &&
				!ldns_dname_is_subdomain(
				get_owner(query_pkt), get_owner(reply))))
			{
				verbose(3, "bad subdomain\n");
				continue;
			}
		}
		if(p->match_serial && get_serial(query_pkt) != p->ixfr_soa_serial) {
				verbose(3, "bad serial\n");
				continue;
		}
		if(p->match_do && !ldns_pkt_edns_do(query_pkt)) {
			verbose(3, "no DO bit set\n");
			continue;
		}
		if(p->match_noedns && ldns_pkt_edns(query_pkt)) {
			verbose(3, "bad; EDNS OPT present\n");
			continue;
		}
		if(p->match_transport != transport_any && p->match_transport != transport) {
			verbose(3, "bad transport\n");
			continue;
		}
		if(p->match_all && !match_all(query_pkt, reply, p->match_ttl)) {
			verbose(3, "bad allmatch\n");
			continue;
		}
		verbose(3, "match!\n");
		return p;
	}
	return NULL;
}
Пример #8
0
int output_cbor(iaddr from, iaddr to, uint8_t proto, unsigned flags, unsigned sport, unsigned dport, my_bpftimeval ts, const u_char *payload, size_t payloadlen) {
    ldns_pkt *pkt = 0;
    ldns_status ldns_rc;

    if (!payload) {
        return DUMP_CBOR_EINVAL;
    }
    if (!payloadlen) {
        return DUMP_CBOR_EINVAL;
    }

/*    if (!cbor_stringrefs) {*/
/*        cbor_stringrefs = calloc(1, cbor_stringref_size);*/
/*    }*/
    if (!cbor_buf) {
        if (!(cbor_buf = calloc(1, cbor_size + cbor_reserve))) {
            return DUMP_CBOR_ENOMEM;
        }
    }
    if (cbor_flushed) {
        CborError cbor_err;

        cbor_encoder_init(&cbor_root, cbor_buf, cbor_size, 0);
/*        cbor_err = cbor_encode_tag(&cbor_root, 256);*/
/*        if (cbor_err == CborNoError)*/
        cbor_err = cbor_encoder_create_array(&cbor_root, &cbor_pkts, CborIndefiniteLength);
        if (cbor_err != CborNoError) {
            fprintf(stderr, "cbor init error[%d]: %s\n", cbor_err, cbor_error_string(cbor_err));
            return DUMP_CBOR_ECBOR;
        }
        cbor_flushed = 0;
    }

    ldns_rc = ldns_wire2pkt(&pkt, payload, payloadlen);

    if (ldns_rc != LDNS_STATUS_OK) {
        fprintf(stderr, "ldns error [%d]: %s\n", ldns_rc, ldns_get_errorstr_by_id(ldns_rc));
        return DUMP_CBOR_ELDNS;
    }
    if (!pkt) {
        return DUMP_CBOR_ELDNS;
    }

    CborEncoder cbor, ip;
    CborError cbor_err = CborNoError;
    int should_flush = 0;

    cbor_err = append_cbor_map(&cbor_pkts, &cbor, CborIndefiniteLength, &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "dateSeconds", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_double(&cbor, (double)ts.tv_sec + ( (double)ts.tv_usec / 1000000 ), &should_flush);
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "dateNanoFractions", &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ts.tv_usec * 1000, &should_flush);*/

    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "ip", &should_flush);
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, proto, &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "sourceIpAddress", &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, ia_str(from), &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "sourcePort", &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, sport, &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "destinationIpAddress", &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, ia_str(to), &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "destinationPort", &should_flush);*/
/*            if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, dport, &should_flush);*/

    if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor, &ip, CborIndefiniteLength, &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&ip, proto, &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&ip, ia_str(from), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&ip, sport, &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&ip, ia_str(to), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&ip, dport, &should_flush);
    if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor, &ip, &should_flush);

    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "ID", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_id(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "QR", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_qr(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "Opcode", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_get_opcode(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "AA", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_aa(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "TC", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_tc(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "RD", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_rd(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "RA", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_ra(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "AD", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_ad(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "CD", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_boolean(&cbor, ldns_pkt_cd(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "RCODE", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_get_rcode(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "QDCOUNT", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_qdcount(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "ANCOUNT", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_ancount(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "NSCOUNT", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_nscount(pkt), &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "ARCOUNT", &should_flush);
    if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_pkt_arcount(pkt), &should_flush);

    /* questionRRs */

    if (ldns_pkt_qdcount(pkt) > 0) {
        ldns_rr_list *list = ldns_pkt_question(pkt);
        ldns_rr *rr;
        size_t n, qdcount = ldns_pkt_qdcount(pkt);
        ldns_buffer *dname;
        char *dname_str;

        if (!list) {
            ldns_pkt_free(pkt);
            return DUMP_CBOR_ELDNS;
        }
        rr = ldns_rr_list_rr(list, 0);
        if (!rr) {
            ldns_pkt_free(pkt);
            return DUMP_CBOR_ELDNS;
        }

        if (!(dname = ldns_buffer_new(512))) {
            ldns_pkt_free(pkt);
            return DUMP_CBOR_ENOMEM;
        }
        if (ldns_rdf2buffer_str_dname(dname, ldns_rr_owner(rr)) != LDNS_STATUS_OK) {
            ldns_buffer_free(dname);
            ldns_pkt_free(pkt);
            return DUMP_CBOR_ELDNS;
        }
        ldns_buffer_write_u8(dname, 0);
        if (!(dname_str = ldns_buffer_export(dname))) {
            ldns_buffer_free(dname);
            ldns_pkt_free(pkt);
            return DUMP_CBOR_ENOMEM;
        }

        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "QNAME", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, dname_str, &should_flush);
        free(dname_str);
        ldns_buffer_free(dname);
        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "QCLASS", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_rr_get_class(rr), &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "QTYPE", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor, ldns_rr_get_type(rr), &should_flush);

        if (qdcount > 1) {
            CborEncoder queries;

            if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "questionRRs", &should_flush);
            if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor, &queries, CborIndefiniteLength, &should_flush);
            for (n = 1; cbor_err == CborNoError && n < qdcount; n++) {
                CborEncoder query;

                rr = ldns_rr_list_rr(list, n);
                if (!rr) {
                    ldns_pkt_free(pkt);
                    return DUMP_CBOR_ELDNS;
                }

                if (!(dname = ldns_buffer_new(512))) {
                    ldns_pkt_free(pkt);
                    return DUMP_CBOR_ENOMEM;
                }
                if (ldns_rdf2buffer_str_dname(dname, ldns_rr_owner(rr)) != LDNS_STATUS_OK) {
                    ldns_buffer_free(dname);
                    ldns_pkt_free(pkt);
                    return DUMP_CBOR_ELDNS;
                }
                ldns_buffer_write_u8(dname, 0);
                if (!(dname_str = ldns_buffer_export(dname))) {
                    ldns_buffer_free(dname);
                    ldns_pkt_free(pkt);
                    return DUMP_CBOR_ENOMEM;
                }

                if (cbor_err == CborNoError) cbor_err = append_cbor_map(&queries, &query, CborIndefiniteLength, &should_flush);
                if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&query, "NAME", &should_flush);
                if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&query, dname_str, &should_flush);
                free(dname_str);
                ldns_buffer_free(dname);
                if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&query, "CLASS", &should_flush);
                if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&query, ldns_rr_get_class(rr), &should_flush);
                if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&query, "TYPE", &should_flush);
                if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&query, ldns_rr_get_type(rr), &should_flush);
                if (cbor_err == CborNoError) cbor_err = close_cbor_container(&queries, &query, &should_flush);
            }
            if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor, &queries, &should_flush);
        }
    }

    /* answerRRs */

    if (ldns_pkt_ancount(pkt) > 0) {
        CborEncoder cbor_rrs;

        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "answerRRs", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor, &cbor_rrs, CborIndefiniteLength, &should_flush);
        cbor_ldns_rr_list(&cbor_rrs, ldns_pkt_answer(pkt), ldns_pkt_ancount(pkt), &should_flush);
        if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor, &cbor_rrs, &should_flush);
    }

    /* authorityRRs */

    if (ldns_pkt_nscount(pkt) > 0) {
        CborEncoder cbor_rrs;

        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "authorityRRs", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor, &cbor_rrs, CborIndefiniteLength, &should_flush);
        cbor_ldns_rr_list(&cbor_rrs, ldns_pkt_authority(pkt), ldns_pkt_nscount(pkt), &should_flush);
        if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor, &cbor_rrs, &should_flush);
    }

    /* additionalRRs */

    if (ldns_pkt_arcount(pkt) > 0) {
        CborEncoder cbor_rrs;

        if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor, "additionalRRs", &should_flush);
        if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor, &cbor_rrs, CborIndefiniteLength, &should_flush);
        cbor_ldns_rr_list(&cbor_rrs, ldns_pkt_additional(pkt), ldns_pkt_arcount(pkt), &should_flush);
        if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor, &cbor_rrs, &should_flush);
    }

    ldns_pkt_free(pkt);

    if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor_pkts, &cbor, &should_flush);

    if (cbor_err != CborNoError) {
        fprintf(stderr, "cbor error[%d]: %s\n", cbor_err, cbor_error_string(cbor_err));
        return DUMP_CBOR_ECBOR;
    }

    if (should_flush) {
        if ((cbor_err = cbor_encoder_close_container_checked(&cbor_root, &cbor_pkts)) != CborNoError) {
            fprintf(stderr, "cbor error[%d]: %s\n", cbor_err, cbor_error_string(cbor_err));
            return DUMP_CBOR_ECBOR;
        }

        fprintf(stderr, "cbor output: %lu bytes\n", cbor_encoder_get_buffer_size(&cbor_root, cbor_buf));

        cbor_flushed = 1;
        return DUMP_CBOR_FLUSH;
    }

    return DUMP_CBOR_OK;
}