ixfr.c 19.5 KB
Newer Older
1 2 3
#include "knot/nameserver/ixfr.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/internet.h"
4
#include "knot/nameserver/process_query.h"
5
#include "knot/nameserver/process_answer.h"
Jan Kadlec's avatar
Jan Kadlec committed
6
#include "knot/updates/apply.h"
7
#include "common/debug.h"
8
#include "libknot/descriptor.h"
9
#include "libknot/util/utils.h"
10
#include "libknot/rrtype/soa.h"
11

12 13
/* ------------------------ IXFR-out processing ----------------------------- */

14 15 16 17 18 19 20 21
/*! \brief IXFR-in processing states. */
enum ixfr_states {
	IXFR_START = 0,  /* IXFR-in starting, expecting final SOA. */
	IXFR_SOA_DEL,    /* Expecting starting SOA. */
	IXFR_SOA_ADD,    /* Expecting ending SOA. */
	IXFR_DEL,        /* Expecting RR to delete. */
	IXFR_ADD,        /* Expecting RR to add. */
	IXFR_DONE        /* Processing done, IXFR-in complete. */
22 23
};

24 25 26
/*! \brief Extended structure for IXFR-in/IXFR-out processing. */
struct ixfr_proc {
	struct xfr_proc proc;          /* Generic transfer processing context. */
27
	changeset_iter_t cur;          /* Current changeset iteration state.*/
28
	knot_rrset_t cur_rr;           /* Currently processed RRSet. */
29
	int state;                     /* IXFR-in state. */
30 31
	knot_rrset_t *final_soa;       /* First SOA received via IXFR. */
	list_t changesets;             /* Processed changesets. */
32
	size_t change_count;           /* Count of changesets received. */
33 34
	zone_t *zone;                  /* Modified zone - for journal access. */
	mm_ctx_t *mm;                  /* Memory context for RR allocations. */
35
	struct query_data *qdata;
36 37
	const knot_rrset_t *soa_from;
	const knot_rrset_t *soa_to;
38 39
};

40
/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */
41
#define IXFROUT_LOG(severity, msg...) \
42
	QUERY_LOG(severity, qdata, "Outgoing IXFR", msg)
43

44 45
/*! \brief Helper macro for putting RRs into packet. */
#define IXFR_SAFE_PUT(pkt, rr) \
46
	ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC); \
47 48 49 50
	if (ret != KNOT_EOK) { \
		return ret; \
	}

51 52 53
/*! \brief Puts current RR into packet, stores state for retries. */
static int ixfr_put_chg_part(knot_pkt_t *pkt, struct ixfr_proc *ixfr,
                             changeset_iter_t *itt)
54
{
55 56
	assert(pkt);
	assert(ixfr);
57
	assert(itt);
58

59 60 61
	if (knot_rrset_empty(&ixfr->cur_rr)) {
		ixfr->cur_rr = changeset_iter_next(itt);
	}
62
	int ret = KNOT_EOK; // Declaration for IXFR_SAFE_PUT macro
63 64 65
	while(!knot_rrset_empty(&ixfr->cur_rr)) {
		IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
		ixfr->cur_rr = changeset_iter_next(itt);
66
	}
67

68
	return ret;
69 70
}

71 72 73 74 75 76
/*! \brief Tests if iteration has started. */
static bool iter_empty(struct ixfr_proc *ixfr)
{
	return EMPTY_LIST(ixfr->cur.iters) && knot_rrset_empty(&ixfr->cur_rr);
}

77 78 79 80 81 82 83
/*!
 * \brief Process single changeset.
 * \note Keep in mind that this function must be able to resume processing,
 *       for example if it fills a packet and returns ESPACE, it is called again
 *       with next empty answer and it must resume the processing exactly where
 *       it's left off.
 */
84 85
static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item,
                                  struct xfr_proc *xfer)
86
{
87
	int ret = KNOT_EOK;
88 89
	struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer;
	changeset_t *chgset = (changeset_t *)item;
90 91

	/* Put former SOA. */
92
	if (ixfr->state == IXFR_SOA_DEL) {
93 94
		IXFR_SAFE_PUT(pkt, chgset->soa_from);
		dbg_ns("%s: put 'REMOVE' SOA\n", __func__);
95
		ixfr->state = IXFR_DEL;
96 97 98
	}

	/* Put REMOVE RRSets. */
99
	if (ixfr->state == IXFR_DEL) {
100 101 102 103 104
		if (iter_empty(ixfr)) {
			ret = changeset_iter_rem(&ixfr->cur, chgset, false);
			if (ret != KNOT_EOK) {
				return ret;
			}
105
		}
106
		ret = ixfr_put_chg_part(pkt, ixfr, &ixfr->cur);
107 108 109
		if (ret != KNOT_EOK) {
			return ret;
		}
110
		changeset_iter_clear(&ixfr->cur);
111
		ixfr->state = IXFR_SOA_ADD;
112 113 114
	}

	/* Put next SOA. */
115
	if (ixfr->state == IXFR_SOA_ADD) {
116
		IXFR_SAFE_PUT(pkt, chgset->soa_to);
117 118
		dbg_ns("%s: put 'IXFR_ADD' SOA\n", __func__);
		ixfr->state = IXFR_ADD;
119 120
	}

121
	/* Put Add RRSets. */
122
	if (ixfr->state == IXFR_ADD) {
123
		if (iter_empty(ixfr)) {
124
			changeset_iter_add(&ixfr->cur, chgset, false);
125 126 127
			if (ret != KNOT_EOK) {
				return ret;
			}
128
		}
129
		ret = ixfr_put_chg_part(pkt, ixfr, &ixfr->cur);
130 131 132
		if (ret != KNOT_EOK) {
			return ret;
		}
133
		changeset_iter_clear(&ixfr->cur);
134
		ixfr->state = IXFR_SOA_DEL;
135
	}
136

137
	/* Finished change set. */
138
	struct query_data *qdata = ixfr->qdata; /*< Required for IXFROUT_LOG() */
139 140 141
	const uint32_t serial_from = knot_soa_serial(&chgset->soa_from->rrs);
	const uint32_t serial_to = knot_soa_serial(&chgset->soa_to->rrs);
	IXFROUT_LOG(LOG_INFO, "Serial %u -> %u.", serial_from, serial_to);
142 143 144 145 146 147

	return ret;
}

#undef IXFR_SAFE_PUT

148
/*! \brief Loads IXFRs from journal. */
149 150
static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
                            const knot_rrset_t *their_soa)
151 152 153 154 155
{
	assert(chgsets);
	assert(zone);

	/* Compare serials. */
156
	uint32_t serial_to = zone_contents_serial(zone->contents);
157
	uint32_t serial_from = knot_soa_serial(&their_soa->rrs);
158
	int ret = knot_serial_compare(serial_to, serial_from);
159 160 161 162
	if (ret <= 0) { /* We have older/same age zone. */
		return KNOT_EUPTODATE;
	}

163
	ret = journal_load_changesets(zone, chgsets, serial_from, serial_to);
164
	if (ret != KNOT_EOK) {
165
		changesets_free(chgsets);
166 167 168 169 170
	}

	return ret;
}

171
/*! \brief Check IXFR query validity. */
172
static int ixfr_query_check(struct query_data *qdata)
173
{
174 175 176
	/* Check if zone exists. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

177 178 179
	/* Need IXFR query type. */
	NS_NEED_QTYPE(qdata, KNOT_RRTYPE_IXFR, KNOT_RCODE_FORMERR);
	/* Need SOA authority record. */
Marek Vavrusa's avatar
Marek Vavrusa committed
180
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
181
	const knot_rrset_t *their_soa = &authority->rr[0];
182
	if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
183
		qdata->rcode = KNOT_RCODE_FORMERR;
184
		return NS_PROC_FAIL;
185 186 187
	}
	/* SOA needs to match QNAME. */
	NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
188

189
	/* Check transcation security and zone contents. */
190
	NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata);
191
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */
192

Marek Vavrusa's avatar
Marek Vavrusa committed
193
	return NS_PROC_DONE;
194
}
195

196
/*! \brief Cleans up ixfr processing context. */
197 198
static void ixfr_answer_cleanup(struct query_data *qdata)
{
199
	struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
200 201 202
	mm_ctx_t *mm = qdata->mm;

	ptrlist_free(&ixfr->proc.nodes, mm);
203
	changeset_iter_clear(&ixfr->cur);
204
	changesets_free(&ixfr->changesets);
205
	mm->free(qdata->ext);
206 207 208

	/* Allow zone changes (finished). */
	rcu_read_unlock();
209 210
}

211
/*! \brief Inits ixfr processing context. */
212 213
static int ixfr_answer_init(struct query_data *qdata)
{
214
	/* Check IXFR query validity. */
215 216
	int state = ixfr_query_check(qdata);
	if (state == NS_PROC_FAIL) {
217 218 219 220 221
		if (qdata->rcode == KNOT_RCODE_FORMERR) {
			return KNOT_EMALF;
		} else {
			return KNOT_EDENIED;
		}
222
	}
223

224
	/* Compare serials. */
225
	const knot_rrset_t *their_soa = &knot_pkt_section(qdata->query, KNOT_AUTHORITY)->rr[0];
226 227
	list_t chgsets;
	init_list(&chgsets);
228 229
	int ret = ixfr_load_chsets(&chgsets, qdata->zone, their_soa);
	if (ret != KNOT_EOK) {
230
		dbg_ns("%s: failed to load changesets => %d\n", __func__, ret);
231 232 233 234 235
		return ret;
	}

	/* Initialize transfer processing. */
	mm_ctx_t *mm = qdata->mm;
236
	struct ixfr_proc *xfer = mm->alloc(mm->ctx, sizeof(struct ixfr_proc));
237
	if (xfer == NULL) {
238
		changesets_free(&chgsets);
239 240
		return KNOT_ENOMEM;
	}
241
	memset(xfer, 0, sizeof(struct ixfr_proc));
242
	gettimeofday(&xfer->proc.tstamp, NULL);
243
	xfer->state = IXFR_SOA_DEL;
244
	init_list(&xfer->proc.nodes);
245
	init_list(&xfer->changesets);
246
	init_list(&xfer->cur.iters);
247
	knot_rrset_init_empty(&xfer->cur_rr);
248
	add_tail_list(&xfer->changesets, &chgsets);
249
	xfer->qdata = qdata;
250

251
	/* Put all changesets to processing queue. */
252
	changeset_t *chs = NULL;
253
	WALK_LIST(chs, xfer->changesets) {
254 255
		ptrlist_add(&xfer->proc.nodes, chs, mm);
	}
256

257
	/* Keep first and last serial. */
258
	chs = HEAD(xfer->changesets);
259
	xfer->soa_from = chs->soa_from;
260
	chs = TAIL(xfer->changesets);
261
	xfer->soa_to = chs->soa_to;
262

263 264 265 266
	/* Set up cleanup callback. */
	qdata->ext = xfer;
	qdata->ext_cleanup = &ixfr_answer_cleanup;

267 268 269
	/* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
	rcu_read_lock();

270 271 272
	return KNOT_EOK;
}

273
/*! \brief Sends response to SOA query. */
274
static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
275 276
{
	dbg_ns("%s: answering IXFR/SOA\n", __func__);
277
	if (pkt == NULL || qdata == NULL) {
278 279 280
		return NS_PROC_FAIL;
	}

281 282 283 284 285
	/* Check query. */
	int state = ixfr_query_check(qdata);
	if (state == NS_PROC_FAIL) {
		return state; /* Malformed query. */
	}
286

287
	/* Reserve space for TSIG. */
288
	knot_pkt_reserve(pkt, tsig_wire_maxsize(qdata->sign.tsig_key));
289

290
	/* Guaranteed to have zone contents. */
291 292
	const zone_node_t *apex = qdata->zone->contents->apex;
	knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
293
	if (knot_rrset_empty(&soa_rr)) {
294 295
		return NS_PROC_FAIL;
	}
296
	int ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
297 298 299 300 301
	if (ret != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		return NS_PROC_FAIL;
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
302
	return NS_PROC_DONE;
303
}
304

305
/* ------------------------- IXFR-in processing ----------------------------- */
306

307 308 309
/* IXFR-in-specific logging (internal, expects 'adata' variable set). */
#define IXFRIN_LOG(severity, msg...) \
	ANSWER_LOG(severity, adata, "Incoming IXFR", msg)
310

311
/*! \brief Cleans up data allocated by IXFR-in processing. */
312 313
static void ixfrin_cleanup(struct answer_data *data)
{
314
	struct ixfr_proc *proc = data->ext;
315
	if (proc) {
316
		changesets_free(&proc->changesets);
317
		knot_rrset_free(&proc->final_soa, proc->mm);
318 319 320 321 322
		mm_free(data->mm, proc);
		data->ext = NULL;
	}
}

323
/*! \brief Initializes IXFR-in processing context. */
324
static int ixfrin_answer_init(struct answer_data *data)
325
{
326
	struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(struct ixfr_proc));
327 328 329
	if (proc == NULL) {
		return KNOT_ENOMEM;
	}
330 331
	memset(proc, 0, sizeof(struct ixfr_proc));
	gettimeofday(&proc->proc.tstamp, NULL);
332

333 334
	init_list(&proc->changesets);

335 336
	proc->state = IXFR_START;
	proc->zone = data->param->zone;
337
	proc->mm = data->mm;
338 339 340 341 342 343 344

	data->ext = proc;
	data->ext_cleanup = &ixfrin_cleanup;

	return KNOT_EOK;
}

345
/*! \brief Finalizes IXFR-in processing. */
346 347
static int ixfrin_finalize(struct answer_data *adata)
{
348 349
	struct ixfr_proc *ixfr = adata->ext;
	assert(ixfr->state == IXFR_DONE);
350 351 352 353 354 355 356 357 358 359 360

	zone_contents_t *new_contents;
	int ret = apply_changesets(ixfr->zone, &ixfr->changesets, &new_contents);
	if (ret != KNOT_EOK) {
		IXFRIN_LOG(LOG_ERR, "Failed to apply changes to zone - %s",
		           knot_strerror(ret));
		return ret;
	}

	/* Write changes to journal. */
	ret = zone_changes_store(ixfr->zone, &ixfr->changesets);
361
	if (ret != KNOT_EOK) {
362 363
		IXFRIN_LOG(LOG_ERR, "Failed to apply changes to zone - %s",
		           knot_strerror(ret));
364 365
		updates_rollback(&ixfr->changesets);
		update_free_old_zone(&new_contents);
366
		return ret;
367
	}
368

369 370 371 372 373 374 375
	/* Switch zone contents. */
	zone_contents_t *old_contents = zone_switch_contents(ixfr->zone, new_contents);
	synchronize_rcu();
	update_free_old_zone(&old_contents);

	updates_cleanup(&ixfr->changesets);

376 377
	struct timeval now = {0};
	gettimeofday(&now, NULL);
378
	IXFRIN_LOG(LOG_INFO, "Finished in %.02fs (%u messages, %s%.*f %s).",
379
	           time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
380
	           ixfr->proc.npkts, SIZE_PARAMS(ixfr->proc.nbytes));
381

382 383 384
	return KNOT_EOK;
}

385
/*! \brief Stores starting SOA into changesets structure. */
386
static int solve_start(const knot_rrset_t *rr, struct ixfr_proc *proc)
387
{
388
	assert(proc->final_soa == NULL);
389
	if (rr->type != KNOT_RRTYPE_SOA) {
390
		return KNOT_EMALF;
391 392 393
	}

	// Store the first SOA for later use.
394 395
	proc->final_soa = knot_rrset_copy(rr, proc->mm);
	if (proc->final_soa == NULL) {
396
		return KNOT_ENOMEM;
397 398
	}

399
	return KNOT_EOK;
400 401
}

402
/*! \brief Decides what to do with a starting SOA (deletions). */
403
static int solve_soa_del(const knot_rrset_t *rr, struct ixfr_proc *proc)
404 405
{
	if (rr->type != KNOT_RRTYPE_SOA) {
406
		return KNOT_EMALF;
407 408 409
	}

	// Create new changeset.
Jan Kadlec's avatar
Jan Kadlec committed
410
	changeset_t *change = changeset_new(proc->zone->name);
411
	if (change == NULL) {
412
		return KNOT_ENOMEM;
413 414 415
	}

	// Store SOA into changeset.
416
	change->soa_from = knot_rrset_copy(rr, NULL);
417
	if (change->soa_from == NULL) {
418
		changeset_clear(change);
419
		return KNOT_ENOMEM;
420 421
	}

422 423 424 425
	// Add changeset.
	add_tail(&proc->changesets, &change->n);
	++proc->change_count;

426
	return KNOT_EOK;
427 428 429
}

/*! \brief Stores ending SOA into changeset. */
430
static int solve_soa_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
431
{
432
	assert(rr->type == KNOT_RRTYPE_SOA);
433
	change->soa_to = knot_rrset_copy(rr, NULL);
434
	if (change->soa_to == NULL) {
435
		return KNOT_ENOMEM;
436 437
	}

438
	return KNOT_EOK;
439 440
}

441 442 443
/*! \brief Adds single RR into remove section of changeset. */
static int solve_del(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
{
444
	return changeset_rem_rrset(change, rr);
445 446 447 448
}

/*! \brief Adds single RR into add section of changeset. */
static int solve_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
449
{
450
	return changeset_add_rrset(change, rr);
451 452
}

453 454
/*! \brief Decides what the next IXFR-in state should be. */
static int ixfrin_next_state(struct ixfr_proc *proc, const knot_rrset_t *rr)
455
{
456
	const bool soa = (rr->type == KNOT_RRTYPE_SOA);
457 458
	if (soa &&
	    (proc->state == IXFR_SOA_ADD || proc->state == IXFR_ADD)) {
459
		// Check end of transfer.
460
		if (knot_rrset_equal(rr, proc->final_soa,
461 462 463 464 465 466 467 468 469
		                     KNOT_RRSET_COMPARE_WHOLE)) {
			// Final SOA encountered, transfer done.
			return IXFR_DONE;
		}
	}

	switch (proc->state) {
	case IXFR_START:
		// Final SOA already stored or transfer start.
470
		return proc->final_soa ? IXFR_SOA_DEL : IXFR_START;
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
	case IXFR_SOA_DEL:
		// Empty delete section or start of delete section.
		return soa ? IXFR_SOA_ADD : IXFR_DEL;
	case IXFR_SOA_ADD:
		// Empty add section or start of add section.
		return soa ? IXFR_SOA_DEL : IXFR_ADD;
	case IXFR_DEL:
		// End of delete section or continue.
		return soa ? IXFR_SOA_ADD : IXFR_DEL;
	case IXFR_ADD:
		// End of add section or continue.
		return soa ? IXFR_SOA_DEL : IXFR_ADD;
	default:
		assert(0);
		return 0;
	}
487 488 489 490 491 492 493
}

/*!
 * \brief Processes single RR according to current IXFR-in state. The states
 *        correspond with IXFR-in message structure, in the order they are
 *        mentioned in the code.
 *
494 495
 * \param rr    RR to process.
 * \param proc  Processing context.
496
 *
497
 * \return KNOT_E*
498
 */
499
static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
500
{
501
	proc->state = ixfrin_next_state(proc, rr);
502
	changeset_t *change = TAIL(proc->changesets);
503 504

	switch (proc->state) {
505
	case IXFR_START:
506
		return solve_start(rr, proc);
507
	case IXFR_SOA_DEL:
508
		return solve_soa_del(rr, proc);
509
	case IXFR_DEL:
510 511 512
		return solve_del(rr, change, proc->mm);
	case IXFR_SOA_ADD:
		return solve_soa_add(rr, change, proc->mm);
513
	case IXFR_ADD:
514 515 516
		return solve_add(rr, change, proc->mm);
	case IXFR_DONE:
		return KNOT_EOK;
517
	default:
518
		return KNOT_ERROR;
519 520 521 522
	}
}

/*! \brief Checks whether journal node limit has not been exceeded. */
523
static bool journal_limit_exceeded(struct ixfr_proc *proc)
524
{
525
	return proc->change_count > JOURNAL_NCOUNT;
526 527 528
}

/*! \brief Checks whether RR belongs into zone. */
529
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc)
530 531 532 533 534 535 536 537
{
	return !knot_dname_is_sub(rr->owner, proc->zone->name) &&
	       !knot_dname_is_equal(rr->owner, proc->zone->name);
}

/*!
 * \brief Processes IXFR reply packet and fills in the changesets structure.
 *
538 539
 * \param pkt    Packet containing the IXFR reply in wire format.
 * \param adata  Answer data, including processing context.
540 541 542
 *
 * \return NS_PROC_MORE, NS_PROC_DONE, NS_PROC_FAIL
 */
543
static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
544
{
545
	struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext;
Daniel Salzman's avatar
Daniel Salzman committed
546

547 548 549 550 551
	// Update counters.
	ixfr->proc.npkts  += 1;
	ixfr->proc.nbytes += pkt->size;

	// Process RRs in the message.
552
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
553 554
	for (uint16_t i = 0; i < answer->count; ++i) {
		if (journal_limit_exceeded(ixfr)) {
555
			IXFRIN_LOG(LOG_WARNING, "Journal is full.");
556
			return NS_PROC_FAIL;
557 558 559
		}

		const knot_rrset_t *rr = &answer->rr[i];
560
		if (out_of_zone(rr, ixfr)) {
561 562 563
			continue;
		}

564 565
		int ret = ixfrin_step(rr, ixfr);
		if (ret != KNOT_EOK) {
566
			IXFRIN_LOG(LOG_ERR, "Failed - %s", knot_strerror(ret));
567
			return NS_PROC_FAIL;
568
		}
569 570 571 572

		if (ixfr->state == IXFR_DONE) {
			// Transfer done, do not consume more RRs.
			return NS_PROC_DONE;
573 574 575
		}
	}

576
	return NS_PROC_MORE;
577 578 579 580 581 582 583 584 585 586 587 588
}

/* --------------------------------- API ------------------------------------ */

int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata)
{
	if (pkt == NULL || qdata == NULL) {
		return NS_PROC_FAIL;
	}

	int ret = KNOT_EOK;
	struct timeval now = {0};
589
	struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
590
	knot_pkt_t *query = qdata->query;
591 592 593 594 595 596 597 598 599 600 601

	/* If IXFR is disabled, respond with SOA. */
	if (qdata->param->proc_flags & NS_QUERY_NO_IXFR) {
		return ixfr_answer_soa(pkt, qdata);
	}

	/* Initialize on first call. */
	if (qdata->ext == NULL) {
		ret = ixfr_answer_init(qdata);
		switch(ret) {
		case KNOT_EOK:      /* OK */
602
			ixfr = (struct ixfr_proc*)qdata->ext;
603 604 605 606 607 608 609 610 611 612
			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. */
			IXFROUT_LOG(LOG_INFO, "Zone is up-to-date.");
			return ixfr_answer_soa(pkt, qdata);
		case KNOT_ERANGE:   /* No history -> AXFR. */
		case KNOT_ENOENT:
			IXFROUT_LOG(LOG_INFO, "Incomplete history, fallback to AXFR.");
613 614 615 616
			knot_pkt_clear(pkt);
			knot_pkt_put_question(pkt, knot_pkt_qname(query),
			                      knot_pkt_qclass(query),
			                      KNOT_RRTYPE_AXFR);
617
			qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
Marek Vavruša's avatar
Marek Vavruša committed
618
			return axfr_query_process(pkt, qdata);
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
		default:            /* Server errors. */
			IXFROUT_LOG(LOG_ERR, "Failed to start (%s).", knot_strerror(ret));
			return NS_PROC_FAIL;
		}
	}

	/* Reserve space for TSIG. */
	knot_pkt_reserve(pkt, tsig_wire_maxsize(qdata->sign.tsig_key));

	/* Answer current packet (or continue). */
	ret = xfr_process_list(pkt, &ixfr_process_changeset, qdata);
	switch(ret) {
	case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */
		return NS_PROC_FULL; /* Check for more. */
	case KNOT_EOK:    /* Last response. */
		gettimeofday(&now, NULL);
635 636
		IXFROUT_LOG(LOG_INFO, "Finished in %.02fs (%u messages, "
		            "%s%.*f %s).",
637
		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
638
		            ixfr->proc.npkts, SIZE_PARAMS(ixfr->proc.nbytes));
639 640 641 642 643 644 645 646 647 648 649
		ret = NS_PROC_DONE;
		break;
	default:          /* Generic error. */
		IXFROUT_LOG(LOG_ERR, "%s", knot_strerror(ret));
		ret = NS_PROC_FAIL;
		break;
	}

	return ret;
}

650
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
651
{
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
	if (pkt == NULL || adata == NULL) {
		return NS_PROC_FAIL;
	}

	/* Check RCODE. */
	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
	if (rcode != KNOT_RCODE_NOERROR) {
		knot_lookup_table_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
		if (lut != NULL) {
			IXFRIN_LOG(LOG_ERR, "Server responded with %s.", lut->name);
		}
		return NS_PROC_FAIL;
	}

	/* Initialize processing with first packet. */
667
	if (adata->ext == NULL) {
Jan Kadlec's avatar
Jan Kadlec committed
668
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
669
		if (!zone_transfer_needed(adata->param->zone, pkt)) {
670 671 672 673
			IXFRIN_LOG(LOG_INFO, "Server has newer zone.");
			return NS_PROC_DONE;
		}

674 675 676 677
		IXFRIN_LOG(LOG_INFO, "Starting.");
		// First packet with IXFR, init context
		int ret = ixfrin_answer_init(adata);
		if (ret != KNOT_EOK) {
678
			IXFRIN_LOG(LOG_ERR, "%s", knot_strerror(ret));
679 680
			return NS_PROC_FAIL;
		}
Jan Kadlec's avatar
Jan Kadlec committed
681 682
	} else {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);
683 684
	}

685
	int ret = process_ixfrin_packet(pkt, adata);
686
	if (ret == NS_PROC_DONE) {
Jan Kadlec's avatar
Jan Kadlec committed
687
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
688 689
		int fret = ixfrin_finalize(adata);
		if (fret != KNOT_EOK) {
690
			ret = NS_PROC_FAIL;
691 692 693 694 695 696
		}
	}

	return ret;
}

697
#undef IXFROUT_LOG