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

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

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

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

17 18
#include <urcu.h>

19 20 21
#include "contrib/mempattern.h"
#include "contrib/print.h"
#include "contrib/sockaddr.h"
22
#include "knot/common/log.h"
23
#include "knot/conf/conf.h"
24 25
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/internet.h"
26
#include "knot/zone/zonefile.h"
27
#include "libknot/libknot.h"
28

29
/* AXFR context. @note aliasing the generic xfr_proc */
30
struct axfr_proc {
31
	struct xfr_proc proc;
32 33 34 35
	hattrie_iter_t *i;
	unsigned cur_rrset;
};

36 37
static int axfr_put_rrsets(knot_pkt_t *pkt, zone_node_t *node,
                           struct axfr_proc *state)
38
{
39 40
	assert(node != NULL);

41
	int ret = KNOT_EOK;
42
	int i = state->cur_rrset;
43
	uint16_t rrset_count = node->rrset_count;
44
	unsigned flags = KNOT_PF_NOTRUNC;
45

46
	/* Append all RRs. */
47
	for (;i < rrset_count; ++i) {
48
		knot_rrset_t rrset = node_rrset_at(node, i);
49
		if (rrset.type == KNOT_RRTYPE_SOA) {
50 51
			continue;
		}
52
		ret = knot_pkt_put(pkt, 0, &rrset, flags);
53 54 55

		/* If something failed, remember the current RR for later. */
		if (ret != KNOT_EOK) {
56 57 58 59 60 61 62 63 64
			state->cur_rrset = i;
			return ret;
		}
	}

	state->cur_rrset = 0;
	return ret;
}

65 66
static int axfr_process_node_tree(knot_pkt_t *pkt, const void *item,
                                  struct xfr_proc *state)
67
{
68 69
	assert(item != NULL);

70 71 72 73 74 75
	struct axfr_proc *axfr = (struct axfr_proc*)state;

	if (axfr->i == NULL) {
		axfr->i = hattrie_iter_begin(item, true);
	}

76 77
	/* Put responses. */
	int ret = KNOT_EOK;
78
	zone_node_t *node = NULL;
79
	while (!hattrie_iter_finished(axfr->i)) {
80
		node = (zone_node_t *)*hattrie_iter_val(axfr->i);
81
		ret = axfr_put_rrsets(pkt, node, axfr);
82 83 84
		if (ret != KNOT_EOK) {
			break;
		}
85
		hattrie_iter_next(axfr->i);
86 87 88
	}

	/* Finished all nodes. */
89 90 91 92
	if (ret == KNOT_EOK) {
		hattrie_iter_free(axfr->i);
		axfr->i = NULL;
	}
93 94 95
	return ret;
}

96
static void axfr_query_cleanup(struct query_data *qdata)
97
{
98
	struct axfr_proc *axfr = (struct axfr_proc *)qdata->ext;
99

100 101
	hattrie_iter_free(axfr->i);
	ptrlist_free(&axfr->proc.nodes, qdata->mm);
102
	mm_free(qdata->mm, axfr);
103 104 105

	/* Allow zone changes (finished). */
	rcu_read_unlock();
106 107
}

108 109 110 111
static int axfr_query_check(struct query_data *qdata)
{
	/* Check valid zone, transaction security and contents. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
112
	NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_TRANSFER);
113 114 115
	/* Check expiration. */
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL);

116
	return KNOT_STATE_DONE;
117 118
}

119
static int axfr_query_init(struct query_data *qdata)
120 121 122
{
	assert(qdata);

123 124
	/* Check AXFR query validity. */
	int state = axfr_query_check(qdata);
125
	if (state == KNOT_STATE_FAIL) {
126 127 128 129 130 131 132
		if (qdata->rcode == KNOT_RCODE_FORMERR) {
			return KNOT_EMALF;
		} else {
			return KNOT_EDENIED;
		}
	}

133
	/* Create transfer processing context. */
134
	knot_mm_t *mm = qdata->mm;
135

136
	zone_contents_t *zone = qdata->zone->contents;
137
	struct axfr_proc *axfr = mm_alloc(mm, sizeof(struct axfr_proc));
138
	if (axfr == NULL) {
139 140
		return KNOT_ENOMEM;
	}
141 142
	memset(axfr, 0, sizeof(struct axfr_proc));
	init_list(&axfr->proc.nodes);
143 144

	/* Put data to process. */
145 146
	gettimeofday(&axfr->proc.tstamp, NULL);
	ptrlist_add(&axfr->proc.nodes, zone->nodes, mm);
147
	/* Put NSEC3 data if exists. */
148
	if (!zone_tree_is_empty(zone->nsec3_nodes)) {
149
		ptrlist_add(&axfr->proc.nodes, zone->nsec3_nodes, mm);
150
	}
151 152

	/* Set up cleanup callback. */
153
	qdata->ext = axfr;
154
	qdata->ext_cleanup = &axfr_query_cleanup;
155

156 157
	/* No zone changes during multipacket answer
	   (unlocked in axfr_answer_cleanup) */
158 159
	rcu_read_lock();

160 161 162
	return KNOT_EOK;
}

163 164
int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb process_item,
                     struct query_data *qdata)
165
{
166 167 168
	if (pkt == NULL || qdata == NULL || qdata->ext == NULL) {
		return KNOT_EINVAL;
	}
169 170

	int ret = KNOT_EOK;
171
	knot_mm_t *mm = qdata->mm;
172
	struct xfr_proc *xfer = qdata->ext;
173

174
	zone_contents_t *zone = qdata->zone->contents;
175
	knot_rrset_t soa_rr = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
176 177

	/* Prepend SOA on first packet. */
178
	if (xfer->npkts == 0) {
179
		ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
180 181 182
		if (ret != KNOT_EOK) {
			return ret;
		}
183
	}
184

185 186 187 188
	/* Process all items in the list. */
	while (!EMPTY_LIST(xfer->nodes)) {
		ptrnode_t *head = HEAD(xfer->nodes);
		ret = process_item(pkt, head->d, xfer);
189
		if (ret == KNOT_EOK) { /* Finished. */
190
			/* Complete change set. */
191
			rem_node((node_t *)head);
192
			mm_free(mm, head);
193
		} else { /* Packet full or other error. */
194 195
			break;
		}
196
	}
197 198 199

	/* Append SOA on last packet. */
	if (ret == KNOT_EOK) {
200
		ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
201 202
	}

203 204 205 206
	/* Update counters. */
	xfer->npkts  += 1;
	xfer->nbytes += pkt->size;

207 208
	return ret;
}
209

210
int axfr_process_query(knot_pkt_t *pkt, struct query_data *qdata)
211
{
212
	if (pkt == NULL || qdata == NULL) {
213
		return KNOT_STATE_FAIL;
214
	}
215

216
	int ret = KNOT_EOK;
217
	struct timeval now = {0};
218 219 220 221

	/* If AXFR is disabled, respond with NOTIMPL. */
	if (qdata->param->proc_flags & NS_QUERY_NO_AXFR) {
		qdata->rcode = KNOT_RCODE_NOTIMPL;
222
		return KNOT_STATE_FAIL;
223
	}
224 225 226

	/* Initialize on first call. */
	if (qdata->ext == NULL) {
227

228
		ret = axfr_query_init(qdata);
229
		if (ret != KNOT_EOK) {
230
			AXFROUT_LOG(LOG_ERR, "failed to start (%s)",
231
			            knot_strerror(ret));
232
			return KNOT_STATE_FAIL;
233
		} else {
234
			AXFROUT_LOG(LOG_INFO, "started, serial %u",
235
			           zone_contents_serial(qdata->zone->contents));
236
		}
237
	}
238

239
	/* Reserve space for TSIG. */
240
	knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
241 242

	/* Answer current packet (or continue). */
243
	struct axfr_proc *axfr = (struct axfr_proc *)qdata->ext;
244
	ret = xfr_process_list(pkt, &axfr_process_node_tree, qdata);
245 246
	switch(ret) {
	case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */
247
		return KNOT_STATE_PRODUCE; /* Check for more. */
248
	case KNOT_EOK:    /* Last response. */
249
		gettimeofday(&now, NULL);
Daniel Salzman's avatar
Daniel Salzman committed
250 251
		AXFROUT_LOG(LOG_INFO,
		            "finished, %.02f seconds, %u messages, %u bytes",
252
		            time_diff(&axfr->proc.tstamp, &now) / 1000.0,
Daniel Salzman's avatar
Daniel Salzman committed
253
		            axfr->proc.npkts, axfr->proc.nbytes);
254
		return KNOT_STATE_DONE;
255 256
		break;
	default:          /* Generic error. */
257
		AXFROUT_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
258
		return KNOT_STATE_FAIL;
259
	}
260
}
261

262 263
static void axfr_answer_cleanup(struct answer_data *data)
{
264 265
	assert(data != NULL);

266 267
	struct xfr_proc *proc = data->ext;
	if (proc) {
268
		zone_contents_deep_free(&proc->contents);
269
		mm_free(data->mm, proc);
270 271 272 273 274
		data->ext = NULL;
	}
}

static int axfr_answer_init(struct answer_data *data)
275
{
276 277 278 279 280 281 282 283 284 285
	assert(data);

	/* Create new zone contents. */
	zone_t *zone = data->param->zone;
	zone_contents_t *new_contents = zone_contents_new(zone->name);
	if (new_contents == NULL) {
		return KNOT_ENOMEM;
	}

	/* Create new processing context. */
286
	struct xfr_proc *proc = mm_alloc(data->mm, sizeof(struct xfr_proc));
287 288 289 290 291 292
	if (proc == NULL) {
		zone_contents_deep_free(&new_contents);
		return KNOT_ENOMEM;
	}

	memset(proc, 0, sizeof(struct xfr_proc));
293
	proc->contents = new_contents;
294 295 296 297 298 299 300 301 302
	gettimeofday(&proc->tstamp, NULL);

	/* Set up cleanup callback. */
	data->ext = proc;
	data->ext_cleanup = &axfr_answer_cleanup;

	return KNOT_EOK;
}

303
static int axfr_answer_finalize(struct answer_data *adata)
304 305 306 307
{
	struct timeval now;
	gettimeofday(&now, NULL);

308
	/*
309 310
	 * Adjust zone so that node count is set properly and nodes are
	 * marked authoritative / delegation point.
311
	 */
312
	struct xfr_proc *proc = adata->ext;
313
	int rc = zone_contents_adjust_full(proc->contents);
314 315 316 317
	if (rc != KNOT_EOK) {
		return rc;
	}

318 319 320
	err_handler_logger_t handler;
	handler._cb.cb = err_handler_logger;
	rc = zone_do_sem_checks(proc->contents, false, &handler._cb);
321

322 323 324 325
	if (rc != KNOT_EOK) {
		return rc;
	}

326
	conf_val_t val = conf_zone_get(adata->param->conf, C_MAX_ZONE_SIZE,
327 328 329 330
	                               proc->contents->apex->owner);
	int64_t size_limit = conf_int(&val);

	if (proc->contents->size > size_limit) {
331
		AXFRIN_LOG(LOG_WARNING, "zone size exceeded");
332 333 334
		return KNOT_STATE_FAIL;
	}

335
	/* Switch contents. */
336
	zone_t *zone = adata->param->zone;
337 338
	zone_contents_t *old_contents =
	                zone_switch_contents(zone, proc->contents);
339
	zone->flags &= ~ZONE_EXPIRED;
340
	synchronize_rcu();
341

342 343 344 345 346 347 348 349 350 351 352 353 354 355
	if (old_contents != NULL) {
		AXFRIN_LOG(LOG_INFO, "finished, "
		           "serial %u -> %u, %.02f seconds, %u messages, %u bytes",
		           zone_contents_serial(old_contents),
		           zone_contents_serial(proc->contents),
		           time_diff(&proc->tstamp, &now) / 1000.0,
		           proc->npkts, proc->nbytes);
	} else {
		AXFRIN_LOG(LOG_INFO, "finished, "
		           "serial %u, %.02f seconds, %u messages, %u bytes",
		           zone_contents_serial(proc->contents),
		           time_diff(&proc->tstamp, &now) / 1000.0,
		           proc->npkts, proc->nbytes);
	}
356 357 358

	/* Do not free new contents with cleanup. */
	zone_contents_deep_free(&old_contents);
359
	proc->contents = NULL;
360 361 362 363

	return KNOT_EOK;
}

364
static int axfr_answer_packet(knot_pkt_t *pkt, struct answer_data *adata)
365
{
366
	assert(adata != NULL);
367
	struct xfr_proc *proc = adata->ext;
368 369
	assert(pkt != NULL);
	assert(proc != NULL);
370

371 372 373
	/* Update counters. */
	proc->npkts  += 1;
	proc->nbytes += pkt->size;
374

375
	conf_val_t val = conf_zone_get(adata->param->conf, C_MAX_ZONE_SIZE,
376 377 378
	                               proc->contents->apex->owner);
	int64_t size_limit = conf_int(&val);

379
	/* Init zone creator. */
380
	zcreator_t zc = {.z = proc->contents, .master = false, .ret = KNOT_EOK };
381 382

	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
383
	const knot_rrset_t *answer_rr = knot_pkt_rr(answer, 0);
384
	for (uint16_t i = 0; i < answer->count; ++i) {
385
		if (answer_rr[i].type == KNOT_RRTYPE_SOA &&
386
		    node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) {
387
			return KNOT_STATE_DONE;
388
		} else {
389
			int ret = zcreator_step(&zc, &answer_rr[i]);
390
			if (ret != KNOT_EOK) {
391
				return KNOT_STATE_FAIL;
392 393
			}
		}
394 395
		proc->contents->size += knot_rrset_size(&answer_rr[i]);
		if (proc->contents->size > size_limit) {
396
			AXFRIN_LOG(LOG_WARNING, "zone size exceeded");
397 398
			return KNOT_STATE_FAIL;
		}
399 400
	}

401
	return KNOT_STATE_CONSUME;
402 403
}

404
int axfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
405
{
406
	if (pkt == NULL || adata == NULL) {
407
		return KNOT_STATE_FAIL;
408 409 410 411 412
	}

	/* Check RCODE. */
	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
	if (rcode != KNOT_RCODE_NOERROR) {
413
		const knot_lookup_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
414
		if (lut != NULL) {
415
			AXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
416
		}
417
		return KNOT_STATE_FAIL;
418 419
	}

420
	/* Initialize processing with first packet. */
421 422
	if (adata->ext == NULL) {
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
423
		AXFRIN_LOG(LOG_INFO, "starting");
424

425
		int ret = axfr_answer_init(adata);
426
		if (ret != KNOT_EOK) {
427
			AXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
428
			return KNOT_STATE_FAIL;
429
		}
430
	} else {
431
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100);
432
	}
433

434
	/* Process answer packet. */
435
	int ret = axfr_answer_packet(pkt, adata);
436
	if (ret == KNOT_STATE_DONE) {
437
		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
438
		/* This was the last packet, finalize zone and publish it. */
439
		int fret = axfr_answer_finalize(adata);
440
		if (fret != KNOT_EOK) {
441
			ret = KNOT_STATE_FAIL;
442
		}
443
	}
444

445
	return ret;
446
}