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

    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 20 21
#include "knot/nameserver/ixfr.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/internet.h"
22
#include "knot/nameserver/process_query.h"
23
#include "knot/nameserver/process_answer.h"
24
#include "knot/updates/apply.h"
25
#include "knot/common/debug.h"
26
#include "knot/zone/serial.h"
27
#include "libknot/libknot.h"
28
#include "libknot/descriptor.h"
29
#include "libknot/internal/utils.h"
30
#include "libknot/rrtype/soa.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
	zone_t *zone;                  /* Modified zone - for journal access. */
54
	mm_ctx_t *mm;                  /* Memory context for RR allocations. */
55
	struct query_data *qdata;
56 57
	const knot_rrset_t *soa_from;
	const knot_rrset_t *soa_to;
58 59
};

60
/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */
61
#define IXFROUT_LOG(severity, msg...) \
62
	QUERY_LOG(severity, qdata, "IXFR, outgoing", msg)
63

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

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

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

88
	return ret;
89 90
}

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

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

	/* Put former SOA. */
112
	if (ixfr->state == IXFR_SOA_DEL) {
113 114
		IXFR_SAFE_PUT(pkt, chgset->soa_from);
		dbg_ns("%s: put 'REMOVE' SOA\n", __func__);
115
		ixfr->state = IXFR_DEL;
116 117 118
	}

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

	/* Put next SOA. */
135
	if (ixfr->state == IXFR_SOA_ADD) {
136
		IXFR_SAFE_PUT(pkt, chgset->soa_to);
137 138
		dbg_ns("%s: put 'IXFR_ADD' SOA\n", __func__);
		ixfr->state = IXFR_ADD;
139 140
	}

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

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

	return ret;
}

#undef IXFR_SAFE_PUT

168
/*! \brief Loads IXFRs from journal. */
169
static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
170
                            const knot_rrset_t *their_soa)
171 172 173 174 175
{
	assert(chgsets);
	assert(zone);

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

183 184 185 186 187
	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);
188

189
	if (ret != KNOT_EOK) {
190
		changesets_free(chgsets);
191 192 193 194 195
	}

	return ret;
}

196
/*! \brief Check IXFR query validity. */
197
static int ixfr_query_check(struct query_data *qdata)
198
{
199 200 201
	/* Check if zone exists. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

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

214
	/* Check transcation security and zone contents. */
215
	NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_XFER);
216
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */
217

218
	return KNOT_STATE_DONE;
219
}
220

221
/*! \brief Cleans up ixfr processing context. */
222 223
static void ixfr_answer_cleanup(struct query_data *qdata)
{
224
	struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
225
	mm_ctx_t *mm = qdata->mm;
226 227

	ptrlist_free(&ixfr->proc.nodes, mm);
228
	changeset_iter_clear(&ixfr->cur);
229
	changesets_free(&ixfr->changesets);
230
	mm_free(mm, qdata->ext);
231 232 233

	/* Allow zone changes (finished). */
	rcu_read_unlock();
234 235
}

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

249
	/* Compare serials. */
250 251
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
	const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
252 253
	list_t chgsets;
	init_list(&chgsets);
254
	int ret = ixfr_load_chsets(&chgsets, (zone_t *)qdata->zone, their_soa);
255
	if (ret != KNOT_EOK) {
256
		dbg_ns("%s: failed to load changesets => %d\n", __func__, ret);
257 258 259 260
		return ret;
	}

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

277
	/* Put all changesets to processing queue. */
278
	changeset_t *chs = NULL;
279
	WALK_LIST(chs, xfer->changesets) {
280 281
		ptrlist_add(&xfer->proc.nodes, chs, mm);
	}
282

283
	/* Keep first and last serial. */
284
	chs = HEAD(xfer->changesets);
285
	xfer->soa_from = chs->soa_from;
286
	chs = TAIL(xfer->changesets);
287
	xfer->soa_to = chs->soa_to;
288

289 290 291 292
	/* Set up cleanup callback. */
	qdata->ext = xfer;
	qdata->ext_cleanup = &ixfr_answer_cleanup;

293 294 295
	/* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
	rcu_read_lock();

296 297 298
	return KNOT_EOK;
}

299
/*! \brief Sends response to SOA query. */
300
static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
301 302
{
	dbg_ns("%s: answering IXFR/SOA\n", __func__);
303
	if (pkt == NULL || qdata == NULL) {
304
		return KNOT_STATE_FAIL;
305 306
	}

307 308
	/* Check query. */
	int state = ixfr_query_check(qdata);
309
	if (state == KNOT_STATE_FAIL) {
310 311
		return state; /* Malformed query. */
	}
312

313
	/* Reserve space for TSIG. */
314
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
315

316
	/* Guaranteed to have zone contents. */
317 318
	const zone_node_t *apex = qdata->zone->contents->apex;
	knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
319
	if (knot_rrset_empty(&soa_rr)) {
320
		return KNOT_STATE_FAIL;
321
	}
322
	int ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
323 324
	if (ret != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
325
		return KNOT_STATE_FAIL;
326 327
	}

328
	return KNOT_STATE_DONE;
329
}
330

331
/* ------------------------- IXFR-in processing ----------------------------- */
332

333 334
/* IXFR-in-specific logging (internal, expects 'adata' variable set). */
#define IXFRIN_LOG(severity, msg...) \
335
	ANSWER_LOG(severity, adata, "IXFR, incoming", msg)
336

337 338 339 340
/*! \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);
341
	return answer->count >= 2 &&
342 343
	       knot_pkt_rr(answer, 0)->type == KNOT_RRTYPE_SOA &&
	       knot_pkt_rr(answer, 1)->type != KNOT_RRTYPE_SOA;
344 345
}

346
/*! \brief Cleans up data allocated by IXFR-in processing. */
347 348
static void ixfrin_cleanup(struct answer_data *data)
{
349
	struct ixfr_proc *proc = data->ext;
350
	if (proc) {
351
		changesets_free(&proc->changesets);
352
		knot_rrset_free(&proc->final_soa, proc->mm);
353
		mm_free(data->mm, proc);
354 355 356 357
		data->ext = NULL;
	}
}

358
/*! \brief Initializes IXFR-in processing context. */
359
static int ixfrin_answer_init(struct answer_data *data)
360
{
361
	struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(struct ixfr_proc));
362 363 364
	if (proc == NULL) {
		return KNOT_ENOMEM;
	}
365 366
	memset(proc, 0, sizeof(struct ixfr_proc));
	gettimeofday(&proc->proc.tstamp, NULL);
367

368 369
	init_list(&proc->changesets);

370 371
	proc->state = IXFR_START;
	proc->zone = data->param->zone;
372
	proc->mm = data->mm;
373 374 375 376 377 378 379

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

	return KNOT_EOK;
}

380
/*! \brief Finalizes IXFR-in processing. */
381 382
static int ixfrin_finalize(struct answer_data *adata)
{
383 384
	struct ixfr_proc *ixfr = adata->ext;
	assert(ixfr->state == IXFR_DONE);
385 386 387 388

	zone_contents_t *new_contents;
	int ret = apply_changesets(ixfr->zone, &ixfr->changesets, &new_contents);
	if (ret != KNOT_EOK) {
389
		IXFRIN_LOG(LOG_WARNING, "failed to apply changes to zone (%s)",
390 391 392 393 394 395
		           knot_strerror(ret));
		return ret;
	}

	/* Write changes to journal. */
	ret = zone_changes_store(ixfr->zone, &ixfr->changesets);
396
	if (ret != KNOT_EOK) {
397
		IXFRIN_LOG(LOG_WARNING, "failed to write changes to journal (%s)",
398
		           knot_strerror(ret));
399
		updates_rollback(&ixfr->changesets);
400
		update_free_zone(&new_contents);
401
		return ret;
402
	}
403

404 405 406
	/* Switch zone contents. */
	zone_contents_t *old_contents = zone_switch_contents(ixfr->zone, new_contents);
	synchronize_rcu();
407
	update_free_zone(&old_contents);
408 409 410

	updates_cleanup(&ixfr->changesets);

411 412
	struct timeval now = {0};
	gettimeofday(&now, NULL);
Daniel Salzman's avatar
Daniel Salzman committed
413 414
	IXFRIN_LOG(LOG_INFO,
	           "finished, %.02f seconds, %u messages, %u bytes",
415
	           time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
416
	           ixfr->proc.npkts, ixfr->proc.nbytes);
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, mm_ctx_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, mm_ctx_t *mm)
479
{
480
	return changeset_rem_rrset(change, rr);
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, mm_ctx_t *mm)
485
{
486
	return changeset_add_rrset(change, rr);
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 616 617 618
}

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

int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata)
{
	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. */
Marek Vavruša's avatar
Marek Vavruša committed
648
			return axfr_query_process(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
		lookup_table_t *lut = 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 717 718
		}

		/* Check for AXFR-style IXFR. */
		if (ixfr_is_axfr(pkt)) {
			IXFRIN_LOG(LOG_NOTICE, "receiving AXFR-style IXFR");
			adata->response_type = KNOT_RESPONSE_AXFR;
			return axfr_answer_process(pkt, adata);
		}
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