internet.c 27.7 KB
Newer Older
1
/*  Copyright (C) 2013 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
#include "knot/common/debug.h"
18
#include "libknot/libknot.h"
19
#include "libknot/descriptor.h"
20 21
#include "libknot/rrtype/rdname.h"
#include "libknot/rrtype/soa.h"
22
#include "knot/dnssec/rrset-sign.h"
23 24
#include "knot/nameserver/internet.h"
#include "knot/nameserver/nsec_proofs.h"
25
#include "knot/nameserver/process_query.h"
26
#include "knot/nameserver/process_answer.h"
27
#include "knot/zone/serial.h"
28
#include "knot/zone/zonedb.h"
29 30

/*! \brief Check if given node was already visited. */
31
static int wildcard_has_visited(struct query_data *qdata, const zone_node_t *node)
32 33 34 35 36 37 38 39 40 41 42
{
	struct wildcard_hit *item = NULL;
	WALK_LIST(item, qdata->wildcards) {
		if (item->node == node) {
			return true;
		}
	}
	return false;
}

/*! \brief Mark given node as visited. */
43
static int wildcard_visit(struct query_data *qdata, const zone_node_t *node, const knot_dname_t *sname)
44 45 46 47
{
	assert(qdata);
	assert(node);

48 49 50 51 52
	/* Already in the list. */
	if (wildcard_has_visited(qdata, node)) {
		return KNOT_EOK;
	}

53 54
	mm_ctx_t *mm = qdata->mm;
	struct wildcard_hit *item = mm_alloc(mm, sizeof(struct wildcard_hit));
55 56 57 58 59 60
	item->node = node;
	item->sname = sname;
	add_tail(&qdata->wildcards, (node_t *)item);
	return KNOT_EOK;
}

61
/*! \brief Synthetizes a CNAME RR from a DNAME. */
62 63 64
static int dname_cname_synth(const knot_rrset_t *dname_rr,
                             const knot_dname_t *qname,
                             knot_rrset_t *cname_rrset,
65
                             mm_ctx_t *mm)
66 67
{
	if (cname_rrset == NULL) {
68
		return KNOT_EINVAL;
69
	}
70
	dbg_ns("%s(%p, %p)\n", __func__, dname_rr, qname);
71

72 73
	knot_dname_t *owner_copy = knot_dname_copy(qname, mm);
	if (owner_copy == NULL) {
74
		return KNOT_ENOMEM;
75
	}
76
	knot_rrset_init(cname_rrset, owner_copy, KNOT_RRTYPE_CNAME, dname_rr->rclass);
77 78

	/* Replace last labels of qname with DNAME. */
79
	const knot_dname_t *dname_wire = dname_rr->owner;
80
	const knot_dname_t *dname_tgt = knot_dname_target(&dname_rr->rrs);
81 82 83
	int labels = knot_dname_labels(dname_wire, NULL);
	knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt);
	if (cname == NULL) {
84
		knot_dname_free(&owner_copy, mm);
85
		return KNOT_ENOMEM;
86 87 88 89
	}

	/* Store DNAME into RDATA. */
	int cname_size = knot_dname_size(cname);
90
	uint8_t cname_rdata[cname_size];
91
	memcpy(cname_rdata, cname, cname_size);
92
	knot_dname_free(&cname, NULL);
93

94
	const knot_rdata_t *dname_data = knot_rdataset_at(&dname_rr->rrs, 0);
95
	int ret = knot_rrset_add_rdata(cname_rrset, cname_rdata, cname_size,
96
	                               knot_rdata_ttl(dname_data), mm);
97 98 99 100 101 102
	if (ret != KNOT_EOK) {
		knot_dname_free(&owner_copy, mm);
		return ret;
	}

	return KNOT_EOK;
103 104 105 106 107 108
}

/*!
 * \brief Checks if the name created by replacing the owner of \a dname_rrset
 *        in the \a qname by the DNAME's target would be longer than allowed.
 */
109
static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname_t *qname)
110 111
{
	if (knot_dname_labels(qname, NULL)
112
		- knot_dname_labels(rrset->owner, NULL)
113
		+ knot_dname_labels(knot_dname_target(&rrset->rrs), NULL)
114
		> KNOT_DNAME_MAXLABELS) {
115 116 117 118 119 120
		return true;
	} else {
		return false;
	}
}

121
/*! \brief DNSSEC both requested & available. */
122
static bool have_dnssec(struct query_data *qdata)
123
{
124
	return knot_pkt_has_dnssec(qdata->query) &&
125
	       zone_contents_is_signed(qdata->zone->contents);
126 127
}

128 129
/*! \brief Synthesize RRSIG for given parameters, store in 'qdata' for later use */
static int put_rrsig(const knot_dname_t *sig_owner, uint16_t type,
130 131
                     const knot_rrset_t *rrsigs,
                     knot_rrinfo_t *rrinfo,
132 133
                     struct query_data *qdata)
{
134 135
	knot_rdataset_t synth_rrs;
	knot_rdataset_init(&synth_rrs);
136
	int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrs, qdata->mm);
137 138 139 140 141 142 143
	if (ret == KNOT_ENOENT) {
		// No signature
		return KNOT_EOK;
	}
	if (ret != KNOT_EOK) {
		return ret;
	}
144 145

	/* Create rrsig info structure. */
146
	struct rrsig_info *info = mm_alloc(qdata->mm, sizeof(struct rrsig_info));
147
	if (info == NULL) {
148
		knot_rdataset_clear(&synth_rrs, qdata->mm);
149 150
		return KNOT_ENOMEM;
	}
151 152 153 154

	/* Store RRSIG into info structure. */
	knot_dname_t *owner_copy = knot_dname_copy(sig_owner, qdata->mm);
	if (owner_copy == NULL) {
155
		mm_free(qdata->mm, info);
156
		knot_rdataset_clear(&synth_rrs, qdata->mm);
157 158
		return KNOT_ENOMEM;
	}
159 160 161 162
	knot_rrset_init(&info->synth_rrsig, owner_copy, rrsigs->type, rrsigs->rclass);
	/* Store filtered signature. */
	info->synth_rrsig.rrs = synth_rrs;

163
	info->rrinfo = rrinfo;
164 165 166 167 168
	add_tail(&qdata->rrsigs, &info->n);

	return KNOT_EOK;
}

169 170 171
/*! \brief This is a wildcard-covered or any other terminal node for QNAME.
 *         e.g. positive answer.
 */
172
static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
173
{
174 175
	knot_rrset_t rrset;
	knot_rrset_init_empty(&rrset);
176

177 178 179 180 181
	/* Wildcard expansion or exact match, either way RRSet owner is
	 * is QNAME. We can fake name synthesis by setting compression hint to
	 * QNAME position. Just need to check if we're answering QNAME and not
	 * a CNAME target.
	 */
182
	uint16_t compr_hint = KNOT_COMPR_HINT_NONE;
183
	if (pkt->rrset_count == 0) { /* Guaranteed first answer. */
184
		compr_hint = KNOT_COMPR_HINT_QNAME;
185 186
	}

187 188
	int ret = KNOT_EOK;
	switch (type) {
189
	case KNOT_RRTYPE_ANY: /* Append all RRSets. */ {
190
		/* If ANY not allowed, set TC bit. */
191
		if ((qdata->param->proc_flags & NS_QUERY_LIMIT_ANY) &&
192 193
		    (qdata->zone->conf->disable_any)) {
			dbg_ns("%s: ANY/UDP disabled for this zone TC=1\n", __func__);
194
			knot_wire_set_tc(pkt->wire);
195
			return KNOT_ESPACE;
196
		}
197
		for (unsigned i = 0; i < qdata->node->rrset_count; ++i) {
198
			rrset = node_rrset_at(qdata->node, i);
199
			ret = ns_put_rr(pkt, &rrset, NULL, compr_hint, 0, qdata);
200 201 202 203 204
			if (ret != KNOT_EOK) {
				break;
			}
		}
		break;
205
	}
206
	default: /* Single RRSet of given type. */
207
		rrset = node_rrset(qdata->node, type);
208
		if (!knot_rrset_empty(&rrset)) {
209
			knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
210
			ret = ns_put_rr(pkt, &rrset, &rrsigs, compr_hint, 0, qdata);
211 212 213 214 215 216 217
		}
		break;
	}

	return ret;
}

218
/*! \brief Puts optional NS RRSet to the Authority section of the response. */
219
static int put_authority_ns(knot_pkt_t *pkt, struct query_data *qdata)
220
{
221
	const zone_contents_t *zone = qdata->zone->contents;
222 223 224 225 226 227 228 229 230 231 232 233 234 235
	dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);

	/* DS/DNSKEY queries are not referrals. NS is optional.
	 * But taking response size into consideration, DS/DNSKEY RRs
	 * are rather large and may trigger fragmentation or even TCP
	 * recovery. */
	uint16_t query_type = knot_pkt_qtype(pkt);
	if (query_type == KNOT_RRTYPE_DS     || /* Too large response */
	    query_type == KNOT_RRTYPE_DNSKEY || /* Too large response */
	    qdata->node == NULL /* CNAME leading to non-existent name.*/ ) {
		dbg_ns("%s: not adding AUTHORITY NS for this response\n", __func__);
		return KNOT_EOK;
	}

236
	knot_rrset_t ns_rrset = node_rrset(zone->apex, KNOT_RRTYPE_NS);
237
	if (!knot_rrset_empty(&ns_rrset)) {
238
		knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
239
		return ns_put_rr(pkt, &ns_rrset, &rrsigs, KNOT_COMPR_HINT_NONE,
240
		                 KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP, qdata);
Marek Vavrusa's avatar
Marek Vavrusa committed
241 242 243 244
	} else {
		dbg_ns("%s: no NS RRSets in this zone, fishy...\n", __func__);
	}
	return KNOT_EOK;
245 246 247
}

/*! \brief Puts optional SOA RRSet to the Authority section of the response. */
248
static int put_authority_soa(knot_pkt_t *pkt, struct query_data *qdata,
249
                             const zone_contents_t *zone)
250 251
{
	dbg_ns("%s(%p, %p)\n", __func__, pkt, zone);
252 253
	knot_rrset_t soa_rrset = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
	knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
254 255 256 257

	// if SOA's TTL is larger than MINIMUM, copy the RRSet and set
	// MINIMUM as TTL
	int ret = KNOT_EOK;
258
	uint32_t flags = KNOT_PF_NOTRUNC;
259
	uint32_t min = knot_soa_minimum(&soa_rrset.rrs);
260 261
	const knot_rdata_t *soa_data = knot_rdataset_at(&soa_rrset.rrs, 0);
	if (min < knot_rdata_ttl(soa_data)) {
262
		knot_rrset_t copy;
263
		knot_dname_t *dname_cpy = knot_dname_copy(soa_rrset.owner, &pkt->mm);
264 265 266
		if (dname_cpy == NULL) {
			return KNOT_ENOMEM;
		}
267
		knot_rrset_init(&copy, dname_cpy, soa_rrset.type, soa_rrset.rclass);
268
		int ret = knot_rdataset_copy(&copy.rrs, &soa_rrset.rrs, &pkt->mm);
269
		if (ret != KNOT_EOK) {
270
			knot_dname_free(&dname_cpy, &pkt->mm);
271 272
			return ret;
		}
273 274
		knot_rdata_t *copy_data = knot_rdataset_at(&copy.rrs, 0);
		knot_rdata_set_ttl(copy_data, min);
275 276

		flags |= KNOT_PF_FREE;
277
		soa_rrset = copy;
278 279
	}

280
	ret = ns_put_rr(pkt, &soa_rrset, &rrsigs, KNOT_COMPR_HINT_NONE, flags, qdata);
281
	if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) {
282
		knot_rrset_clear(&soa_rrset, &pkt->mm);
283 284 285
	}

	return ret;
286 287 288 289 290 291
}

/*! \brief Put the delegation NS RRSet to the Authority section. */
static int put_delegation(knot_pkt_t *pkt, struct query_data *qdata)
{
	/* Find closest delegation point. */
292
	while (!(qdata->node->flags & NODE_FLAGS_DELEG)) {
293
		qdata->node = qdata->node->parent;
294 295 296
	}

	/* Insert NS record. */
297 298
	knot_rrset_t rrset = node_rrset(qdata->node, KNOT_RRTYPE_NS);
	knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
299
	return ns_put_rr(pkt, &rrset, &rrsigs, KNOT_COMPR_HINT_NONE, 0, qdata);
300 301
}

302
/*! \brief Put additional records for given RR. */
303 304
static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
                          struct query_data *qdata, knot_rrinfo_t *info)
305
{
306
	/* Valid types for ADDITIONALS insertion. */
307
	/* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */
308
	static const uint16_t ar_type_list[] = {KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA};
309
	static const int ar_type_count = 2;
310 311

	int ret = KNOT_EOK;
312
	uint32_t flags = KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP;
313
	uint16_t hint = KNOT_COMPR_HINT_NONE;
314
	const zone_node_t *node = NULL;
315

316
	/* All RRs should have additional node cached or NULL. */
317
	uint16_t rr_rdata_count = rr->rrs.rr_count;
318
	for (uint16_t i = 0; i < rr_rdata_count; i++) {
319
		hint = knot_pkt_compr_hint(info, KNOT_COMPR_HINT_RDATA + i);
320 321
		node = rr->additional[i];

322 323
		/* No additional node for this record. */
		if (node == NULL) {
324
			continue;
325
		}
326

327
		knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
328
		for (int k = 0; k < ar_type_count; ++k) {
329
			knot_rrset_t additional = node_rrset(node, ar_type_list[k]);
330
			if (knot_rrset_empty(&additional)) {
331 332
				continue;
			}
333
			ret = ns_put_rr(pkt, &additional, &rrsigs,
334
			                hint, flags, qdata);
335
			if (ret != KNOT_EOK) {
336
				break;
337 338 339 340 341
			}
		}
	}

	return ret;
342 343
}

344
static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, struct query_data *qdata)
345
{
346
	dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);
347

348 349 350
	const zone_node_t *cname_node = qdata->node;
	knot_rrset_t cname_rr = node_rrset(qdata->node, rrtype);
	knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
351 352
	int ret = KNOT_EOK;

353
	assert(!knot_rrset_empty(&cname_rr));
354

355 356
	/* Check whether RR is already in the packet. */
	uint16_t flags = KNOT_PF_CHECKDUP;
357 358 359

	/* Now, try to put CNAME to answer. */
	uint16_t rr_count_before = pkt->rrset_count;
360
	ret = ns_put_rr(pkt, &cname_rr, &rrsigs, 0, flags, qdata);
361 362 363 364 365 366 367 368 369
	switch (ret) {
	case KNOT_EOK:    break;
	case KNOT_ESPACE: return TRUNC;
	default:          return ERROR;
	}

	/* Check if RR count increased. */
	if (pkt->rrset_count <= rr_count_before) {
		dbg_ns("%s: RR %p already inserted => CNAME loop\n",
370
		       __func__, &cname_rr);
371 372
		qdata->node = NULL; /* Act is if the name leads to nowhere. */
		return HIT;
373 374
	}

375 376
	/* Synthesize CNAME if followed DNAME. */
	if (rrtype == KNOT_RRTYPE_DNAME) {
377
		if (dname_cname_cannot_synth(&cname_rr, qdata->name)) {
378 379 380
			qdata->rcode = KNOT_RCODE_YXDOMAIN;
			return ERROR;
		}
381 382 383 384 385 386 387 388
		knot_rrset_t dname_rr = cname_rr;
		ret = dname_cname_synth(&dname_rr, qdata->name, &cname_rr,
		                        &pkt->mm);
		if (ret != KNOT_EOK) {
			qdata->rcode = KNOT_RCODE_SERVFAIL;
			return ERROR;
		}
		ret = ns_put_rr(pkt, &cname_rr, NULL, 0, KNOT_PF_FREE, qdata);
389 390 391 392
		switch (ret) {
		case KNOT_EOK:    break;
		case KNOT_ESPACE: return TRUNC;
		default:          return ERROR;
393
		}
394 395
	}

396 397 398 399 400 401 402 403 404
	/* If node is a wildcard, follow only if we didn't visit the same node
	 * earlier, as that would mean a CNAME loop. */
	if (knot_dname_is_wildcard(cname_node->owner)) {

		/* Check if is not in wildcard nodes (loop). */
		dbg_ns("%s: CNAME node %p is wildcard\n", __func__, cname_node);
		if (wildcard_has_visited(qdata, cname_node)) {
			dbg_ns("%s: node %p already visited => CNAME loop\n",
			       __func__, cname_node);
405
			qdata->node = NULL; /* Act is if the name leads to nowhere. */
406 407 408 409 410 411 412 413 414
			return HIT;
		}

		/* Put to wildcard node list. */
		if (wildcard_visit(qdata, cname_node, qdata->name) != KNOT_EOK) {
			return ERROR;
		}
	}

415
	/* Now follow the next CNAME TARGET. */
416
	qdata->name = knot_cname_name(&cname_rr.rrs);
417 418

#ifdef KNOT_NS_DEBUG
419 420
	char *cname_str = knot_dname_to_str_alloc(cname_node->owner);
	char *target_str = knot_dname_to_str_alloc(qdata->name);
421 422 423 424 425 426 427 428
	dbg_ns("%s: FOLLOW '%s' -> '%s'\n", __func__, cname_str, target_str);
	free(cname_str);
	free(target_str);
#endif /* KNOT_NS_DEBUG */

	return FOLLOW;
}

429
static int name_found(knot_pkt_t *pkt, struct query_data *qdata)
430 431
{
	uint16_t qtype = knot_pkt_qtype(pkt);
432
	dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);
433

434
	if (node_rrtype_exists(qdata->node, KNOT_RRTYPE_CNAME)
435
	    && qtype != KNOT_RRTYPE_CNAME
436
	    && qtype != KNOT_RRTYPE_RRSIG
437 438
	    && qtype != KNOT_RRTYPE_ANY) {
		dbg_ns("%s: solving CNAME\n", __func__);
439
		return follow_cname(pkt, KNOT_RRTYPE_CNAME, qdata);
440 441
	}

442 443 444
	/* DS query is answered normally, but everything else at/below DP
	 * triggers referral response. */
	if (qtype != KNOT_RRTYPE_DS &&
445
	    ((qdata->node->flags & NODE_FLAGS_DELEG) || qdata->node->flags & NODE_FLAGS_NONAUTH)) {
446 447 448 449
		dbg_ns("%s: solving REFERRAL\n", __func__);
		return DELEG;
	}

450
	uint16_t old_rrcount = pkt->rrset_count;
451
	int ret = put_answer(pkt, qtype, qdata);
452
	if (ret != KNOT_EOK) {
453 454 455 456 457 458 459
		dbg_ns("%s: failed answer from node %p (%s)\n",
		       __func__, qdata->node, knot_strerror(ret));
		if (ret == KNOT_ESPACE) {
			return TRUNC;
		} else {
			return ERROR;
		}
460 461
	}

462 463
	/* Check for NODATA (=0 RRs added). */
	if (old_rrcount == pkt->rrset_count) {
464 465 466 467 468 469
		return NODATA;
	} else {
		return HIT;
	}
}

470
static int name_not_found(knot_pkt_t *pkt, struct query_data *qdata)
471
{
472
	dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);
473 474

	/* Name is covered by wildcard. */
475
	if (qdata->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) {
476
		dbg_ns("%s: name %p covered by wildcard\n", __func__, qdata->name);
477 478

		/* Find wildcard child in the zone. */
479
		const zone_node_t *wildcard_node =
480
		                zone_contents_find_wildcard_child(
481
		                        qdata->zone->contents, qdata->encloser);
Lubos Slovak's avatar
Lubos Slovak committed
482

483
		qdata->node = wildcard_node;
484 485
		assert(qdata->node != NULL);

486
		/* keep encloser */
487
		qdata->previous = NULL;
488

489 490 491
		/* Follow expanded wildcard. */
		int next_state = name_found(pkt, qdata);

492
		/* Put to wildcard node list. */
493
		if (wildcard_visit(qdata, wildcard_node, qdata->name) != KNOT_EOK) {
494
			next_state = ERROR;
495 496
		}

497
		return next_state;
498 499 500
	}

	/* Name is under DNAME, use it for substitution. */
501
	knot_rrset_t dname_rrset = node_rrset(qdata->encloser, KNOT_RRTYPE_DNAME);
502
	if (!knot_rrset_empty(&dname_rrset)) {
503 504 505
		dbg_ns("%s: solving DNAME for name %p\n", __func__, qdata->name);
		qdata->node = qdata->encloser; /* Follow encloser as new node. */
		return follow_cname(pkt, KNOT_RRTYPE_DNAME, qdata);
506 507
	}

508
	/* Name is below delegation. */
509
	if ((qdata->encloser->flags & NODE_FLAGS_DELEG)) {
510
		dbg_ns("%s: name below delegation point %p\n", __func__, qdata->name);
511 512 513 514
		qdata->node = qdata->encloser;
		return DELEG;
	}

515
	dbg_ns("%s: name not found in zone %p\n", __func__, qdata->name);
516 517 518
	return MISS;
}

519
static int solve_name(int state, knot_pkt_t *pkt, struct query_data *qdata)
520
{
521
	dbg_ns("%s(%d, %p, %p)\n", __func__, state, pkt, qdata);
522
	int ret = zone_contents_find_dname(qdata->zone->contents, qdata->name,
523 524 525 526
	                                        &qdata->node, &qdata->encloser,
	                                        &qdata->previous);

	switch(ret) {
527
	case ZONE_NAME_FOUND:
528
		return name_found(pkt, qdata);
529
	case ZONE_NAME_NOT_FOUND:
530
		return name_not_found(pkt, qdata);
531 532 533 534 535 536 537 538
	case KNOT_EOUTOFZONE:
		assert(state == FOLLOW); /* CNAME/DNAME chain only. */
		return HIT;
	default:
		return ERROR;
	}
}

539
static int solve_answer(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
540 541
{
	/* Get answer to QNAME. */
542
	state = solve_name(state, pkt, qdata);
543 544 545 546 547 548 549 550 551

	/* Is authoritative answer unless referral.
	 * Must check before we chase the CNAME chain. */
	if (state != DELEG) {
		knot_wire_set_aa(pkt->wire);
	}

	/* Additional resolving for CNAME/DNAME chain. */
	while (state == FOLLOW) {
552
		state = solve_name(state, pkt, qdata);
553 554 555 556 557
	}

	return state;
}

558
static int solve_answer_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
559
{
560
	if (!have_dnssec(qdata)) {
561 562 563
		return state; /* DNSSEC not supported. */
	}

564
	/* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */
565
	int ret = nsec_append_rrsigs(pkt, qdata, false);
566 567 568 569
	switch(ret) {
	case KNOT_ESPACE: return TRUNC;
	case KNOT_EOK:    return state;
	default:          return ERROR;
570 571 572
	}
}

573
static int solve_authority(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
574 575
{
	int ret = KNOT_ERROR;
576
	const zone_contents_t *zone_contents = qdata->zone->contents;
577 578 579

	switch (state) {
	case HIT:    /* Positive response, add (optional) AUTHORITY NS. */
580
		dbg_ns("%s: answer is POSITIVE\n", __func__);
581
		ret = put_authority_ns(pkt, qdata);
582 583 584
		break;
	case MISS:   /* MISS, set NXDOMAIN RCODE. */
		dbg_ns("%s: answer is NXDOMAIN\n", __func__);
585
		qdata->rcode = KNOT_RCODE_NXDOMAIN;
586
		ret = put_authority_soa(pkt, qdata, zone_contents);
587
		break;
588
	case NODATA: /* NODATA append AUTHORITY SOA. */
589
		dbg_ns("%s: answer is NODATA\n", __func__);
590
		ret = put_authority_soa(pkt, qdata, zone_contents);
591
		break;
592
	case DELEG:  /* Referral response. */
593
		ret = put_delegation(pkt, qdata);
594
		break;
595 596 597 598
	case TRUNC:  /* Truncated ANSWER. */
		ret = KNOT_ESPACE;
		break;
	case ERROR:  /* Error resolving ANSWER. */
599 600 601 602 603
		break;
	default:
		assert(0);
		break;
	}
604

605 606 607 608 609 610 611 612
	/* Evaluate final state. */
	switch (ret) {
	case KNOT_EOK:    return state; /* Keep current state. */
	case KNOT_ESPACE: return TRUNC; /* Truncated. */
	default:          return ERROR; /* Error. */
	}
}

613
static int solve_authority_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
614
{
615
	if (!have_dnssec(qdata)) {
616 617
		return state; /* DNSSEC not supported. */
	}
618

619 620
	int ret = KNOT_ERROR;

621
	/* Authenticated denial of existence. */
622
	switch (state) {
623
	case HIT:    ret = KNOT_EOK; break;
624 625 626 627
	case MISS:   ret = nsec_prove_nxdomain(pkt, qdata); break;
	case NODATA: ret = nsec_prove_nodata(pkt, qdata); break;
	case DELEG:  ret = nsec_prove_dp_security(pkt, qdata); break;
	case TRUNC:  ret = KNOT_ESPACE; break;
628
	case ERROR:  ret = KNOT_ERROR; break;
629 630 631 632
	default:
		assert(0);
		break;
	}
633

634 635
	/* RFC4035 3.1.3 Prove visited wildcards.
	 * Wildcard expansion applies for Name Error, Wildcard Answer and
636 637 638
	 * No Data proofs if at one point the search expanded a wildcard node.
	 * \note Do not attempt to prove non-authoritative data. */
	if (ret == KNOT_EOK && state != DELEG) {
639 640 641
		ret = nsec_prove_wildcards(pkt, qdata);
	}

642 643
	/* RFC4035, section 3.1 RRSIGs for RRs in AUTHORITY are mandatory. */
	if (ret == KNOT_EOK) {
644
		ret = nsec_append_rrsigs(pkt, qdata, false);
645 646 647 648 649 650 651 652 653 654
	}

	/* Evaluate final state. */
	switch (ret) {
	case KNOT_EOK:    return state; /* Keep current state. */
	case KNOT_ESPACE: return TRUNC; /* Truncated. */
	default:          return ERROR; /* Error. */
	}
}

655 656
static int solve_additional(int state, knot_pkt_t *pkt,
                            struct query_data *qdata, void *ctx)
657
{
658
	int ret = KNOT_EOK;
659

660
	/* Scan all RRs in ANSWER/AUTHORITY. */
661 662
	for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
		/* Skip types for which it doesn't apply. */
663
		if (!knot_rrtype_additional_needed(pkt->rr[i].type)) {
664 665 666
			continue;
		}
		/* Put additional records for given type. */
667
		ret = put_additional(pkt, &pkt->rr[i], qdata, &pkt->rr_info[i]);
668 669 670 671
		if (ret != KNOT_EOK) {
			break;
		}
	}
672

673 674 675 676 677 678
	/* Evaluate final state. */
	switch (ret) {
	case KNOT_EOK:    return state; /* Keep current state. */
	case KNOT_ESPACE: return TRUNC; /* Truncated. */
	default:          return ERROR; /* Error. */
	}
679 680
}

681
static int solve_additional_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
682
{
683
	if (!have_dnssec(qdata)) {
684 685 686
		return state; /* DNSSEC not supported. */
	}

687
	/* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */
688
	int ret = nsec_append_rrsigs(pkt, qdata, true);
689 690 691 692 693 694 695
	switch(ret) {
	case KNOT_ESPACE: return TRUNC;
	case KNOT_EOK:    return state;
	default:          return ERROR;
	}
}

696 697
int ns_put_rr(knot_pkt_t *pkt, const knot_rrset_t *rr,
              const knot_rrset_t *rrsigs, uint16_t compr_hint,
698
              uint32_t flags, struct query_data *qdata)
699
{
700
	if (rr->rrs.rr_count < 1) {
701
		dbg_ns("%s: refusing to put empty RR of type %u\n", __func__, rr->type);
702 703 704
		return KNOT_EMALF;
	}

705 706 707 708 709 710 711
	/* Wildcard expansion applies only for answers. */
	bool expand = false;
	if (pkt->current == KNOT_ANSWER) {
		/* Expand if RR is wildcard & we didn't query for wildcard. */
		expand = (knot_dname_is_wildcard(rr->owner) && !knot_dname_is_wildcard(qdata->name));
	}

712 713 714 715
	/* If we already have compressed name on the wire and compression hint,
	 * we can just insert RRSet and fake synthesis by using compression
	 * hint. */
	int ret = KNOT_EOK;
716
	knot_rrset_t to_add;
717
	if (compr_hint == KNOT_COMPR_HINT_NONE && expand) {
718
		knot_dname_t *qname_cpy = knot_dname_copy(qdata->name, &pkt->mm);
719
		if (qname_cpy == NULL) {
720 721
			return KNOT_ENOMEM;
		}
722
		knot_rrset_init(&to_add, qname_cpy, rr->type, rr->rclass);
723
		int ret = knot_rdataset_copy(&to_add.rrs, &rr->rrs, &pkt->mm);
724 725 726
		if (ret != KNOT_EOK) {
			knot_dname_free(&qname_cpy, &pkt->mm);
		}
727
		to_add.additional = rr->additional;
728
		flags |= KNOT_PF_FREE;
729 730
	} else {
		to_add = *rr;
731 732 733
	}

	uint16_t prev_count = pkt->rrset_count;
734 735 736
	ret = knot_pkt_put(pkt, compr_hint, &to_add, flags);
	if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) {
		knot_rrset_clear(&to_add, &pkt->mm);
737 738 739
		return ret;
	}

740
	const bool inserted = (prev_count != pkt->rrset_count);
741 742
	if (inserted &&
	    !knot_rrset_empty(rrsigs) && rr->type != KNOT_RRTYPE_RRSIG) {
743
		// Get rrinfo of just inserted RR.
744 745
		knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count - 1];
		ret = put_rrsig(rr->owner, rr->type, rrsigs, rrinfo, qdata);
746 747
	}

748
	return ret;
749 750
}

751
/*! \brief Helper for internet_query repetitive code. */
752 753
#define SOLVE_STEP(solver, state, context) \
	state = (solver)(state, response, qdata, context); \
754
	if (state == TRUNC) { \
755
		return KNOT_NS_PROC_DONE; \
756
	} else if (state == ERROR) { \
757
		return KNOT_NS_PROC_FAIL; \
758 759
	}

760 761 762 763 764 765
static int default_answer(knot_pkt_t *response, struct query_data *qdata)
{
	int state = BEGIN;

	/* Resolve ANSWER. */
	dbg_ns("%s: writing %p ANSWER\n", __func__, response);
766
	knot_pkt_begin(response, KNOT_ANSWER);
767
	SOLVE_STEP(solve_answer, state, NULL);
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
	SOLVE_STEP(solve_answer_dnssec, state, NULL);

	/* Resolve AUTHORITY. */
	dbg_ns("%s: writing %p AUTHORITY\n", __func__, response);
	knot_pkt_begin(response, KNOT_AUTHORITY);
	SOLVE_STEP(solve_authority, state, NULL);
	SOLVE_STEP(solve_authority_dnssec, state, NULL);

	/* Resolve ADDITIONAL. */
	dbg_ns("%s: writing %p ADDITIONAL\n", __func__, response);
	knot_pkt_begin(response, KNOT_ADDITIONAL);
	SOLVE_STEP(solve_additional, state, NULL);
	SOLVE_STEP(solve_additional_dnssec, state, NULL);

	/* Write resulting RCODE. */
	knot_wire_set_rcode(response->wire, qdata->rcode);

785
	return KNOT_NS_PROC_DONE;
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
}

static int planned_answer(struct query_plan *plan, knot_pkt_t *response, struct query_data *qdata)
{
	/* Before query processing code. */
	int state = BEGIN;
	struct query_step *step = NULL;
	WALK_LIST(step, plan->stage[QPLAN_BEGIN]) {
		SOLVE_STEP(step->process, state, step->ctx);
	}

	/* Begin processing. */
	for (int section = KNOT_ANSWER; section <= KNOT_ADDITIONAL; ++section) {
		dbg_ns("%s: writing section %u\n", __func__, section);
		knot_pkt_begin(response, section);
		WALK_LIST(step, plan->stage[QPLAN_STAGE + section]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}

	/* After query processing code. */
	WALK_LIST(step, plan->stage[QPLAN_END]) {
		SOLVE_STEP(step->process, state, step->ctx);
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
811 812 813
	/* Write resulting RCODE. */
	knot_wire_set_rcode(response->wire, qdata->rcode);

814
	return KNOT_NS_PROC_DONE;
815 816 817 818
}

#undef SOLVE_STEP

819
int internet_query(knot_pkt_t *response, struct query_data *qdata)
820 821 822
{
	dbg_ns("%s(%p, %p)\n", __func__, response, qdata);
	if (response == NULL || qdata == NULL) {
823
		return KNOT_NS_PROC_FAIL;
824 825
	}

826 827 828
	/* Check valid zone, transaction security (optional) and contents. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED);

829
	/* No applicable ACL, refuse transaction security. */
Lubos Slovak's avatar
Lubos Slovak committed
830
	if (knot_pkt_has_tsig(qdata->query)) {
831
		/* We have been challenged... */
832
		NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata);
833 834

		/* Reserve space for TSIG. */
835
		knot_pkt_reserve(response, knot_tsig_wire_maxsize(qdata->sign.tsig_key));
836
	}
837

838
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Expired */
839

840 841
	/* Get answer to QNAME. */
	qdata->name = knot_pkt_qname(qdata->query);
842

843 844 845 846 847
	/* If the zone doesn't have a query plan, go for fast default. */
	conf_zone_t *zone_config = qdata->zone->conf;
	if (zone_config->query_plan == NULL) {
		return default_answer(response, qdata);
	}
848

849 850
	return planned_answer(zone_config->query_plan, response, qdata);
}
851

852 853
int internet_query_plan(struct query_plan *plan)
{
854
	query_plan_step(plan, QPLAN_ANSWER, solve_answer, NULL);
855 856 857 858 859
	query_plan_step(plan, QPLAN_ANSWER, solve_answer_dnssec, NULL);
	query_plan_step(plan, QPLAN_AUTHORITY, solve_authority, NULL);
	query_plan_step(plan, QPLAN_AUTHORITY, solve_authority_dnssec, NULL);
	query_plan_step(plan, QPLAN_ADDITIONAL, solve_additional, NULL);
	query_plan_step(plan, QPLAN_ADDITIONAL, solve_additional_dnssec, NULL);
860

861
	return KNOT_EOK;
862
}
863 864

/*! \brief Process answer to SOA query. */
Marek Vavruša's avatar
Marek Vavruša committed
865
static int process_soa_answer(knot_pkt_t *pkt, struct answer_data *data)
866
{
867
	zone_t *zone = data->param->zone;
868 869 870

	/* Expect SOA in answer section. */
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
871 872
	const knot_rrset_t *first_rr = knot_pkt_rr(answer, 0);
	if (answer->count < 1 || first_rr->type != KNOT_RRTYPE_SOA) {
873
		return KNOT_NS_PROC_FAIL;
874 875
	}

876 877 878
	/* Our zone is expired, schedule transfer. */
	if (zone_contents_is_empty(zone->contents)) {
		zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW);
879
		return KNOT_NS_PROC_DONE;
880 881
	}

882 883 884
	/* Check if master has newer zone and schedule transfer. */
	knot_rdataset_t *soa = node_rdataset(zone->contents->apex, KNOT_RRTYPE_SOA);
	uint32_t our_serial = knot_soa_serial(soa);
885
	uint32_t their_serial =	knot_soa_serial(&first_rr->rrs);
886
	if (serial_compare(our_serial, their_serial) >= 0) {
887
		ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "zone is up-to-date");
888
		zone_events_cancel(zone, ZONE_EVENT_EXPIRE);
889
		return KNOT_NS_PROC_DONE; /* Our zone is up to date. */
890 891 892
	}

	/* Our zone is outdated, schedule zone transfer. */
893
	ANSWER_LOG(LOG_INFO, data, "refresh, outgoing", "master has newer serial %u -> %u",
894
	           our_serial, their_serial);
895
	zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW);
896
	return KNOT_NS_PROC_DONE;
897 898
}

899
int internet_process_answer(knot_pkt_t *pkt, struct answer_data *data)
900 901
{
	if (pkt == NULL || data == NULL) {
902
		return KNOT_NS_PROC_FAIL;
903 904
	}

905 906
	NS_NEED_TSIG_SIGNED(&data->param->tsig_ctx, 0);

907 908 909
	/* As of now, server can only issue SOA queries. */
	switch(knot_pkt_qtype(pkt)) {
	case KNOT_RRTYPE_SOA:
Marek Vavruša's avatar
Marek Vavruša committed
910
		return process_soa_answer(pkt, data);
911
	default:
912
		return KNOT_NS_PROC_NOOP;
913 914
	}
}