Esempio n. 1
0
static void
encode_dname(query_type *q, domain_type *domain)
{
	while (domain->parent && query_get_dname_offset(q, domain) == 0) {
		query_put_dname_offset(q, domain, buffer_position(q->packet));
		DEBUG(DEBUG_NAME_COMPRESSION, 2,
		      (LOG_INFO, "dname: %s, number: %lu, offset: %u\n",
		       dname_to_string(domain_dname(domain), NULL),
		       (unsigned long) domain->number,
		       query_get_dname_offset(q, domain)));
		buffer_write(q->packet, dname_name(domain_dname(domain)),
			     label_length(dname_name(domain_dname(domain))) + 1U);
		domain = domain->parent;
	}
	if (domain->parent) {
		DEBUG(DEBUG_NAME_COMPRESSION, 2,
		      (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n",
		       dname_to_string(domain_dname(domain), NULL),
		       (unsigned long) domain->number,
		       query_get_dname_offset(q, domain)));
		assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET);
		buffer_write_u16(q->packet,
				 0xc000 | query_get_dname_offset(q, domain));
	} else {
		buffer_write_u8(q->packet, 0);
	}
}
Esempio n. 2
0
void
xfrd_handle_passed_packet(buffer_type* packet, int acl_num)
{
	uint8_t qnamebuf[MAXDOMAINLEN];
	uint16_t qtype, qclass;
	const dname_type* dname;
	region_type* tempregion = region_create(xalloc, free);
	xfrd_zone_t* zone;

	buffer_skip(packet, QHEADERSZ);
	if(!packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) {
		region_destroy(tempregion);
		return; /* drop bad packet */
	}

	dname = dname_make(tempregion, qnamebuf, 1);
	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: got passed packet for %s, acl "
					   "%d", dname_to_string(dname,0), acl_num));

	/* find the zone */
	zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname);
	if(!zone) {
		log_msg(LOG_INFO, "xfrd: incoming packet for unknown zone %s",
			dname_to_string(dname,0));
		region_destroy(tempregion);
		return; /* drop packet for unknown zone */
	}
	region_destroy(tempregion);

	/* handle */
	if(OPCODE(packet) == OPCODE_NOTIFY) {
		xfrd_soa_t soa;
		int have_soa = 0;
		int next;
		/* get serial from a SOA */
		if(ANCOUNT(packet) == 1 && packet_skip_dname(packet) &&
			xfrd_parse_soa_info(packet, &soa)) {
				have_soa = 1;
		}
		if(xfrd_handle_incoming_notify(zone, have_soa?&soa:NULL)) {
			if(zone->zone_handler.fd == -1
				&& zone->tcp_conn == -1 &&
				!zone->tcp_waiting && !zone->udp_waiting) {
					xfrd_set_refresh_now(zone);
			}
		}
		next = find_same_master_notify(zone, acl_num);
		if(next != -1) {
			zone->next_master = next;
			DEBUG(DEBUG_XFRD,1, (LOG_INFO,
				"xfrd: notify set next master to query %d",
				next));
		}
	}
	else {
		/* TODO handle incoming IXFR udp reply via port 53 */
	}
}
Esempio n. 3
0
static void
delete_zone_rrs(namedb_type* db, zone_type* zone)
{
	rrset_type *rrset;
	domain_type *domain = zone->apex;
	domain_type *next = NULL;
	zone->updated = 1;
#ifdef NSEC3
#ifndef FULL_PREHASH
	zone_nsec3_domains_destroy(db, zone);
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */

	/* go through entire tree below the zone apex (incl subzones) */
	while(domain && dname_is_subdomain(
		domain_dname(domain), domain_dname(zone->apex)))
	{
		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s",
			dname_to_string(domain_dname(domain),0)));
		/* delete all rrsets of the zone */
		while((rrset = domain_find_any_rrset(domain, zone))) {
			(void)rrset_delete(db, domain, rrset);
		}
		next = domain_next(domain);
		domain->nextdiff = next;
		domain = next;
	}
#ifdef NSEC3
#ifndef FULL_PREHASH
	if (0 != zone_nsec3_domains_create(db, zone)) {
		log_msg(LOG_ERR,
			"Zone %s: unable to create zone NSEC3 prehash table",
			dname_to_string(domain_dname(zone->apex),
			NULL));
	}
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */

	DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes",
		(unsigned long) region_get_recycle_size(db->region)));
#ifndef NDEBUG
	if(nsd_debug_level >= 1)
		region_log_stats(db->region);
#endif

	assert(zone->soa_rrset == 0);
	/* keep zone->soa_nx_rrset alloced */
	assert(zone->ns_rrset == 0);
	assert(zone->is_secure == 0);
	assert(zone->updated == 1);
}
Esempio n. 4
0
/*
 * Answer if this is an AXFR or IXFR query.
 */
query_state_type
answer_axfr_ixfr(struct nsd *nsd, struct query *q)
{
	struct acl_options *acl = NULL;
	/* Is it AXFR? */
	switch (q->qtype) {
	case TYPE_AXFR:
		if (q->tcp) {
			struct zone_options* zone_opt;
			zone_opt = zone_options_find(nsd->options, q->qname);
			if(!zone_opt ||
			   acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1)
			{
				if (verbosity >= 2) {
					char a[128];
					addr2str(&q->addr, a, sizeof(a));
					VERBOSITY(2, (LOG_INFO, "axfr for %s from %s refused, %s",
						dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches"));
				}
				DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s",
					acl?"blocked":"no acl matches"));
				if (!zone_opt) {
					RCODE_SET(q->packet, RCODE_NOTAUTH);
				} else {
					RCODE_SET(q->packet, RCODE_REFUSE);
				}
				return QUERY_PROCESSED;
			}
			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s",
				acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
			if (verbosity >= 1) {
				char a[128];
				addr2str(&q->addr, a, sizeof(a));
				VERBOSITY(1, (LOG_INFO, "%s for %s from %s",
					(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
					dname_to_string(q->qname, NULL), a));
			}
			return query_axfr(nsd, q);
		}
		/** Fallthrough: AXFR over UDP queries are discarded. */
		/* fallthrough */
	case TYPE_IXFR:
		RCODE_SET(q->packet, RCODE_IMPL);
		return QUERY_PROCESSED;
	default:
		return QUERY_DISCARDED;
	}
}
Esempio n. 5
0
static int
rdata_ipsecgateway_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* rr)
{
	int gateway_type = rdata_atom_data(rr->rdatas[1])[0];
	switch(gateway_type) {
	case IPSECKEY_NOGATEWAY:
		buffer_printf(output, ".");
		break;
	case IPSECKEY_IP4:
		rdata_a_to_string(output, rdata, rr);
		break;
	case IPSECKEY_IP6:
		rdata_aaaa_to_string(output, rdata, rr);
		break;
	case IPSECKEY_DNAME:
		{
			region_type* temp = region_create(xalloc, free);
			const dname_type* d = dname_make(temp,
				rdata_atom_data(rdata), 0);
			if(!d) {
				region_destroy(temp);
				return 0;
			}
			buffer_printf(output, "%s", dname_to_string(d, NULL));
			region_destroy(temp);
		}
		break;
	default:
		return 0;
	}
	return 1;
}
Esempio n. 6
0
static domain_type*
rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
{
	int i;
	/* find previous */
	rrset_type** pp = &domain->rrsets;
	while(*pp && *pp != rrset) {
		pp = &( (*pp)->next );
	}
	if(!*pp) {
		/* rrset does not exist for domain */
		return NULL;
	}
	*pp = rrset->next;

	DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s",
		dname_to_string(domain_dname(domain),0),
		rrtype_to_string(rrset_rrtype(rrset))));

	/* is this a SOA rrset ? */
	if(rrset->zone->soa_rrset == rrset) {
		rrset->zone->soa_rrset = 0;
		rrset->zone->updated = 1;
		domain->has_SOA = 0;
	}
	if(rrset->zone->ns_rrset == rrset) {
		rrset->zone->ns_rrset = 0;
	}
	if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) {
		for (i = 0; i < rrset->rr_count; ++i) {
			if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) {
				rrset->zone->is_secure = 0;
				break;
			}
		}
	}

#ifdef NSEC3
#ifndef FULL_PREHASH
	if (rrset->rrs[0].type == TYPE_NSEC3) {
		namedb_del_nsec3_domain(db, domain, rrset->zone);
	}
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */

	/* recycle the memory space of the rrset */
	for (i = 0; i < rrset->rr_count; ++i)
		add_rdata_to_recyclebin(db, &rrset->rrs[i]);
	region_recycle(db->region, rrset->rrs,
		sizeof(rr_type) * rrset->rr_count);
	rrset->rr_count = 0;
	region_recycle(db->region, rrset, sizeof(rrset_type));

	/* is the node now an empty node (completely deleted) */
	if (domain->rrsets == 0) {
		return domain;
	}
	return NULL;
}
Esempio n. 7
0
static int
rdata_dname_to_string(buffer_type *output, rdata_atom_type rdata,
	rr_type* ATTR_UNUSED(rr))
{
	buffer_printf(output,
		      "%s",
		      dname_to_string(domain_dname(rdata_atom_domain(rdata)),
				      NULL));
	return 1;
}
Esempio n. 8
0
/*
 * Answer if this is an AXFR or IXFR query.
 */
query_state_type
answer_axfr_ixfr(struct nsd *nsd, struct query *q)
{
	acl_options_t *acl;
	/* Is it AXFR? */
	switch (q->qtype) {
	case TYPE_AXFR:
		if (q->tcp) {
			zone_options_t* zone_opt;
			zone_opt = zone_options_find(nsd->options, q->qname);
			if(!zone_opt ||
			   acl_check_incoming(zone_opt->provide_xfr, q, &acl)==-1)
			{
				if (verbosity > 0) {
					char address[128];
					if (addr2ip(q->addr, address, sizeof(address))) {
						DEBUG(DEBUG_XFRD,1, (LOG_INFO,
							"addr2ip failed"));
						strlcpy(address, "[unknown]", sizeof(address));
					}
					VERBOSITY(1, (LOG_INFO, "axfr for zone %s from client %s refused, %s",
						dname_to_string(q->qname, NULL), address, acl?"blocked":"no acl matches"));
				}
				DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s",
					acl?"blocked":"no acl matches"));
				if (!zone_opt) {
					RCODE_SET(q->packet, RCODE_NOTAUTH);
				} else {
					RCODE_SET(q->packet, RCODE_REFUSE);
				}
				return QUERY_PROCESSED;
			}
			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s",
				acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
			return query_axfr(nsd, q);
		}
	case TYPE_IXFR:
		RCODE_SET(q->packet, RCODE_IMPL);
		return QUERY_PROCESSED;
	default:
		return QUERY_DISCARDED;
	}
}
Esempio n. 9
0
int
print_rr(FILE *out,
         struct state_pretty_rr *state,
         rr_type *record)
{
	region_type *region = region_create(xalloc, free);
        buffer_type *output = buffer_create(region, MAX_RDLENGTH);
        rrtype_descriptor_type *descriptor
                = rrtype_descriptor_by_type(record->type);
        int result;
        const dname_type *owner = domain_dname(record->owner);
        const dname_type *owner_origin
                = dname_origin(region, owner);
        int owner_changed
                = (!state->previous_owner
                   || dname_compare(state->previous_owner, owner) != 0);
        if (owner_changed) {
                int origin_changed = (!state->previous_owner_origin
                                      || dname_compare(
                                              state->previous_owner_origin,
                                              owner_origin) != 0);
                if (origin_changed) {
                        buffer_printf(
                                output,
                                "$ORIGIN %s\n",
                                dname_to_string(owner_origin, NULL));
                }

                set_previous_owner(state, owner);
                buffer_printf(output,
                              "%s",
                              dname_to_string(owner,
                                              state->previous_owner_origin));
        }

        buffer_printf(output,
                      "\t%lu\t%s\t%s",
                      (unsigned long) record->ttl,
                      rrclass_to_string(record->klass),
                      rrtype_to_string(record->type));

        result = print_rdata(output, descriptor, record);
        if (!result) {
                /*
                 * Some RDATA failed to print, so print the record's
                 * RDATA in unknown format.
                 */
                result = rdata_atoms_to_unknown_string(output,
                                                       descriptor,
                                                       record->rdata_count,
                                                       record->rdatas);
        }

        if (result) {
                buffer_printf(output, "\n");
                buffer_flip(output);
		(void)write_data(out, buffer_current(output), buffer_remaining(output));
/*              fflush(out); */
        }

	region_destroy(region);
        return result;
}
Esempio n. 10
0
/** inspect a chunk in the file */
static void*
inspect_chunk(void* base, void* cv, struct inspect_totals* t)
{
	udb_chunk_d* cp = (udb_chunk_d*)cv;
	udb_void c = (udb_void)(cv - base);
	udb_void data;
	uint8_t exp = cp->exp;
	uint8_t tp = cp->type;
	uint8_t flags = cp->flags;
	uint64_t sz = 0;
	if(exp == UDB_EXP_XL) {
		sz = ((udb_xl_chunk_d*)cp)->size;
		data = c + sizeof(udb_xl_chunk_d);
	} else {
		sz = (uint64_t)1<<exp;
		data = c + sizeof(udb_chunk_d);
	}
	if(v) {
		/* print chunk details */
		printf("chunk at %llu exp=%d type=%d (%s) flags=0x%x "
			"size=%llu\n", ULL c, exp, tp, chunk_type2str(tp),
			flags, ULL sz);
		if(tp == udb_chunk_type_free) {
			udb_free_chunk_d* fp = (udb_free_chunk_d*)cp;
			printf("prev:			%llu\n", ULL fp->prev);
			printf("next:			%llu\n", ULL fp->next);
		} else {
			printf("ptrlist:		%llu\n",
				ULL cp->ptrlist);
		}
	}
	if(v>=2 && tp != udb_chunk_type_free && cp->ptrlist) {
		/* follow the pointer list */
		udb_rel_ptr* pl = UDB_REL_PTR(cp->ptrlist);
		while(pl->next) {
			printf("relptr %llu\n", ULL UDB_SYSTOREL(base, pl));
			printf("    prev-ptrlist:	%llu\n", ULL pl->prev);
			printf("    data:		%llu\n", ULL pl->data);
			printf("    next-ptrlist:	%llu\n", ULL pl->next);
			pl = UDB_REL_PTR(pl->next);
		}
	}
	/* print data details */
    if(v>=2) {
	if(cp->type == udb_chunk_type_radtree) {
		struct udb_radtree_d* d = (struct udb_radtree_d*)UDB_REL(base,
			data);
		printf("	radtree count=%llu root=%llu\n",
			ULL d->count, ULL d->root.data);
	} else if(cp->type == udb_chunk_type_radnode) {
		struct udb_radnode_d* d = (struct udb_radnode_d*)UDB_REL(base,
			data);
		printf("	radnode pidx=%d offset=%d elem=%llu "
			"parent=%llu lookup=%llu\n",
			(int)d->pidx, (int)d->offset, ULL d->elem.data,
			ULL d->parent.data, ULL d->lookup.data);
	} else if(cp->type == udb_chunk_type_radarray) {
		struct udb_radarray_d* d = (struct udb_radarray_d*)UDB_REL(
			base, data);
		unsigned i;
		printf("	radarray len=%d capacity=%d str_cap=%d\n",
			(int)d->len, (int)d->capacity, (int)d->str_cap);
		for(i=0; i<d->len; i++)
			if(d->array[i].node.data) {
				printf("	[%u] node=%llu len=%d ",
					i, ULL d->array[i].node.data,
					(int)d->array[i].len);
				print_escaped(
					((uint8_t*)&d->array[d->capacity])+
					i*d->str_cap, (size_t)d->array[i].len);
				printf("\n");
			}
	} else if(cp->type == udb_chunk_type_zone) {
		struct zone_d* d = (struct zone_d*)UDB_REL(base, data);
		printf("	zone ");
		print_dname(d->name, d->namelen);
		printf(" rr_count=%llu rrset_count=%llu expired=%d "
			"node=%llu domains=%llu\n",
			ULL d->rr_count, ULL d->rrset_count, (int)d->expired,
			ULL d->node.data, ULL d->domains.data);
	} else if(cp->type == udb_chunk_type_domain) {
		struct domain_d* d = (struct domain_d*)UDB_REL(base, data);
		printf("	domain ");
		print_dname(d->name, d->namelen);
		printf(" node=%llu rrsets=%llu\n", ULL d->node.data,
			ULL d->rrsets.data);
	} else if(cp->type == udb_chunk_type_rrset) {
		struct rrset_d* d = (struct rrset_d*)UDB_REL(base, data);
		printf("	rrset type=%d next=%llu rrs=%llu\n",
			(int)d->type, ULL d->next.data, ULL d->rrs.data);
	} else if(cp->type == udb_chunk_type_rr) {
		struct rr_d* d = (struct rr_d*)UDB_REL(base, data);
		printf("	rr type=%d class=%d ttl=%u next=%llu len=%d ",
			(int)d->type, (int)d->klass, (unsigned)d->ttl,
			ULL d->next.data, (int)d->len);
		print_hex(d->wire, d->len);
		printf("\n");
	} else if(cp->type == udb_chunk_type_task) {
		struct task_list_d* d = (struct task_list_d*)UDB_REL(base, data);
		printf("	task type=%d next=%llu yesno=%d oldserial=%u newserial=%u zone=%s\n",
			(int)d->task_type, ULL d->next.data, (int)d->yesno,
			(unsigned)d->oldserial, (unsigned)d->newserial,
			d->size > sizeof(*d)?
			dname_to_string(d->zname, NULL):"\"\"");
	}
   } /* end verbosity 2 */

	/* update stats */
	t->exp_num[exp]++;
	if(tp == udb_chunk_type_free) {
		t->exp_free[exp]++;
	} else {
		t->type_num[tp]++;
		t->type_exp_num[tp][exp]++;
	}

	/* check end marker */
	if(exp == UDB_EXP_XL) {
		if(sz != *(uint64_t*)(cv+sz-2*sizeof(uint64_t))) {
			printf("  end xl size is wrong:	%llu\n",
				ULL *(uint64_t*)(cv+sz-2*sizeof(uint64_t)));
		}
	}
	if(exp != *(uint8_t*)(cv+sz-1)) {
		printf("  end exp is wrong:	%d\n",  *(uint8_t*)(cv+sz-1));
	}
	return cv+sz;
}
Esempio n. 11
0
/* return value 0: syntaxerror,badIXFR, 1:OK, 2:done_and_skip_it */
static int
apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos,
	const char* zone, uint32_t serialno, nsd_options_t* opt,
	uint16_t id, uint32_t seq_nr, uint32_t seq_total,
	int* is_axfr, int* delete_mode, int* rr_count,
	size_t child_count)
{
	uint32_t filelen, msglen, pkttype, timestamp[2];
	int qcount, ancount, counter;
	buffer_type* packet;
	region_type* region;
	int i;
	uint16_t rrlen;
	const dname_type *dname_zone, *dname;
	zone_type* zone_db;
	domain_type* last_in_list;
	char file_zone_name[3072];
	uint32_t file_serial, file_seq_nr;
	uint16_t file_id;
	off_t mempos;

	memmove(&mempos, startpos, sizeof(off_t));
	if(fseeko(in, mempos, SEEK_SET) == -1) {
		log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno));
		return 0;
	}
	/* read ixfr packet RRs and apply to in memory db */

	if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) {
		log_msg(LOG_ERR, "could not read type or wrong type");
		return 0;
	}
	if(!diff_read_32(in, &timestamp[0]) ||
	   !diff_read_32(in, &timestamp[1])) {
		log_msg(LOG_ERR, "could not read timestamp");
		return 0;
	}

	if(!diff_read_32(in, &filelen)) {
		log_msg(LOG_ERR, "could not read len");
		return 0;
	}

	/* read header */
	if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) {
		log_msg(LOG_ERR, "msg too short");
		return 0;
	}

	region = region_create(xalloc, free);
	if(!region) {
		log_msg(LOG_ERR, "out of memory");
		return 0;
	}

	if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) ||
		!diff_read_32(in, &file_serial) ||
		!diff_read_16(in, &file_id) ||
		!diff_read_32(in, &file_seq_nr))
	{
		log_msg(LOG_ERR, "could not part data");
		region_destroy(region);
		return 0;
	}

	if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial ||
		id != file_id || seq_nr != file_seq_nr) {
		log_msg(LOG_ERR, "internal error: reading part with changed id");
		region_destroy(region);
		return 0;
	}
	msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t)
		- strlen(file_zone_name);
	packet = buffer_create(region, QIOBUFSZ);
	dname_zone = dname_parse(region, zone);
	zone_db = find_zone(db, dname_zone, opt, child_count);
	if(!zone_db) {
		log_msg(LOG_ERR, "no zone exists");
		region_destroy(region);
		/* break out and stop the IXFR, ignore it */
		return 2;
	}

	if(msglen > QIOBUFSZ) {
		log_msg(LOG_ERR, "msg too long");
		region_destroy(region);
		return 0;
	}
	buffer_clear(packet);
	if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
		log_msg(LOG_ERR, "short fread: %s", strerror(errno));
		region_destroy(region);
		return 0;
	}
	buffer_set_limit(packet, msglen);

	/* only answer section is really used, question, additional and
	   authority section RRs are skipped */
	qcount = QDCOUNT(packet);
	ancount = ANCOUNT(packet);
	buffer_skip(packet, QHEADERSZ);

	/* skip queries */
	for(i=0; i<qcount; ++i)
		if(!packet_skip_rr(packet, 1)) {
			log_msg(LOG_ERR, "bad RR in question section");
			region_destroy(region);
			return 0;
		}

	DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: started packet for zone %s",
			dname_to_string(dname_zone, 0)));
	/* first RR: check if SOA and correct zone & serialno */
	if(*rr_count == 0) {
		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parse first RR",
			dname_to_string(dname_zone, 0)));
		dname = dname_make_from_packet(region, packet, 1, 1);
		if(!dname) {
			log_msg(LOG_ERR, "could not parse dname");
			region_destroy(region);
			return 0;
		}
		if(dname_compare(dname_zone, dname) != 0) {
			log_msg(LOG_ERR, "SOA dname %s not equal to zone",
				dname_to_string(dname,0));
			log_msg(LOG_ERR, "zone dname is %s",
				dname_to_string(dname_zone,0));
			region_destroy(region);
			return 0;
		}
		if(!buffer_available(packet, 10)) {
			log_msg(LOG_ERR, "bad SOA RR");
			region_destroy(region);
			return 0;
		}
		if(buffer_read_u16(packet) != TYPE_SOA ||
			buffer_read_u16(packet) != CLASS_IN) {
			log_msg(LOG_ERR, "first RR not SOA IN");
			region_destroy(region);
			return 0;
		}
		buffer_skip(packet, sizeof(uint32_t)); /* ttl */
		if(!buffer_available(packet, buffer_read_u16(packet)) ||
			!packet_skip_dname(packet) /* skip prim_ns */ ||
			!packet_skip_dname(packet) /* skip email */) {
			log_msg(LOG_ERR, "bad SOA RR");
			region_destroy(region);
			return 0;
		}
		if(buffer_read_u32(packet) != serialno) {
			buffer_skip(packet, -4);
			log_msg(LOG_ERR, "SOA serial %d different from commit %d",
				buffer_read_u32(packet), serialno);
			region_destroy(region);
			return 0;
		}
		buffer_skip(packet, sizeof(uint32_t)*4);
		counter = 1;
		*rr_count = 1;
		*is_axfr = 0;
		*delete_mode = 0;

		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d",
			dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
	}
	else  counter = 0;

	last_in_list = zone_db->apex;
	for(; counter < ancount; ++counter,++(*rr_count))
	{
		uint16_t type, klass;
		uint32_t ttl;

		if(!(dname=dname_make_from_packet(region, packet, 1,1))) {
			log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count);
			region_destroy(region);
			return 0;
		}
		if(!buffer_available(packet, 10)) {
			log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count);
			region_destroy(region);
			return 0;
		}
		type = buffer_read_u16(packet);
		klass = buffer_read_u16(packet);
		ttl = buffer_read_u32(packet);
		rrlen = buffer_read_u16(packet);
		if(!buffer_available(packet, rrlen)) {
			log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d",
				*rr_count, rrlen, (int)buffer_remaining(packet));
			region_destroy(region);
			return 0;
		}
		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d",
			dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));

		if(*rr_count == 1 && type != TYPE_SOA) {
			/* second RR: if not SOA: this is an AXFR; delete all zone contents */
			delete_zone_rrs(db, zone_db);
			/* add everything else (incl end SOA) */
			*delete_mode = 0;
			*is_axfr = 1;
			DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d",
				dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
		}
		if(*rr_count == 1 && type == TYPE_SOA) {
			/* if the serial no of the SOA equals the serialno, then AXFR */
			size_t bufpos = buffer_position(packet);
			uint32_t thisserial;
			if(!packet_skip_dname(packet) ||
				!packet_skip_dname(packet) ||
				buffer_remaining(packet) < sizeof(uint32_t)*5)
			{
				log_msg(LOG_ERR, "bad xfr SOA RR formerr.");
				region_destroy(region);
				return 0;
			}
			thisserial = buffer_read_u32(packet);
			if(thisserial == serialno) {
				/* AXFR */
				delete_zone_rrs(db, zone_db);
				*delete_mode = 0;
				*is_axfr = 1;
			}
			/* must have stuff in memory for a successful IXFR,
			 * the serial number of the SOA has been checked
			 * previously (by check_for_bad_serial) if it exists */
			if(!*is_axfr && !domain_find_rrset(zone_db->apex,
				zone_db, TYPE_SOA)) {
				log_msg(LOG_ERR, "%s SOA serial %d is not "
					"in memory, skip IXFR", zone, serialno);
				region_destroy(region);
				/* break out and stop the IXFR, ignore it */
				return 2;
			}
			buffer_set_position(packet, bufpos);
		}
		if(type == TYPE_SOA && !*is_axfr) {
			/* switch from delete-part to add-part and back again,
			   just before soa - so it gets deleted and added too */
			/* this means we switch to delete mode for the final SOA */
			*delete_mode = !*delete_mode;
			DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d",
				dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
		}
		if(type == TYPE_TSIG || type == TYPE_OPT) {
			/* ignore pseudo RRs */
			buffer_skip(packet, rrlen);
			continue;
		}

		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s",
			*delete_mode?"del":"add",
			dname_to_string(dname,0), rrtype_to_string(type)));
		if(*delete_mode) {
			/* delete this rr */
			if(!*is_axfr && type == TYPE_SOA && counter==ancount-1
				&& seq_nr == seq_total-1) {
				continue; /* do not delete final SOA RR for IXFR */
			}
			if(!delete_RR(db, dname, type, klass, last_in_list, packet,
				rrlen, zone_db, region, *is_axfr)) {
				region_destroy(region);
				return 0;
			}
			if (!*is_axfr && last_in_list->nextdiff) {
				last_in_list = last_in_list->nextdiff;
			}
		}
		else
		{
			/* add this rr */
			if(!add_RR(db, dname, type, klass, ttl, packet,
				rrlen, zone_db, *is_axfr)) {
				region_destroy(region);
				return 0;
			}
		}
	}
	fix_empty_terminals(zone_db);
	region_destroy(region);
	return 1;
}
Esempio n. 12
0
static zone_type*
find_zone(namedb_type* db, const dname_type* zone_name, nsd_options_t* opt,
	size_t child_count)
{
	domain_type *domain;
	zone_type* zone;
	zone_options_t* opts;
	domain = domain_table_find(db->domains, zone_name);
	if(!domain) {
		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating domain %s",
			dname_to_string(zone_name,0)));
		/* create the zone and domain of apex (zone has config options) */
		domain = domain_table_insert(db->domains, zone_name);
	} else {
		/* O(1) if SOA exists */
		zone = domain_find_zone(domain);
		/* if domain was empty (no rrsets, empty zone) search in zonelist */
		/* check apex to make sure we don't find a parent zone */
		if(!zone || zone->apex != domain)
			zone = namedb_find_zone(db, domain);
		if(zone) {
			assert(zone->apex == domain);
			return zone;
		}
	}
	/* lookup in config */
	opts = zone_options_find(opt, domain_dname(domain));
	if(!opts) {
		log_msg(LOG_ERR, "xfr: zone %s not in config.",
			dname_to_string(zone_name,0));
		return 0;
	}
	/* create the zone */
	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating zone_type %s",
		dname_to_string(zone_name,0)));
	zone = (zone_type *) region_alloc(db->region, sizeof(zone_type));
	if(!zone) {
		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
		exit(1);
	}
	zone->next = db->zones;
	zone->opts = opts;
	db->zones = zone;
	db->zone_count++;
	zone->apex = domain;
	zone->soa_rrset = 0;
	zone->soa_nx_rrset = 0;
	zone->ns_rrset = 0;
#ifdef NSEC3
	zone->nsec3_soa_rr = NULL;
	zone->nsec3_last = NULL;
#endif
	zone->dirty = region_alloc(db->region, sizeof(uint8_t)*child_count);
	if(!zone->dirty) {
		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
		exit(1);
	}
	memset(zone->dirty, 0, sizeof(uint8_t)*child_count);
#ifdef NSEC3
#ifndef FULL_PREHASH
	zone->nsec3_domains = NULL;

	if (0 != zone_nsec3_domains_create(db, zone)) {
		log_msg(LOG_ERR,
			"xfr: zone NSEC3 domains "
			"memory allocation failure");
		return 0;
	}
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */
	zone->number = db->zone_count;
	zone->is_secure = 0;
	zone->updated = 1;
	zone->is_ok = 0;
	return zone;
}
Esempio n. 13
0
static int
add_RR(namedb_type* db, const dname_type* dname,
	uint16_t type, uint16_t klass, uint32_t ttl,
	buffer_type* packet, size_t rdatalen, zone_type *zone,
	int is_axfr)
{
	domain_type* domain;
	rrset_type* rrset;
	rdata_atom_type *rdatas;
	rr_type *rrs_old;
	ssize_t rdata_num;
	int rrnum;
	domain = domain_table_find(db->domains, dname);
	if(!domain) {
		/* create the domain */
		domain = domain_table_insert(db->domains, dname);
	}
	rrset = domain_find_rrset(domain, zone, type);
	if(!rrset) {
		/* create the rrset */
		rrset = region_alloc(db->region, sizeof(rrset_type));
		if(!rrset) {
			log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
			exit(1);
		}
		rrset->zone = zone;
		rrset->rrs = 0;
		rrset->rr_count = 0;
		domain_add_rrset(domain, rrset);
	}

	/* dnames in rdata are normalized, conform RFC 4035,
	 * Section 6.2
	 */
	rdata_num = rdata_wireformat_to_rdata_atoms(
		db->region, db->domains, type, rdatalen, packet, &rdatas);
	if(rdata_num == -1) {
		log_msg(LOG_ERR, "diff: bad rdata for %s",
			dname_to_string(dname,0));
		return 0;
	}
	rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num);
	if(rrnum != -1) {
		DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists",
			dname_to_string(dname,0), rrtype_to_string(type)));
		/* ignore already existing RR: lenient accepting of messages */
		return 1;
	}

	/* re-alloc the rrs and add the new */
	rrs_old = rrset->rrs;
	rrset->rrs = region_alloc(db->region,
		(rrset->rr_count+1) * sizeof(rr_type));
	if(!rrset->rrs) {
		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
		exit(1);
	}
	if(rrs_old)
		memcpy(rrset->rrs, rrs_old, rrset->rr_count * sizeof(rr_type));
	region_recycle(db->region, rrs_old, sizeof(rr_type) * rrset->rr_count);
	rrset->rr_count ++;

	rrset->rrs[rrset->rr_count - 1].owner = domain;
	rrset->rrs[rrset->rr_count - 1].rdatas = rdatas;
	rrset->rrs[rrset->rr_count - 1].ttl = ttl;
	rrset->rrs[rrset->rr_count - 1].type = type;
	rrset->rrs[rrset->rr_count - 1].klass = klass;
	rrset->rrs[rrset->rr_count - 1].rdata_count = rdata_num;

	/* see if it is a SOA */
	if(domain == zone->apex) {
		if(type == TYPE_SOA) {
			uint32_t soa_minimum;
			zone->soa_rrset = rrset;
			zone->updated = 1;
			/* BUG #103 tweaked SOA ttl value */
			if(zone->soa_nx_rrset == 0) {
				zone->soa_nx_rrset = region_alloc(db->region,
					sizeof(rrset_type));
				if(!zone->soa_nx_rrset) {
					log_msg(LOG_ERR, "out of memory, %s:%d",
						__FILE__, __LINE__);
					exit(1);
				}
				zone->soa_nx_rrset->rr_count = 1;
				zone->soa_nx_rrset->next = 0;
				zone->soa_nx_rrset->zone = zone;
				zone->soa_nx_rrset->rrs = region_alloc(db->region,
					sizeof(rr_type));
				if(!zone->soa_nx_rrset->rrs) {
					log_msg(LOG_ERR, "out of memory, %s:%d",
						__FILE__, __LINE__);
					exit(1);
				}
			}
			memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type));
			memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]),
				rdata_atom_size(rrset->rrs->rdatas[6]));
			if (rrset->rrs->ttl > ntohl(soa_minimum)) {
				rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum);
			}
			domain->has_SOA = 1;
		}
		if(type == TYPE_NS) {
			zone->ns_rrset = rrset;
		}
		if(type == TYPE_RRSIG) {
			int i;
			for (i = 0; i < rrset->rr_count; ++i) {
				if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) {
					zone->is_secure = 1;
					break;
				}
			}
		}
	}

#ifdef NSEC3
#ifndef FULL_PREHASH
	if ((type == TYPE_NSEC3) &&
	    (rrset->rr_count == 1)) {
		/* NSEC3 RRset just added */
		if (0 != namedb_add_nsec3_domain(db, domain, zone))
			return 0;
	}
	if (is_axfr == 0) {
		struct domain *parent = domain;
		do {
			if (0 != namedb_add_nsec3_mod_domain(db, parent))
				return 0;
			parent = parent->parent;
		} while (parent != zone->apex->parent);
	}
#else
	(void)is_axfr;
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */

	return 1;
}
Esempio n. 14
0
static int
delete_RR(namedb_type* db, const dname_type* dname,
	uint16_t type, uint16_t klass,
	domain_type* prevdomain,
	buffer_type* packet, size_t rdatalen, zone_type *zone,
	region_type* temp_region, int is_axfr)
{
	domain_type *domain;
	rrset_type *rrset;
	domain = domain_table_find(db->domains, dname);
	if(!domain) {
		log_msg(LOG_WARNING, "diff: domain %s does not exist",
			dname_to_string(dname,0));
		buffer_skip(packet, rdatalen);
		return 1; /* not fatal error */
	}
	rrset = domain_find_rrset(domain, zone, type);
	if(!rrset) {
		log_msg(LOG_WARNING, "diff: rrset %s does not exist",
			dname_to_string(dname,0));
		buffer_skip(packet, rdatalen);
		return 1; /* not fatal error */
	} else {
		/* find the RR in the rrset */
		domain_table_type *temptable;
		rdata_atom_type *rdatas;
		ssize_t rdata_num;
		int rrnum;
		temptable = domain_table_create(temp_region);
		/* This will ensure that the dnames in rdata are
		 * normalized, conform RFC 4035, section 6.2
		 */
		rdata_num = rdata_wireformat_to_rdata_atoms(
			temp_region, temptable, type, rdatalen, packet, &rdatas);
		if(rdata_num == -1) {
			log_msg(LOG_ERR, "diff: bad rdata for %s",
				dname_to_string(dname,0));
			return 0;
		}
		rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num);
		if(rrnum == -1) {
			log_msg(LOG_WARNING, "diff: RR <%s, %s> does not exist",
				dname_to_string(dname,0), rrtype_to_string(type));
			return 1; /* not fatal error */
		}
#ifdef NSEC3
#ifndef FULL_PREHASH
		if (is_axfr == 0) {
			struct domain *parent = domain;
			do {
				if (0 != namedb_add_nsec3_mod_domain(db,
								    parent)) {
					return 0;
				}
				parent = parent->parent;
			} while (parent != zone->apex->parent);
		}
#else
		(void)is_axfr;
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */

		if(rrset->rr_count == 1) {
			/* delete entire rrset */
			domain = rrset_delete(db, domain, rrset);
			if (domain && domain != prevdomain && !domain->nextdiff) {
				/* this domain is not yet in the diff chain */
				prevdomain->nextdiff = domain;
			}
		} else {
			/* swap out the bad RR and decrease the count */
			rr_type* rrs_orig = rrset->rrs;
			add_rdata_to_recyclebin(db, &rrset->rrs[rrnum]);
			if(rrnum < rrset->rr_count-1)
				rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1];
			memset(&rrset->rrs[rrset->rr_count-1], 0, sizeof(rr_type));
			/* realloc the rrs array one smaller */
			rrset->rrs = region_alloc_init(db->region, rrs_orig,
				sizeof(rr_type) * (rrset->rr_count-1));
			if(!rrset->rrs) {
				log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
				exit(1);
			}
			region_recycle(db->region, rrs_orig,
				sizeof(rr_type) * rrset->rr_count);
			rrset->rr_count --;
		}
	}
	return 1;
}
Esempio n. 15
0
struct namedb *
namedb_open (const char *filename, nsd_options_t* opt, size_t num_children)
{
    namedb_type *db;

    /*
     * Temporary region used while loading domain names from the
     * database.  The region is freed after each time a dname is
     * read from the database.
     */
    region_type *dname_region;

    /*
     * Temporary region used to store array of domains and zones
     * while loading the database.  The region is freed before
     * returning.
     */
    region_type *temp_region;

    uint32_t dname_count;
    domain_type **domains;	/* Indexed by domain number.  */

    uint32_t zone_count;
    zone_type **zones;	/* Indexed by zone number.  */

    uint32_t i;
    uint32_t rrset_count = 0;
    uint32_t rr_count = 0;

    rrset_type *rrset;

    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(namedb_type) = %lu\n", (unsigned long) sizeof(namedb_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(zone_type) = %lu\n", (unsigned long) sizeof(zone_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(domain_type) = %lu\n", (unsigned long) sizeof(domain_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(rrset_type) = %lu\n", (unsigned long) sizeof(rrset_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(rr_type) = %lu\n", (unsigned long) sizeof(rr_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(rdata_atom_type) = %lu\n", (unsigned long) sizeof(rdata_atom_type)));
    DEBUG(DEBUG_DBACCESS, 2,
          (LOG_INFO, "sizeof(rbnode_t) = %lu\n", (unsigned long) sizeof(rbnode_t)));

    if ((db = namedb_create()) == NULL) {
        log_msg(LOG_ERR,
                "insufficient memory to create database");
        return NULL;
    }
    db->filename = region_strdup(db->region, filename);

    if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
        log_msg(LOG_ERR, "unable to load %s: cannot initialize"
                "timestamp", db->filename);
        namedb_destroy(db);
        return NULL;
    }

    /* Open it... */
    db->fd = fopen(db->filename, "r");
    if (db->fd == NULL) {
        log_msg(LOG_ERR, "unable to load %s: %s",
                db->filename, strerror(errno));
        namedb_destroy(db);
        return NULL;
    }

    if (!read_magic(db)) {
        log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename);
        log_msg(LOG_ERR, "cannot load database, incompatible version "
                "number. Please rebuild database and "
                "start again.");
        namedb_close(db);
        return NULL;
    }

    if (!read_size(db, &zone_count)) {
        log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
        namedb_close(db);
        return NULL;
    }

    DEBUG(DEBUG_DBACCESS, 1,
          (LOG_INFO, "Retrieving %lu zones\n", (unsigned long) zone_count));

    temp_region = region_create(xalloc, free);
    dname_region = region_create(xalloc, free);

    db->zone_count = zone_count;
    zones = (zone_type **) region_alloc(temp_region,
                                        zone_count * sizeof(zone_type *));
    for (i = 0; i < zone_count; ++i) {
        const dname_type *dname = read_dname(db->fd, dname_region);
        if (!dname) {
            log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename);
            region_destroy(dname_region);
            region_destroy(temp_region);
            namedb_close(db);
            return NULL;
        }
        zones[i] = (zone_type *) region_alloc(db->region,
                                              sizeof(zone_type));
        zones[i]->next = db->zones;
        db->zones = zones[i];
        zones[i]->apex = domain_table_insert(db->domains, dname);
        zones[i]->soa_rrset = NULL;
        zones[i]->soa_nx_rrset = NULL;
        zones[i]->ns_rrset = NULL;
#ifdef NSEC3
        zones[i]->nsec3_soa_rr = NULL;
        zones[i]->nsec3_last = NULL;
#endif
        zones[i]->opts = zone_options_find(opt, domain_dname(zones[i]->apex));
        zones[i]->number = i + 1;
        zones[i]->is_secure = 0;
        zones[i]->updated = 1;
        zones[i]->is_ok = 0;
        zones[i]->dirty = region_alloc(db->region, sizeof(uint8_t)*num_children);
        memset(zones[i]->dirty, 0, sizeof(uint8_t)*num_children);
        if(!zones[i]->opts) {
            log_msg(LOG_ERR, "cannot load database. Zone %s in db "
                    "%s, but not in config file (might "
                    "happen if you edited the config "
                    "file). Please rebuild database and "
                    "start again.",
                    dname_to_string(dname, NULL), db->filename);
            region_destroy(dname_region);
            region_destroy(temp_region);
            namedb_close(db);
            return NULL;
        }
#ifdef NSEC3
#ifndef FULL_PREHASH
        zones[i]->nsec3_domains = NULL;
        if (0 != zone_nsec3_domains_create(db, zones[i])) {
            log_msg(LOG_ERR,
                    "insufficient memory for NSEC3 tree, "
                    "unable to read database");
            region_destroy(dname_region);
            region_destroy(temp_region);
            namedb_close(db);
            return NULL;
        }
#endif /* !FULL_PREHASH */
#endif /* NSEC3 */
        region_free_all(dname_region);
    }

    if (!read_size(db, &dname_count)) {
        log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
        region_destroy(dname_region);
        region_destroy(temp_region);
        namedb_close(db);
        return NULL;
    }

    DEBUG(DEBUG_DBACCESS, 1,
          (LOG_INFO, "Retrieving %lu domain names\n", (unsigned long) dname_count));

    domains = (domain_type **) region_alloc(
                  temp_region, dname_count * sizeof(domain_type *));
    for (i = 0; i < dname_count; ++i) {
        const dname_type *dname = read_dname(db->fd, dname_region);
        if (!dname) {
            log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename);
            region_destroy(dname_region);
            region_destroy(temp_region);
            namedb_close(db);
            return NULL;
        }
        domains[i] = domain_table_insert(db->domains, dname);
        region_free_all(dname_region);
    }

    region_destroy(dname_region);

#ifndef NDEBUG
    fprintf(stderr, "database region after loading domain names: ");
    region_dump_stats(db->region, stderr);
    fprintf(stderr, "\n");
#endif

    while ((rrset = read_rrset(db, dname_count, domains, zone_count, zones))) {
        ++rrset_count;
        rr_count += rrset->rr_count;
    }

    DEBUG(DEBUG_DBACCESS, 1,
          (LOG_INFO, "Retrieved %lu RRs in %lu RRsets\n",
           (unsigned long) rr_count, (unsigned long) rrset_count));

    region_destroy(temp_region);

    if ((db->crc_pos = ftello(db->fd)) == -1) {
        log_msg(LOG_ERR, "ftello %s failed: %s",
                db->filename, strerror(errno));
        namedb_close(db);
        return NULL;
    }
    if (!read_size(db, &db->crc)) {
        log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
        namedb_close(db);
        return NULL;
    }
    if (!read_magic(db)) {
        log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename);
        log_msg(LOG_ERR, "cannot load database, incompatible version "
                "number. Please rebuild database and "
                "start again.");
        namedb_close(db);
        return NULL;
    }

    fclose(db->fd);
    db->fd = NULL;

#ifndef NDEBUG
    fprintf(stderr, "database region after loading database: ");
    region_dump_stats(db->region, stderr);
    fprintf(stderr, "\n");
#endif

    return db;
}