process_query.c 25 KB
Newer Older
1
/*  Copyright (C) 2018 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
#include "libdnssec/tsig.h"
20
#include "knot/common/log.h"
21
#include "knot/dnssec/rrset-sign.h"
22
#include "knot/nameserver/process_query.h"
23
#include "knot/nameserver/query_module.h"
24 25 26 27 28
#include "knot/nameserver/chaos.h"
#include "knot/nameserver/internet.h"
#include "knot/nameserver/axfr.h"
#include "knot/nameserver/ixfr.h"
#include "knot/nameserver/update.h"
29
#include "knot/nameserver/nsec_proofs.h"
30
#include "knot/nameserver/notify.h"
31
#include "knot/server/server.h"
Daniel Salzman's avatar
Daniel Salzman committed
32
#include "libknot/libknot.h"
33
#include "contrib/macros.h"
Daniel Salzman's avatar
Daniel Salzman committed
34
#include "contrib/mempattern.h"
35

36
/*! \brief Accessor to query-specific data. */
37
#define QUERY_DATA(ctx) ((knotd_qdata_t *)(ctx)->data)
38

39
static knotd_query_type_t query_type(const knot_pkt_t *pkt)
40
{
41
	switch (knot_wire_get_opcode(pkt->wire)) {
42
	case KNOT_OPCODE_QUERY:
43 44 45 46 47
		switch (knot_pkt_qtype(pkt)) {
		case 0 /* RESERVED */: return KNOTD_QUERY_TYPE_INVALID;
		case KNOT_RRTYPE_AXFR: return KNOTD_QUERY_TYPE_AXFR;
		case KNOT_RRTYPE_IXFR: return KNOTD_QUERY_TYPE_IXFR;
		default:               return KNOTD_QUERY_TYPE_NORMAL;
48
		}
49 50 51
	case KNOT_OPCODE_NOTIFY: return KNOTD_QUERY_TYPE_NOTIFY;
	case KNOT_OPCODE_UPDATE: return KNOTD_QUERY_TYPE_UPDATE;
	default:                 return KNOTD_QUERY_TYPE_INVALID;
52 53 54
	}
}

55
/*! \brief Reinitialize query data structure. */
56 57
static void query_data_init(knot_layer_t *ctx, knotd_qdata_params_t *params,
                            knotd_qdata_extra_t *extra)
58
{
59
	/* Initialize persistent data. */
60 61
	knotd_qdata_t *data = QUERY_DATA(ctx);
	memset(data, 0, sizeof(*data));
62
	data->mm = ctx->mm;
63 64
	data->params = params;
	data->extra = extra;
65

66
	/* Initialize lists. */
67 68 69
	memset(extra, 0, sizeof(*extra));
	init_list(&extra->wildcards);
	init_list(&extra->rrsigs);
70 71
}

72
static int process_query_begin(knot_layer_t *ctx, void *params)
73 74 75
{
	/* Initialize context. */
	assert(ctx);
76 77
	ctx->data = mm_alloc(ctx->mm, sizeof(knotd_qdata_t));
	knotd_qdata_extra_t *extra = mm_alloc(ctx->mm, sizeof(*extra));
78 79

	/* Initialize persistent data. */
80
	query_data_init(ctx, params, extra);
81 82

	/* Await packet. */
83
	return KNOT_STATE_CONSUME;
84 85
}

86
static int process_query_reset(knot_layer_t *ctx)
87 88
{
	assert(ctx);
89
	knotd_qdata_t *qdata = QUERY_DATA(ctx);
90

91
	/* Remember persistent parameters. */
92 93
	knotd_qdata_params_t *params = qdata->params;
	knotd_qdata_extra_t *extra = qdata->extra;
94

95
	/* Free allocated data. */
96
	knot_rrset_clear(&qdata->opt_rr, qdata->mm);
97 98 99 100
	ptrlist_free(&extra->wildcards, qdata->mm);
	nsec_clear_rrsigs(qdata);
	if (extra->ext_cleanup != NULL) {
		extra->ext_cleanup(qdata);
101
	}
102 103

	/* Initialize persistent data. */
104
	query_data_init(ctx, params, extra);
105

106
	/* Await packet. */
107
	return KNOT_STATE_CONSUME;
108
}
109

110
static int process_query_finish(knot_layer_t *ctx)
111
{
112
	process_query_reset(ctx);
113
	mm_free(ctx->mm, ctx->data);
114 115
	ctx->data = NULL;

116
	return KNOT_STATE_NOOP;
117
}
118

119
static int process_query_in(knot_layer_t *ctx, knot_pkt_t *pkt)
120 121
{
	assert(pkt && ctx);
122
	knotd_qdata_t *qdata = QUERY_DATA(ctx);
123

124 125
	/* Check if at least header is parsed. */
	if (pkt->parsed < KNOT_WIRE_HEADER_SIZE) {
126
		return KNOT_STATE_NOOP; /* Ignore. */
127
	}
128

129 130
	/* Accept only queries. */
	if (knot_wire_get_qr(pkt->wire)) {
131
		return KNOT_STATE_NOOP; /* Ignore. */
132 133 134
	}

	/* Store for processing. */
135
	qdata->query = pkt;
136
	qdata->type = query_type(pkt);
137 138

	/* Declare having response. */
139
	return KNOT_STATE_PRODUCE;
140 141
}

142 143 144
/*!
 * \brief Create a response for a given query in the INTERNET class.
 */
145
static int query_internet(knot_pkt_t *pkt, knot_layer_t *ctx)
146
{
147 148 149 150 151 152 153 154
	knotd_qdata_t *data = QUERY_DATA(ctx);

	switch (data->type) {
	case KNOTD_QUERY_TYPE_NORMAL: return internet_process_query(pkt, data);
	case KNOTD_QUERY_TYPE_NOTIFY: return notify_process_query(pkt, data);
	case KNOTD_QUERY_TYPE_AXFR:   return axfr_process_query(pkt, data);
	case KNOTD_QUERY_TYPE_IXFR:   return ixfr_process_query(pkt, data);
	case KNOTD_QUERY_TYPE_UPDATE: return update_process_query(pkt, data);
155
	default:
156 157
		/* Nothing else is supported. */
		data->rcode = KNOT_RCODE_NOTIMPL;
158
		return KNOT_STATE_FAIL;
159 160 161
	}
}

162 163 164
/*!
 * \brief Create a response for a given query in the CHAOS class.
 */
165
static int query_chaos(knot_pkt_t *pkt, knot_layer_t *ctx)
166
{
167
	knotd_qdata_t *data = QUERY_DATA(ctx);
168

169
	/* Nothing except normal queries is supported. */
170
	if (data->type != KNOTD_QUERY_TYPE_NORMAL) {
171
		data->rcode = KNOT_RCODE_NOTIMPL;
172
		return KNOT_STATE_FAIL;
173
	}
174

175 176
	data->rcode = knot_chaos_answer(pkt);
	if (data->rcode != KNOT_RCODE_NOERROR) {
177
		return KNOT_STATE_FAIL;
178
	}
179

180
	return KNOT_STATE_DONE;
181
}
182

183
/*! \brief Find zone for given question. */
184
static const zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
185
{
186 187 188
	uint16_t qtype = knot_pkt_qtype(query);
	uint16_t qclass = knot_pkt_qclass(query);
	const knot_dname_t *qname = knot_pkt_qname(query);
189
	const zone_t *zone = NULL;
190 191 192

	// search for zone only for IN and ANY classes
	if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) {
193
		return NULL;
194 195
	}

196 197 198 199 200 201 202 203 204 205 206 207 208 209
	/* In case of DS query, we strip the leftmost label when searching for
	 * the zone (but use whole qname in search for the record), as the DS
	 * records are only present in a parent zone.
	 */
	if (qtype == KNOT_RRTYPE_DS) {
		const knot_dname_t *parent = knot_wire_next_label(qname, NULL);
		zone = knot_zonedb_find_suffix(zonedb, parent);
		/* If zone does not exist, search for its parent zone,
		   this will later result to NODATA answer. */
		/*! \note This is not 100% right, it may lead to DS name for example
		 *        when following a CNAME chain, that should also be answered
		 *        from the parent zone (if it exists).
		 */
	}
210

211
	if (zone == NULL) {
212
		if (query_type(query) == KNOTD_QUERY_TYPE_NORMAL) {
213 214
			zone = knot_zonedb_find_suffix(zonedb, qname);
		} else {
215
			// Direct match required.
216 217
			zone = knot_zonedb_find(zonedb, qname);
		}
218
	}
219

220
	return zone;
221 222
}

223
static int answer_edns_reserve(knot_pkt_t *resp, knotd_qdata_t *qdata)
224 225 226 227 228 229 230 231 232
{
	if (knot_rrset_empty(&qdata->opt_rr)) {
		return KNOT_EOK;
	}

	/* Reserve size in the response. */
	return knot_pkt_reserve(resp, knot_edns_wire_size(&qdata->opt_rr));
}

233
static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp,
234
                            knotd_qdata_t *qdata)
235 236 237 238 239 240
{
	if (!knot_pkt_has_edns(query)) {
		return KNOT_EOK;
	}

	/* Initialize OPT record. */
241
	int16_t max_payload;
242
	switch (qdata->params->remote->ss_family) {
243 244 245 246 247 248 249 250 251 252 253
	case AF_INET:
		max_payload = conf()->cache.srv_max_ipv4_udp_payload;
		break;
	case AF_INET6:
		max_payload = conf()->cache.srv_max_ipv6_udp_payload;
		break;
	default:
		return KNOT_ERROR;
	}
	int ret = knot_edns_init(&qdata->opt_rr, max_payload, 0,
	                         KNOT_EDNS_VERSION, qdata->mm);
254 255 256 257 258 259
	if (ret != KNOT_EOK) {
		return ret;
	}

	/* Check supported version. */
	if (knot_edns_get_version(query->opt_rr) != KNOT_EDNS_VERSION) {
Lubos Slovak's avatar
Lubos Slovak committed
260
		qdata->rcode = KNOT_RCODE_BADVERS;
261 262 263 264 265 266 267 268
	}

	/* Set DO bit if set (DNSSEC requested). */
	if (knot_pkt_has_dnssec(query)) {
		knot_edns_set_do(&qdata->opt_rr);
	}

	/* Append NSID if requested and available. */
269
	if (knot_pkt_edns_option(query, KNOT_EDNS_OPTION_NSID) != NULL) {
270
		conf_val_t *nsid = &conf()->cache.srv_nsid;
271
		size_t nsid_len;
272
		const uint8_t *nsid_data = conf_bin(nsid, &nsid_len);
273

274
		if (nsid->code != KNOT_EOK) {
275 276 277 278 279 280 281 282
			ret = knot_edns_add_option(&qdata->opt_rr,
			                           KNOT_EDNS_OPTION_NSID,
			                           strlen(conf()->hostname),
			                           (uint8_t *)conf()->hostname,
			                           qdata->mm);
			if (ret != KNOT_EOK) {
				return ret;
			}
283
		} else if (nsid_len > 0) {
284 285
			ret = knot_edns_add_option(&qdata->opt_rr,
			                           KNOT_EDNS_OPTION_NSID,
286
			                           nsid_len, nsid_data,
287 288 289 290
			                           qdata->mm);
			if (ret != KNOT_EOK) {
				return ret;
			}
291 292 293
		}
	}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	/* Initialize EDNS Client Subnet if configured and present in query. */
	if (conf()->cache.use_ecs) {
		uint8_t *ecs_opt = knot_pkt_edns_option(query, KNOT_EDNS_OPTION_CLIENT_SUBNET);
		if (ecs_opt != NULL) {
			qdata->ecs = mm_alloc(qdata->mm, sizeof(knot_edns_client_subnet_t));
			if (qdata->ecs == NULL) {
				return KNOT_ENOMEM;
			}
			const uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt);
			uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt);
			ret = knot_edns_client_subnet_parse(qdata->ecs, ecs_data, ecs_len);
			if (ret != KNOT_EOK) {
				qdata->rcode = KNOT_RCODE_FORMERR;
				return ret;
			}
			qdata->ecs->scope_len = 0;

			/* Reserve space for the option in the answer. */
			ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET,
			                               ecs_len, NULL, qdata->mm);
			if (ret != KNOT_EOK) {
				return ret;
			}
		}
	} else {
		qdata->ecs = NULL;
	}

322
	return answer_edns_reserve(resp, qdata);
323 324
}

325
static int answer_edns_put(knot_pkt_t *resp, knotd_qdata_t *qdata)
326 327 328 329 330
{
	if (knot_rrset_empty(&qdata->opt_rr)) {
		return KNOT_EOK;
	}

331 332 333 334 335 336 337 338 339 340 341 342 343 344
	/* Add ECS if present. */
	int ret = KNOT_EOK;
	if (qdata->ecs != NULL) {
		uint8_t *ecs_opt = knot_edns_get_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET);
		if (ecs_opt != NULL) {
			uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt);
			uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt);
			ret = knot_edns_client_subnet_write(ecs_data, ecs_len, qdata->ecs);
			if (ret != KNOT_EOK) {
				return ret;
			}
		}
	}

345
	/* Reclaim reserved size. */
346
	ret = knot_pkt_reclaim(resp, knot_edns_wire_size(&qdata->opt_rr));
347 348 349 350
	if (ret != KNOT_EOK) {
		return ret;
	}

Lubos Slovak's avatar
Lubos Slovak committed
351 352
	uint8_t *wire_end = resp->wire + resp->size;

353
	/* Write to packet. */
354
	assert(resp->current == KNOT_ADDITIONAL);
355
	ret = knot_pkt_put(resp, KNOT_COMPR_HINT_NONE, &qdata->opt_rr, 0);
Lubos Slovak's avatar
Lubos Slovak committed
356
	if (ret == KNOT_EOK) {
Lubos Slovak's avatar
Lubos Slovak committed
357
		/* Save position of the OPT RR. */
358
		qdata->extra->opt_rr_pos = wire_end;
Lubos Slovak's avatar
Lubos Slovak committed
359 360 361
	}

	return ret;
362 363
}

364
/*! \brief Initialize response, sizes and find zone from which we're going to answer. */
365
static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_layer_t *ctx)
366
{
367 368
	knotd_qdata_t *qdata = QUERY_DATA(ctx);
	server_t *server = qdata->params->server;
369

370 371 372 373 374
	/* Initialize response. */
	int ret = knot_pkt_init_response(resp, query);
	if (ret != KNOT_EOK) {
		return ret;
	}
375
	knot_wire_clear_cd(resp->wire);
376

377 378
	/* Setup EDNS. */
	ret = answer_edns_init(query, resp, qdata);
Lubos Slovak's avatar
Lubos Slovak committed
379
	if (ret != KNOT_EOK || qdata->rcode != 0) {
380
		return KNOT_ERROR;
381 382
	}

383
	/* Update maximal answer size. */
384
	bool has_limit = qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE;
385 386 387
	if (has_limit) {
		resp->max_size = KNOT_WIRE_MIN_PKTSIZE;
		if (knot_pkt_has_edns(query)) {
388
			uint16_t server;
389
			switch (qdata->params->remote->ss_family) {
390 391 392 393 394 395 396 397 398
			case AF_INET:
				server = conf()->cache.srv_max_ipv4_udp_payload;
				break;
			case AF_INET6:
				server = conf()->cache.srv_max_ipv6_udp_payload;
				break;
			default:
				return KNOT_ERROR;
			}
399 400 401 402 403 404
			uint16_t client = knot_edns_get_payload(query->opt_rr);
			uint16_t transfer = MIN(client, server);
			resp->max_size = MAX(resp->max_size, transfer);
		}
	} else {
		resp->max_size = KNOT_WIRE_MAX_PKTSIZE;
405 406
	}

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
	/* Query MUST carry a question. */
	const knot_dname_t *qname = knot_pkt_qname(query);
	if (qname == NULL) {
		qdata->rcode = KNOT_RCODE_FORMERR;
		return KNOT_EMALF;
	}

	/* Convert query QNAME to lowercase, but keep original QNAME case.
	 * Already checked for absence of compression and length.
	 */
	memcpy(qdata->extra->orig_qname, qname, query->qname_size);
	process_query_qname_case_lower(query);

	/* Find zone for QNAME. */
	qdata->extra->zone = answer_zone_find(query, server->zone_db);

	return KNOT_EOK;
424
}
425

426
static void set_rcode_to_packet(knot_pkt_t *pkt, knotd_qdata_t *qdata)
Lubos Slovak's avatar
Lubos Slovak committed
427 428 429 430
{
	uint8_t ext_rcode = KNOT_EDNS_RCODE_HI(qdata->rcode);

	if (ext_rcode != 0) {
431
		/* No OPT RR and Ext RCODE results in SERVFAIL. */
432
		if (qdata->extra->opt_rr_pos == NULL) {
433 434
			knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL);
			return;
Lubos Slovak's avatar
Lubos Slovak committed
435
		}
436

437
		knot_edns_set_ext_rcode_wire(qdata->extra->opt_rr_pos, ext_rcode);
Lubos Slovak's avatar
Lubos Slovak committed
438 439 440 441 442
	}

	knot_wire_set_rcode(pkt->wire, KNOT_EDNS_RCODE_LO(qdata->rcode));
}

443
static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
444
{
445 446
	assert(ctx && pkt);

447
	knotd_qdata_t *qdata = QUERY_DATA(ctx);
448 449 450

	/* Initialize response from query packet. */
	knot_pkt_t *query = qdata->query;
451
	(void)knot_pkt_init_response(pkt, query);
452 453 454 455 456 457
	knot_wire_clear_cd(pkt->wire);

	/* Set TC bit if required. */
	if (qdata->err_truncated) {
		knot_wire_set_tc(pkt->wire);
	}
458

459
	/* Restore original QNAME. */
460
	process_query_qname_case_restore(pkt, qdata);
461

462
	/* Move to Additionals to add OPT and TSIG. */
463
	if (pkt->current != KNOT_ADDITIONAL) {
464
		(void)knot_pkt_begin(pkt, KNOT_ADDITIONAL);
465 466
	}

467
	/* Put OPT RR to the additional section. */
468 469 470
	if (answer_edns_reserve(pkt, qdata) != KNOT_EOK ||
	    answer_edns_put(pkt, qdata) != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_FORMERR;
471 472
	}

473
	/* Set final RCODE to packet. */
474 475 476 477
	if (qdata->rcode == KNOT_RCODE_NOERROR) {
		/* Default RCODE is SERVFAIL if not otherwise specified. */
		qdata->rcode = KNOT_RCODE_SERVFAIL;
	}
478
	set_rcode_to_packet(pkt, qdata);
479

480
	/* Transaction security (if applicable). */
481 482 483
	if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
		set_rcode_to_packet(pkt, qdata);
	}
484

485
	return KNOT_STATE_DONE;
486 487
}

488 489
#define PROCESS_BEGIN(plan, step, next_state, qdata) \
	if (plan != NULL) { \
490
		WALK_LIST(step, plan->stage[KNOTD_STAGE_BEGIN]) { \
491 492 493 494 495 496 497 498
			next_state = step->process(next_state, pkt, qdata, step->ctx); \
			if (next_state == KNOT_STATE_FAIL) { \
				goto finish; \
			} \
		} \
	}

#define PROCESS_END(plan, step, next_state, qdata) \
499
	if (plan != NULL) { \
500
		WALK_LIST(step, plan->stage[KNOTD_STAGE_END]) { \
501 502 503 504 505
			next_state = step->process(next_state, pkt, qdata, step->ctx); \
			if (next_state == KNOT_STATE_FAIL) { \
				next_state = process_query_err(ctx, pkt); \
			} \
		} \
506 507
	}

508
static int process_query_out(knot_layer_t *ctx, knot_pkt_t *pkt)
509 510
{
	assert(pkt && ctx);
511 512 513

	rcu_read_lock();

514
	knotd_qdata_t *qdata = QUERY_DATA(ctx);
515
	struct query_plan *plan = conf()->query_plan;
516
	struct query_plan *zone_plan = NULL;
517 518
	struct query_step *step = NULL;

519 520
	int next_state = KNOT_STATE_PRODUCE;

521 522 523 524
	/* Check parse state. */
	knot_pkt_t *query = qdata->query;
	if (query->parsed < query->size) {
		qdata->rcode = KNOT_RCODE_FORMERR;
525
		next_state = KNOT_STATE_FAIL;
526 527 528
		goto finish;
	}

529
	/* Preprocessing. */
530
	if (prepare_answer(query, pkt, ctx) != KNOT_EOK) {
531
		next_state = KNOT_STATE_FAIL;
532 533 534
		goto finish;
	}

535 536
	if (qdata->extra->zone != NULL && qdata->extra->zone->query_plan != NULL) {
		zone_plan = qdata->extra->zone->query_plan;
537 538
	}

539
	/* Before query processing code. */
540 541
	PROCESS_BEGIN(plan, step, next_state, qdata);
	PROCESS_BEGIN(zone_plan, step, next_state, qdata);
542

543
	/* Answer based on qclass. */
544
	if (next_state == KNOT_STATE_PRODUCE) {
545 546 547 548 549 550 551 552 553 554
		switch (knot_pkt_qclass(pkt)) {
		case KNOT_CLASS_CH:
			next_state = query_chaos(pkt, ctx);
			break;
		case KNOT_CLASS_ANY:
		case KNOT_CLASS_IN:
			next_state = query_internet(pkt, ctx);
			break;
		default:
			qdata->rcode = KNOT_RCODE_REFUSED;
555
			next_state = KNOT_STATE_FAIL;
556 557
			break;
		}
558 559
	}

560
	/* Postprocessing. */
561
	if (next_state == KNOT_STATE_DONE || next_state == KNOT_STATE_PRODUCE) {
562
		/* Restore original QNAME. */
563
		process_query_qname_case_restore(pkt, qdata);
564

565
		/* Move to Additionals to add OPT and TSIG. */
566
		if (pkt->current != KNOT_ADDITIONAL) {
567
			(void)knot_pkt_begin(pkt, KNOT_ADDITIONAL);
568 569
		}

570
		/* Put OPT RR to the additional section. */
571
		if (answer_edns_put(pkt, qdata) != KNOT_EOK) {
572
			qdata->rcode = KNOT_RCODE_FORMERR;
573
			next_state = KNOT_STATE_FAIL;
574
			goto finish;
575 576 577
		}

		/* Transaction security (if applicable). */
578
		if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
579
			next_state = KNOT_STATE_FAIL;
580
			goto finish;
581 582 583 584
		}
	}

finish:
585 586 587 588
	switch (next_state) {
	case KNOT_STATE_NOOP:
		break;
	case KNOT_STATE_FAIL:
589 590
		/* Error processing. */
		next_state = process_query_err(ctx, pkt);
591
		break;
592 593 594 595
	case KNOT_STATE_FINAL:
		/* Just skipped postprocessing. */
		next_state = KNOT_STATE_DONE;
		break;
596 597
	default:
		set_rcode_to_packet(pkt, qdata);
Lubos Slovak's avatar
Lubos Slovak committed
598 599
	}

600
	/* After query processing code. */
601 602
	PROCESS_END(plan, step, next_state, qdata);
	PROCESS_END(zone_plan, step, next_state, qdata);
603

604
	rcu_read_unlock();
605

606 607 608
	return next_state;
}

609
bool process_query_acl_check(conf_t *conf, const knot_dname_t *zone_name,
610
                             acl_action_t action, knotd_qdata_t *qdata)
611 612
{
	knot_pkt_t *query = qdata->query;
613
	const struct sockaddr_storage *query_source = qdata->params->remote;
614
	knot_tsig_key_t tsig = { 0 };
615 616

	/* Skip if already checked and valid. */
617
	if (qdata->sign.tsig_key.name != NULL) {
618 619 620 621 622
		return true;
	}

	/* Authenticate with NOKEY if the packet isn't signed. */
	if (query->tsig_rr) {
623 624
		tsig.name = query->tsig_rr->owner;
		tsig.algorithm = knot_tsig_rdata_alg(query->tsig_rr);
625 626
	}

627
	/* Check if authenticated. */
628 629
	conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name);
	if (!acl_allowed(conf, &acl, action, query_source, &tsig)) {
630
		char addr_str[SOCKADDR_STRLEN] = { 0 };
631
		sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)query_source);
632 633
		const knot_lookup_t *act = knot_lookup_by_id((knot_lookup_t *)acl_actions,
		                                             action);
634 635 636
		char *key_name = knot_dname_to_str_alloc(tsig.name);

		log_zone_debug(zone_name,
637
		               "ACL, denied, action %s, remote %s, key %s%s%s",
638 639 640 641 642 643 644
		               (act != NULL) ? act->name : "query",
		               addr_str,
		               (key_name != NULL) ? "'" : "",
		               (key_name != NULL) ? key_name : "none",
		               (key_name != NULL) ? "'" : "");
		free(key_name);

645
		qdata->rcode = KNOT_RCODE_NOTAUTH;
646
		qdata->rcode_tsig = KNOT_RCODE_BADKEY;
647 648 649 650
		return false;
	}

	/* Remember used TSIG key. */
651 652
	qdata->sign.tsig_key = tsig;

653 654 655
	return true;
}

656
int process_query_verify(knotd_qdata_t *qdata)
657 658 659 660 661 662 663 664 665 666 667
{
	knot_pkt_t *query = qdata->query;
	knot_sign_context_t *ctx = &qdata->sign;

	/* NOKEY => no verification. */
	if (query->tsig_rr == NULL) {
		return KNOT_EOK;
	}

	/* Keep digest for signing response. */
	/*! \note This memory will be rewritten for multi-pkt answers. */
668 669
	ctx->tsig_digest = (uint8_t *)knot_tsig_rdata_mac(query->tsig_rr);
	ctx->tsig_digestlen = knot_tsig_rdata_mac_length(query->tsig_rr);
670 671

	/* Checking query. */
672
	process_query_qname_case_restore(query, qdata);
673
	int ret = knot_tsig_server_check(query->tsig_rr, query->wire,
674
	                                 query->size, &ctx->tsig_key);
675
	process_query_qname_case_lower(query);
676 677 678 679 680 681 682 683

	/* Evaluate TSIG check results. */
	switch(ret) {
	case KNOT_EOK:
		qdata->rcode = KNOT_RCODE_NOERROR;
		break;
	case KNOT_TSIG_EBADKEY:
		qdata->rcode = KNOT_RCODE_NOTAUTH;
684
		qdata->rcode_tsig = KNOT_RCODE_BADKEY;
685 686 687
		break;
	case KNOT_TSIG_EBADSIG:
		qdata->rcode = KNOT_RCODE_NOTAUTH;
688
		qdata->rcode_tsig = KNOT_RCODE_BADSIG;
689 690 691
		break;
	case KNOT_TSIG_EBADTIME:
		qdata->rcode = KNOT_RCODE_NOTAUTH;
692
		qdata->rcode_tsig = KNOT_RCODE_BADTIME;
693
		ctx->tsig_time_signed = knot_tsig_rdata_time_signed(query->tsig_rr);
694 695 696 697 698 699 700 701 702
		break;
	case KNOT_EMALF:
		qdata->rcode = KNOT_RCODE_FORMERR;
		break;
	default:
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		break;
	}

703
	/* Log possible error. */
704 705 706 707
	if (qdata->rcode == KNOT_RCODE_SERVFAIL) {
		log_zone_error(qdata->extra->zone->name,
		               "TSIG, verification failed (%s)", knot_strerror(ret));
	} else if (qdata->rcode != KNOT_RCODE_NOERROR) {
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
		const knot_lookup_t *item = NULL;
		if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
			item = knot_lookup_by_id(knot_tsig_rcode_names, qdata->rcode_tsig);
			if (item == NULL) {
				item = knot_lookup_by_id(knot_rcode_names, qdata->rcode_tsig);
			}
		} else {
			item = knot_lookup_by_id(knot_rcode_names, qdata->rcode);
		}

		char *key_name = knot_dname_to_str_alloc(ctx->tsig_key.name);
		log_zone_debug(qdata->extra->zone->name,
		               "TSIG, key '%s', verification failed '%s'",
		               (key_name != NULL) ? key_name : "",
		               (item != NULL) ? item->name : "");
		free(key_name);
	}

726 727
	return ret;
}
728

729
int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata)
730 731 732 733 734 735 736 737 738 739 740
{
	if (pkt->size == 0) {
		// Nothing to sign.
		return KNOT_EOK;
	}

	int ret = KNOT_EOK;
	knot_pkt_t *query = qdata->query;
	knot_sign_context_t *ctx = &qdata->sign;

	/* KEY provided and verified TSIG or BADTIME allows signing. */
741
	if (ctx->tsig_key.name != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) {
742
		/* Sign query response. */
743
		size_t new_digest_len = dnssec_tsig_algorithm_size(ctx->tsig_key.algorithm);
744 745 746 747
		if (ctx->pkt_count == 0) {
			ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size,
			                     ctx->tsig_digest, ctx->tsig_digestlen,
			                     ctx->tsig_digest, &new_digest_len,
748
			                     &ctx->tsig_key, qdata->rcode_tsig,
749 750 751 752 753
			                     ctx->tsig_time_signed);
		} else {
			ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size,
			                          ctx->tsig_digest, ctx->tsig_digestlen,
			                          ctx->tsig_digest, &new_digest_len,
754
			                          &ctx->tsig_key,
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
			                          pkt->wire, pkt->size);
		}
		if (ret != KNOT_EOK) {
			goto fail; /* Failed to sign. */
		} else {
			++ctx->pkt_count;
		}
	} else {
		/* Copy TSIG from query and set RCODE. */
		if (query->tsig_rr && qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
			ret = knot_tsig_add(pkt->wire, &pkt->size, pkt->max_size,
			                    qdata->rcode_tsig, query->tsig_rr);
			if (ret != KNOT_EOK) {
				goto fail; /* Whatever it is, it's server fail. */
			}
		}
	}

773
	return KNOT_EOK;
774 775 776 777 778 779 780 781

	/* Server failure in signing. */
fail:
	qdata->rcode = KNOT_RCODE_SERVFAIL;
	qdata->rcode_tsig = KNOT_RCODE_NOERROR; /* Don't sign again. */
	return ret;
}

782 783 784
/*! \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,
                     const knot_rrset_t *rrsigs, knot_rrinfo_t *rrinfo,
785
                     knotd_qdata_t *qdata)
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 811
{
	knot_rdataset_t synth_rrs;
	knot_rdataset_init(&synth_rrs);
	int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrs, qdata->mm);
	if (ret == KNOT_ENOENT) {
		// No signature
		return KNOT_EOK;
	}
	if (ret != KNOT_EOK) {
		return ret;
	}

	/* Create rrsig info structure. */
	struct rrsig_info *info = mm_alloc(qdata->mm, sizeof(struct rrsig_info));
	if (info == NULL) {
		knot_rdataset_clear(&synth_rrs, qdata->mm);
		return KNOT_ENOMEM;
	}

	/* Store RRSIG into info structure. */
	knot_dname_t *owner_copy = knot_dname_copy(sig_owner, qdata->mm);
	if (owner_copy == NULL) {
		mm_free(qdata->mm, info);
		knot_rdataset_clear(&synth_rrs, qdata->mm);
		return KNOT_ENOMEM;
	}
812 813
	knot_rrset_init(&info->synth_rrsig, owner_copy, rrsigs->type,
	                rrsigs->rclass, rrsigs->ttl);
814 815 816 817
	/* Store filtered signature. */
	info->synth_rrsig.rrs = synth_rrs;

	info->rrinfo = rrinfo;
818
	add_tail(&qdata->extra->rrsigs, &info->n);
819 820 821 822

	return KNOT_EOK;
}

823
int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata,
824 825 826
                         const knot_rrset_t *rr, const knot_rrset_t *rrsigs,
                         uint16_t compr_hint, uint32_t flags)
{
827
	if (rr->rrs.count < 1) {
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
		return KNOT_EMALF;
	}

	/* 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));
	}

	int ret = KNOT_EOK;

	/* If we already have compressed name on the wire and compression hint,
	 * we can just insert RRSet and fake synthesis by using compression
	 * hint. */
	knot_rrset_t to_add;
	if (compr_hint == KNOT_COMPR_HINT_NONE && expand) {
		knot_dname_t *qname_cpy = knot_dname_copy(qdata->name, &pkt->mm);
		if (qname_cpy == NULL) {
			return KNOT_ENOMEM;
		}
849
		knot_rrset_init(&to_add, qname_cpy, rr->type, rr->rclass, rr->ttl);
850 851
		ret = knot_rdataset_copy(&to_add.rrs, &rr->rrs, &pkt->mm);
		if (ret != KNOT_EOK) {
852
			knot_dname_free(qname_cpy, &pkt->mm);
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
			return ret;
		}
		to_add.additional = rr->additional;
		flags |= KNOT_PF_FREE;
	} else {
		to_add = *rr;
	}

	uint16_t prev_count = pkt->rrset_count;
	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);
		return ret;
	}

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

	return ret;
}

879
/*! \brief Module implementation. */
880
const knot_layer_api_t *process_query_layer(void)
881
{
882
	static const knot_layer_api_t api = {
883 884 885 886 887 888
		.begin   = &process_query_begin,
		.reset   = &process_query_reset,
		.finish  = &process_query_finish,
		.consume = &process_query_in,
		.produce = &process_query_out,
	};
889
	return &api;
890
}