/**
 * Process TSIG RR.
 *
 */
static ldns_pkt_rcode
query_process_tsig(query_type* q)
{
    if (!q || !q->tsig_rr) {
        return LDNS_RCODE_SERVFAIL;
    }
    if (q->tsig_rr->status == TSIG_ERROR) {
        return LDNS_RCODE_FORMERR;
    }
    if (q->tsig_rr->status == TSIG_OK) {
        if (!tsig_rr_lookup(q->tsig_rr)) {
            ods_log_debug("[%s] tsig unknown key/algorithm", query_str);
            return LDNS_RCODE_REFUSED;
        }
        buffer_set_limit(q->buffer, q->tsig_rr->position);
        buffer_pkt_set_arcount(q->buffer, buffer_pkt_arcount(q->buffer)-1);
        tsig_rr_prepare(q->tsig_rr);
        tsig_rr_update(q->tsig_rr, q->buffer, buffer_limit(q->buffer));
        if (!tsig_rr_verify(q->tsig_rr)) {
            ods_log_debug("[%s] bad tsig signature", query_str);
            return LDNS_RCODE_NOTAUTH;
        }
    }
    return LDNS_RCODE_NOERROR;
}
/**
 * Process EDNS OPT RR.
 *
 */
static ldns_pkt_rcode
query_process_edns(query_type* q)
{
    if (!q || !q->edns_rr) {
        return LDNS_RCODE_SERVFAIL;
    }
    if (q->edns_rr->status == EDNS_ERROR) {
        /* The only error is VERSION not implemented */
        return LDNS_RCODE_FORMERR;
    }
    if (q->edns_rr->status == EDNS_OK) {
        /* Only care about UDP size larger than normal... */
        if (!q->tcp && q->edns_rr->maxlen > UDP_MAX_MESSAGE_LEN) {
            if (q->edns_rr->maxlen < EDNS_MAX_MESSAGE_LEN) {
                q->maxlen = q->edns_rr->maxlen;
            } else {
                q->maxlen = EDNS_MAX_MESSAGE_LEN;
            }
        }
        /* Strip the OPT resource record off... */
        buffer_set_position(q->buffer, q->edns_rr->position);
        buffer_set_limit(q->buffer, q->edns_rr->position);
        buffer_pkt_set_arcount(q->buffer, buffer_pkt_arcount(q->buffer) - 1);
    }
    return LDNS_RCODE_NOERROR;
}
/**
 * SERVFAIL.
 *
 */
static query_state
query_servfail(query_type* q)
{
    if (!q) {
        return QUERY_DISCARDED;
    }
    ods_log_debug("[%s] servfail", query_str);
    buffer_set_position(q->buffer, 0);
    buffer_set_limit(q->buffer, BUFFER_PKT_HEADER_SIZE);
    buffer_pkt_set_qdcount(q->buffer, 0);
    return query_error(q, LDNS_RCODE_SERVFAIL);
}
Example #4
0
static int
xfrd_xfr_process_tsig(xfrd_zone_t* zone, buffer_type* packet)
{
	int have_tsig = 0;
	assert(zone && zone->master && zone->master->key_options
		&& zone->master->key_options->tsig_key && packet);
	if(!tsig_find_rr(&zone->tsig, packet)) {
		log_msg(LOG_ERR, "xfrd: zone %s, from %s: malformed tsig RR",
			zone->apex_str, zone->master->ip_address_spec);
		return 0;
	}
	if(zone->tsig.status == TSIG_OK) {
		have_tsig = 1;
	}
	if(have_tsig) {
		/* strip the TSIG resource record off... */
		buffer_set_limit(packet, zone->tsig.position);
		ARCOUNT_SET(packet, ARCOUNT(packet) - 1);
	}

	/* keep running the TSIG hash */
	tsig_update(&zone->tsig, packet, buffer_limit(packet));
	if(have_tsig) {
		if (!tsig_verify(&zone->tsig)) {
			log_msg(LOG_ERR, "xfrd: zone %s, from %s: bad tsig signature",
				zone->apex_str, zone->master->ip_address_spec);
			return 0;
		}
		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, from %s: good tsig signature",
			zone->apex_str, zone->master->ip_address_spec));
		/* prepare for next tsigs */
		tsig_prepare(&zone->tsig);
	}
	else if(zone->tsig.updates_since_last_prepare > XFRD_TSIG_MAX_UNSIGNED) {
		/* we allow a number of non-tsig signed packets */
		log_msg(LOG_INFO, "xfrd: zone %s, from %s: too many consecutive "
			"packets without TSIG", zone->apex_str,
			zone->master->ip_address_spec);
		return 0;
	}

	if(!have_tsig && zone->msg_seq_nr == 0) {
		log_msg(LOG_ERR, "xfrd: zone %s, from %s: no tsig in first packet of reply",
			zone->apex_str, zone->master->ip_address_spec);
		return 0;
	}
	return 1;
}
Example #5
0
int
xfrd_udp_read_packet(buffer_type* packet, int fd)
{
	ssize_t received;

	/* read the data */
	buffer_clear(packet);
	received = recvfrom(fd, buffer_begin(packet), buffer_remaining(packet),
		0, NULL, NULL);
	if(received == -1) {
		log_msg(LOG_ERR, "xfrd: recvfrom failed: %s",
			strerror(errno));
		return 0;
	}
	buffer_set_limit(packet, received);
	return 1;
}
/**
 * Read packet from udp.
 *
 */
static int
notify_udp_read_packet(notify_type* notify)
{
    xfrhandler_type* xfrhandler = NULL;
    ssize_t received = 0;
    ods_log_assert(notify);
    xfrhandler = (xfrhandler_type*) notify->xfrhandler;
    ods_log_assert(xfrhandler);
    buffer_clear(xfrhandler->packet);
    received = recvfrom(notify->handler.fd, buffer_begin(xfrhandler->packet),
        buffer_remaining(xfrhandler->packet), 0, NULL, NULL);
    if (received == -1) {
        ods_log_error("[%s] unable to read packet: recvfrom() failed fd %d "
            "(%s)", notify_str, notify->handler.fd, strerror(errno));
        return 0;
    }
    buffer_set_limit(xfrhandler->packet, received);
    return 1;
}
/**
 * Prepare response.
 *
 */
void
query_prepare(query_type* q)
{
    uint16_t limit = 0;
    uint16_t flags = 0;
    ods_log_assert(q);
    ods_log_assert(q->buffer);
    limit = buffer_limit(q->buffer);
    flags = buffer_pkt_flags(q->buffer);
    flags &= 0x0100U; /* preserve the rd flag */
    flags |= 0x8000U; /* set the qr flag */
    buffer_pkt_set_flags(q->buffer, flags);
    buffer_clear(q->buffer);
    buffer_set_position(q->buffer, limit);
    buffer_set_limit(q->buffer, buffer_capacity(q->buffer));
    q->reserved_space = edns_rr_reserved_space(q->edns_rr);
    q->reserved_space += tsig_rr_reserved_space(q->tsig_rr);
    return;
}
Example #8
0
query_state_type
query_axfr(struct nsd *nsd, struct query *query)
{
	domain_type *closest_match;
	domain_type *closest_encloser;
	int exact;
	int added;
	uint16_t total_added = 0;

	if (query->axfr_is_done)
		return QUERY_PROCESSED;

	if (query->maxlen > AXFR_MAX_MESSAGE_LEN)
		query->maxlen = AXFR_MAX_MESSAGE_LEN;

	assert(!query_overflow(query));
	/* only keep running values for most packets */
	query->tsig_prepare_it = 0;
	query->tsig_update_it = 1;
	if(query->tsig_sign_it) {
		/* prepare for next updates */
		query->tsig_prepare_it = 1;
		query->tsig_sign_it = 0;
	}

	if (query->axfr_zone == NULL) {
		domain_type* qdomain;
		/* Start AXFR.  */
		STATUP(nsd, raxfr);
		exact = namedb_lookup(nsd->db,
				      query->qname,
				      &closest_match,
				      &closest_encloser);

		qdomain = closest_encloser;
		query->axfr_zone = domain_find_zone(nsd->db, closest_encloser);

		if (!exact
		    || query->axfr_zone == NULL
		    || query->axfr_zone->apex != qdomain
		    || query->axfr_zone->soa_rrset == NULL)
		{
			/* No SOA no transfer */
			RCODE_SET(query->packet, RCODE_NOTAUTH);
			return QUERY_PROCESSED;
		}
		ZTATUP(nsd, query->axfr_zone, raxfr);

		query->axfr_current_domain = qdomain;
		query->axfr_current_rrset = NULL;
		query->axfr_current_rr = 0;
		if(query->tsig.status == TSIG_OK) {
			query->tsig_sign_it = 1; /* sign first packet in stream */
		}

		query_add_compression_domain(query, qdomain, QHEADERSZ);

		assert(query->axfr_zone->soa_rrset->rr_count == 1);
		added = packet_encode_rr(query,
					 query->axfr_zone->apex,
					 &query->axfr_zone->soa_rrset->rrs[0],
					 query->axfr_zone->soa_rrset->rrs[0].ttl);
		if (!added) {
			/* XXX: This should never happen... generate error code? */
			abort();
		}
		++total_added;
	} else {
		/*
		 * Query name and EDNS need not be repeated after the
		 * first response packet.
		 */
		query->edns.status = EDNS_NOT_PRESENT;
		buffer_set_limit(query->packet, QHEADERSZ);
		QDCOUNT_SET(query->packet, 0);
		query_prepare_response(query);
	}

	/* Add zone RRs until answer is full.  */
	while (query->axfr_current_domain != NULL &&
			domain_is_subdomain(query->axfr_current_domain,
					    query->axfr_zone->apex))
	{
		if (!query->axfr_current_rrset) {
			query->axfr_current_rrset = domain_find_any_rrset(
				query->axfr_current_domain,
				query->axfr_zone);
			query->axfr_current_rr = 0;
		}
		while (query->axfr_current_rrset) {
			if (query->axfr_current_rrset != query->axfr_zone->soa_rrset
			    && query->axfr_current_rrset->zone == query->axfr_zone)
			{
				while (query->axfr_current_rr < query->axfr_current_rrset->rr_count) {
					added = packet_encode_rr(
						query,
						query->axfr_current_domain,
						&query->axfr_current_rrset->rrs[query->axfr_current_rr],
						query->axfr_current_rrset->rrs[query->axfr_current_rr].ttl);
					if (!added)
						goto return_answer;
					++total_added;
					++query->axfr_current_rr;
				}
			}

			query->axfr_current_rrset = query->axfr_current_rrset->next;
			query->axfr_current_rr = 0;
		}
		assert(query->axfr_current_domain);
		query->axfr_current_domain
			= domain_next(query->axfr_current_domain);
	}

	/* Add terminating SOA RR.  */
	assert(query->axfr_zone->soa_rrset->rr_count == 1);
	added = packet_encode_rr(query,
				 query->axfr_zone->apex,
				 &query->axfr_zone->soa_rrset->rrs[0],
				 query->axfr_zone->soa_rrset->rrs[0].ttl);
	if (added) {
		++total_added;
		query->tsig_sign_it = 1; /* sign last packet */
		query->axfr_is_done = 1;
	}

return_answer:
	AA_SET(query->packet);
	ANCOUNT_SET(query->packet, total_added);
	NSCOUNT_SET(query->packet, 0);
	ARCOUNT_SET(query->packet, 0);

	/* check if it needs tsig signatures */
	if(query->tsig.status == TSIG_OK) {
		if(query->tsig.updates_since_last_prepare >= AXFR_TSIG_SIGN_EVERY_NTH) {
			query->tsig_sign_it = 1;
		}
	}
	query_clear_compression_tables(query);
	return QUERY_IN_AXFR;
}
/**
 * NOTIFY.
 *
 */
static query_state
query_process_notify(query_type* q, ldns_rr_type qtype, void* engine)
{
    engine_type* e = (engine_type*) engine;
    dnsin_type* dnsin = NULL;
    uint16_t count = 0;
    uint16_t rrcount = 0;
    uint32_t serial = 0;
    size_t pos = 0;
    char address[128];
    if (!e || !q || !q->zone) {
        return QUERY_DISCARDED;
    }
    ods_log_assert(e->dnshandler);
    ods_log_assert(q->zone->name);
    ods_log_debug("[%s] incoming notify for zone %s", query_str,
        q->zone->name);
    if (buffer_pkt_rcode(q->buffer) != LDNS_RCODE_NOERROR ||
        buffer_pkt_qr(q->buffer) ||
        !buffer_pkt_aa(q->buffer) ||
        buffer_pkt_tc(q->buffer) ||
        buffer_pkt_rd(q->buffer) ||
        buffer_pkt_ra(q->buffer) ||
        buffer_pkt_ad(q->buffer) ||
        buffer_pkt_cd(q->buffer) ||
        buffer_pkt_qdcount(q->buffer) != 1 ||
        buffer_pkt_ancount(q->buffer) > 1 ||
        qtype != LDNS_RR_TYPE_SOA) {
        return query_formerr(q);
    }
    if (!q->zone->adinbound || q->zone->adinbound->type != ADAPTER_DNS) {
        ods_log_error("[%s] zone %s is not configured to have input dns "
            "adapter", query_str, q->zone->name);
        return query_notauth(q);
    }
    ods_log_assert(q->zone->adinbound->config);
    dnsin = (dnsin_type*) q->zone->adinbound->config;
    if (!acl_find(dnsin->allow_notify, &q->addr, q->tsig_rr)) {
        if (addr2ip(q->addr, address, sizeof(address))) {
            ods_log_info("[%s] unauthorized notify for zone %s from client %s: "
                "no acl matches", query_str, q->zone->name, address);
        } else {
            ods_log_info("[%s] unauthorized notify for zone %s from unknown "
                "client: no acl matches", query_str, q->zone->name);
        }
        return query_notauth(q);
    }
    ods_log_assert(q->zone->xfrd);
    /* skip header and question section */
    buffer_skip(q->buffer, BUFFER_PKT_HEADER_SIZE);
    count = buffer_pkt_qdcount(q->buffer);
    for (rrcount = 0; rrcount < count; rrcount++) {
        if (!buffer_skip_rr(q->buffer, 1)) {
            ods_log_error("[%s] dropped packet: zone %s received bad notify "
                "(bad question section)", query_str, q->zone->name);
            return QUERY_DISCARDED;
        }
    }
    pos = buffer_position(q->buffer);

    /* examine answer section */
    count = buffer_pkt_ancount(q->buffer);
    if (count) {
        if (!buffer_skip_dname(q->buffer) ||
            !query_parse_soa(q->buffer, &serial)) {
            ods_log_error("[%s] dropped packet: zone %s received bad notify "
                "(bad soa in answer section)", query_str, q->zone->name);
            return QUERY_DISCARDED;
        }
        lock_basic_lock(&q->zone->xfrd->serial_lock);
        q->zone->xfrd->serial_notify = serial;
        q->zone->xfrd->serial_notify_acquired = time_now();
        if (!util_serial_gt(q->zone->xfrd->serial_notify,
            q->zone->xfrd->serial_disk)) {
            ods_log_debug("[%s] ignore notify: already got zone %s serial "
                "%u on disk", query_str, q->zone->name,
                q->zone->xfrd->serial_notify);
            lock_basic_unlock(&q->zone->xfrd->serial_lock);
            goto send_notify_ok;
        }
        lock_basic_unlock(&q->zone->xfrd->serial_lock);
    } else {
        lock_basic_lock(&q->zone->xfrd->serial_lock);
        q->zone->xfrd->serial_notify = 0;
        q->zone->xfrd->serial_notify_acquired = 0;
        lock_basic_unlock(&q->zone->xfrd->serial_lock);
    }
    /* forward notify to xfrd */
    xfrd_set_timer_now(q->zone->xfrd);
    dnshandler_fwd_notify(e->dnshandler, buffer_begin(q->buffer),
        buffer_remaining(q->buffer));

send_notify_ok:
    /* send notify ok */
    buffer_pkt_set_qr(q->buffer);
    buffer_pkt_set_aa(q->buffer);
    buffer_pkt_set_ancount(q->buffer, 0);

    buffer_clear(q->buffer); /* lim = pos, pos = 0; */
    buffer_set_position(q->buffer, pos);
    buffer_set_limit(q->buffer, buffer_capacity(q->buffer));
    q->reserved_space = edns_rr_reserved_space(q->edns_rr);
    q->reserved_space += tsig_rr_reserved_space(q->tsig_rr);
    return QUERY_PROCESSED;
}
Example #10
0
static void
handle_tcp_reading(netio_type *netio,
		   netio_handler_type *handler,
		   netio_event_types_type event_types)
{
	struct tcp_handler_data *data
		= (struct tcp_handler_data *) handler->user_data;
	ssize_t received;

	if (event_types & NETIO_EVENT_TIMEOUT) {
		/* Connection timed out.  */
		cleanup_tcp_handler(netio, handler);
		return;
	}

	if (data->nsd->tcp_query_count > 0 &&
		data->query_count >= data->nsd->tcp_query_count) {
		/* No more queries allowed on this tcp connection.  */
		cleanup_tcp_handler(netio, handler);
		return;
	}

	assert(event_types & NETIO_EVENT_READ);

	if (data->bytes_transmitted == 0) {
		query_reset(data->query, TCP_MAX_MESSAGE_LEN, 1);
	}

	/*
	 * Check if we received the leading packet length bytes yet.
	 */
	if (data->bytes_transmitted < sizeof(uint16_t)) {
		received = read(handler->fd,
				(char *) &data->query->tcplen
				+ data->bytes_transmitted,
				sizeof(uint16_t) - data->bytes_transmitted);
		if (received == -1) {
			if (errno == EAGAIN || errno == EINTR) {
				/*
				 * Read would block, wait until more
				 * data is available.
				 */
				return;
			} else {
#ifdef ECONNRESET
				if (verbosity >= 2 || errno != ECONNRESET)
#endif /* ECONNRESET */
				log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno));
				cleanup_tcp_handler(netio, handler);
				return;
			}
		} else if (received == 0) {
			/* EOF */
			cleanup_tcp_handler(netio, handler);
			return;
		}

		data->bytes_transmitted += received;
		if (data->bytes_transmitted < sizeof(uint16_t)) {
			/*
			 * Not done with the tcplen yet, wait for more
			 * data to become available.
			 */
			return;
		}

		assert(data->bytes_transmitted == sizeof(uint16_t));

		data->query->tcplen = ntohs(data->query->tcplen);

		/*
		 * Minimum query size is:
		 *
		 *     Size of the header (12)
		 *   + Root domain name   (1)
		 *   + Query class        (2)
		 *   + Query type         (2)
		 */
		if (data->query->tcplen < QHEADERSZ + 1 + sizeof(uint16_t) + sizeof(uint16_t)) {
			VERBOSITY(2, (LOG_WARNING, "packet too small, dropping tcp connection"));
			cleanup_tcp_handler(netio, handler);
			return;
		}

		if (data->query->tcplen > data->query->maxlen) {
			VERBOSITY(2, (LOG_WARNING, "insufficient tcp buffer, dropping connection"));
			cleanup_tcp_handler(netio, handler);
			return;
		}

		buffer_set_limit(data->query->packet, data->query->tcplen);
	}

	assert(buffer_remaining(data->query->packet) > 0);

	/* Read the (remaining) query data.  */
	received = read(handler->fd,
			buffer_current(data->query->packet),
			buffer_remaining(data->query->packet));
	if (received == -1) {
		if (errno == EAGAIN || errno == EINTR) {
			/*
			 * Read would block, wait until more data is
			 * available.
			 */
			return;
		} else {
#ifdef ECONNRESET
			if (verbosity >= 2 || errno != ECONNRESET)
#endif /* ECONNRESET */
			log_msg(LOG_ERR, "failed reading from tcp: %s", strerror(errno));
			cleanup_tcp_handler(netio, handler);
			return;
		}
	} else if (received == 0) {
		/* EOF */
		cleanup_tcp_handler(netio, handler);
		return;
	}

	data->bytes_transmitted += received;
	buffer_skip(data->query->packet, received);
	if (buffer_remaining(data->query->packet) > 0) {
		/*
		 * Message not yet complete, wait for more data to
		 * become available.
		 */
		return;
	}

	assert(buffer_position(data->query->packet) == data->query->tcplen);

	/* Account... */
#ifdef BIND8_STATS
# ifndef INET6
	STATUP(data->nsd, ctcp);
# else
	if (data->query->addr.ss_family == AF_INET) {
		STATUP(data->nsd, ctcp);
	} else if (data->query->addr.ss_family == AF_INET6) {
		STATUP(data->nsd, ctcp6);
	}
# endif
#endif /* BIND8_STATS */

	/* We have a complete query, process it.  */

	/* tcp-query-count: handle query counter ++ */
	data->query_count++;

	buffer_flip(data->query->packet);
	data->query_state = server_process_query(data->nsd, data->query);
	if (data->query_state == QUERY_DISCARDED) {
		/* Drop the packet and the entire connection... */
		STATUP(data->nsd, dropped);
#if defined(BIND8_STATS) && defined(USE_ZONE_STATS)
		if (data->query->zone) {
			ZTATUP(data->query->zone, dropped);
		}
#endif
		cleanup_tcp_handler(netio, handler);
		return;
	}

#ifdef BIND8_STATS
	if (RCODE(data->query->packet) == RCODE_OK
	    && !AA(data->query->packet))
	{
		STATUP(data->nsd, nona);
		ZTATUP(data->query->zone, nona);
	}

# ifdef USE_ZONE_STATS
#  ifndef INET6
	ZTATUP(data->query->zone, ctcp);
#  else
	if (data->query->addr.ss_family == AF_INET) {
		ZTATUP(data->query->zone, ctcp);
	} else if (data->query->addr.ss_family == AF_INET6) {
		ZTATUP(data->query->zone, ctcp6);
	}
#  endif
# endif /* USE_ZONE_STATS */

#endif /* BIND8_STATS */

	query_add_optional(data->query, data->nsd);

	/* Switch to the tcp write handler.  */
	buffer_flip(data->query->packet);
	data->query->tcplen = buffer_remaining(data->query->packet);
	data->bytes_transmitted = 0;

	handler->timeout->tv_sec = data->nsd->tcp_timeout;
	handler->timeout->tv_nsec = 0L;
	timespec_add(handler->timeout, netio_current_time(netio));

	handler->event_types = NETIO_EVENT_WRITE | NETIO_EVENT_TIMEOUT;
	handler->event_handler = handle_tcp_writing;
}
Example #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;
}