Commit 380672f3 authored by Jan Kadlec's avatar Jan Kadlec

zone-events: Initial IXFR-in implementation.

parent 8d008539
......@@ -17,8 +17,8 @@ enum {
ADD
};
/*! \brief Extended structure for IXFR processing. */
struct ixfr_proc {
/*! \brief Extended structure for IXFR-out processing. */
struct ixfrout_proc {
struct xfr_proc proc;
node_t *cur;
unsigned state;
......@@ -28,9 +28,12 @@ struct ixfr_proc {
};
/* IXFR-specific logging (internal, expects 'qdata' variable set). */
#define IXFR_LOG(severity, msg...) \
#define IXFROUT_LOG(severity, msg...) \
QUERY_LOG(severity, qdata, "Outgoing IXFR", msg)
#define IXFRIN_LOG(severity, msg...) \
ANSWER_LOG(severity, adata, "Incoming IXFR", msg)
/*! \brief Helper macro for putting RRs into packet. */
#define IXFR_SAFE_PUT(pkt, rr) \
ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC); \
......@@ -38,7 +41,7 @@ struct ixfr_proc {
return ret; \
}
static int ixfr_put_rrlist(knot_pkt_t *pkt, struct ixfr_proc *ixfr, list_t *list)
static int ixfr_put_rrlist(knot_pkt_t *pkt, struct ixfrout_proc *ixfr, list_t *list)
{
assert(pkt);
assert(ixfr);
......@@ -77,7 +80,7 @@ static int ixfr_put_rrlist(knot_pkt_t *pkt, struct ixfr_proc *ixfr, list_t *list
static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, struct xfr_proc *xfer)
{
int ret = KNOT_EOK;
struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer;
struct ixfrout_proc *ixfr = (struct ixfrout_proc *)xfer;
knot_changeset_t *chgset = (knot_changeset_t *)item;
/* Put former SOA. */
......@@ -115,8 +118,8 @@ static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, struct xfr_
}
/* Finished change set. */
struct query_data *qdata = ixfr->qdata; /*< Required for IXFR_LOG() */
IXFR_LOG(LOG_INFO, "Serial %u -> %u.", chgset->serial_from, chgset->serial_to);
struct query_data *qdata = ixfr->qdata; /*< Required for IXFROUT_LOG() */
IXFROUT_LOG(LOG_INFO, "Serial %u -> %u.", chgset->serial_from, chgset->serial_to);
return ret;
}
......@@ -145,7 +148,7 @@ static int ixfr_load_chsets(knot_changesets_t **chgsets, const zone_t *zone,
ret = journal_load_changesets(zone->conf->ixfr_db, *chgsets,
serial_from, serial_to);
if (ret != KNOT_EOK) {
knot_changesets_free(chgsets);
knot_changesets_free(chgsets, NULL);
}
return ret;
......@@ -177,11 +180,11 @@ static int ixfr_query_check(struct query_data *qdata)
static void ixfr_answer_cleanup(struct query_data *qdata)
{
struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
struct ixfrout_proc *ixfr = (struct ixfrout_proc *)qdata->ext;
mm_ctx_t *mm = qdata->mm;
ptrlist_free(&ixfr->proc.nodes, mm);
knot_changesets_free(&ixfr->changesets);
knot_changesets_free(&ixfr->changesets, NULL);
mm->free(qdata->ext);
/* Allow zone changes (finished). */
......@@ -211,12 +214,12 @@ static int ixfr_answer_init(struct query_data *qdata)
/* Initialize transfer processing. */
mm_ctx_t *mm = qdata->mm;
struct ixfr_proc *xfer = mm->alloc(mm->ctx, sizeof(struct ixfr_proc));
struct ixfrout_proc *xfer = mm->alloc(mm->ctx, sizeof(struct ixfrout_proc));
if (xfer == NULL) {
knot_changesets_free(&chgsets);
knot_changesets_free(&chgsets, NULL);
return KNOT_ENOMEM;
}
memset(xfer, 0, sizeof(struct ixfr_proc));
memset(xfer, 0, sizeof(struct ixfrout_proc));
gettimeofday(&xfer->proc.tstamp, NULL);
init_list(&xfer->proc.nodes);
xfer->qdata = qdata;
......@@ -276,7 +279,7 @@ static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
return NS_PROC_DONE;
}
int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata)
{
if (pkt == NULL || qdata == NULL) {
return NS_PROC_FAIL;
......@@ -284,7 +287,7 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
int ret = KNOT_EOK;
struct timeval now = {0};
struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
struct ixfrout_proc *ixfr = (struct ixfrout_proc*)qdata->ext;
/* If IXFR is disabled, respond with SOA. */
if (qdata->param->proc_flags & NS_QUERY_NO_IXFR) {
......@@ -296,21 +299,21 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
ret = ixfr_answer_init(qdata);
switch(ret) {
case KNOT_EOK: /* OK */
ixfr = (struct ixfr_proc*)qdata->ext;
IXFR_LOG(LOG_INFO, "Started (serial %u -> %u).",
knot_soa_serial(&ixfr->soa_from->rrs),
knot_soa_serial(&ixfr->soa_to->rrs));
ixfr = (struct ixfrout_proc*)qdata->ext;
IXFROUT_LOG(LOG_INFO, "Started (serial %u -> %u).",
knot_soa_serial(&ixfr->soa_from->rrs),
knot_soa_serial(&ixfr->soa_to->rrs));
break;
case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */
IXFR_LOG(LOG_INFO, "Zone is up-to-date.");
IXFROUT_LOG(LOG_INFO, "Zone is up-to-date.");
return ixfr_answer_soa(pkt, qdata);
case KNOT_ERANGE: /* No history -> AXFR. */
case KNOT_ENOENT:
IXFR_LOG(LOG_INFO, "Incomplete history, fallback to AXFR.");
IXFROUT_LOG(LOG_INFO, "Incomplete history, fallback to AXFR.");
qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
return axfr_query(pkt, qdata);
default: /* Server errors. */
IXFR_LOG(LOG_ERR, "Failed to start (%s).", knot_strerror(ret));
IXFROUT_LOG(LOG_ERR, "Failed to start (%s).", knot_strerror(ret));
return NS_PROC_FAIL;
}
}
......@@ -325,13 +328,13 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
return NS_PROC_FULL; /* Check for more. */
case KNOT_EOK: /* Last response. */
gettimeofday(&now, NULL);
IXFR_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).",
time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
ixfr->proc.npkts, ixfr->proc.nbytes / 1024.0);
IXFROUT_LOG(LOG_INFO, "Finished in %.02fs (%u messages, ~%.01fkB).",
time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
ixfr->proc.npkts, ixfr->proc.nbytes / 1024.0);
ret = NS_PROC_DONE;
break;
default: /* Generic error. */
IXFR_LOG(LOG_ERR, "%s", knot_strerror(ret));
IXFROUT_LOG(LOG_ERR, "%s", knot_strerror(ret));
ret = NS_PROC_FAIL;
break;
}
......@@ -339,90 +342,98 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata)
return ret;
}
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *data)
static void ixfrin_cleanup(struct answer_data *data)
{
struct ixfrin_proc *proc = data->ext;
if (proc) {
knot_changesets_free(&proc->changesets, data->mm);
mm_free(data->mm, proc);
data->ext = NULL;
}
}
static int ixfrin_answer_init(struct answer_data *data)
{
#warning TODO: reimplement ixfr_process_answer
#if 0
dbg_ns("ns_process_ixfrin: incoming packet\n");
/*
* [TSIG] Here we assume that 'xfr' contains TSIG information
* and the digest of the query sent to the master or the previous
* digest.
*/
int ret = xfrin_process_ixfr_packet(pkt, xfr);
if (ret == XFRIN_RES_FALLBACK) {
dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n");
ret = KNOT_ENOIXFR;
struct ixfrin_proc *proc = mm_alloc(data->mm, sizeof(struct ixfrin_proc));
data->ext = malloc(sizeof(struct ixfrin_proc));
if (proc == NULL) {
return KNOT_ENOMEM;
}
memset(proc, 0, sizeof(struct ixfrin_proc));
proc->changesets = knot_changesets_create(0);
if (proc->changesets == NULL) {
mm_free(data->mm, proc);
return KNOT_ENOMEM;
}
proc->state = IXFR_START;
proc->zone = data->param->zone;
data->ext = proc;
data->ext_cleanup = &ixfrin_cleanup;
return KNOT_EOK;
}
static int ixfrin_finalize(struct answer_data *adata)
{
struct ixfrin_proc *proc = adata->ext;
zone_t *zone = proc->zone;
knot_changesets_t *changesets = proc->changesets;
#warning if we need to check serials, here's the place
if (knot_changesets_empty(changesets) || proc->state != IXFR_DONE) {
ixfrin_cleanup(adata);
IXFRIN_LOG(LOG_INFO, "Fallback to AXFR.");
return KNOT_ENOIXFR;
}
if (ret < 0) {
knot_pkt_free(&xfr->query);
int ret = zone_change_apply_and_store(changesets, zone, "IXFR", adata->mm);
if (ret != KNOT_EOK) {
free(proc);
return ret;
} else if (ret > 0) {
dbg_ns("ns_process_ixfrin: IXFR finished\n");
gettimeofday(&xfr->t_end, NULL);
knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
if (chgsets == NULL || chgsets->first_soa == NULL) {
// nothing to be done??
dbg_ns("No changesets created for incoming IXFR!\n");
return ret;
}
}
// find zone associated with the changesets
/* Must not search for the zone in zonedb as it may fetch a
* different zone than the one the transfer started on. */
zone_t *zone = xfr->zone;
if (zone == NULL) {
dbg_ns("No zone found for incoming IXFR!\n");
knot_changesets_free(
(knot_changesets_t **)(&xfr->data));
return KNOT_ENOZONE;
}
proc->changesets = NULL; // Free'd by apply_and_store()
ixfrin_cleanup(adata);
switch (ret) {
case XFRIN_RES_COMPLETE:
break;
case XFRIN_RES_SOA_ONLY: {
// compare the SERIAL from the changeset with the zone's
// serial
uint32_t zone_serial = zone_contents_serial(zone->contents);
if (knot_serial_compare(
knot_soa_serial(&chgsets->first_soa->rrs),
zone_serial)
> 0) {
if ((xfr->flags & XFR_FLAG_UDP) != 0) {
// IXFR over UDP
dbg_ns("Update did not fit.\n");
return KNOT_EIXFRSPACE;
} else {
// fallback to AXFR
dbg_ns("ns_process_ixfrin: "
"Fallback to AXFR.\n");
knot_changesets_free(
(knot_changesets_t **)&xfr->data);
knot_pkt_free(&xfr->query);
return KNOT_ENOIXFR;
}
IXFRIN_LOG(LOG_INFO, "Finished.");
#warning TODO: schedule zone events, count transfer size and message count, time
return KNOT_EOK;
}
int ixfrin_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
{
if (adata->ext == NULL) {
IXFRIN_LOG(LOG_INFO, "Starting.");
// First packet with IXFR, init context
int ret = ixfrin_answer_init(adata);
if (ret != KNOT_EOK) {
IXFRIN_LOG(LOG_ERR, "Failed - %s", knot_strerror(ret));
return NS_PROC_FAIL;
}
}
int ret = xfrin_process_ixfr_packet(pkt, (struct ixfrin_proc *)adata->ext);
if (ret == NS_PROC_DONE) {
int fret = ixfrin_finalize(adata);
#warning get rid of this rcode mix
if (fret != KNOT_EOK) {
if (fret != KNOT_ENOIXFR) {
ret = NS_PROC_FAIL;
} else {
// free changesets
dbg_ns("No update needed.\n");
knot_changesets_free(
(knot_changesets_t **)(&xfr->data));
return KNOT_ENOXFR;
return KNOT_ENOIXFR;
}
} break;
}
}
/*! \todo In case of error, shouldn't the zone be destroyed here? */
if (ret == NS_PROC_FAIL) {
IXFRIN_LOG(LOG_ERR, "Failed.");
}
return ret;
#endif
return NS_PROC_FAIL;
}
#undef IXFR_LOG
#undef IXFROUT_LOG
......@@ -28,9 +28,30 @@
#define _KNOT_IXFR_H_
#include "libknot/packet/pkt.h"
#include "knot/updates/changesets.h"
#include "knot/zone/zone.h"
struct query_data;
struct answer_data;
struct xfr_proc;
/*! \brief IXFR-in processing states. */
enum ixfrin_states {
IXFR_START = 0,
IXFR_SOA_FROM = 1,
IXFR_SOA_TO = 2,
IXFR_DEL = 3,
IXFR_ADD = 4,
IXFR_DONE = 5
};
/*! \brief Extended structure for IXFR-in processing. */
struct ixfrin_proc {
int state;
knot_changesets_t *changesets;
zone_t *zone;
mm_ctx_t *mm;
};
/*!
* \brief IXFR query processing module.
......@@ -39,7 +60,7 @@ struct answer_data;
* \retval FAIL if it encountered an error.
* \retval DONE if finished.
*/
int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata);
int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata);
/*!
* \brief Process an IXFR query response.
......@@ -64,7 +85,7 @@ int ixfr_answer(knot_pkt_t *pkt, struct query_data *qdata);
* \retval Other If any other error occured. The connection should be closed.
*
*/
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *data);
int ixfrin_process_answer(knot_pkt_t *pkt, struct answer_data *adata);
#endif /* _KNOT_IXFR_H_ */
......
......@@ -125,7 +125,7 @@ int process_answer(knot_pkt_t *pkt, knot_process_t *ctx)
next_state = axfr_process_answer(pkt, data);
break;
case KNOT_RESPONSE_IXFR:
next_state = ixfr_process_answer(pkt, data);
next_state = ixfrin_process_answer(pkt, data);
break;
case KNOT_RESPONSE_NOTIFY:
next_state = notify_process_answer(pkt, data);
......@@ -139,4 +139,4 @@ int process_answer(knot_pkt_t *pkt, knot_process_t *ctx)
return next_state;
}
#undef ANSWER_REQUIRES
\ No newline at end of file
#undef ANSWER_REQUIRES
......@@ -372,7 +372,7 @@ static int query_internet(knot_pkt_t *pkt, knot_process_t *ctx)
next_state = axfr_query(pkt, data);
break;
case KNOT_QUERY_IXFR:
next_state = ixfr_answer(pkt, data);
next_state = ixfr_query(pkt, data);
break;
case KNOT_QUERY_UPDATE:
next_state = update_answer(pkt, data);
......
......@@ -162,7 +162,7 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents,
&refresh_at);
}
if (ret != KNOT_EOK) {
knot_changesets_free(&sec_chs);
knot_changesets_free(&sec_chs, NULL);
return ret;
}
......@@ -170,14 +170,14 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents,
zone_contents_set_gen_old(new_contents);
ret = xfrin_apply_changesets_directly(new_contents, sec_chs);
if (ret != KNOT_EOK) {
knot_changesets_free(&sec_chs);
knot_changesets_free(&sec_chs, NULL);
return ret;
}
// Merge changesets
ret = knot_changeset_merge(ddns_ch, sec_ch);
if (ret != KNOT_EOK) {
knot_changesets_free(&sec_chs);
knot_changesets_free(&sec_chs, NULL);
return ret;
}
......@@ -211,7 +211,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
knot_changeset_t *ddns_ch = knot_changesets_get_last(ddns_chs);
ret = ddns_process_update(zone, query, ddns_ch, rcode);
if (ret != KNOT_EOK) {
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
return ret;
}
......@@ -225,11 +225,11 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
} else {
*rcode = KNOT_RCODE_SERVFAIL;
}
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
return ret;
}
} else {
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
*rcode = KNOT_RCODE_NOERROR;
return KNOT_EOK;
}
......@@ -239,7 +239,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
ret = sign_update(zone, zone->contents, new_contents, ddns_ch);
if (ret != KNOT_EOK) {
xfrin_rollback_update(ddns_chs, &new_contents);
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
*rcode = KNOT_RCODE_SERVFAIL;
return ret;
}
......@@ -249,7 +249,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
ret = zone_change_store(zone, ddns_chs);
if (ret != KNOT_EOK) {
xfrin_rollback_update(ddns_chs, &new_contents);
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
*rcode = KNOT_RCODE_SERVFAIL;
return ret;
}
......@@ -261,7 +261,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
xfrin_zone_contents_free(&old_contents);
xfrin_cleanup_successful_update(ddns_chs);
knot_changesets_free(&ddns_chs);
knot_changesets_free(&ddns_chs, NULL);
/* Trim extra heap. */
mem_trim();
......
......@@ -862,7 +862,7 @@ static int xfr_task_xfer(xfrhandler_t *xfr, knot_ns_xfr_t *rq, knot_pkt_t *pkt)
ret = axfr_process_answer(pkt, rq);
break;
case XFR_TYPE_IIN:
ret = ixfr_process_answer(pkt, rq);
ret = ixfrin_process_answer(pkt, rq);
break;
default:
ret = KNOT_EINVAL;
......
......@@ -65,7 +65,7 @@ knot_changesets_t *knot_changesets_create(unsigned count)
for (unsigned i = 0; i < count; ++i) {
knot_changeset_t *change = knot_changesets_create_changeset(ch);
if (change == NULL) {
knot_changesets_free(&ch);
knot_changesets_free(&ch, NULL);
return NULL;
}
}
......@@ -260,7 +260,7 @@ static void knot_free_changeset(knot_changeset_t *changeset)
free(changeset->data);
}
static void knot_changesets_deinit(knot_changesets_t *changesets)
static void knot_changesets_deinit(knot_changesets_t *changesets, mm_ctx_t *rr_mm)
{
if (!EMPTY_LIST(changesets->sets)) {
knot_changeset_t *chg = NULL;
......@@ -274,28 +274,28 @@ static void knot_changesets_deinit(knot_changesets_t *changesets)
// Free pool with RRs in sets / changes
mp_delete(changesets->mmc_rr.ctx);
knot_rrset_free(&changesets->first_soa, NULL);
knot_rrset_free(&changesets->first_soa, rr_mm);
}
void knot_changesets_free(knot_changesets_t **changesets)
void knot_changesets_free(knot_changesets_t **changesets, mm_ctx_t *rr_mm)
{
if (changesets == NULL || *changesets == NULL) {
return;
}
knot_changesets_deinit(*changesets);
knot_changesets_deinit(*changesets, rr_mm);
free(*changesets);
*changesets = NULL;
}
int knot_changesets_clear(knot_changesets_t *changesets)
int knot_changesets_clear(knot_changesets_t *changesets, mm_ctx_t *rr_mm)
{
if (changesets == NULL) {
return KNOT_EINVAL;
}
knot_changesets_deinit(changesets);
knot_changesets_deinit(changesets, rr_mm);
return knot_changesets_init(changesets);
}
......@@ -101,15 +101,8 @@ typedef enum {
*/
knot_changesets_t *knot_changesets_create(unsigned count);
/*!
* \brief Frees the 'changesets' structure, including all its internal data.
*
* \param changesets Double pointer to changesets structure to be freed.
*/
void knot_changesets_free(knot_changesets_t **changesets);
/*! \brief Reinitialize changesets structure. */
int knot_changesets_clear(knot_changesets_t *changesets);
int knot_changesets_clear(knot_changesets_t *changesets, mm_ctx_t *rr_mm);
/*!
* \brief Creates new changeset structure and returns it to caller.
......@@ -204,9 +197,10 @@ int knot_changeset_apply(knot_changeset_t *changeset,
/*!
* \brief Frees the 'changesets' structure, including all its internal data.
*
* \param changesets Double pointer to changesets structure to be freed.
* \param changesets Double pointer to changesets structure to be freed.
* \param mm Memory context used to allocate RRSets.
*/
void knot_changesets_free(knot_changesets_t **changesets);
void knot_changesets_free(knot_changesets_t **changesets, mm_ctx_t *rr_mm);
/*!
* \brief Merges two changesets together, second changeset's lists are kept.
......
......@@ -24,6 +24,7 @@
#include "libknot/packet/wire.h"
#include "common/debug.h"
#include "libknot/packet/pkt.h"
#include "libknot/processing/process.h"
#include "libknot/dname.h"
#include "knot/zone/zone.h"
#include "knot/zone/zonefile.h"
......@@ -40,6 +41,7 @@
#include "libknot/util/utils.h"
#include "libknot/rdata/soa.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/ixfr.h"
#define KNOT_NS_TSIG_FREQ 100
......@@ -252,292 +254,172 @@ int xfrin_process_axfr_packet(knot_pkt_t *pkt, struct xfr_proc *proc)
/*----------------------------------------------------------------------------*/
int xfrin_process_ixfr_packet(knot_pkt_t *pkt, knot_ns_xfr_t *xfr)
static int solve_start(const knot_rrset_t *rr, knot_changesets_t *changesets, mm_ctx_t *mm)
{
knot_changesets_t **chs = (knot_changesets_t **)(&xfr->data);
if (pkt == NULL || chs == NULL) {
dbg_xfrin("Wrong parameters supported.\n");
return KNOT_EINVAL;
assert(changesets->first_soa == NULL);
if (rr->type != KNOT_RRTYPE_SOA) {
return NS_PROC_FAIL;
}
uint16_t rr_id = 0;
const knot_rrset_t *rr = NULL;
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
xfrin_take_rr(answer, &rr, &rr_id);
if (rr == NULL) {
return KNOT_EXFRREFUSED; /* Empty, try again with AXFR */
// Store the first SOA for later use.
changesets->first_soa = knot_rrset_copy(rr, mm);
if (changesets->first_soa == NULL) {
return NS_PROC_FAIL;
}
// state of the transfer
// -1 .. a SOA is expected to create a new changeset
int state = 0;
int ret = KNOT_EOK;
if (*chs == NULL) {
dbg_xfrin_verb("Changesets empty, creating new.\n");
*chs = knot_changesets_create(0);
if (*chs == NULL) {
return KNOT_ENOMEM;
}
return NS_PROC_MORE;
}
// the first RR must be a SOA
if (rr->type != KNOT_RRTYPE_SOA) {
dbg_xfrin("First RR is not a SOA RR!\n");
ret = KNOT_EMALF;
goto cleanup;
}
static int solve_soa_from(const knot_rrset_t *rr, knot_changesets_t *changesets,
int *state, mm_ctx_t *mm)
{
if (rr->type != KNOT_RRTYPE_SOA) {
return NS_PROC_FAIL;
}
// just store the first SOA for later use
(*chs)->first_soa = knot_rrset_copy(rr, NULL);
if ((*chs)->first_soa == NULL) {
ret = KNOT_ENOMEM;
goto cleanup;
}
state = -1;
dbg_xfrin_verb("First SOA of IXFR saved, state set to -1.\n");
// take next RR
xfrin_take_rr(answer, &rr, &rr_id);
/*
* If there is no other records in the response than the SOA, it
* means one of these three cases:
*
* 1) The server does not have newer zone than ours.
* This is indicated by serial equal to the one of our zone.
* 2) The server wants to send the transfer but is unable to fit
* it in the packet. This is indicated by serial different
* (newer) from the one of our zone, but applies only for
* IXFR/UDP.
* 3) The master is weird and sends only SOA in the first packet
* of a fallback to AXFR answer (PowerDNS does this).
*
* The serials must be compared in other parts of the server, so
* just indicate that the answer contains only one SOA.
*/
if (rr == NULL) {
dbg_xfrin("Response containing only SOA,\n");
return XFRIN_RES_SOA_ONLY;
} else if (rr->type != KNOT_RRTYPE_SOA) {
dbg_xfrin("Fallback to AXFR.\n");
return XFRIN_RES_FALLBACK;
}
} else {
if ((*chs)->first_soa == NULL) {
dbg_xfrin("Changesets don't contain SOA first!\n");
ret = KNOT_EINVAL;
goto cleanup;
}
dbg_xfrin_detail("Changesets present.\n");
if (knot_rrset_equal(rr, changesets->first_soa, KNOT_RRSET_COMPARE_WHOLE)) {
// Last SOA encountered, transfer done.
*state = IXFR_DONE;
return NS_PROC_DONE;
}
/*
* Process the next RR. Different requirements are in place in
* different cases:
*
* 1) Last changeset has both soa_from and soa_to.
* a) The next RR is a SOA.
* i) The next RR is equal to the first_soa saved in changesets.
* This denotes the end of the transfer. It may be dropped and
* the end should be signalised by returning positive value.
*
* ii) The next RR is some other SOA.
* This means a start of new changeset - create it and add it
* to the list.
*
* b) The next RR is not a SOA.
* Put the RR into the ADD part of the last changeset as this is
* not finished yet. Continue while SOA is not encountered. Then
* jump to 1-a.
*
* 2) Last changeset has only the soa_from and does not have soa_to.
* a) The next RR is a SOA.
* This means start of the ADD section. Put the SOA to the
* changeset. Continue adding RRs to the ADD section while SOA
* is not encountered. This is identical to 1-b.
*
* b) The next RR is not a SOA.
* This means the REMOVE part is not finished yet. Add the RR to
* the REMOVE part. Continue adding next RRs until a SOA is
* encountered. Then jump to 2-a.
*/
// Create new changeset.
knot_changeset_t *change = knot_changesets_create_changeset(changesets);
if (change == NULL) {
return NS_PROC_FAIL;
}
// first, find out in what state we are
/*! \todo It would be more elegant to store the state in the
* changesets structure, or in some place persistent between
* calls to this function.
*/
knot_changeset_t *chset = knot_changesets_get_last(*chs);
if (state != -1) {
dbg_xfrin_detail("State is not -1, deciding...\n");
// there should be at least one started changeset right now
if (EMPTY_LIST((*chs)->sets)) {
ret = KNOT_EMALF;
goto cleanup;
}
// Store SOA into changeset.
change->soa_from = knot_rrset_copy(rr, mm);
if (change->soa_from == NULL) {
return NS_PROC_FAIL;
}
change->serial_from = knot_soa_serial(&rr->rrs);
// a changeset should be created only when there is a SOA
assert(chset->