nsec_proofs.c 20.3 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 <assert.h>

19
#include "libknot/libknot.h"
20
#include "knot/nameserver/nsec_proofs.h"
21
#include "knot/nameserver/internet.h"
22
#include "knot/dnssec/zone-nsec.h"
23

24 25 26 27 28 29 30 31 32 33
/*!
 * \brief Check if node is empty non-terminal.
 */
static bool empty_nonterminal(const zone_node_t *node)
{
	return node && node->rrset_count == 0;
}

/*!
 * \brief Check if wildcard expansion happened for given node and QNAME.
34
 */
35 36 37 38 39 40
static bool wildcard_expanded(const zone_node_t *node, const knot_dname_t *qname)
{
	return !knot_dname_is_wildcard(qname) && knot_dname_is_wildcard(node->owner);
}

/*!
Jan Včelák's avatar
Jan Včelák committed
41
 * \brief Check if opt-out can take an effect.
42
 */
43
static bool ds_optout(const zone_node_t *node)
44
{
45
	return node->nsec3_node == NULL && node->flags & NODE_FLAGS_DELEG;
46 47 48
}

/*!
49 50 51
 * \brief Check if node is part of the NSEC chain.
 *
 * NSEC is created for each node with authoritative data or delegation.
52
 *
53
 * \see https://tools.ietf.org/html/rfc4035#section-2.3
54
 */
55
static bool node_in_nsec(const zone_node_t *node)
56
{
57
	return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !empty_nonterminal(node);
58 59
}

60 61 62 63 64 65 66 67 68 69 70 71 72
/*!
 * \brief Check if node is part of the NSEC3 chain.
 *
 * NSEC3 is created for each node with authoritative data, empty-non terminal,
 * and delegation (unless opt-out is in effect).
 *
 * \see https://tools.ietf.org/html/rfc5155#section-7.1
 */
static bool node_in_nsec3(const zone_node_t *node)
{
	return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !ds_optout(node);
}

73
/*!
74 75
 * \brief Walk previous names until we reach a node in NSEC chain.
 *
76
 */
77
static const zone_node_t *nsec_previous(const zone_node_t *previous)
78 79 80
{
	assert(previous);

81
	while (!node_in_nsec(previous)) {
82 83 84 85 86 87
		previous = previous->prev;
		assert(previous);
	}

	return previous;
}
88

89
/*!
90 91 92 93 94 95 96 97 98 99 100 101 102 103
 * \brief Get closest provable encloser from closest matching parent node.
 */
static const zone_node_t *nsec3_encloser(const zone_node_t *closest)
{
	assert(closest);

	while (!node_in_nsec3(closest)) {
		closest = closest->parent;
		assert(closest);
	}

	return closest;
}

104 105 106 107 108
/*!
 * \brief Create a 'next closer name' to the given domain name.
 *
 * Next closer is the name one label longer than the closest provable encloser
 * of a name.
109
 *
110
 * \see https://tools.ietf.org/html/rfc5155#section-1.3
111
 *
112 113
 * \param closest_encloser  Closest provable encloser of \a name.
 * \param name              Domain name to create the 'next closer' name to.
114
 *
115
 * \return Next closer name, NULL on error.
116
 */
117 118
static knot_dname_t *get_next_closer(const knot_dname_t *closest_encloser,
                                     const knot_dname_t *name)
119 120 121 122 123
{
	int ce_labels = knot_dname_labels(closest_encloser, NULL);
	int qname_labels = knot_dname_labels(name, NULL);

	// the common labels should match
124
	assert(knot_dname_matched_labels(closest_encloser, name) == ce_labels);
125 126 127 128 129 130

	// chop some labels from the qname
	for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) {
		name = knot_wire_next_label(name, NULL);
	}

131
	return knot_dname_copy(name, NULL);
132 133
}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/*!
 * \brief Create a wildcard child of a name.
 *
 * \param name  Parent of the wildcard.
 *
 * \return Wildcard child name, NULL on error.
 */
static knot_dname_t *wildcard_child_name(const knot_dname_t *name)
{
	assert(name != NULL);

	knot_dname_t *wildcard = knot_dname_from_str_alloc("*");
	if (wildcard == NULL) {
		return NULL;
	}

	wildcard = knot_dname_cat(wildcard, name);
	if (wildcard == NULL) {
		return NULL;
	}

	return wildcard;
}


159
/*!
160
 * \brief Put NSEC/NSEC3 record with corresponding RRSIG into the response.
161
 */
162 163 164 165
static int put_nxt_from_node(const zone_node_t *node,
                             uint16_t type,
                             struct query_data *qdata,
                             knot_pkt_t *resp)
166
{
167 168 169
	assert(type == KNOT_RRTYPE_NSEC || type == KNOT_RRTYPE_NSEC3);

	knot_rrset_t rrset = node_rrset(node, type);
170
	if (knot_rrset_empty(&rrset)) {
171 172 173
		return KNOT_EOK;
	}

174
	knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
175

176 177 178 179 180 181 182 183 184 185 186 187 188
	return ns_put_rr(resp, &rrset, &rrsigs, KNOT_COMPR_HINT_NONE,
	                 KNOT_PF_CHECKDUP, qdata);
}

/*!
 * \brief Put NSEC record with corresponding RRSIG into the response.
 */
static int put_nsec_from_node(const zone_node_t *node,
                              struct query_data *qdata,
                              knot_pkt_t *resp)
{
	return put_nxt_from_node(node, KNOT_RRTYPE_NSEC, qdata, resp);
}
189

190 191 192 193 194 195 196 197
/*!
 * \brief Put NSEC3 record with corresponding RRSIG into the response.
 */
static int put_nsec3_from_node(const zone_node_t *node,
                               struct query_data *qdata,
                               knot_pkt_t *resp)
{
	return put_nxt_from_node(node, KNOT_RRTYPE_NSEC3, qdata, resp);
198 199 200
}

/*!
201
 * \brief Find NSEC for given name and put it into the response.
202
 *
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
 * Note this function allows the name to match the QNAME. The NODATA proof
 * for empty non-terminal is equivalent to NXDOMAIN proof, except that the
 * names may exist. This is why.
 */
static int put_covering_nsec(const zone_contents_t *zone,
                             const knot_dname_t *name,
                             struct query_data *qdata,
                             knot_pkt_t *resp)
{
	const zone_node_t *match = NULL;
	const zone_node_t *closest = NULL;
	const zone_node_t *prev = NULL;

	const zone_node_t *proof = NULL;

	int ret = zone_contents_find_dname(zone, name, &match, &closest, &prev);
	if (ret == ZONE_NAME_FOUND) {
		proof = match;
	} else if (ret == ZONE_NAME_NOT_FOUND) {
222
		proof = nsec_previous(prev);
223 224 225 226 227 228 229 230 231 232
	} else {
		assert(ret < 0);
		return ret;
	}

	return put_nsec_from_node(proof, qdata, resp);
}

/*!
 * \brief Find NSEC3 covering the given name and put it into the response.
233
 */
234 235 236 237
static int put_covering_nsec3(const zone_contents_t *zone,
                              const knot_dname_t *name,
                              struct query_data *qdata,
                              knot_pkt_t *resp)
238
{
239 240 241
	const zone_node_t *prev = NULL;
	const zone_node_t *node = NULL;

242
	int match = zone_contents_find_nsec3_for_name(zone, name, &node, &prev);
243
	if (match < 0) {
244
		// ignore if missing
245 246 247
		return KNOT_EOK;
	}

248
	if (match == ZONE_NAME_FOUND || prev == NULL){
249
		return KNOT_ERROR;
250 251
	}

252
	return put_nsec3_from_node(prev, qdata, resp);
253 254 255
}

/*!
256
 * \brief Add NSEC3 covering the next closer name to closest encloser.
257
 *
258 259 260 261 262
 * \param cpe    Closest provable encloser of \a qname.
 * \param qname  Source QNAME.
 * \param zone   Source zone.
 * \param qdata  Query processing data.
 * \param resp   Response packet.
263
 *
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
 * \return KNOT_E*
 */
static int put_nsec3_next_closer(const zone_node_t *cpe,
                                 const knot_dname_t *qname,
                                 const zone_contents_t *zone,
                                 struct query_data *qdata,
                                 knot_pkt_t *resp)
{
	knot_dname_t *next_closer = get_next_closer(cpe->owner, qname);
	if (!next_closer) {
		return KNOT_ENOMEM;
	}

	int ret = put_covering_nsec3(zone, next_closer, qdata, resp);

	knot_dname_free(&next_closer, NULL);

	return ret;
}

/*!
 * \brief Add NSEC3s for closest encloser proof.
 *
 * Adds up to two NSEC3 records. The first one proves that closest encloser
 * of the queried name exists, the second one proves that the name bellow the
 * encloser doesn't.
290
 *
291
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.1
292
 *
293 294 295 296 297 298 299
 * \param qname  Source QNAME.
 * \param zone   Source zone.
 * \param cpe    Closest provable encloser of \a qname.
 * \param qdata  Query processing data.
 * \param resp   Response packet.
 *
 * \return KNOT_E*
300
 */
301 302 303
static int put_closest_encloser_proof(const knot_dname_t *qname,
                                      const zone_contents_t *zone,
                                      const zone_node_t *cpe,
304 305
                                      struct query_data *qdata,
                                      knot_pkt_t *resp)
306
{
307
	// An NSEC3 RR that matches the closest (provable) encloser.
308

309 310
	int ret = put_nsec3_from_node(cpe->nsec3_node, qdata, resp);
	if (ret !=  KNOT_EOK) {
311 312 313
		return ret;
	}

314
	// An NSEC3 RR that covers the "next closer" name to the closest encloser.
315

316
	return put_nsec3_next_closer(cpe, qname, zone, qdata, resp);
317 318 319
}

/*!
320
 * \brief Put NSEC for wildcard answer into the response.
321
 *
322
 * Add NSEC record proving that no better match on QNAME exists.
323
 *
324
 * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.3
325
 *
326 327 328
 * \param previous  Previous name for QNAME.
 * \param qdata     Query processing data.
 * \param resp      Response packet.
329
 *
330
 * \return KNOT_E*
331
 */
332 333 334
static int put_nsec_wildcard(const zone_node_t *previous,
                             struct query_data *qdata,
                             knot_pkt_t *resp)
335
{
336
	return put_nsec_from_node(previous, qdata, resp);
337 338 339
}

/*!
340
 * \brief Put NSEC3s for wildcard answer into the response.
341
 *
342
 * Add NSEC3 record proving that no better match on QNAME exists.
343
 *
344
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.6
345
 *
346 347 348 349 350
 * \param wildcard  Wildcard node that was used for expansion.
 * \param qname     Source QNAME.
 * \param zone      Source zone.
 * \param qdata     Query processing data.
 * \param resp      Response packet.
351
 */
352
static int put_nsec3_wildcard(const zone_node_t *wildcard,
353
                              const knot_dname_t *qname,
354
                              const zone_contents_t *zone,
355 356
                              struct query_data *qdata,
                              knot_pkt_t *resp)
357
{
358 359 360
	const zone_node_t *cpe = nsec3_encloser(wildcard->parent);

	return put_nsec3_next_closer(cpe, qname, zone, qdata, resp);
361 362 363
}

/*!
364
 * \brief Put NSECs or NSEC3s for wildcard expansion in the response.
365
 *
366
 * \return KNOT_E*
367
 */
368
static int put_wildcard_answer(const zone_node_t *wildcard,
369 370 371 372 373
                               const zone_node_t *previous,
                               const zone_contents_t *zone,
                               const knot_dname_t *qname,
                               struct query_data *qdata,
                               knot_pkt_t *resp)
374
{
375 376 377
	if (!wildcard_expanded(wildcard, qname)) {
		return KNOT_EOK;
	}
378

379 380 381 382 383
	int ret = 0;

	if (knot_is_nsec3_enabled(zone)) {
		ret = put_nsec3_wildcard(wildcard, qname, zone, qdata, resp);
	} else {
384
		previous = nsec_previous(previous);
385
		ret = put_nsec_wildcard(previous, qdata, resp);
386
	}
387

388 389 390 391
	return ret;
}

/*!
392
 * \brief Put NSECs for NXDOMAIN error into the response.
393
 *
394 395
 * Adds up to two NSEC records. We have to prove that the queried name doesn't
 * exist and that no wildcard expansion is possible for that name.
396
 *
397 398 399 400 401 402 403 404
 * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.2
 *
 * \param qname     Source QNAME.
 * \param zone      Source zone.
 * \param previous  Previous node to \a qname in the zone.
 * \param closest   Closest matching parent of \a qname.
 * \param qdata     Query data.
 * \param resp      Response packet.
405
 *
406
 * \return KNOT_E*
407
 */
408 409 410
static int put_nsec_nxdomain(const knot_dname_t *qname,
                             const zone_contents_t *zone,
                             const zone_node_t *previous,
411
                             const zone_node_t *closest,
412 413
                             struct query_data *qdata,
                             knot_pkt_t *resp)
414
{
415 416
	assert(previous);
	assert(closest);
417

418
	// An NSEC RR proving that there is no exact match for <SNAME, SCLASS>.
419

420
	previous = nsec_previous(previous);
421
	int ret = put_nsec_from_node(previous, qdata, resp);
422 423 424 425
	if (ret != KNOT_EOK) {
		return ret;
	}

426 427
	// An NSEC RR proving that the zone contains no RRsets that would match
	// <SNAME, SCLASS> via wildcard name expansion.
428

429
	// NOTE: closest may be empty non-terminal and thus not authoritative.
430

431 432 433
	knot_dname_t *wildcard = wildcard_child_name(closest->owner);
	if (wildcard == NULL) {
		return KNOT_ENOMEM;
434
	}
435
	ret = put_covering_nsec(zone, wildcard, qdata, resp);
436
	knot_dname_free(&wildcard, NULL);
437

438
	return ret;
439 440 441
}

/*!
442
 * \brief Put NSEC3s for NXDOMAIN error into the response.
443
 *
444 445 446
 * Adds up to three NSEC3 records. We have to proove that some parent name
 * exists (closest encloser proof) and that no wildcard expansion is possible
 * bellow that closest encloser.
447
 *
448
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.2
449
 *
450 451 452 453 454 455 456
 * \param qname    Source QNAME.
 * \param zone     Source zone.
 * \param closest  Closest matching parent of \a qname.
 * \param qdata    Query processing data.
 * \param resp     Response packet.
 *
 * \retval KNOT_E*
457
 */
458 459 460
static int put_nsec3_nxdomain(const knot_dname_t *qname,
                              const zone_contents_t *zone,
                              const zone_node_t *closest,
461 462
                              struct query_data *qdata,
                              knot_pkt_t *resp)
463
{
464
	const zone_node_t *cpe = nsec3_encloser(closest);
465 466 467 468 469 470

	// Closest encloser proof.

	int ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp);
	if (ret != KNOT_EOK) {
		return ret;
471 472
	}

473 474 475 476 477 478 479 480 481 482
	// NSEC3 covering the (nonexistent) wildcard at the closest encloser.

	knot_dname_t *wildcard = wildcard_child_name(cpe->owner);
	if (!wildcard) {
		return KNOT_ENOMEM;
	}

	ret = put_covering_nsec3(zone, wildcard, qdata, resp);
	knot_dname_free(&wildcard, NULL);

483 484 485 486
	return ret;
}

/*!
487 488 489 490 491 492 493 494
 * \brief Put NSECs or NSEC3s for the NXDOMAIN error into the response.
 *
 * \param zone      Zone used for answering.
 * \param previous  Previous node to \a qname.
 * \param closest   Closest matching parent name for \a qname.
 * \param qname     Source QNAME.
 * \param qdata     Query processing data.
 * \param resp      Response packet.
495
 *
496
 * \return KNOT_E*
497
 */
498 499
static int put_nxdomain(const zone_contents_t *zone,
                        const zone_node_t *previous,
500
                        const zone_node_t *closest,
501 502 503
                        const knot_dname_t *qname,
                        struct query_data *qdata,
                        knot_pkt_t *resp)
504 505
{
	int ret = 0;
506

507
	if (knot_is_nsec3_enabled(zone)) {
508
		ret = put_nsec3_nxdomain(qname, zone, closest, qdata, resp);
509
	} else {
510
		ret = put_nsec_nxdomain(qname, zone, previous, closest, qdata, resp);
511
	}
512

513 514 515 516
	return ret;
}

/*!
517
 * \brief Put NSEC for NODATA error into the response.
518
 *
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
 * Then NSEC matching the QNAME must be added into the response and the bitmap
 * will indicate that the QTYPE doesn't exist. As NSECs for empty non-terminals
 * don't exist, the proof for NODATA match on non-terminal is proved as for
 * NXDOMAIN.
 *
 * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.1
 * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.2 (empty non-terminal)
 *
 * \param qname     Source QNAME.
 * \param zone      Source zone.
 * \param match     Node matching QNAME.
 * \param previous  Previous node to \a qname in the zone.
 * \param qdata     Query procssing data.
 * \param resp      Response packet.
 *
 * \return KNOT_E*
 */
static int put_nsec_nodata(const knot_dname_t *qname,
                           const zone_contents_t *zone,
                           const zone_node_t *match,
                           const zone_node_t *closest,
                           const zone_node_t *previous,
                           struct query_data *qdata,
                           knot_pkt_t *resp)
{
	if (empty_nonterminal(match)) {
		return put_nsec_nxdomain(qname, zone, previous, closest, qdata, resp);
	}

	return put_nsec_from_node(match, qdata, resp);
}

/*!
 * \brief Put NSEC3 for NODATA error into the response.
 *
 * The NSEC3 matching the QNAME is added into the response and the bitmap
 * will indicate that the QTYPE doesn't exist. For QTYPE==DS, the server
 * may alternatively serve a closest encloser proof with opt-out. For wildcard
 * expansion, the closest encloser proof must included as well.
 *
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.3
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.4
 * \see https://tools.ietf.org/html/rfc5155#section-7.2.5
 */
static int put_nsec3_nodata(const knot_dname_t *qname,
                           const zone_contents_t *zone,
                           const zone_node_t *match,
                           const zone_node_t *closest,
                           struct query_data *qdata,
                           knot_pkt_t *resp)
{
	int ret = KNOT_EOK;

	// NSEC3 matching QNAME is always included.

	if (match->nsec3_node) {
		ret = put_nsec3_from_node(match->nsec3_node, qdata, resp);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

	// Closest encloser proof for wildcard effect or NSEC3 opt-out.

583
	if (wildcard_expanded(match, qname) || ds_optout(match)) {
584
		const zone_node_t *cpe = nsec3_encloser(closest);
585 586 587 588 589 590 591 592
		ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp);
	}

	return ret;
}

/*!
 * \brief Put NSECs or NSEC3s for the NODATA error into the response.
593
 *
594 595 596
 * \param node   Source node.
 * \param qdata  Query processing data.
 * \param resp   Response packet.
597
 */
598
static int put_nodata(const zone_node_t *node,
599
                      const zone_node_t *closest,
600 601 602 603 604
                      const zone_node_t *previous,
                      const zone_contents_t *zone,
                      const knot_dname_t *qname,
                      struct query_data *qdata,
                      knot_pkt_t *resp)
605
{
606
	int ret = 0;
607

608
	if (knot_is_nsec3_enabled(zone)) {
609
		ret = put_nsec3_nodata(qname, zone, node, closest, qdata, resp);
610
	} else {
611
		ret = put_nsec_nodata(qname, zone, node, closest, previous, qdata, resp);
612 613 614 615 616 617 618
	}

	return ret;
}

int nsec_prove_wildcards(knot_pkt_t *pkt, struct query_data *qdata)
{
619 620 621
	if (qdata->zone->contents == NULL) {
		return KNOT_EINVAL;
	}
622

623 624 625 626
	int ret = KNOT_EOK;
	struct wildcard_hit *item = NULL;

	WALK_LIST(item, qdata->wildcards) {
627 628 629
		if (item->node == NULL) {
			return KNOT_EINVAL;
		}
630 631
		ret = put_wildcard_answer(item->node, item->prev,
		                          qdata->zone->contents,
632
		                          item->sname, qdata, pkt);
633 634 635 636 637 638 639 640 641 642
		if (ret != KNOT_EOK) {
			break;
		}
	}

	return ret;
}

int nsec_prove_nodata(knot_pkt_t *pkt, struct query_data *qdata)
{
643
	if (qdata->zone->contents == NULL || qdata->node == NULL) {
644 645
		return KNOT_EINVAL;
	}
646

647 648
	return put_nodata(qdata->node, qdata->encloser, qdata->previous,
	                  qdata->zone->contents, qdata->name, qdata, pkt);
649 650 651 652
}

int nsec_prove_nxdomain(knot_pkt_t *pkt, struct query_data *qdata)
{
653
	if (qdata->zone->contents == NULL) {
654 655
		return KNOT_EINVAL;
	}
656

657 658 659
	return put_nxdomain(qdata->zone->contents,
	                    qdata->previous, qdata->encloser,
	                    qdata->name, qdata, pkt);
660 661 662 663
}

int nsec_prove_dp_security(knot_pkt_t *pkt, struct query_data *qdata)
{
664 665 666 667
	if (qdata->node == NULL || qdata->encloser == NULL ||
	    qdata->zone->contents == NULL) {
		return KNOT_EINVAL;
	}
668

669 670
	// Add DS into the response.

671
	knot_rrset_t rrset = node_rrset(qdata->node, KNOT_RRTYPE_DS);
672
	if (!knot_rrset_empty(&rrset)) {
673
		knot_rrset_t rrsigs = node_rrset(qdata->node, KNOT_RRTYPE_RRSIG);
674
		return ns_put_rr(pkt, &rrset, &rrsigs, KNOT_COMPR_HINT_NONE, 0, qdata);
675 676
	}

677 678
	// Alternatively prove that DS doesn't exist.

679 680
	return put_nodata(qdata->node, qdata->encloser, qdata->previous,
	                  qdata->zone->contents, qdata->name, qdata, pkt);
681 682
}

683
int nsec_append_rrsigs(knot_pkt_t *pkt, struct query_data *qdata, bool optional)
684 685
{
	int ret = KNOT_EOK;
686
	uint32_t flags = optional ? KNOT_PF_NOTRUNC : KNOT_PF_NULL;
687
	flags |= KNOT_PF_FREE; // Free all RRSIGs, they are synthesized
688

689
	/* Append RRSIGs for section. */
690 691
	struct rrsig_info *info = NULL;
	WALK_LIST(info, qdata->rrsigs) {
692
		knot_rrset_t *rrsig = &info->synth_rrsig;
693
		uint16_t compr_hint = info->rrinfo->compress_ptr[KNOT_COMPR_HINT_OWNER];
694
		ret = knot_pkt_put(pkt, compr_hint, rrsig, flags);
695
		if (ret != KNOT_EOK) {
696
			break;
697
		}
698
		/* RRSIG is owned by packet now. */
699
		knot_rdataset_init(&info->synth_rrsig.rrs);
700
	};
701

702 703 704
	/* Clear the list. */
	nsec_clear_rrsigs(qdata);

705
	return ret;
706
}
707 708 709 710 711 712 713 714 715

void nsec_clear_rrsigs(struct query_data *qdata)
{
	if (qdata == NULL) {
		return;
	}

	struct rrsig_info *info = NULL;
	WALK_LIST(info, qdata->rrsigs) {
716
		knot_rrset_t *rrsig = &info->synth_rrsig;
717
		knot_rrset_clear(rrsig, qdata->mm);
718 719 720 721 722
	};

	ptrlist_free(&qdata->rrsigs, qdata->mm);
	init_list(&qdata->rrsigs);
}