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

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

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

24 25 26
/*! \brief Extended structure for IXFR-in/IXFR-out processing. */
struct ixfr_proc {
	struct xfr_proc proc;          /* Generic transfer processing context. */
27
	node_t *cur;
28 29 30 31
	int state;                     /* IXFR-in state. */
	changesets_t *changesets;      /* Processed changesets. */
	zone_t *zone;                  /* Modified zone - for journal access. */
	mm_ctx_t *mm;                  /* Memory context for RR allocations. */
32
	struct query_data *qdata;
33 34
	const knot_rrset_t *soa_from;
	const knot_rrset_t *soa_to;
35 36
};

37
/* IXFR-out-specific logging (internal, expects 'qdata' variable set). */
38
#define IXFROUT_LOG(severity, msg...) \
39
	QUERY_LOG(severity, qdata, "IXFR, outgoing", msg)
40

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

48
static int ixfr_put_rrlist(knot_pkt_t *pkt, struct ixfr_proc *ixfr, list_t *list)
49
{
50 51 52 53 54 55 56 57 58 59 60 61
	assert(pkt);
	assert(ixfr);
	assert(list);

	/* If at the beginning, fetch first RR. */
	int ret = KNOT_EOK;
	if (ixfr->cur == NULL) {
		ixfr->cur = HEAD(*list);
	}
	/* Now iterate until it hits the last one,
	 * this is done without for() loop because we can
	 * rejoin the iteration at any point. */
62 63
	while(ixfr->cur->next) {
		knot_rr_ln_t *rr_item = (knot_rr_ln_t *)(ixfr->cur);
64
		if (rr_item->rr->rrs.rr_count > 0) {
65 66 67 68 69 70 71
			IXFR_SAFE_PUT(pkt, rr_item->rr);
		} else {
			dbg_ns("%s: empty RR %p, skipping\n", __func__, rr_item->rr);
		}

		ixfr->cur = ixfr->cur->next;
	}
72 73

	ixfr->cur = NULL;
74
	return ret;
75 76
}

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

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

	/* Put REMOVE RRSets. */
99
	if (ixfr->state == IXFR_DEL) {
100 101 102 103 104
		ret = ixfr_put_rrlist(pkt, ixfr, &chgset->remove);
		if (ret != KNOT_EOK) {
			return ret;
		}
		dbg_ns("%s: put 'REMOVE' RRs\n", __func__);
105
		ixfr->state = IXFR_SOA_ADD;
106 107 108
	}

	/* Put next SOA. */
109
	if (ixfr->state == IXFR_SOA_ADD) {
110
		IXFR_SAFE_PUT(pkt, chgset->soa_to);
111 112
		dbg_ns("%s: put 'IXFR_ADD' SOA\n", __func__);
		ixfr->state = IXFR_ADD;
113 114
	}

115
	/* Put REMOVE RRSets. */
116
	if (ixfr->state == IXFR_ADD) {
117 118 119 120
		ret = ixfr_put_rrlist(pkt, ixfr, &chgset->add);
		if (ret != KNOT_EOK) {
			return ret;
		}
121 122
		dbg_ns("%s: put 'IXFR_ADD' RRs\n", __func__);
		ixfr->state = IXFR_SOA_DEL;
123
	}
124

125
	/* Finished change set. */
126
	struct query_data *qdata = ixfr->qdata; /*< Required for IXFROUT_LOG() */
127 128
	const uint32_t serial_from = knot_soa_serial(&chgset->soa_from->rrs);
	const uint32_t serial_to = knot_soa_serial(&chgset->soa_to->rrs);
129
	IXFROUT_LOG(LOG_INFO, "serial %u -> %u", serial_from, serial_to);
130 131 132 133 134 135

	return ret;
}

#undef IXFR_SAFE_PUT

136
static int ixfr_load_chsets(changesets_t **chgsets, const zone_t *zone,
137 138 139 140 141 142
			    const knot_rrset_t *their_soa)
{
	assert(chgsets);
	assert(zone);

	/* Compare serials. */
143
	uint32_t serial_to = zone_contents_serial(zone->contents);
144
	uint32_t serial_from = knot_soa_serial(&their_soa->rrs);
145
	int ret = knot_serial_compare(serial_to, serial_from);
146 147 148 149
	if (ret <= 0) { /* We have older/same age zone. */
		return KNOT_EUPTODATE;
	}

150
	*chgsets = changesets_create(0);
151 152 153 154
	if (*chgsets == NULL) {
		return KNOT_ENOMEM;
	}

155 156
	ret = journal_load_changesets(zone->conf->ixfr_db, *chgsets,
	                              serial_from, serial_to);
157
	if (ret != KNOT_EOK) {
158
		changesets_free(chgsets, NULL);
159 160 161 162 163
	}

	return ret;
}

164
static int ixfr_query_check(struct query_data *qdata)
165
{
166 167 168
	/* Check if zone exists. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);

169 170 171
	/* Need IXFR query type. */
	NS_NEED_QTYPE(qdata, KNOT_RRTYPE_IXFR, KNOT_RCODE_FORMERR);
	/* Need SOA authority record. */
172
	const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
173
	const knot_rrset_t *their_soa = &authority->rr[0];
174
	if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
175
		qdata->rcode = KNOT_RCODE_FORMERR;
176
		return NS_PROC_FAIL;
177 178 179
	}
	/* SOA needs to match QNAME. */
	NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
180

181
	/* Check transcation security and zone contents. */
182
	NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata);
183
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */
184

185
	return NS_PROC_DONE;
186
}
187

188 189
static void ixfr_answer_cleanup(struct query_data *qdata)
{
190
	struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->ext;
191 192 193
	mm_ctx_t *mm = qdata->mm;

	ptrlist_free(&ixfr->proc.nodes, mm);
194
	changesets_free(&ixfr->changesets, NULL);
195
	mm->free(qdata->ext);
196 197 198

	/* Allow zone changes (finished). */
	rcu_read_unlock();
199 200
}

201 202
static int ixfr_answer_init(struct query_data *qdata)
{
203
	/* Check IXFR query validity. */
204 205
	int state = ixfr_query_check(qdata);
	if (state == NS_PROC_FAIL) {
206 207 208 209 210
		if (qdata->rcode == KNOT_RCODE_FORMERR) {
			return KNOT_EMALF;
		} else {
			return KNOT_EDENIED;
		}
211
	}
212

213
	/* Compare serials. */
214
	const knot_rrset_t *their_soa = &knot_pkt_section(qdata->query, KNOT_AUTHORITY)->rr[0];
215
	changesets_t *chgsets = NULL;
216 217
	int ret = ixfr_load_chsets(&chgsets, qdata->zone, their_soa);
	if (ret != KNOT_EOK) {
218
		dbg_ns("%s: failed to load changesets => %d\n", __func__, ret);
219 220 221 222 223
		return ret;
	}

	/* Initialize transfer processing. */
	mm_ctx_t *mm = qdata->mm;
224
	struct ixfr_proc *xfer = mm->alloc(mm->ctx, sizeof(struct ixfr_proc));
225
	if (xfer == NULL) {
226
		changesets_free(&chgsets, NULL);
227 228
		return KNOT_ENOMEM;
	}
229
	memset(xfer, 0, sizeof(struct ixfr_proc));
230
	gettimeofday(&xfer->proc.tstamp, NULL);
231
	xfer->state = IXFR_SOA_DEL;
232
	init_list(&xfer->proc.nodes);
233
	xfer->qdata = qdata;
234

235
	/* Put all changesets to processing queue. */
236
	xfer->changesets = chgsets;
237
	changeset_t *chs = NULL;
238 239 240
	WALK_LIST(chs, chgsets->sets) {
		ptrlist_add(&xfer->proc.nodes, chs, mm);
	}
241

242 243 244 245 246
	/* Keep first and last serial. */
	chs = HEAD(chgsets->sets);
	xfer->soa_from = chs->soa_from;
	chs = TAIL(chgsets->sets);
	xfer->soa_to = chs->soa_to;
247

248 249 250 251
	/* Set up cleanup callback. */
	qdata->ext = xfer;
	qdata->ext_cleanup = &ixfr_answer_cleanup;

252 253 254
	/* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
	rcu_read_lock();

255 256 257
	return KNOT_EOK;
}

258
static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
259 260
{
	dbg_ns("%s: answering IXFR/SOA\n", __func__);
261
	if (pkt == NULL || qdata == NULL) {
262 263 264
		return NS_PROC_FAIL;
	}

265 266 267 268 269
	/* Check query. */
	int state = ixfr_query_check(qdata);
	if (state == NS_PROC_FAIL) {
		return state; /* Malformed query. */
	}
270

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

274
	/* Guaranteed to have zone contents. */
275 276
	const zone_node_t *apex = qdata->zone->contents->apex;
	knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
277
	if (knot_rrset_empty(&soa_rr)) {
278 279
		return NS_PROC_FAIL;
	}
280
	int ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
281 282 283 284 285
	if (ret != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		return NS_PROC_FAIL;
	}

286
	return NS_PROC_DONE;
287
}
288

289
/* ------------------------- IXFR-in processing ----------------------------- */
290

291 292
/* IXFR-in-specific logging (internal, expects 'adata' variable set). */
#define IXFRIN_LOG(severity, msg...) \
293
	ANSWER_LOG(severity, adata, "IXFR, incoming", msg)
294

295
/*! \brief Cleans up data allocated by IXFR-in processing. */
296 297
static void ixfrin_cleanup(struct answer_data *data)
{
298
	struct ixfr_proc *proc = data->ext;
299
	if (proc) {
300
		changesets_free(&proc->changesets, data->mm);
301 302 303 304 305
		mm_free(data->mm, proc);
		data->ext = NULL;
	}
}

306
/*! \brief Initializes IXFR-in processing context. */
307
static int ixfrin_answer_init(struct answer_data *data)
308
{
309
	struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(struct ixfr_proc));
310 311 312
	if (proc == NULL) {
		return KNOT_ENOMEM;
	}
313 314
	memset(proc, 0, sizeof(struct ixfr_proc));
	gettimeofday(&proc->proc.tstamp, NULL);
315

316
	proc->changesets = changesets_create(0);
317 318 319 320 321 322
	if (proc->changesets == NULL) {
		mm_free(data->mm, proc);
		return KNOT_ENOMEM;
	}
	proc->state = IXFR_START;
	proc->zone = data->param->zone;
323
	proc->mm = data->mm;
324 325 326 327 328 329 330

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

	return KNOT_EOK;
}

331
/*! \brief Finalizes IXFR-in processing. */
332 333
static int ixfrin_finalize(struct answer_data *adata)
{
334
	struct ixfr_proc *ixfr = adata->ext;
335

336
	assert(ixfr->state == IXFR_DONE);
337
	int ret = zone_change_apply_and_store(&ixfr->changesets,
338
	                                      ixfr->zone, "IXFR", adata->mm);
339
	if (ret != KNOT_EOK) {
340
		IXFRIN_LOG(LOG_ERR, "failed to apply changes to zone (%s)",
341
		           knot_strerror(ret));
342
		return ret;
343
	}
344

345 346
	struct timeval now = {0};
	gettimeofday(&now, NULL);
347
	IXFRIN_LOG(LOG_INFO, "finished, %.02fs, %u messages, " SIZE_FORMAT,
348
	           time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
349
	           ixfr->proc.npkts, SIZE_PARAMS(ixfr->proc.nbytes));
350

351 352 353
	return KNOT_EOK;
}

354
/*! \brief Stores starting SOA into changesets structure. */
355
static int solve_start(const knot_rrset_t *rr, changesets_t *changesets, mm_ctx_t *mm)
356 357 358
{
	assert(changesets->first_soa == NULL);
	if (rr->type != KNOT_RRTYPE_SOA) {
359
		return KNOT_EMALF;
360 361 362 363 364
	}

	// Store the first SOA for later use.
	changesets->first_soa = knot_rrset_copy(rr, mm);
	if (changesets->first_soa == NULL) {
365
		return KNOT_ENOMEM;
366 367
	}

368
	return KNOT_EOK;
369 370
}

371
/*! \brief Decides what to do with a starting SOA (deletions). */
372 373
static int solve_soa_del(const knot_rrset_t *rr, changesets_t *changesets,
                         mm_ctx_t *mm)
374 375
{
	if (rr->type != KNOT_RRTYPE_SOA) {
376
		return KNOT_EMALF;
377 378 379
	}

	// Create new changeset.
380
	changeset_t *change = changesets_create_changeset(changesets);
381
	if (change == NULL) {
382
		return KNOT_ENOMEM;
383 384 385 386 387
	}

	// Store SOA into changeset.
	change->soa_from = knot_rrset_copy(rr, mm);
	if (change->soa_from == NULL) {
388
		return KNOT_ENOMEM;
389 390
	}

391
	return KNOT_EOK;
392 393 394
}

/*! \brief Stores ending SOA into changeset. */
395
static int solve_soa_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
396
{
397
	assert(rr->type == KNOT_RRTYPE_SOA);
398 399
	change->soa_to= knot_rrset_copy(rr, mm);
	if (change->soa_to == NULL) {
400
		return KNOT_ENOMEM;
401 402
	}

403
	return KNOT_EOK;
404 405 406
}

/*! \brief Adds single RR into given section of changeset. */
407
static int add_part(const knot_rrset_t *rr, changeset_t *change, int part, mm_ctx_t *mm)
408 409 410 411
{
	assert(rr->type != KNOT_RRTYPE_SOA);
	knot_rrset_t *copy = knot_rrset_copy(rr, mm);
	if (copy) {
412
		int ret = changeset_add_rrset(change, copy, part);
413
		if (ret != KNOT_EOK) {
414
			return ret;
415
		}
416
		return KNOT_EOK;
417
	} else {
418
		return KNOT_ENOMEM;
419 420 421
	}
}

422 423 424 425 426 427 428 429
/*! \brief Adds single RR into remove section of changeset. */
static int solve_del(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
{
	return add_part(rr, change, CHANGESET_REMOVE, mm);
}

/*! \brief Adds single RR into add section of changeset. */
static int solve_add(const knot_rrset_t *rr, changeset_t *change, mm_ctx_t *mm)
430
{
431
	return add_part(rr, change, CHANGESET_ADD, mm);
432 433
}

434 435
/*! \brief Decides what the next IXFR-in state should be. */
static int ixfrin_next_state(struct ixfr_proc *proc, const knot_rrset_t *rr)
436
{
437
	const bool soa = (rr->type == KNOT_RRTYPE_SOA);
438 439
	if (soa &&
	    (proc->state == IXFR_SOA_ADD || proc->state == IXFR_ADD)) {
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
		// Check end of transfer.
		if (knot_rrset_equal(rr, proc->changesets->first_soa,
		                     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.
		return proc->changesets->first_soa ? IXFR_SOA_DEL : IXFR_START;
	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;
	}
468 469 470 471 472 473 474
}

/*!
 * \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.
 *
475 476
 * \param rr    RR to process.
 * \param proc  Processing context.
477
 *
478
 * \return KNOT_E*
479
 */
480
static int ixfrin_step(const knot_rrset_t *rr, struct ixfr_proc *proc)
481
{
482 483 484 485
	proc->state = ixfrin_next_state(proc, rr);
	changeset_t *change = changesets_get_last(proc->changesets);

	switch (proc->state) {
486
	case IXFR_START:
487 488 489
		return solve_start(rr, proc->changesets, proc->mm);
	case IXFR_SOA_DEL:
		return solve_soa_del(rr, proc->changesets, proc->mm);
490
	case IXFR_DEL:
491 492 493
		return solve_del(rr, change, proc->mm);
	case IXFR_SOA_ADD:
		return solve_soa_add(rr, change, proc->mm);
494
	case IXFR_ADD:
495 496 497
		return solve_add(rr, change, proc->mm);
	case IXFR_DONE:
		return KNOT_EOK;
498
	default:
499
		return KNOT_ERROR;
500 501 502 503
	}
}

/*! \brief Checks whether journal node limit has not been exceeded. */
504
static bool journal_limit_exceeded(struct ixfr_proc *proc)
505 506 507 508 509
{
	return proc->changesets->count > JOURNAL_NCOUNT;
}

/*! \brief Checks whether RR belongs into zone. */
510
static bool out_of_zone(const knot_rrset_t *rr, struct ixfr_proc *proc)
511 512 513 514 515 516 517 518
{
	return !knot_dname_is_sub(rr->owner, proc->zone->name) &&
	       !knot_dname_is_equal(rr->owner, proc->zone->name);
}

/*!
 * \brief Processes IXFR reply packet and fills in the changesets structure.
 *
519 520
 * \param pkt    Packet containing the IXFR reply in wire format.
 * \param adata  Answer data, including processing context.
521 522 523
 *
 * \return NS_PROC_MORE, NS_PROC_DONE, NS_PROC_FAIL
 */
524
static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
525
{
526
	struct ixfr_proc *ixfr = (struct ixfr_proc *)adata->ext;
527

528 529 530 531 532
	// Update counters.
	ixfr->proc.npkts  += 1;
	ixfr->proc.nbytes += pkt->size;

	// Process RRs in the message.
533
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
534 535
	for (uint16_t i = 0; i < answer->count; ++i) {
		if (journal_limit_exceeded(ixfr)) {
536
			IXFRIN_LOG(LOG_WARNING, "journal is full");
537
			return NS_PROC_FAIL;
538 539 540
		}

		const knot_rrset_t *rr = &answer->rr[i];
541
		if (out_of_zone(rr, ixfr)) {
542 543 544
			continue;
		}

545 546
		int ret = ixfrin_step(rr, ixfr);
		if (ret != KNOT_EOK) {
547
			IXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
548
			return NS_PROC_FAIL;
549
		}
550 551 552 553

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

557
	return NS_PROC_MORE;
558 559 560 561 562 563 564 565 566 567 568 569
}

/* --------------------------------- 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};
570
	struct ixfr_proc *ixfr = (struct ixfr_proc*)qdata->ext;
571
	knot_pkt_t *query = qdata->query;
572 573 574 575 576 577 578 579 580 581 582

	/* 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 */
583
			ixfr = (struct ixfr_proc*)qdata->ext;
584
			IXFROUT_LOG(LOG_INFO, "started, serial %u -> %u",
585 586 587 588
			            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. */
589
			IXFROUT_LOG(LOG_INFO, "zone is up-to-date");
590 591 592
			return ixfr_answer_soa(pkt, qdata);
		case KNOT_ERANGE:   /* No history -> AXFR. */
		case KNOT_ENOENT:
593
			IXFROUT_LOG(LOG_INFO, "incomplete history, fallback to AXFR");
594 595 596 597
			knot_pkt_clear(pkt);
			knot_pkt_put_question(pkt, knot_pkt_qname(query),
			                      knot_pkt_qclass(query),
			                      KNOT_RRTYPE_AXFR);
598
			qdata->packet_type = KNOT_QUERY_AXFR; /* Solve as AXFR. */
Marek Vavruša's avatar
Marek Vavruša committed
599
			return axfr_query_process(pkt, qdata);
600
		default:            /* Server errors. */
601
			IXFROUT_LOG(LOG_ERR, "failed to start (%s)", knot_strerror(ret));
602 603 604 605 606 607 608 609 610 611 612 613 614 615
			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);
616
		IXFROUT_LOG(LOG_INFO, "finished, %.02fs, %u messages, " SIZE_FORMAT,
617
		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
618
		            ixfr->proc.npkts, SIZE_PARAMS(ixfr->proc.nbytes));
619 620 621
		ret = NS_PROC_DONE;
		break;
	default:          /* Generic error. */
622
		IXFROUT_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
623 624 625 626 627 628 629
		ret = NS_PROC_FAIL;
		break;
	}

	return ret;
}

630
int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
631
{
632 633 634 635 636 637 638 639 640
	if (pkt == NULL || adata == NULL) {
		return NS_PROC_FAIL;
	}

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

	/* Initialize processing with first packet. */
647
	if (adata->ext == NULL) {
648
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
649
		if (!zone_transfer_needed(adata->param->zone, pkt)) {
650
			IXFRIN_LOG(LOG_INFO, "server has newer zone");
651 652 653
			return NS_PROC_DONE;
		}

654
		IXFRIN_LOG(LOG_INFO, "starting");
655 656 657
		// First packet with IXFR, init context
		int ret = ixfrin_answer_init(adata);
		if (ret != KNOT_EOK) {
658
			IXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
659 660
			return NS_PROC_FAIL;
		}
661 662
	} else {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);
663 664
	}

665
	int ret = process_ixfrin_packet(pkt, adata);
666
	if (ret == NS_PROC_DONE) {
667
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
668 669
		int fret = ixfrin_finalize(adata);
		if (fret != KNOT_EOK) {
670
			ret = NS_PROC_FAIL;
671 672 673 674 675 676
		}
	}

	return ret;
}

677
#undef IXFROUT_LOG