ixfr.c 21.4 KB
Newer Older
1
/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

17 18
#include <urcu.h>

19
#include "knot/common/log.h"
20 21 22
#include "knot/nameserver/ixfr.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/internet.h"
23
#include "knot/updates/apply.h"
24
#include "knot/zone/serial.h"
25
#include "libknot/libknot.h"
Daniel Salzman's avatar
Daniel Salzman committed
26
#include "contrib/mempattern.h"
27
#include "contrib/print.h"
28
#include "contrib/sockaddr.h"
29

30 31
/* ------------------------ IXFR-out processing ----------------------------- */

32 33 34 35 36 37 38 39
/*! \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. */
40 41
};

42 43 44
/*! \brief Extended structure for IXFR-in/IXFR-out processing. */
struct ixfr_proc {
	struct xfr_proc proc;          /* Generic transfer processing context. */
45
	changeset_iter_t cur;          /* Current changeset iteration state.*/
46
	knot_rrset_t cur_rr;           /* Currently processed RRSet. */
47
	int state;                     /* IXFR-in state. */
48 49
	knot_rrset_t *final_soa;       /* First SOA received via IXFR. */
	list_t changesets;             /* Processed changesets. */
50
	size_t change_count;           /* Count of changesets received. */
51
	zone_t *zone;                  /* Modified zone - for journal access. */
52
	knot_mm_t *mm;                 /* Memory context for RR allocations. */
53
	struct query_data *qdata;
54 55
	const knot_rrset_t *soa_from;
	const knot_rrset_t *soa_to;
56 57
};

58
/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */
Daniel Salzman's avatar
Daniel Salzman committed
59
#define IXFROUT_LOG(severity, msg, ...) \
60
	QUERY_LOG(severity, qdata, "IXFR, outgoing", msg, ##__VA_ARGS__)
61

62 63
/*! \brief Helper macro for putting RRs into packet. */
#define IXFR_SAFE_PUT(pkt, rr) \
64
	ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC); \
65 66 67 68
	if (ret != KNOT_EOK) { \
		return ret; \
	}

69 70 71
/*! \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)
72
{
73 74
	assert(pkt);
	assert(ixfr);
75
	assert(itt);
76

77 78 79
	if (knot_rrset_empty(&ixfr->cur_rr)) {
		ixfr->cur_rr = changeset_iter_next(itt);
	}
80
	int ret = KNOT_EOK; // Declaration for IXFR_SAFE_PUT macro
81 82 83
	while(!knot_rrset_empty(&ixfr->cur_rr)) {
		IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
		ixfr->cur_rr = changeset_iter_next(itt);
84
	}
85

86
	return ret;
87 88
}

89 90 91 92 93 94
/*! \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);
}

95 96 97 98 99 100 101
/*!
 * \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.
 */
102 103
static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item,
                                  struct xfr_proc *xfer)
104
{
105
	int ret = KNOT_EOK;
106 107
	struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer;
	changeset_t *chgset = (changeset_t *)item;
108 109

	/* Put former SOA. */
110
	if (ixfr->state == IXFR_SOA_DEL) {
111
		IXFR_SAFE_PUT(pkt, chgset->soa_from);
112
		ixfr->state = IXFR_DEL;
113 114 115
	}

	/* Put REMOVE RRSets. */
116
	if (ixfr->state == IXFR_DEL) {
117 118 119 120 121
		if (iter_empty(ixfr)) {
			ret = changeset_iter_rem(&ixfr->cur, chgset, false);
			if (ret != KNOT_EOK) {
				return ret;
			}
122
		}
123
		ret = ixfr_put_chg_part(pkt, ixfr, &ixfr->cur);
124 125 126
		if (ret != KNOT_EOK) {
			return ret;
		}
127
		changeset_iter_clear(&ixfr->cur);
128
		ixfr->state = IXFR_SOA_ADD;
129 130 131
	}

	/* Put next SOA. */
132
	if (ixfr->state == IXFR_SOA_ADD) {
133
		IXFR_SAFE_PUT(pkt, chgset->soa_to);
134
		ixfr->state = IXFR_ADD;
135 136
	}

137
	/* Put Add RRSets. */
138
	if (ixfr->state == IXFR_ADD) {
139
		if (iter_empty(ixfr)) {
140
			ret = changeset_iter_add(&ixfr->cur, chgset, false);
141 142 143
			if (ret != KNOT_EOK) {
				return ret;
			}
144
		}
145
		ret = ixfr_put_chg_part(pkt, ixfr, &ixfr->cur);
146 147 148
		if (ret != KNOT_EOK) {
			return ret;
		}
149
		changeset_iter_clear(&ixfr->cur);
150
		ixfr->state = IXFR_SOA_DEL;
151
	}
152

153
	/* Finished change set. */
154
	struct query_data *qdata = ixfr->qdata; /*< Required for IXFROUT_LOG() */
155 156
	const uint32_t serial_from = knot_soa_serial(&chgset->soa_from->rrs);
	const uint32_t serial_to = knot_soa_serial(&chgset->soa_to->rrs);
157
	IXFROUT_LOG(LOG_DEBUG, "serial %u -> %u", serial_from, serial_to);
158 159 160 161 162 163

	return ret;
}

#undef IXFR_SAFE_PUT

164
/*! \brief Loads IXFRs from journal. */
165
static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
166
                            const knot_rrset_t *their_soa)
167 168 169 170 171
{
	assert(chgsets);
	assert(zone);

	/* Compare serials. */
172
	uint32_t serial_to = zone_contents_serial(zone->contents);
173
	uint32_t serial_from = knot_soa_serial(&their_soa->rrs);
174
	int ret = serial_compare(serial_to, serial_from);
175 176 177 178
	if (ret <= 0) { /* We have older/same age zone. */
		return KNOT_EUPTODATE;
	}

179 180 181 182 183
	char *path = conf_journalfile(conf(), zone->name);
	pthread_mutex_lock((pthread_mutex_t *)&zone->journal_lock);
	ret = journal_load_changesets(path, zone, chgsets, serial_from, serial_to);
	pthread_mutex_unlock((pthread_mutex_t *)&zone->journal_lock);
	free(path);
184

185
	if (ret != KNOT_EOK) {
186
		changesets_free(chgsets);
187 188 189 190 191
	}

	return ret;
}

192
/*! \brief Check IXFR query validity. */
193
static int ixfr_query_check(struct query_data *qdata)
194
{
195 196 197
	/* Check if zone exists. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

198 199 200
	/* Need IXFR query type. */
	NS_NEED_QTYPE(qdata, KNOT_RRTYPE_IXFR, KNOT_RCODE_FORMERR);
	/* Need SOA authority record. */
201
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
202
	const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
203
	if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
204
		qdata->rcode = KNOT_RCODE_FORMERR;
205
		return KNOT_STATE_FAIL;
206 207 208
	}
	/* SOA needs to match QNAME. */
	NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
209

210
	/* Check transcation security and zone contents. */
211
	NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_TRANSFER);
212
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */
213

214
	return KNOT_STATE_DONE;
215
}
216

217
/*! \brief Cleans up ixfr processing context. */
218 219
static void ixfr_answer_cleanup(struct query_data *qdata)
{
220
	struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
221
	knot_mm_t *mm = qdata->mm;
222 223

	ptrlist_free(&ixfr->proc.nodes, mm);
224
	changeset_iter_clear(&ixfr->cur);
225
	changesets_free(&ixfr->changesets);
226
	mm_free(mm, qdata->ext);
227 228 229

	/* Allow zone changes (finished). */
	rcu_read_unlock();
230 231
}

232
/*! \brief Inits ixfr processing context. */
233 234
static int ixfr_answer_init(struct query_data *qdata)
{
235
	/* Check IXFR query validity. */
236
	int state = ixfr_query_check(qdata);
237
	if (state == KNOT_STATE_FAIL) {
238 239 240 241 242
		if (qdata->rcode == KNOT_RCODE_FORMERR) {
			return KNOT_EMALF;
		} else {
			return KNOT_EDENIED;
		}
243
	}
244

245
	/* Compare serials. */
246 247
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
	const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
248 249
	list_t chgsets;
	init_list(&chgsets);
250
	int ret = ixfr_load_chsets(&chgsets, (zone_t *)qdata->zone, their_soa);
251 252 253 254 255
	if (ret != KNOT_EOK) {
		return ret;
	}

	/* Initialize transfer processing. */
256
	knot_mm_t *mm = qdata->mm;
257
	struct ixfr_proc *xfer = mm_alloc(mm, sizeof(struct ixfr_proc));
258
	if (xfer == NULL) {
259
		changesets_free(&chgsets);
260 261
		return KNOT_ENOMEM;
	}
262
	memset(xfer, 0, sizeof(struct ixfr_proc));
263
	gettimeofday(&xfer->proc.tstamp, NULL);
264
	xfer->state = IXFR_SOA_DEL;
265
	init_list(&xfer->proc.nodes);
266
	init_list(&xfer->changesets);
267
	init_list(&xfer->cur.iters);
268
	knot_rrset_init_empty(&xfer->cur_rr);
269
	add_tail_list(&xfer->changesets, &chgsets);
270
	xfer->qdata = qdata;
271

272
	/* Put all changesets to processing queue. */
273
	changeset_t *chs = NULL;
274
	WALK_LIST(chs, xfer->changesets) {
275 276
		ptrlist_add(&xfer->proc.nodes, chs, mm);
	}
277

278
	/* Keep first and last serial. */
279
	chs = HEAD(xfer->changesets);
280
	xfer->soa_from = chs->soa_from;
281
	chs = TAIL(xfer->changesets);
282
	xfer->soa_to = chs->soa_to;
283

284 285 286 287
	/* Set up cleanup callback. */
	qdata->ext = xfer;
	qdata->ext_cleanup = &ixfr_answer_cleanup;

288 289 290
	/* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
	rcu_read_lock();

291 292 293
	return KNOT_EOK;
}

294
/*! \brief Sends response to SOA query. */
295
static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
296
{
297
	if (pkt == NULL || qdata == NULL) {
298
		return KNOT_STATE_FAIL;
299 300
	}

301 302
	/* Check query. */
	int state = ixfr_query_check(qdata);
303
	if (state == KNOT_STATE_FAIL) {
304 305
		return state; /* Malformed query. */
	}
306

307
	/* Reserve space for TSIG. */
308
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
309

310
	/* Guaranteed to have zone contents. */
311 312
	const zone_node_t *apex = qdata->zone->contents->apex;
	knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
313
	if (knot_rrset_empty(&soa_rr)) {
314
		return KNOT_STATE_FAIL;
315
	}
316
	int ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
317 318
	if (ret != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
319
		return KNOT_STATE_FAIL;
320 321
	}

322
	return KNOT_STATE_DONE;
323
}
324

325
/* ------------------------- IXFR-in processing ----------------------------- */
326

327
/* IXFR-in-specific logging (internal, expects 'adata' variable set). */
Daniel Salzman's avatar
Daniel Salzman committed
328
#define IXFRIN_LOG(severity, msg, ...) \
329
	ANSWER_LOG(severity, adata, "IXFR, incoming", msg, ##__VA_ARGS__)
330

331 332 333 334
/*! \brief Checks whether server responded with AXFR-style IXFR. */
static bool ixfr_is_axfr(const knot_pkt_t *pkt)
{
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
335
	return answer->count >= 2 &&
336 337
	       knot_pkt_rr(answer, 0)->type == KNOT_RRTYPE_SOA &&
	       knot_pkt_rr(answer, 1)->type != KNOT_RRTYPE_SOA;
338 339
}

340
/*! \brief Cleans up data allocated by IXFR-in processing. */
341 342
static void ixfrin_cleanup(struct answer_data *data)
{
343
	struct ixfr_proc *proc = data->ext;
344
	if (proc) {
345
		changesets_free(&proc->changesets);
346
		knot_rrset_free(&proc->final_soa, proc->mm);
347
		mm_free(data->mm, proc);
348 349 350 351
		data->ext = NULL;
	}
}

352
/*! \brief Initializes IXFR-in processing context. */
353
static int ixfrin_answer_init(struct answer_data *data)
354
{
355
	struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(struct ixfr_proc));
356 357 358
	if (proc == NULL) {
		return KNOT_ENOMEM;
	}
359 360
	memset(proc, 0, sizeof(struct ixfr_proc));
	gettimeofday(&proc->proc.tstamp, NULL);
361

362 363
	init_list(&proc->changesets);

364 365
	proc->state = IXFR_START;
	proc->zone = data->param->zone;
366
	proc->mm = data->mm;
367 368 369 370 371 372 373

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

	return KNOT_EOK;
}

374
/*! \brief Finalizes IXFR-in processing. */
375 376
static int ixfrin_finalize(struct answer_data *adata)
{
377 378
	struct ixfr_proc *ixfr = adata->ext;
	assert(ixfr->state == IXFR_DONE);
379

380
	apply_ctx_t a_ctx = { { 0 } };
381 382
	apply_init_ctx(&a_ctx);

383
	zone_contents_t *new_contents;
384
	int ret = apply_changesets(&a_ctx, ixfr->zone, &ixfr->changesets, &new_contents);
385
	if (ret != KNOT_EOK) {
386
		IXFRIN_LOG(LOG_WARNING, "failed to apply changes to zone (%s)",
387 388 389 390 391
		           knot_strerror(ret));
		return ret;
	}

	/* Write changes to journal. */
392
	ret = zone_changes_store(adata->param->conf, ixfr->zone, &ixfr->changesets);
393
	if (ret != KNOT_EOK) {
394
		IXFRIN_LOG(LOG_WARNING, "failed to write changes to journal (%s)",
395
		           knot_strerror(ret));
396
		update_rollback(&a_ctx);
397
		update_free_zone(&new_contents);
398
		return ret;
399
	}
400

401 402
	/* Switch zone contents. */
	zone_contents_t *old_contents = zone_switch_contents(ixfr->zone, new_contents);
403
	ixfr->zone->flags &= ~ZONE_EXPIRED;
404 405
	synchronize_rcu();

406 407
	struct timeval now = {0};
	gettimeofday(&now, NULL);
408 409 410 411
	IXFRIN_LOG(LOG_INFO, "finished, "
	           "serial %u -> %u, %.02f seconds, %u messages, %u bytes",
	           zone_contents_serial(old_contents),
	           zone_contents_serial(new_contents),
412
	           time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
413
	           ixfr->proc.npkts, ixfr->proc.nbytes);
414

415
	update_free_zone(&old_contents);
416
	update_cleanup(&a_ctx);
417

418 419 420
	return KNOT_EOK;
}

421
/*! \brief Stores starting SOA into changesets structure. */
422
static int solve_start(const knot_rrset_t *rr, struct ixfr_proc *proc)
423
{
424
	assert(proc->final_soa == NULL);
425
	if (rr->type != KNOT_RRTYPE_SOA) {
426
		return KNOT_EMALF;
427 428 429
	}

	// Store the first SOA for later use.
430 431
	proc->final_soa = knot_rrset_copy(rr, proc->mm);
	if (proc->final_soa == NULL) {
432
		return KNOT_ENOMEM;
433 434
	}

435
	return KNOT_EOK;
436 437
}

438
/*! \brief Decides what to do with a starting SOA (deletions). */
439
static int solve_soa_del(const knot_rrset_t *rr, struct ixfr_proc *proc)
440 441
{
	if (rr->type != KNOT_RRTYPE_SOA) {
442
		return KNOT_EMALF;
443 444 445
	}

	// Create new changeset.
446
	changeset_t *change = changeset_new(proc->zone->name);
447
	if (change == NULL) {
448
		return KNOT_ENOMEM;
449 450 451
	}

	// Store SOA into changeset.
452
	change->soa_from = knot_rrset_copy(rr, NULL);
453
	if (change->soa_from == NULL) {
454
		changeset_clear(change);
455
		return KNOT_ENOMEM;
456 457
	}

458 459 460 461
	// Add changeset.
	add_tail(&proc->changesets, &change->n);
	++proc->change_count;

462
	return KNOT_EOK;
463 464 465
}

/*! \brief Stores ending SOA into changeset. */
466
static int solve_soa_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
467
{
468
	assert(rr->type == KNOT_RRTYPE_SOA);
469
	change->soa_to = knot_rrset_copy(rr, NULL);
470
	if (change->soa_to == NULL) {
471
		return KNOT_ENOMEM;
472 473
	}

474
	return KNOT_EOK;
475 476
}

477
/*! \brief Adds single RR into remove section of changeset. */
478
static int solve_del(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
479
{
480
	return changeset_rem_rrset(change, rr, 0);
481 482 483
}

/*! \brief Adds single RR into add section of changeset. */
484
static int solve_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
485
{
486
	return changeset_add_rrset(change, rr, 0);
487 488
}

489 490
/*! \brief Decides what the next IXFR-in state should be. */
static int ixfrin_next_state(struct ixfr_proc *proc, const knot_rrset_t *rr)
491
{
492
	const bool soa = (rr->type == KNOT_RRTYPE_SOA);
493 494
	if (soa &&
	    (proc->state == IXFR_SOA_ADD || proc->state == IXFR_ADD)) {
495
		// Check end of transfer.
496
		if (knot_rrset_equal(rr, proc->final_soa,
497 498 499 500 501 502 503 504 505
		                     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.
506
		return proc->final_soa ? IXFR_SOA_DEL : IXFR_START;
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
	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;
	}
523 524 525 526 527 528 529
}

/*!
 * \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.
 *
530 531
 * \param rr    RR to process.
 * \param proc  Processing context.
532
 *
533
 * \return KNOT_E*
534
 */
535
static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
536
{
537
	proc->state = ixfrin_next_state(proc, rr);
538
	changeset_t *change = TAIL(proc->changesets);
539 540

	switch (proc->state) {
541
	case IXFR_START:
542
		return solve_start(rr, proc);
543
	case IXFR_SOA_DEL:
544
		return solve_soa_del(rr, proc);
545
	case IXFR_DEL:
546 547 548
		return solve_del(rr, change, proc->mm);
	case IXFR_SOA_ADD:
		return solve_soa_add(rr, change, proc->mm);
549
	case IXFR_ADD:
550 551 552
		return solve_add(rr, change, proc->mm);
	case IXFR_DONE:
		return KNOT_EOK;
553
	default:
554
		return KNOT_ERROR;
555 556 557 558
	}
}

/*! \brief Checks whether journal node limit has not been exceeded. */
559
static bool journal_limit_exceeded(struct ixfr_proc *proc)
560
{
561
	return proc->change_count > JOURNAL_NCOUNT;
562 563 564
}

/*! \brief Checks whether RR belongs into zone. */
565
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc)
566
{
567
	return !knot_dname_in(proc->zone->name, rr->owner);
568 569 570 571 572
}

/*!
 * \brief Processes IXFR reply packet and fills in the changesets structure.
 *
573 574
 * \param pkt    Packet containing the IXFR reply in wire format.
 * \param adata  Answer data, including processing context.
575
 *
576
 * \return KNOT_STATE_CONSUME, KNOT_STATE_DONE, KNOT_STATE_FAIL
577
 */
578
static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
579
{
580
	struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext;
581

582 583 584 585 586
	// Update counters.
	ixfr->proc.npkts  += 1;
	ixfr->proc.nbytes += pkt->size;

	// Process RRs in the message.
587
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
588 589
	for (uint16_t i = 0; i < answer->count; ++i) {
		if (journal_limit_exceeded(ixfr)) {
590
			IXFRIN_LOG(LOG_WARNING, "journal is full");
591
			return KNOT_STATE_FAIL;
592 593
		}

594
		const knot_rrset_t *rr = knot_pkt_rr(answer, i);
595
		if (out_of_zone(rr, ixfr)) {
596 597 598
			continue;
		}

599 600
		int ret = ixfrin_step(rr, ixfr);
		if (ret != KNOT_EOK) {
601
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
602
			return KNOT_STATE_FAIL;
603
		}
604 605 606

		if (ixfr->state == IXFR_DONE) {
			// Transfer done, do not consume more RRs.
607
			return KNOT_STATE_DONE;
608 609 610
		}
	}

611
	return KNOT_STATE_CONSUME;
612 613 614 615
}

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

616
int ixfr_process_query(knot_pkt_t *pkt, struct query_data *qdata)
617 618
{
	if (pkt == NULL || qdata == NULL) {
619
		return KNOT_STATE_FAIL;
620 621 622 623
	}

	int ret = KNOT_EOK;
	struct timeval now = {0};
624
	struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
625 626 627 628 629 630 631 632 633 634 635

	/* 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 */
636
			ixfr = (struct ixfr_proc*)qdata->ext;
637
			IXFROUT_LOG(LOG_INFO, "started, serial %u -> %u",
638 639 640 641
			            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. */
642
			IXFROUT_LOG(LOG_INFO, "zone is up-to-date");
643 644 645
			return ixfr_answer_soa(pkt, qdata);
		case KNOT_ERANGE:   /* No history -> AXFR. */
		case KNOT_ENOENT:
646
			IXFROUT_LOG(LOG_INFO, "incomplete history, fallback to AXFR");
647
			qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
648
			return axfr_process_query(pkt, qdata);
649
		default:            /* Server errors. */
650
			IXFROUT_LOG(LOG_ERR, "failed to start (%s)", knot_strerror(ret));
651
			return KNOT_STATE_FAIL;
652 653 654 655
		}
	}

	/* Reserve space for TSIG. */
656
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
657 658 659 660 661

	/* 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. */
662
		return KNOT_STATE_PRODUCE; /* Check for more. */
663 664
	case KNOT_EOK:    /* Last response. */
		gettimeofday(&now, NULL);
Daniel Salzman's avatar
Daniel Salzman committed
665
		IXFROUT_LOG(LOG_INFO,
666
		            "finished, %.02f seconds, %u messages, %u bytes",
667
		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
668
		            ixfr->proc.npkts, ixfr->proc.nbytes);
669
		ret = KNOT_STATE_DONE;
670 671
		break;
	default:          /* Generic error. */
672
		IXFROUT_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
673
		ret = KNOT_STATE_FAIL;
674 675 676 677 678 679
		break;
	}

	return ret;
}

680 681 682 683
static int check_format(knot_pkt_t *pkt)
{
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);

684
	if (answer->count >= 1 && knot_pkt_rr(answer, 0)->type == KNOT_RRTYPE_SOA) {
685 686 687 688 689 690
		return KNOT_EOK;
	} else {
		return KNOT_EMALF;
	}
}

691
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
692
{
693
	if (pkt == NULL || adata == NULL) {
694
		return KNOT_STATE_FAIL;
695
	}
696

697 698 699
	/* Check RCODE. */
	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
	if (rcode != KNOT_RCODE_NOERROR) {
700
		const knot_lookup_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
701
		if (lut != NULL) {
702
			IXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
703
		}
704
		return KNOT_STATE_FAIL;
705
	}
706

707
	if (adata->ext == NULL) {
708 709
		if (check_format(pkt) != KNOT_EOK) {
			IXFRIN_LOG(LOG_WARNING, "malformed response");
710
			return KNOT_STATE_FAIL;
711 712 713 714 715 716
		}

		/* Check for AXFR-style IXFR. */
		if (ixfr_is_axfr(pkt)) {
			IXFRIN_LOG(LOG_NOTICE, "receiving AXFR-style IXFR");
			adata->response_type = KNOT_RESPONSE_AXFR;
717
			return axfr_process_answer(pkt, adata);
718
		}
719

720
		/* Initialize processing with first packet. */
721
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
722
		if (!zone_transfer_needed(adata->param->zone, pkt)) {
723
			if (knot_pkt_section(pkt, KNOT_ANSWER)->count > 1) {
724
				IXFRIN_LOG(LOG_WARNING, "old data, ignoring");
725 726 727 728
			} else {
				/* Single-SOA answer. */
				IXFRIN_LOG(LOG_INFO, "zone is up-to-date");
			}
729
			return KNOT_STATE_DONE;
730 731
		}

732
		IXFRIN_LOG(LOG_INFO, "starting");
733 734 735
		// First packet with IXFR, init context
		int ret = ixfrin_answer_init(adata);
		if (ret != KNOT_EOK) {
736
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
737
			return KNOT_STATE_FAIL;
738
		}
739 740
	} else {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);
741 742
	}

743
	int ret = process_ixfrin_packet(pkt, adata);
744
	if (ret == KNOT_STATE_DONE) {
745
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
746 747
		int fret = ixfrin_finalize(adata);
		if (fret != KNOT_EOK) {
748
			ret = KNOT_STATE_FAIL;
749 750 751 752 753 754
		}
	}

	return ret;
}

755
#undef IXFROUT_LOG