ixfr.c 22.2 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/>.
*/

Daniel Salzman's avatar
Daniel Salzman committed
17 18
#include <urcu.h>

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

32 33
/* ------------------------ IXFR-out processing ----------------------------- */

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

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

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

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

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

85
	return ret;
86 87
}

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

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

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

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

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

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

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

	return ret;
}

#undef IXFR_SAFE_PUT

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

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

Daniel Salzman's avatar
Daniel Salzman committed
178 179 180 181 182
	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);
183

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

	return ret;
}

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

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

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

213
	return KNOT_STATE_DONE;
214
}
215

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

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

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

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

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

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

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

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

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

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

290 291 292
	return KNOT_EOK;
}

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

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

306
	/* Reserve space for TSIG. */
Daniel Salzman's avatar
Daniel Salzman committed
307
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
308

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

321
	return KNOT_STATE_DONE;
322
}
323

324
/* ------------------------- IXFR-in processing ----------------------------- */
325

326 327 328 329
/*! \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);
330
	return answer->count >= 2 &&
331 332
	       knot_pkt_rr(answer, 0)->type == KNOT_RRTYPE_SOA &&
	       knot_pkt_rr(answer, 1)->type != KNOT_RRTYPE_SOA;
333 334
}

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

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

357 358
	init_list(&proc->changesets);

359 360
	proc->state = IXFR_START;
	proc->zone = data->param->zone;
361
	proc->mm = data->mm;
362 363 364 365 366 367 368

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

	return KNOT_EOK;
}

369
/*! \brief Finalizes IXFR-in processing. */
370 371
static int ixfrin_finalize(struct answer_data *adata)
{
372 373
	struct ixfr_proc *ixfr = adata->ext;
	assert(ixfr->state == IXFR_DONE);
374

375
	apply_ctx_t a_ctx = { { 0 } };
376 377
	apply_init_ctx(&a_ctx);

378
	zone_contents_t *new_contents;
379
	int ret = apply_changesets(&a_ctx, ixfr->zone, &ixfr->changesets, &new_contents);
380
	if (ret != KNOT_EOK) {
381
		IXFRIN_LOG(LOG_WARNING, "failed to apply changes to zone (%s)",
382 383 384 385
		           knot_strerror(ret));
		return ret;
	}

386 387 388
	err_handler_logger_t handler;
	handler._cb.cb = err_handler_logger;
	ret = zone_do_sem_checks(new_contents, false, &handler._cb);
389

390 391
	if (ret != KNOT_EOK) {
		IXFRIN_LOG(LOG_WARNING, "failed to apply changes to zone (%s)",
392
		           knot_strerror(ret));
393 394 395 396 397
		update_rollback(&a_ctx);
		update_free_zone(&new_contents);
		return ret;
	}

398
	conf_val_t val = conf_zone_get(adata->param->conf, C_MAX_ZONE_SIZE,
399 400 401
	                               ixfr->zone->name);
	const int64_t size_limit = conf_int(&val);

402 403
	if (new_contents->size > size_limit) {
		IXFRIN_LOG(LOG_WARNING, "zone size exceeded");
404 405 406 407 408
		update_rollback(&a_ctx);
		update_free_zone(&new_contents);
		return KNOT_STATE_FAIL;
	}

409
	/* Write changes to journal. */
410
	ret = zone_changes_store(adata->param->conf, ixfr->zone, &ixfr->changesets);
411
	if (ret != KNOT_EOK) {
412
		IXFRIN_LOG(LOG_WARNING, "failed to write changes to journal (%s)",
413
		           knot_strerror(ret));
414
		update_rollback(&a_ctx);
Jan Kadlec's avatar
Jan Kadlec committed
415
		update_free_zone(&new_contents);
416
		return ret;
417
	}
418

419 420
	/* Switch zone contents. */
	zone_contents_t *old_contents = zone_switch_contents(ixfr->zone, new_contents);
421
	ixfr->zone->flags &= ~ZONE_EXPIRED;
422 423
	synchronize_rcu();

424 425
	struct timeval now = {0};
	gettimeofday(&now, NULL);
426 427 428 429
	IXFRIN_LOG(LOG_INFO, "finished, "
	           "serial %u -> %u, %.02f seconds, %u messages, %u bytes",
	           zone_contents_serial(old_contents),
	           zone_contents_serial(new_contents),
430
	           time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
431
	           ixfr->proc.npkts, ixfr->proc.nbytes);
432

433
	update_free_zone(&old_contents);
434
	update_cleanup(&a_ctx);
435

436 437 438
	return KNOT_EOK;
}

439
/*! \brief Stores starting SOA into changesets structure. */
440
static int solve_start(const knot_rrset_t *rr, struct ixfr_proc *proc)
441
{
442
	assert(proc->final_soa == NULL);
443
	if (rr->type != KNOT_RRTYPE_SOA) {
444
		return KNOT_EMALF;
445 446 447
	}

	// Store the first SOA for later use.
448 449
	proc->final_soa = knot_rrset_copy(rr, proc->mm);
	if (proc->final_soa == NULL) {
450
		return KNOT_ENOMEM;
451 452
	}

453
	return KNOT_EOK;
454 455
}

456
/*! \brief Decides what to do with a starting SOA (deletions). */
457
static int solve_soa_del(const knot_rrset_t *rr, struct ixfr_proc *proc)
458 459
{
	if (rr->type != KNOT_RRTYPE_SOA) {
460
		return KNOT_EMALF;
461 462 463
	}

	// Create new changeset.
Jan Kadlec's avatar
Jan Kadlec committed
464
	changeset_t *change = changeset_new(proc->zone->name);
465
	if (change == NULL) {
466
		return KNOT_ENOMEM;
467 468 469
	}

	// Store SOA into changeset.
470
	change->soa_from = knot_rrset_copy(rr, NULL);
471
	if (change->soa_from == NULL) {
472
		changeset_clear(change);
473
		return KNOT_ENOMEM;
474 475
	}

476 477 478 479
	// Add changeset.
	add_tail(&proc->changesets, &change->n);
	++proc->change_count;

480
	return KNOT_EOK;
481 482 483
}

/*! \brief Stores ending SOA into changeset. */
484
static int solve_soa_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
485
{
486
	assert(rr->type == KNOT_RRTYPE_SOA);
487
	change->soa_to = knot_rrset_copy(rr, NULL);
488
	if (change->soa_to == NULL) {
489
		return KNOT_ENOMEM;
490 491
	}

492
	return KNOT_EOK;
493 494
}

495
/*! \brief Adds single RR into remove section of changeset. */
496
static int solve_del(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
497
{
498
	return changeset_rem_rrset(change, rr, 0);
499 500 501
}

/*! \brief Adds single RR into add section of changeset. */
502
static int solve_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
503
{
504
	return changeset_add_rrset(change, rr, 0);
505 506
}

507 508
/*! \brief Decides what the next IXFR-in state should be. */
static int ixfrin_next_state(struct ixfr_proc *proc, const knot_rrset_t *rr)
509
{
510
	const bool soa = (rr->type == KNOT_RRTYPE_SOA);
511 512
	if (soa &&
	    (proc->state == IXFR_SOA_ADD || proc->state == IXFR_ADD)) {
513
		// Check end of transfer.
514
		if (knot_rrset_equal(rr, proc->final_soa,
515 516 517 518 519 520 521 522 523
		                     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.
524
		return proc->final_soa ? IXFR_SOA_DEL : IXFR_START;
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
	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;
	}
541 542 543 544 545 546 547
}

/*!
 * \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.
 *
548 549
 * \param rr    RR to process.
 * \param proc  Processing context.
550
 *
551
 * \return KNOT_E*
552
 */
553
static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
554
{
555
	proc->state = ixfrin_next_state(proc, rr);
556
	changeset_t *change = TAIL(proc->changesets);
557

558
	int ret;
559
	switch (proc->state) {
560
	case IXFR_START:
561
		return solve_start(rr, proc);
562
	case IXFR_SOA_DEL:
563
		ret = solve_soa_del(rr, proc);
564
		break;
565
	case IXFR_DEL:
566
		ret = solve_del(rr, change, proc->mm);
567
		break;
568
	case IXFR_SOA_ADD:
569
		ret = solve_soa_add(rr, change, proc->mm);
570
		break;
571
	case IXFR_ADD:
572
		ret = solve_add(rr, change, proc->mm);
573
		break;
574 575
	case IXFR_DONE:
		return KNOT_EOK;
576
	default:
577
		return KNOT_ERROR;
578
	}
579 580 581 582
	if (ret == KNOT_EOK) {
		proc->change_size += knot_rrset_size(rr);
	}
	return ret;
583 584 585
}

/*! \brief Checks whether journal node limit has not been exceeded. */
586
static bool journal_limit_exceeded(struct ixfr_proc *proc)
587
{
588
	return proc->change_count > JOURNAL_NCOUNT;
589 590 591
}

/*! \brief Checks whether RR belongs into zone. */
592
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc)
593
{
594
	return !knot_dname_in(proc->zone->name, rr->owner);
595 596 597 598 599
}

/*!
 * \brief Processes IXFR reply packet and fills in the changesets structure.
 *
600 601
 * \param pkt    Packet containing the IXFR reply in wire format.
 * \param adata  Answer data, including processing context.
602
 *
603
 * \return KNOT_STATE_CONSUME, KNOT_STATE_DONE, KNOT_STATE_FAIL
604
 */
605
static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
606
{
607
	struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext;
Daniel Salzman's avatar
Daniel Salzman committed
608

609 610 611 612
	// Update counters.
	ixfr->proc.npkts  += 1;
	ixfr->proc.nbytes += pkt->size;

613
	conf_val_t val = conf_zone_get(adata->param->conf, C_MAX_ZONE_SIZE,
614 615 616
	                               ixfr->zone->name);
	const int64_t size_limit = conf_int(&val);

617
	// Process RRs in the message.
618
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
619 620
	for (uint16_t i = 0; i < answer->count; ++i) {
		if (journal_limit_exceeded(ixfr)) {
621
			IXFRIN_LOG(LOG_WARNING, "journal is full");
622
			return KNOT_STATE_FAIL;
623 624
		}

625
		const knot_rrset_t *rr = knot_pkt_rr(answer, i);
626
		if (out_of_zone(rr, ixfr)) {
627 628 629
			continue;
		}

630 631
		int ret = ixfrin_step(rr, ixfr);
		if (ret != KNOT_EOK) {
632
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
633
			return KNOT_STATE_FAIL;
634
		}
635 636 637

		if (ixfr->state == IXFR_DONE) {
			// Transfer done, do not consume more RRs.
638
			return KNOT_STATE_DONE;
639
		}
640

641 642
		if (ixfr->change_size > 2 * size_limit) {
			IXFRIN_LOG(LOG_WARNING, "transfer size exceeded");
643 644
		}

645 646
	}

647
	return KNOT_STATE_CONSUME;
648 649 650 651
}

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

652
int ixfr_process_query(knot_pkt_t *pkt, struct query_data *qdata)
653 654
{
	if (pkt == NULL || qdata == NULL) {
655
		return KNOT_STATE_FAIL;
656 657 658 659
	}

	int ret = KNOT_EOK;
	struct timeval now = {0};
660
	struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
661 662 663 664 665 666 667 668 669 670 671

	/* 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 */
672
			ixfr = (struct ixfr_proc*)qdata->ext;
673
			IXFROUT_LOG(LOG_INFO, "started, serial %u -> %u",
674 675 676 677
			            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. */
678
			IXFROUT_LOG(LOG_INFO, "zone is up-to-date");
679 680 681
			return ixfr_answer_soa(pkt, qdata);
		case KNOT_ERANGE:   /* No history -> AXFR. */
		case KNOT_ENOENT:
682
			IXFROUT_LOG(LOG_INFO, "incomplete history, fallback to AXFR");
683
			qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
684
			return axfr_process_query(pkt, qdata);
685
		default:            /* Server errors. */
686
			IXFROUT_LOG(LOG_ERR, "failed to start (%s)", knot_strerror(ret));
687
			return KNOT_STATE_FAIL;
688 689 690 691
		}
	}

	/* Reserve space for TSIG. */
Daniel Salzman's avatar
Daniel Salzman committed
692
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
693 694 695 696 697

	/* 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. */
698
		return KNOT_STATE_PRODUCE; /* Check for more. */
699 700
	case KNOT_EOK:    /* Last response. */
		gettimeofday(&now, NULL);
Daniel Salzman's avatar
Daniel Salzman committed
701
		IXFROUT_LOG(LOG_INFO,
702
		            "finished, %.02f seconds, %u messages, %u bytes",
703
		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
704
		            ixfr->proc.npkts, ixfr->proc.nbytes);
705
		ret = KNOT_STATE_DONE;
706 707
		break;
	default:          /* Generic error. */
708
		IXFROUT_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
709
		ret = KNOT_STATE_FAIL;
710 711 712 713 714 715
		break;
	}

	return ret;
}

716 717 718 719
static int check_format(knot_pkt_t *pkt)
{
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);

720
	if (answer->count >= 1 && knot_pkt_rr(answer, 0)->type == KNOT_RRTYPE_SOA) {
721 722 723 724 725 726
		return KNOT_EOK;
	} else {
		return KNOT_EMALF;
	}
}

727
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
728
{
729
	if (pkt == NULL || adata == NULL) {
730
		return KNOT_STATE_FAIL;
731
	}
732

733 734 735
	/* Check RCODE. */
	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
	if (rcode != KNOT_RCODE_NOERROR) {
736
		const knot_lookup_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
737
		if (lut != NULL) {
738
			IXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
739
		}
740
		return KNOT_STATE_FAIL;
741
	}
742

743
	if (adata->ext == NULL) {
Lubos Slovak's avatar
Lubos Slovak committed
744 745
		if (check_format(pkt) != KNOT_EOK) {
			IXFRIN_LOG(LOG_WARNING, "malformed response");
746
			return KNOT_STATE_FAIL;
Lubos Slovak's avatar
Lubos Slovak committed
747 748 749 750 751 752
		}

		/* Check for AXFR-style IXFR. */
		if (ixfr_is_axfr(pkt)) {
			IXFRIN_LOG(LOG_NOTICE, "receiving AXFR-style IXFR");
			adata->response_type = KNOT_RESPONSE_AXFR;
753
			return axfr_process_answer(pkt, adata);
Lubos Slovak's avatar
Lubos Slovak committed
754
		}
755

Lubos Slovak's avatar
Lubos Slovak committed
756
		/* Initialize processing with first packet. */
Jan Kadlec's avatar
Jan Kadlec committed
757
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
758
		if (!zone_transfer_needed(adata->param->zone, pkt)) {
759
			if (knot_pkt_section(pkt, KNOT_ANSWER)->count > 1) {
760
				IXFRIN_LOG(LOG_WARNING, "old data, ignoring");
761 762 763 764
			} else {
				/* Single-SOA answer. */
				IXFRIN_LOG(LOG_INFO, "zone is up-to-date");
			}
765
			return KNOT_STATE_DONE;
766 767
		}

768
		IXFRIN_LOG(LOG_INFO, "starting");
769 770 771
		// First packet with IXFR, init context
		int ret = ixfrin_answer_init(adata);
		if (ret != KNOT_EOK) {
772
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
773
			return KNOT_STATE_FAIL;
774
		}
Jan Kadlec's avatar
Jan Kadlec committed
775 776
	} else {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);