ixfr.c 20.7 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 19
#include "knot/nameserver/ixfr.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/internet.h"
20
#include "knot/nameserver/process_query.h"
21
#include "knot/nameserver/process_answer.h"
22
#include "knot/updates/apply.h"
23
#include "common/debug.h"
24
#include "libknot/descriptor.h"
25
#include "libknot/util/utils.h"
26
#include "libknot/rrtype/soa.h"
27

28 29
/* ------------------------ IXFR-out processing ----------------------------- */

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

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

56
/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */
57
#define IXFROUT_LOG(severity, msg...) \
58
	QUERY_LOG(severity, qdata, "IXFR, outgoing", msg)
59

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

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

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

84
	return ret;
85 86
}

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

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

	/* Put former SOA. */
108
	if (ixfr->state == IXFR_SOA_DEL) {
109 110
		IXFR_SAFE_PUT(pkt, chgset->soa_from);
		dbg_ns("%s: put 'REMOVE' SOA\n", __func__);
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 134
		dbg_ns("%s: put 'IXFR_ADD' SOA\n", __func__);
		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_INFO, "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 166
static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone,
                            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 = knot_serial_compare(serial_to, serial_from);
175 176 177 178
	if (ret <= 0) { /* We have older/same age zone. */
		return KNOT_EUPTODATE;
	}

179
	ret = journal_load_changesets(zone, chgsets, serial_from, serial_to);
180
	if (ret != KNOT_EOK) {
181
		changesets_free(chgsets);
182 183 184 185 186
	}

	return ret;
}

187
/*! \brief Check IXFR query validity. */
188
static int ixfr_query_check(struct query_data *qdata)
189
{
190 191 192
	/* Check if zone exists. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

193 194 195
	/* Need IXFR query type. */
	NS_NEED_QTYPE(qdata, KNOT_RRTYPE_IXFR, KNOT_RCODE_FORMERR);
	/* Need SOA authority record. */
196
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
197
	const knot_rrset_t *their_soa = &authority->rr[0];
198
	if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
199
		qdata->rcode = KNOT_RCODE_FORMERR;
200
		return NS_PROC_FAIL;
201 202 203
	}
	/* SOA needs to match QNAME. */
	NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
204

205
	/* Check transcation security and zone contents. */
206
	NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata);
207
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */
208

209
	return NS_PROC_DONE;
210
}
211

212
/*! \brief Cleans up ixfr processing context. */
213 214
static void ixfr_answer_cleanup(struct query_data *qdata)
{
215
	struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
216
	knot_mm_ctx_t *mm = qdata->mm;
217 218

	ptrlist_free(&ixfr->proc.nodes, mm);
219
	changeset_iter_clear(&ixfr->cur);
220
	changesets_free(&ixfr->changesets);
221
	knot_mm_free(mm, qdata->ext);
222 223 224

	/* Allow zone changes (finished). */
	rcu_read_unlock();
225 226
}

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

240
	/* Compare serials. */
241
	const knot_rrset_t *their_soa = &knot_pkt_section(qdata->query, KNOT_AUTHORITY)->rr[0];
242 243
	list_t chgsets;
	init_list(&chgsets);
244 245
	int ret = ixfr_load_chsets(&chgsets, qdata->zone, their_soa);
	if (ret != KNOT_EOK) {
246
		dbg_ns("%s: failed to load changesets => %d\n", __func__, ret);
247 248 249 250
		return ret;
	}

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

267
	/* Put all changesets to processing queue. */
268
	changeset_t *chs = NULL;
269
	WALK_LIST(chs, xfer->changesets) {
270 271
		ptrlist_add(&xfer->proc.nodes, chs, mm);
	}
272

273
	/* Keep first and last serial. */
274
	chs = HEAD(xfer->changesets);
275
	xfer->soa_from = chs->soa_from;
276
	chs = TAIL(xfer->changesets);
277
	xfer->soa_to = chs->soa_to;
278

279 280 281 282
	/* Set up cleanup callback. */
	qdata->ext = xfer;
	qdata->ext_cleanup = &ixfr_answer_cleanup;

283 284 285
	/* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
	rcu_read_lock();

286 287 288
	return KNOT_EOK;
}

289
/*! \brief Sends response to SOA query. */
290
static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
291 292
{
	dbg_ns("%s: answering IXFR/SOA\n", __func__);
293
	if (pkt == NULL || qdata == NULL) {
294 295 296
		return NS_PROC_FAIL;
	}

297 298 299 300 301
	/* Check query. */
	int state = ixfr_query_check(qdata);
	if (state == NS_PROC_FAIL) {
		return state; /* Malformed query. */
	}
302

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

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

318
	return NS_PROC_DONE;
319
}
320

321
/* ------------------------- IXFR-in processing ----------------------------- */
322

323 324
/* IXFR-in-specific logging (internal, expects 'adata' variable set). */
#define IXFRIN_LOG(severity, msg...) \
325
	ANSWER_LOG(severity, adata, "IXFR, incoming", msg)
326

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
/*! \brief Checks whether IXFR response contains enough data for processing. */
static bool ixfr_enough_data(const knot_pkt_t *pkt)
{
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
	return answer->count >= 2;
}

/*! \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);
	return answer->rr[0].type == KNOT_RRTYPE_SOA &&
	       answer->rr[1].type != KNOT_RRTYPE_SOA;
}

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

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

364 365
	init_list(&proc->changesets);

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

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

	return KNOT_EOK;
}

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

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

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

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

	updates_cleanup(&ixfr->changesets);

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

414 415 416
	return KNOT_EOK;
}

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

	// Store the first SOA for later use.
426 427
	proc->final_soa = knot_rrset_copy(rr, proc->mm);
	if (proc->final_soa == NULL) {
428
		return KNOT_ENOMEM;
429 430
	}

431
	return KNOT_EOK;
432 433
}

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

	// Create new changeset.
442
	changeset_t *change = changeset_new(proc->zone->name);
443
	if (change == NULL) {
444
		return KNOT_ENOMEM;
445 446 447
	}

	// Store SOA into changeset.
448
	change->soa_from = knot_rrset_copy(rr, NULL);
449
	if (change->soa_from == NULL) {
450
		changeset_clear(change);
451
		return KNOT_ENOMEM;
452 453
	}

454 455 456 457
	// Add changeset.
	add_tail(&proc->changesets, &change->n);
	++proc->change_count;

458
	return KNOT_EOK;
459 460 461
}

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

470
	return KNOT_EOK;
471 472
}

473
/*! \brief Adds single RR into remove section of changeset. */
474
static int solve_del(const knot_rrset_t *rr, changeset_t *change, knot_mm_ctx_t *mm)
475
{
476
	return changeset_rem_rrset(change, rr);
477 478 479
}

/*! \brief Adds single RR into add section of changeset. */
480
static int solve_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_ctx_t *mm)
481
{
482
	return changeset_add_rrset(change, rr);
483 484
}

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

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

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

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

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

/*!
 * \brief Processes IXFR reply packet and fills in the changesets structure.
 *
569 570
 * \param pkt    Packet containing the IXFR reply in wire format.
 * \param adata  Answer data, including processing context.
571 572 573
 *
 * \return NS_PROC_MORE, NS_PROC_DONE, NS_PROC_FAIL
 */
574
static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
575
{
576
	struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext;
577

578 579 580 581 582
	// Update counters.
	ixfr->proc.npkts  += 1;
	ixfr->proc.nbytes += pkt->size;

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

		const knot_rrset_t *rr = &answer->rr[i];
591
		if (out_of_zone(rr, ixfr)) {
592 593 594
			continue;
		}

595 596
		int ret = ixfrin_step(rr, ixfr);
		if (ret != KNOT_EOK) {
597
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
598
			return NS_PROC_FAIL;
599
		}
600 601 602 603

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

607
	return NS_PROC_MORE;
608 609 610 611 612 613 614 615 616 617 618 619
}

/* --------------------------------- 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};
620
	struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
621 622 623 624 625 626 627 628 629 630 631

	/* 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 */
632
			ixfr = (struct ixfr_proc*)qdata->ext;
633
			IXFROUT_LOG(LOG_INFO, "started, serial %u -> %u",
634 635 636 637
			            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. */
638
			IXFROUT_LOG(LOG_INFO, "zone is up-to-date");
639 640 641
			return ixfr_answer_soa(pkt, qdata);
		case KNOT_ERANGE:   /* No history -> AXFR. */
		case KNOT_ENOENT:
642
			IXFROUT_LOG(LOG_INFO, "incomplete history, fallback to AXFR");
643
			qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
Marek Vavruša's avatar
Marek Vavruša committed
644
			return axfr_query_process(pkt, qdata);
645
		default:            /* Server errors. */
646
			IXFROUT_LOG(LOG_ERR, "failed to start (%s)", knot_strerror(ret));
647 648 649 650 651 652 653 654 655 656 657 658 659 660
			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);
Daniel Salzman's avatar
Daniel Salzman committed
661
		IXFROUT_LOG(LOG_INFO,
662
		            "finished, %.02f seconds, %u messages, %u bytes",
663
		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
664
		            ixfr->proc.npkts, ixfr->proc.nbytes);
665 666 667
		ret = NS_PROC_DONE;
		break;
	default:          /* Generic error. */
668
		IXFROUT_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
669 670 671 672 673 674 675
		ret = NS_PROC_FAIL;
		break;
	}

	return ret;
}

676
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
677
{
678 679 680
	if (pkt == NULL || adata == NULL) {
		return NS_PROC_FAIL;
	}
681 682 683 684 685 686 687
	
	if (!ixfr_enough_data(pkt)) {
		return NS_PROC_FAIL;
	}
	
	/* Check for AXFR-style IXFR. */
	if (ixfr_is_axfr(pkt)) {
688
		IXFRIN_LOG(LOG_NOTICE, "fallback to AXFR");
689 690 691
		return axfr_answer_process(pkt, adata);
	}
	
692 693 694 695 696
	/* 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) {
697
			IXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
698 699 700
		}
		return NS_PROC_FAIL;
	}
701
	
702
	/* Initialize processing with first packet. */
703
	if (adata->ext == NULL) {
704
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
705
		if (!zone_transfer_needed(adata->param->zone, pkt)) {
706
			IXFRIN_LOG(LOG_INFO, "server has newer zone");
707 708 709
			return NS_PROC_DONE;
		}

710
		IXFRIN_LOG(LOG_INFO, "starting");
711 712 713
		// First packet with IXFR, init context
		int ret = ixfrin_answer_init(adata);
		if (ret != KNOT_EOK) {
714
			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
715 716
			return NS_PROC_FAIL;
		}
717 718
	} else {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);
719 720
	}

721
	int ret = process_ixfrin_packet(pkt, adata);
722
	if (ret == NS_PROC_DONE) {
723
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
724 725
		int fret = ixfrin_finalize(adata);
		if (fret != KNOT_EOK) {
726
			ret = NS_PROC_FAIL;
727 728 729 730 731 732
		}
	}

	return ret;
}

733
#undef IXFROUT_LOG