internet.c 26.3 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 "libknot/libknot.h"
18
#include "libknot/descriptor.h"
19 20
#include "libknot/rrtype/rdname.h"
#include "libknot/rrtype/soa.h"
21
#include "knot/common/log.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/query/query.h"
Daniel Salzman's avatar
Daniel Salzman committed
27
#include "knot/nameserver/query_module.h"
28
#include "knot/zone/serial.h"
29
#include "knot/zone/zonedb.h"
Daniel Salzman's avatar
Daniel Salzman committed
30
#include "contrib/mempattern.h"
31
#include "contrib/sockaddr.h"
32

33 34 35 36 37 38
/*! \brief Kind of additional record. */
enum additional_kind {
	ADDITIONAL_OPTIONAL = 0,
	ADDITIONAL_MANDATORY,
};

39
/*! \brief Check if given node was already visited. */
40
static int wildcard_has_visited(struct query_data *qdata, const zone_node_t *node)
41 42 43 44 45 46 47 48 49 50 51
{
	struct wildcard_hit *item = NULL;
	WALK_LIST(item, qdata->wildcards) {
		if (item->node == node) {
			return true;
		}
	}
	return false;
}

/*! \brief Mark given node as visited. */
52 53
static int wildcard_visit(struct query_data *qdata, const zone_node_t *node,
                          const zone_node_t *prev, const knot_dname_t *sname)
54 55 56 57
{
	assert(qdata);
	assert(node);

58 59 60 61 62
	/* Already in the list. */
	if (wildcard_has_visited(qdata, node)) {
		return KNOT_EOK;
	}

63
	knot_mm_t *mm = qdata->mm;
64
	struct wildcard_hit *item = mm_alloc(mm, sizeof(struct wildcard_hit));
65
	item->node = node;
66
	item->prev = prev;
67 68 69 70 71
	item->sname = sname;
	add_tail(&qdata->wildcards, (node_t *)item);
	return KNOT_EOK;
}

72
/*! \brief Synthetizes a CNAME RR from a DNAME. */
73 74 75
static int dname_cname_synth(const knot_rrset_t *dname_rr,
                             const knot_dname_t *qname,
                             knot_rrset_t *cname_rrset,
76
                             knot_mm_t *mm)
77 78
{
	if (cname_rrset == NULL) {
79
		return KNOT_EINVAL;
80
	}
81 82
	knot_dname_t *owner_copy = knot_dname_copy(qname, mm);
	if (owner_copy == NULL) {
83
		return KNOT_ENOMEM;
84
	}
85
	knot_rrset_init(cname_rrset, owner_copy, KNOT_RRTYPE_CNAME, dname_rr->rclass);
86 87

	/* Replace last labels of qname with DNAME. */
88
	const knot_dname_t *dname_wire = dname_rr->owner;
89
	const knot_dname_t *dname_tgt = knot_dname_target(&dname_rr->rrs);
90 91 92
	int labels = knot_dname_labels(dname_wire, NULL);
	knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt);
	if (cname == NULL) {
93
		knot_dname_free(&owner_copy, mm);
94
		return KNOT_ENOMEM;
95 96 97 98
	}

	/* Store DNAME into RDATA. */
	int cname_size = knot_dname_size(cname);
99
	uint8_t cname_rdata[cname_size];
100
	memcpy(cname_rdata, cname, cname_size);
101
	knot_dname_free(&cname, NULL);
102

103
	const knot_rdata_t *dname_data = knot_rdataset_at(&dname_rr->rrs, 0);
104
	int ret = knot_rrset_add_rdata(cname_rrset, cname_rdata, cname_size,
105
	                               knot_rdata_ttl(dname_data), mm);
106 107 108 109 110 111
	if (ret != KNOT_EOK) {
		knot_dname_free(&owner_copy, mm);
		return ret;
	}

	return KNOT_EOK;
112 113 114 115 116 117
}

/*!
 * \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.
 */
118
static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname_t *qname)
119 120
{
	if (knot_dname_labels(qname, NULL)
121
		- knot_dname_labels(rrset->owner, NULL)
122
		+ knot_dname_labels(knot_dname_target(&rrset->rrs), NULL)
123
		> KNOT_DNAME_MAXLABELS) {
124 125 126 127 128 129
		return true;
	} else {
		return false;
	}
}

130
/*! \brief DNSSEC both requested & available. */
131
static bool have_dnssec(struct query_data *qdata)
132
{
133
	return knot_pkt_has_dnssec(qdata->query) &&
134
	       zone_contents_is_signed(qdata->zone->contents);
135 136
}

137 138
/*! \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,
139 140
                     const knot_rrset_t *rrsigs,
                     knot_rrinfo_t *rrinfo,
141 142
                     struct query_data *qdata)
{
143 144
	knot_rdataset_t synth_rrs;
	knot_rdataset_init(&synth_rrs);
145
	int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrs, qdata->mm);
146 147 148 149 150 151 152
	if (ret == KNOT_ENOENT) {
		// No signature
		return KNOT_EOK;
	}
	if (ret != KNOT_EOK) {
		return ret;
	}
153 154

	/* Create rrsig info structure. */
155
	struct rrsig_info *info = mm_alloc(qdata->mm, sizeof(struct rrsig_info));
156
	if (info == NULL) {
157
		knot_rdataset_clear(&synth_rrs, qdata->mm);
158 159
		return KNOT_ENOMEM;
	}
160 161 162 163

	/* Store RRSIG into info structure. */
	knot_dname_t *owner_copy = knot_dname_copy(sig_owner, qdata->mm);
	if (owner_copy == NULL) {
164
		mm_free(qdata->mm, info);
165
		knot_rdataset_clear(&synth_rrs, qdata->mm);
166 167
		return KNOT_ENOMEM;
	}
168 169 170 171
	knot_rrset_init(&info->synth_rrsig, owner_copy, rrsigs->type, rrsigs->rclass);
	/* Store filtered signature. */
	info->synth_rrsig.rrs = synth_rrs;

172
	info->rrinfo = rrinfo;
173 174 175 176 177
	add_tail(&qdata->rrsigs, &info->n);

	return KNOT_EOK;
}

178 179 180
/*! \brief This is a wildcard-covered or any other terminal node for QNAME.
 *         e.g. positive answer.
 */
181
static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
182
{
183 184
	knot_rrset_t rrset;
	knot_rrset_init_empty(&rrset);
185

186 187 188 189 190
	/* 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.
	 */
191
	uint16_t compr_hint = KNOT_COMPR_HINT_NONE;
192
	if (pkt->rrset_count == 0) { /* Guaranteed first answer. */
193
		compr_hint = KNOT_COMPR_HINT_QNAME;
194 195
	}

196 197
	int ret = KNOT_EOK;
	switch (type) {
198
	case KNOT_RRTYPE_ANY: /* Append all RRSets. */ {
Daniel Salzman's avatar
Daniel Salzman committed
199 200
		conf_val_t val = conf_zone_get(conf(), C_DISABLE_ANY,
		                               qdata->zone->name);
201
		/* If ANY not allowed, set TC bit. */
202
		if ((qdata->param->proc_flags & NS_QUERY_LIMIT_ANY) &&
Daniel Salzman's avatar
Daniel Salzman committed
203
		    conf_bool(&val)) {
204
			knot_wire_set_tc(pkt->wire);
205
			return KNOT_ESPACE;
206
		}
207
		for (unsigned i = 0; i < qdata->node->rrset_count; ++i) {
208
			rrset = node_rrset_at(qdata->node, i);
209
			ret = ns_put_rr(pkt, &rrset, NULL, compr_hint, 0, qdata);
210 211 212 213 214
			if (ret != KNOT_EOK) {
				break;
			}
		}
		break;
215
	}
216
	default: /* Single RRSet of given type. */
217
		rrset = node_rrset(qdata->node, type);
218
		if (!knot_rrset_empty(&rrset)) {
219
			knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
220
			ret = ns_put_rr(pkt, &rrset, &rrsigs, compr_hint, 0, qdata);
221 222 223 224 225 226 227
		}
		break;
	}

	return ret;
}

228
/*! \brief Puts optional SOA RRSet to the Authority section of the response. */
229
static int put_authority_soa(knot_pkt_t *pkt, struct query_data *qdata,
230
                             const zone_contents_t *zone)
231
{
232 233
	knot_rrset_t soa_rrset = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
	knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
234 235 236 237

	// if SOA's TTL is larger than MINIMUM, copy the RRSet and set
	// MINIMUM as TTL
	int ret = KNOT_EOK;
238
	uint32_t flags = KNOT_PF_NOTRUNC;
239
	uint32_t min = knot_soa_minimum(&soa_rrset.rrs);
240 241
	const knot_rdata_t *soa_data = knot_rdataset_at(&soa_rrset.rrs, 0);
	if (min < knot_rdata_ttl(soa_data)) {
242
		knot_rrset_t copy;
243
		knot_dname_t *dname_cpy = knot_dname_copy(soa_rrset.owner, &pkt->mm);
244 245 246
		if (dname_cpy == NULL) {
			return KNOT_ENOMEM;
		}
247
		knot_rrset_init(&copy, dname_cpy, soa_rrset.type, soa_rrset.rclass);
248
		int ret = knot_rdataset_copy(&copy.rrs, &soa_rrset.rrs, &pkt->mm);
249
		if (ret != KNOT_EOK) {
250
			knot_dname_free(&dname_cpy, &pkt->mm);
251 252
			return ret;
		}
253 254
		knot_rdata_t *copy_data = knot_rdataset_at(&copy.rrs, 0);
		knot_rdata_set_ttl(copy_data, min);
255 256

		flags |= KNOT_PF_FREE;
257
		soa_rrset = copy;
258 259
	}

260
	ret = ns_put_rr(pkt, &soa_rrset, &rrsigs, KNOT_COMPR_HINT_NONE, flags, qdata);
261
	if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) {
262
		knot_rrset_clear(&soa_rrset, &pkt->mm);
263 264 265
	}

	return ret;
266 267 268 269 270 271
}

/*! \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. */
272
	while (!(qdata->node->flags & NODE_FLAGS_DELEG)) {
273
		qdata->node = qdata->node->parent;
274 275 276
	}

	/* Insert NS record. */
277 278
	knot_rrset_t rrset = node_rrset(qdata->node, KNOT_RRTYPE_NS);
	knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
279
	return ns_put_rr(pkt, &rrset, &rrsigs, KNOT_COMPR_HINT_NONE, 0, qdata);
280 281
}

282
/*! \brief Put additional records for given RR. */
283
static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
284
                          struct query_data *qdata, knot_rrinfo_t *info,
285
                          int state, enum additional_kind kind)
286
{
287
	/* Valid types for ADDITIONALS insertion. */
288
	/* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */
289
	static const uint16_t ar_type_list[] = {KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA};
290
	static const int ar_type_count = 2;
291 292

	int ret = KNOT_EOK;
293

294
	/* All RRs should have additional node cached or NULL. */
295
	for (uint16_t i = 0; i < rr->rrs.rr_count; i++) {
296
		const zone_node_t *node = rr->additional[i];
297
		if (node == NULL) {
298
			continue;
299
		}
Jan Včelák's avatar
Jan Včelák committed
300

301 302 303 304 305 306 307 308 309 310
		bool is_notauth = (node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH));
		bool is_glue = is_notauth &&
		               state == DELEG && rr->type == KNOT_RRTYPE_NS &&
		               knot_dname_in(rr->owner, node->owner);

		/* Non-authoritative node allowed only as a glue. */
		if (is_notauth && !is_glue) {
			continue;
		}

311
		/* Glue is required as per RFC 1034 Section 4.3.2 step 3b. */
312 313 314
		if (kind != (is_glue ? ADDITIONAL_MANDATORY : ADDITIONAL_OPTIONAL)) {
			continue;
		}
315

316
		uint32_t flags = KNOT_PF_CHECKDUP | (is_glue ? 0 : KNOT_PF_NOTRUNC);
317
		uint16_t hint = knot_pkt_compr_hint(info, KNOT_COMPR_HINT_RDATA + i);
318
		knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
319
		for (int k = 0; k < ar_type_count; ++k) {
320
			knot_rrset_t additional = node_rrset(node, ar_type_list[k]);
321
			if (knot_rrset_empty(&additional)) {
322 323
				continue;
			}
324
			ret = ns_put_rr(pkt, &additional, &rrsigs, hint, flags, qdata);
325
			if (ret != KNOT_EOK) {
326
				break;
327 328 329 330 331
			}
		}
	}

	return ret;
332 333
}

334
static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, struct query_data *qdata)
335
{
336 337 338
	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);
339 340
	int ret = KNOT_EOK;

341
	assert(!knot_rrset_empty(&cname_rr));
342

343 344
	/* Check whether RR is already in the packet. */
	uint16_t flags = KNOT_PF_CHECKDUP;
345 346 347

	/* Now, try to put CNAME to answer. */
	uint16_t rr_count_before = pkt->rrset_count;
348
	ret = ns_put_rr(pkt, &cname_rr, &rrsigs, 0, flags, qdata);
349 350 351 352 353 354 355 356 357 358
	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) {
		qdata->node = NULL; /* Act is if the name leads to nowhere. */
		return HIT;
359 360
	}

361 362
	/* Synthesize CNAME if followed DNAME. */
	if (rrtype == KNOT_RRTYPE_DNAME) {
363
		if (dname_cname_cannot_synth(&cname_rr, qdata->name)) {
364 365 366
			qdata->rcode = KNOT_RCODE_YXDOMAIN;
			return ERROR;
		}
367 368 369 370 371 372 373 374
		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);
375 376 377 378
		switch (ret) {
		case KNOT_EOK:    break;
		case KNOT_ESPACE: return TRUNC;
		default:          return ERROR;
379
		}
380 381
	}

382 383 384 385 386 387
	/* 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). */
		if (wildcard_has_visited(qdata, cname_node)) {
388
			qdata->node = NULL; /* Act is if the name leads to nowhere. */
389 390 391 392
			return HIT;
		}

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

398
	/* Now follow the next CNAME TARGET. */
399
	qdata->name = knot_cname_name(&cname_rr.rrs);
400 401 402 403

	return FOLLOW;
}

404
static int name_found(knot_pkt_t *pkt, struct query_data *qdata)
405 406 407
{
	uint16_t qtype = knot_pkt_qtype(pkt);

408
	if (node_rrtype_exists(qdata->node, KNOT_RRTYPE_CNAME)
409
	    && qtype != KNOT_RRTYPE_CNAME
410
	    && qtype != KNOT_RRTYPE_RRSIG
411
	    && qtype != KNOT_RRTYPE_NSEC
412
	    && qtype != KNOT_RRTYPE_ANY) {
413
		return follow_cname(pkt, KNOT_RRTYPE_CNAME, qdata);
414 415
	}

416 417 418
	/* DS query is answered normally, but everything else at/below DP
	 * triggers referral response. */
	if (qtype != KNOT_RRTYPE_DS &&
419
	    ((qdata->node->flags & NODE_FLAGS_DELEG) || qdata->node->flags & NODE_FLAGS_NONAUTH)) {
420 421 422
		return DELEG;
	}

423
	uint16_t old_rrcount = pkt->rrset_count;
424
	int ret = put_answer(pkt, qtype, qdata);
425
	if (ret != KNOT_EOK) {
426 427 428 429 430
		if (ret == KNOT_ESPACE) {
			return TRUNC;
		} else {
			return ERROR;
		}
431 432
	}

433 434
	/* Check for NODATA (=0 RRs added). */
	if (old_rrcount == pkt->rrset_count) {
435 436 437 438 439 440
		return NODATA;
	} else {
		return HIT;
	}
}

441
static int name_not_found(knot_pkt_t *pkt, struct query_data *qdata)
442 443
{
	/* Name is covered by wildcard. */
444
	if (qdata->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) {
445
		/* Find wildcard child in the zone. */
446
		const zone_node_t *wildcard_node =
447
		                zone_contents_find_wildcard_child(
448
		                        qdata->zone->contents, qdata->encloser);
Lubos Slovak's avatar
Lubos Slovak committed
449

450
		qdata->node = wildcard_node;
451 452
		assert(qdata->node != NULL);

453 454 455
		/* Follow expanded wildcard. */
		int next_state = name_found(pkt, qdata);

456
		/* Put to wildcard node list. */
457
		if (wildcard_visit(qdata, wildcard_node, qdata->previous, qdata->name) != KNOT_EOK) {
458
			next_state = ERROR;
459 460
		}

461
		return next_state;
462 463 464
	}

	/* Name is under DNAME, use it for substitution. */
465
	knot_rrset_t dname_rrset = node_rrset(qdata->encloser, KNOT_RRTYPE_DNAME);
466
	if (!knot_rrset_empty(&dname_rrset)) {
467 468
		qdata->node = qdata->encloser; /* Follow encloser as new node. */
		return follow_cname(pkt, KNOT_RRTYPE_DNAME, qdata);
469 470
	}

471
	/* Name is below delegation. */
472
	if ((qdata->encloser->flags & NODE_FLAGS_DELEG)) {
473 474 475 476
		qdata->node = qdata->encloser;
		return DELEG;
	}

477 478 479
	return MISS;
}

480
static int solve_name(int state, knot_pkt_t *pkt, struct query_data *qdata)
481
{
482
	int ret = zone_contents_find_dname(qdata->zone->contents, qdata->name,
483 484 485 486
	                                        &qdata->node, &qdata->encloser,
	                                        &qdata->previous);

	switch(ret) {
487
	case ZONE_NAME_FOUND:
488
		return name_found(pkt, qdata);
489
	case ZONE_NAME_NOT_FOUND:
490
		return name_not_found(pkt, qdata);
491 492 493 494 495 496 497 498
	case KNOT_EOUTOFZONE:
		assert(state == FOLLOW); /* CNAME/DNAME chain only. */
		return HIT;
	default:
		return ERROR;
	}
}

Marek Vavruša's avatar
Marek Vavruša committed
499
static int solve_answer(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
500 501
{
	/* Get answer to QNAME. */
502
	state = solve_name(state, pkt, qdata);
503 504 505 506 507 508 509 510 511

	/* 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) {
512
		state = solve_name(state, pkt, qdata);
513 514 515 516 517
	}

	return state;
}

518
static int solve_answer_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
519
{
520
	if (!have_dnssec(qdata)) {
Marek Vavrusa's avatar
Marek Vavrusa committed
521 522 523
		return state; /* DNSSEC not supported. */
	}

524
	/* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */
525
	int ret = nsec_append_rrsigs(pkt, qdata, false);
526 527 528 529
	switch(ret) {
	case KNOT_ESPACE: return TRUNC;
	case KNOT_EOK:    return state;
	default:          return ERROR;
530 531 532
	}
}

533
static int solve_authority(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
534 535
{
	int ret = KNOT_ERROR;
536
	const zone_contents_t *zone_contents = qdata->zone->contents;
537 538

	switch (state) {
539 540
	case HIT:    /* Positive response. */
		ret = KNOT_EOK;
541 542
		break;
	case MISS:   /* MISS, set NXDOMAIN RCODE. */
543
		qdata->rcode = KNOT_RCODE_NXDOMAIN;
544
		ret = put_authority_soa(pkt, qdata, zone_contents);
545
		break;
546
	case NODATA: /* NODATA append AUTHORITY SOA. */
547
		ret = put_authority_soa(pkt, qdata, zone_contents);
548
		break;
549
	case DELEG:  /* Referral response. */
550
		ret = put_delegation(pkt, qdata);
551
		break;
552 553 554 555
	case TRUNC:  /* Truncated ANSWER. */
		ret = KNOT_ESPACE;
		break;
	case ERROR:  /* Error resolving ANSWER. */
556 557 558 559 560
		break;
	default:
		assert(0);
		break;
	}
561

562 563 564 565 566 567 568 569
	/* Evaluate final state. */
	switch (ret) {
	case KNOT_EOK:    return state; /* Keep current state. */
	case KNOT_ESPACE: return TRUNC; /* Truncated. */
	default:          return ERROR; /* Error. */
	}
}

570
static int solve_authority_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
571
{
572
	if (!have_dnssec(qdata)) {
Marek Vavrusa's avatar
Marek Vavrusa committed
573 574
		return state; /* DNSSEC not supported. */
	}
575

576 577
	int ret = KNOT_ERROR;

578
	/* Authenticated denial of existence. */
579
	switch (state) {
580
	case HIT:    ret = KNOT_EOK; break;
581 582 583 584
	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;
585
	case ERROR:  ret = KNOT_ERROR; break;
586 587 588 589
	default:
		assert(0);
		break;
	}
590

591 592
	/* RFC4035 3.1.3 Prove visited wildcards.
	 * Wildcard expansion applies for Name Error, Wildcard Answer and
593 594 595
	 * 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) {
596 597 598
		ret = nsec_prove_wildcards(pkt, qdata);
	}

599 600
	/* RFC4035, section 3.1 RRSIGs for RRs in AUTHORITY are mandatory. */
	if (ret == KNOT_EOK) {
601
		ret = nsec_append_rrsigs(pkt, qdata, false);
602 603 604 605 606 607 608 609 610 611
	}

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

612 613
static int solve_additional_kind(int state, knot_pkt_t *pkt, struct query_data *qdata,
                                 enum additional_kind kind)
614
{
Lubos Slovak's avatar
Lubos Slovak committed
615
	int ret = KNOT_EOK;
616

617 618 619 620 621
	/* Only glue can be mandatory. */
	if (kind == ADDITIONAL_MANDATORY && state != DELEG) {
		return ret;
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
622
	/* Scan all RRs in ANSWER/AUTHORITY. */
623
	for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
624 625 626
		knot_rrset_t *rr = &pkt->rr[i];
		knot_rrinfo_t *info = &pkt->rr_info[i];

627
		/* Skip types for which it doesn't apply. */
628
		if (!knot_rrtype_additional_needed(pkt->rr[i].type)) {
629 630
			continue;
		}
631

632
		/* Put additional records for given type. */
633
		ret = put_additional(pkt, rr, qdata, info, state, kind);
634 635 636 637
		if (ret != KNOT_EOK) {
			break;
		}
	}
638

639 640 641 642 643 644 645 646 647 648 649 650 651 652
	return ret;
}

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

	/* First mandatory, then optional. */
	ret = solve_additional_kind(state, pkt, qdata, ADDITIONAL_MANDATORY);
	if (ret == KNOT_EOK) {
		ret = solve_additional_kind(state, pkt, qdata, ADDITIONAL_OPTIONAL);
	}

653 654 655 656 657 658
	/* Evaluate final state. */
	switch (ret) {
	case KNOT_EOK:    return state; /* Keep current state. */
	case KNOT_ESPACE: return TRUNC; /* Truncated. */
	default:          return ERROR; /* Error. */
	}
659 660
}

661
static int solve_additional_dnssec(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
662
{
663
	if (!have_dnssec(qdata)) {
Marek Vavrusa's avatar
Marek Vavrusa committed
664 665 666
		return state; /* DNSSEC not supported. */
	}

667
	/* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */
668
	int ret = nsec_append_rrsigs(pkt, qdata, true);
669 670 671 672 673 674 675
	switch(ret) {
	case KNOT_ESPACE: return TRUNC;
	case KNOT_EOK:    return state;
	default:          return ERROR;
	}
}

676 677
int ns_put_rr(knot_pkt_t *pkt, const knot_rrset_t *rr,
              const knot_rrset_t *rrsigs, uint16_t compr_hint,
678
              uint32_t flags, struct query_data *qdata)
679
{
680
	if (rr->rrs.rr_count < 1) {
681 682 683
		return KNOT_EMALF;
	}

684 685 686 687 688 689 690
	/* 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));
	}

691 692 693 694
	/* 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;
695
	knot_rrset_t to_add;
696
	if (compr_hint == KNOT_COMPR_HINT_NONE && expand) {
697
		knot_dname_t *qname_cpy = knot_dname_copy(qdata->name, &pkt->mm);
698
		if (qname_cpy == NULL) {
699 700
			return KNOT_ENOMEM;
		}
701
		knot_rrset_init(&to_add, qname_cpy, rr->type, rr->rclass);
702
		int ret = knot_rdataset_copy(&to_add.rrs, &rr->rrs, &pkt->mm);
703 704 705
		if (ret != KNOT_EOK) {
			knot_dname_free(&qname_cpy, &pkt->mm);
		}
706
		to_add.additional = rr->additional;
707
		flags |= KNOT_PF_FREE;
708 709
	} else {
		to_add = *rr;
710 711 712
	}

	uint16_t prev_count = pkt->rrset_count;
713 714 715
	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);
716 717 718
		return ret;
	}

719
	const bool inserted = (prev_count != pkt->rrset_count);
720 721
	if (inserted &&
	    !knot_rrset_empty(rrsigs) && rr->type != KNOT_RRTYPE_RRSIG) {
722
		// Get rrinfo of just inserted RR.
723 724
		knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count - 1];
		ret = put_rrsig(rr->owner, rr->type, rrsigs, rrinfo, qdata);
725 726
	}

727
	return ret;
728 729
}

730
/*! \brief Helper for internet_query repetitive code. */
731 732
#define SOLVE_STEP(solver, state, context) \
	state = (solver)(state, response, qdata, context); \
733
	if (state == TRUNC) { \
734
		return KNOT_STATE_DONE; \
735
	} else if (state == ERROR) { \
736
		return KNOT_STATE_FAIL; \
737 738
	}

739
static int answer_query(struct query_plan *plan, knot_pkt_t *response, struct query_data *qdata)
740 741
{
	int state = BEGIN;
742 743
	struct query_plan *global_plan = conf()->query_plan;
	struct query_step *step = NULL;
744

745 746 747 748 749 750 751
	/* Before query processing code. */
	if (plan != NULL) {
		WALK_LIST(step, plan->stage[QPLAN_BEGIN]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}

752
	/* Resolve ANSWER. */
753
	knot_pkt_begin(response, KNOT_ANSWER);
754 755 756 757 758
	if (global_plan != NULL) {
		WALK_LIST(step, global_plan->stage[QPLAN_ANSWER]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}
Marek Vavruša's avatar
Marek Vavruša committed
759
	SOLVE_STEP(solve_answer, state, NULL);
760
	SOLVE_STEP(solve_answer_dnssec, state, NULL);
761 762 763 764 765
	if (plan != NULL) {
		WALK_LIST(step, plan->stage[QPLAN_ANSWER]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}
766 767 768

	/* Resolve AUTHORITY. */
	knot_pkt_begin(response, KNOT_AUTHORITY);
769 770 771 772 773
	if (global_plan != NULL) {
		WALK_LIST(step, global_plan->stage[QPLAN_AUTHORITY]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}
774 775
	SOLVE_STEP(solve_authority, state, NULL);
	SOLVE_STEP(solve_authority_dnssec, state, NULL);
776 777 778 779 780
	if (plan != NULL) {
		WALK_LIST(step, plan->stage[QPLAN_AUTHORITY]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}
781 782 783

	/* Resolve ADDITIONAL. */
	knot_pkt_begin(response, KNOT_ADDITIONAL);
784 785 786 787 788
	if (global_plan != NULL) {
		WALK_LIST(step, global_plan->stage[QPLAN_ADDITIONAL]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}
789 790
	SOLVE_STEP(solve_additional, state, NULL);
	SOLVE_STEP(solve_additional_dnssec, state, NULL);
791 792
	if (plan != NULL) {
		WALK_LIST(step, plan->stage[QPLAN_ADDITIONAL]) {
793 794 795 796 797
			SOLVE_STEP(step->process, state, step->ctx);
		}
	}

	/* After query processing code. */
798 799 800 801
	if (plan != NULL) {
		WALK_LIST(step, plan->stage[QPLAN_END]) {
			SOLVE_STEP(step->process, state, step->ctx);
		}
802 803
	}

Marek Vavrusa's avatar
Marek Vavrusa committed
804 805 806
	/* Write resulting RCODE. */
	knot_wire_set_rcode(response->wire, qdata->rcode);

807
	return KNOT_STATE_DONE;
808 809 810 811
}

#undef SOLVE_STEP

812
int internet_process_query(knot_pkt_t *response, struct query_data *qdata)
813 814
{
	if (response == NULL || qdata == NULL) {
815
		return KNOT_STATE_FAIL;
816 817
	}

818 819 820
	/* Check valid zone, transaction security (optional) and contents. */
	NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED);

821
	/* No applicable ACL, refuse transaction security. */
Lubos Slovak's avatar
Lubos Slovak committed
822
	if (knot_pkt_has_tsig(qdata->query)) {
823
		/* We have been challenged... */
Daniel Salzman's avatar
Daniel Salzman committed
824
		NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_NONE);
825 826

		/* Reserve space for TSIG. */
Daniel Salzman's avatar
Daniel Salzman committed
827
		knot_pkt_reserve(response, knot_tsig_wire_maxsize(&qdata->sign.tsig_key));
828
	}
829

830
	NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Expired */
831

Marek Vavrusa's avatar
Marek Vavrusa committed
832 833
	/* Get answer to QNAME. */
	qdata->name = knot_pkt_qname(qdata->query);
834

835
	return answer_query(qdata->zone->query_plan, response, qdata);
836
}
837

838 839 840 841 842
#include "knot/nameserver/log.h"
#define REFRESH_LOG(priority, msg, ...) \
	NS_PROC_LOG(priority, (data)->param->zone->name, (data)->param->remote, \
	            "refresh, outgoing", msg, ##__VA_ARGS__)

843
/*! \brief Process answer to SOA query. */
Marek Vavruša's avatar
cleanup  
Marek Vavruša committed
844
static int process_soa_answer(knot_pkt_t *pkt, struct answer_data *data)
845
{
846
	zone_t *zone = data->param->zone;
847

848 849 850 851 852
	/* Check RCODE. */
	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
	if (rcode != KNOT_RCODE_NOERROR) {
		const knot_lookup_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
		if (lut != NULL) {
853
			REFRESH_LOG(LOG_WARNING, "server responded with %s", lut->name);
854 855 856 857
		}
		return KNOT_STATE_FAIL;
	}

858 859
	/* Expect SOA in answer section. */
	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
860 861
	const knot_rrset_t *first_rr = knot_pkt_rr(answer, 0);
	if (answer->count < 1 || first_rr->type != KNOT_RRTYPE_SOA) {
862
		REFRESH_LOG(LOG_WARNING, "malformed message");