pkt.c 18.8 KB
Newer Older
1
/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Lubos Slovak's avatar
Lubos Slovak committed
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 <assert.h>
18
#include <stdlib.h>
19
#include <stdbool.h>
20

21
#include "libknot/attribute.h"
22
#include "libknot/packet/pkt.h"
23
#include "libknot/codes.h"
24
#include "libknot/descriptor.h"
25
#include "libknot/errcode.h"
26
#include "libknot/rrtype/tsig.h"
27
#include "libknot/tsig-op.h"
Daniel Salzman's avatar
Daniel Salzman committed
28
#include "libknot/packet/wire.h"
29
#include "libknot/packet/rrset-wire.h"
30
#include "libknot/wire.h"
31
#include "contrib/mempattern.h"
32
#include "contrib/wire_ctx.h"
Lubos Slovak's avatar
Lubos Slovak committed
33

34 35 36 37
/*! \brief Packet RR array growth step. */
#define NEXT_RR_ALIGN 16
#define NEXT_RR_COUNT(count) (((count) / NEXT_RR_ALIGN + 1) * NEXT_RR_ALIGN)

38
/*! \brief Scan packet for RRSet existence. */
Daniel Salzman's avatar
Daniel Salzman committed
39
static bool pkt_contains(const knot_pkt_t *packet, const knot_rrset_t *rrset)
40
{
41 42
	assert(packet);
	assert(rrset);
43

44
	for (int i = 0; i < packet->rrset_count; ++i) {
45
		const uint16_t type = packet->rr[i].type;
46
		const knot_rdata_t *data = packet->rr[i].rrs.data;
47
		if (type == rrset->type && data == rrset->rrs.data) {
48
			return true;
49 50
		}
	}
51

52
	return false;
53 54
}

55
/*! \brief Free all RRSets and reset RRSet count. */
56
static void pkt_free_data(knot_pkt_t *pkt)
57
{
58 59
	assert(pkt);

60 61 62
	/* Free RRSets if applicable. */
	for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
		if (pkt->rr_info[i].flags & KNOT_PF_FREE) {
63
			knot_rrset_clear(&pkt->rr[i], &pkt->mm);
64
		}
65
	}
66
	pkt->rrset_count = 0;
67

68 69 70
	/* Free EDNS option positions. */
	mm_free(&pkt->mm, pkt->edns_opts);
	pkt->edns_opts = 0;
71
}
Jan Včelák's avatar
Jan Včelák committed
72

73
/*! \brief Allocate new wireformat of given length. */
74 75
static int pkt_wire_alloc(knot_pkt_t *pkt, uint16_t len)
{
76
	assert(pkt);
Daniel Salzman's avatar
Daniel Salzman committed
77 78 79 80

	if (len < KNOT_WIRE_HEADER_SIZE) {
		return KNOT_ERANGE;
	}
81

82
	pkt->wire = mm_alloc(&pkt->mm, len);
83 84
	if (pkt->wire == NULL) {
		return KNOT_ENOMEM;
85
	}
Jan Včelák's avatar
Jan Včelák committed
86

87 88
	pkt->flags |= KNOT_PF_FREE;
	pkt->max_size = len;
Daniel Salzman's avatar
Daniel Salzman committed
89

90
	knot_pkt_clear(pkt);
Daniel Salzman's avatar
Daniel Salzman committed
91

92
	return KNOT_EOK;
93 94
}

95
/*! \brief Set packet wireformat to an existing memory. */
96 97
static void pkt_wire_set(knot_pkt_t *pkt, void *wire, uint16_t len)
{
98 99
	assert(pkt);

100 101 102 103
	pkt->wire = wire;
	pkt->size = pkt->max_size = len;
	pkt->parsed = 0;
}
104

105
/*! \brief Calculate remaining size in the packet. */
106
static uint16_t pkt_remaining(knot_pkt_t *pkt)
107
{
108 109
	assert(pkt);

110
	return pkt->max_size - pkt->size - pkt->reserved;
111
}
112

113 114 115 116 117 118 119 120
/*! \brief Return RR count for given section (from wire xxCOUNT in header). */
static uint16_t pkt_rr_wirecount(knot_pkt_t *pkt, knot_section_t section_id)
{
	assert(pkt);
	switch (section_id) {
	case KNOT_ANSWER:     return knot_wire_get_ancount(pkt->wire);
	case KNOT_AUTHORITY:  return knot_wire_get_nscount(pkt->wire);
	case KNOT_ADDITIONAL: return knot_wire_get_arcount(pkt->wire);
121
	default: assert(0);   return 0;
122
	}
123
}
Lubos Slovak's avatar
Lubos Slovak committed
124

125 126 127 128 129 130
/*! \brief Update RR count for given section (wire xxCOUNT in header). */
static void pkt_rr_wirecount_add(knot_pkt_t *pkt, knot_section_t section_id,
                                 int16_t val)
{
	assert(pkt);
	switch (section_id) {
131 132 133
	case KNOT_ANSWER:     knot_wire_add_ancount(pkt->wire, val); break;
	case KNOT_AUTHORITY:  knot_wire_add_nscount(pkt->wire, val); break;
	case KNOT_ADDITIONAL: knot_wire_add_arcount(pkt->wire, val); break;
Lubos Slovak's avatar
Lubos Slovak committed
134
	}
135
}
Lubos Slovak's avatar
Lubos Slovak committed
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/*! \brief Reserve enough space in the RR arrays. */
static int pkt_rr_array_alloc(knot_pkt_t *pkt, uint16_t count)
{
	/* Enough space. */
	if (pkt->rrset_allocd >= count) {
		return KNOT_EOK;
	}

	/* Allocate rr_info and rr fields to next size. */
	size_t next_size = NEXT_RR_COUNT(count);
	knot_rrinfo_t *rr_info = mm_alloc(&pkt->mm, sizeof(knot_rrinfo_t) * next_size);
	if (rr_info == NULL) {
		return KNOT_ENOMEM;
	}

	knot_rrset_t *rr = mm_alloc(&pkt->mm, sizeof(knot_rrset_t) * next_size);
	if (rr == NULL) {
		mm_free(&pkt->mm, rr_info);
		return KNOT_ENOMEM;
	}

	/* Copy the old data. */
	memcpy(rr_info, pkt->rr_info, pkt->rrset_allocd * sizeof(knot_rrinfo_t));
	memcpy(rr, pkt->rr, pkt->rrset_allocd * sizeof(knot_rrset_t));

	/* Reassign and free old data. */
	mm_free(&pkt->mm, pkt->rr);
	mm_free(&pkt->mm, pkt->rr_info);
	pkt->rr = rr;
	pkt->rr_info = rr_info;
	pkt->rrset_allocd = next_size;

	return KNOT_EOK;
}

172 173 174 175 176 177 178 179 180 181 182 183 184
static void compr_clear(knot_compr_t *compr)
{
	compr->rrinfo = NULL;
	compr->suffix.pos = 0;
	compr->suffix.labels = 0;
}

static void compr_init(knot_compr_t *compr, uint8_t *wire)
{
	compr_clear(compr);
	compr->wire = wire;
}

185
/*! \brief Clear the packet and switch wireformat pointers (possibly allocate new). */
186
static int pkt_init(knot_pkt_t *pkt, void *wire, uint16_t len, knot_mm_t *mm)
187
{
188
	assert(pkt);
189

190
	memset(pkt, 0, sizeof(knot_pkt_t));
191 192

	/* No data to free, set memory context. */
193
	memcpy(&pkt->mm, mm, sizeof(knot_mm_t));
194 195

	/* Initialize wire. */
196
	int ret = KNOT_EOK;
197 198 199 200 201 202
	if (wire == NULL) {
		ret = pkt_wire_alloc(pkt, len);
	} else {
		pkt_wire_set(pkt, wire, len);
	}

203 204 205
	/* Initialize compression context. */
	compr_init(&pkt->compr, pkt->wire);

206 207 208
	return ret;
}

209
/*! \brief Reset packet parse state. */
Daniel Salzman's avatar
Daniel Salzman committed
210
static void sections_reset(knot_pkt_t *pkt)
211 212 213
{
	pkt->current = KNOT_ANSWER;
	memset(pkt->sections, 0, sizeof(pkt->sections));
Daniel Salzman's avatar
Daniel Salzman committed
214
	(void)knot_pkt_begin(pkt, KNOT_ANSWER);
215 216 217
}

/*! \brief Allocate new packet using memory context. */
218
static knot_pkt_t *pkt_new_mm(void *wire, uint16_t len, knot_mm_t *mm)
219
{
220 221
	assert(mm);

222
	knot_pkt_t *pkt = mm_alloc(mm, sizeof(knot_pkt_t));
223
	if (pkt == NULL) {
224 225 226
		return NULL;
	}

227
	if (pkt_init(pkt, wire, len, mm) != KNOT_EOK) {
228
		mm_free(mm, pkt);
229
		return NULL;
230
	}
Jan Včelák's avatar
Jan Včelák committed
231

232 233
	return pkt;
}
Jan Včelák's avatar
Jan Včelák committed
234

235
_public_
236
knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm)
237 238
{
	/* Default memory allocator if NULL. */
239
	knot_mm_t _mm;
240
	if (mm == NULL) {
241
		mm_ctx_init(&_mm);
242
		mm = &_mm;
243 244
	}

245
	return pkt_new_mm(wire, len, mm);
246 247
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
static int append_tsig(knot_pkt_t *dst, const knot_pkt_t *src)
{
	/* Check if a wire TSIG is available. */
	if (src->tsig_wire.pos != NULL) {
		if (dst->max_size < src->size + src->tsig_wire.len) {
			return KNOT_ESPACE;
		}
		memcpy(dst->wire + dst->size, src->tsig_wire.pos,
		       src->tsig_wire.len);
		dst->size += src->tsig_wire.len;

		/* Increment arcount. */
		knot_wire_set_arcount(dst->wire,
		                      knot_wire_get_arcount(dst->wire) + 1);
	} else {
		return knot_tsig_append(dst->wire, &dst->size, dst->max_size,
		                        src->tsig_rr);
	}

	return KNOT_EOK;
}

270
_public_
271
int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src)
272
{
273 274
	if (dst == NULL || src == NULL) {
		return KNOT_EINVAL;
275 276
	}

277 278
	if (dst->max_size < src->size) {
		return KNOT_ESPACE;
279
	}
280
	memcpy(dst->wire, src->wire, src->size);
281
	dst->size = src->size;
282

283
	/* Append TSIG record. */
284
	if (src->tsig_rr) {
285
		int ret = append_tsig(dst, src);
286
		if (ret != KNOT_EOK) {
287
			return ret;
288 289 290
		}
	}

291
	/* Invalidate arrays. */
292 293 294 295 296
	dst->rr = NULL;
	dst->rr_info = NULL;
	dst->rrset_count = 0;
	dst->rrset_allocd = 0;

297
	/* @note This could be done more effectively if needed. */
298
	return knot_pkt_parse(dst, 0);
299 300
}

Daniel Salzman's avatar
Daniel Salzman committed
301 302 303 304 305 306 307 308 309 310 311 312 313
static void payload_clear(knot_pkt_t *pkt)
{
	assert(pkt);

	/* Keep question. */
	pkt->parsed = 0;
	pkt->reserved = 0;

	/* Free RRSets if applicable. */
	pkt_free_data(pkt);

	/* Reset sections. */
	sections_reset(pkt);
314 315 316 317 318 319 320 321

	/* Reset special types. */
	pkt->opt_rr = NULL;
	pkt->tsig_rr = NULL;

	/* Reset TSIG wire reference. */
	pkt->tsig_wire.pos = NULL;
	pkt->tsig_wire.len = 0;
Daniel Salzman's avatar
Daniel Salzman committed
322 323
}

324
_public_
325
int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query)
326
{
327 328
	if (pkt == NULL || query == NULL) {
		return KNOT_EINVAL;
329
	}
330

331
	/* Header + question size. */
332 333
	size_t base_size = KNOT_WIRE_HEADER_SIZE + knot_pkt_question_size(query);
	if (base_size > pkt->max_size) {
334
		return KNOT_ESPACE;
335
	}
336

337 338
	pkt->size = base_size;
	memcpy(pkt->wire, query->wire, base_size);
339

340 341 342 343 344 345
	pkt->qname_size = query->qname_size;
	if (query->qname_size == 0) {
		/* Reset question count if malformed. */
		knot_wire_set_qdcount(pkt->wire, 0);
	}

Daniel Salzman's avatar
Daniel Salzman committed
346 347 348 349 350
	/* Update flags and section counters. */
	knot_wire_set_ancount(pkt->wire, 0);
	knot_wire_set_nscount(pkt->wire, 0);
	knot_wire_set_arcount(pkt->wire, 0);

351 352 353 354
	knot_wire_set_qr(pkt->wire);
	knot_wire_clear_tc(pkt->wire);
	knot_wire_clear_ad(pkt->wire);
	knot_wire_clear_ra(pkt->wire);
355
	knot_wire_clear_aa(pkt->wire);
356
	knot_wire_clear_z(pkt->wire);
357

358
	/* Clear payload. */
Daniel Salzman's avatar
Daniel Salzman committed
359
	payload_clear(pkt);
360

Lubos Slovak's avatar
Lubos Slovak committed
361
	return KNOT_EOK;
362 363
}

364
_public_
365
void knot_pkt_clear(knot_pkt_t *pkt)
366
{
367 368 369
	if (pkt == NULL) {
		return;
	}
370

371 372
	/* Reset to header size. */
	pkt->size = KNOT_WIRE_HEADER_SIZE;
373
	memset(pkt->wire, 0, pkt->size);
374

Daniel Salzman's avatar
Daniel Salzman committed
375 376 377
	/* Clear payload. */
	payload_clear(pkt);

378 379
	/* Clear compression context. */
	compr_clear(&pkt->compr);
380
}
Lubos Slovak's avatar
Lubos Slovak committed
381

382
_public_
383
void knot_pkt_free(knot_pkt_t *pkt)
384
{
385
	if (pkt == NULL) {
386
		return;
387 388
	}

389
	/* Free temporary RRSets. */
390
	pkt_free_data(pkt);
391

392
	/* Free RR/RR info arrays. */
393 394
	mm_free(&pkt->mm, pkt->rr);
	mm_free(&pkt->mm, pkt->rr_info);
395

396
	/* Free the space for wireformat. */
397 398
	if (pkt->flags & KNOT_PF_FREE) {
		mm_free(&pkt->mm, pkt->wire);
399 400
	}

401
	mm_free(&pkt->mm, pkt);
402
}
403

404
_public_
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size)
{
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

	/* Reserve extra space (if possible). */
	if (pkt_remaining(pkt) >= size) {
		pkt->reserved += size;
		return KNOT_EOK;
	} else {
		return KNOT_ERANGE;
	}
}

420
_public_
421 422 423 424 425 426 427 428 429 430 431 432 433
int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size)
{
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

	if (pkt->reserved >= size) {
		pkt->reserved -= size;
		return KNOT_EOK;
	} else {
		return KNOT_ERANGE;
	}
}
434

435
_public_
436 437
int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id)
{
438
	if (pkt == NULL || section_id < pkt->current) {
439 440 441
		return KNOT_EINVAL;
	}

442
	/* Remember watermark but not on repeated calls. */
443
	pkt->sections[section_id].pkt = pkt;
444 445 446 447 448
	if (section_id > pkt->current) {
		pkt->sections[section_id].pos = pkt->rrset_count;
	}

	pkt->current = section_id;
449

Lubos Slovak's avatar
Lubos Slovak committed
450
	return KNOT_EOK;
451 452
}

453
_public_
454
int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
455
{
456
	if (pkt == NULL || qname == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
457
		return KNOT_EINVAL;
458 459
	}

460 461
	assert(pkt->size == KNOT_WIRE_HEADER_SIZE);
	assert(pkt->rrset_count == 0);
462

463
	/* Copy name wireformat. */
464 465 466 467 468
	wire_ctx_t wire = wire_ctx_init(pkt->wire, pkt->max_size);
	wire_ctx_set_offset(&wire, KNOT_WIRE_HEADER_SIZE);

	int qname_len = knot_dname_to_wire(wire.position,
	                                   qname, wire_ctx_available(&wire));
469 470 471
	if (qname_len < 0) {
		return qname_len;
	}
472
	wire_ctx_skip(&wire, qname_len);
Jan Včelák's avatar
Jan Včelák committed
473

474
	/* Copy QTYPE & QCLASS */
475 476 477 478 479 480 481
	wire_ctx_write_u16(&wire, qtype);
	wire_ctx_write_u16(&wire, qclass);

	/* Check errors. */
	if (wire.error != KNOT_EOK) {
		return wire.error;
	}
482

483 484
	/* Update question count and sizes. */
	knot_wire_set_qdcount(pkt->wire, 1);
485
	pkt->size = wire_ctx_offset(&wire);
486
	pkt->qname_size = qname_len;
487

488 489
	/* Start writing ANSWER. */
	return knot_pkt_begin(pkt, KNOT_ANSWER);
490 491
}

492
_public_
493 494
int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
                        uint16_t rotate, uint16_t flags)
495
{
496 497 498
	if (pkt == NULL || rr == NULL) {
		return KNOT_EINVAL;
	}
499

500 501 502 503 504 505
	/* Reserve memory for RR descriptors. */
	int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1);
	if (ret != KNOT_EOK) {
		return ret;
	}

506 507
	/* Check for double insertion. */
	if ((flags & KNOT_PF_CHECKDUP) && pkt_contains(pkt, rr)) {
508
		return KNOT_EOK;
509 510
	}

511 512 513 514
	knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count];
	memset(rrinfo, 0, sizeof(knot_rrinfo_t));
	rrinfo->pos = pkt->size;
	rrinfo->flags = flags;
515
	rrinfo->compress_ptr[0] = compr_hint;
516
	memcpy(pkt->rr + pkt->rrset_count, rr, sizeof(knot_rrset_t));
517

518 519 520 521 522 523 524 525 526 527 528 529 530
	/* Disable compression if no QNAME is available. */
	knot_compr_t *compr = NULL;
	if (knot_pkt_qname(pkt) != NULL) {
		/* Initialize compression context if it did not happen yet. */
		pkt->compr.rrinfo = rrinfo;
		if (pkt->compr.suffix.pos == 0) {
			pkt->compr.suffix.pos = KNOT_WIRE_HEADER_SIZE;
			pkt->compr.suffix.labels =
				knot_dname_labels(pkt->compr.wire + pkt->compr.suffix.pos,
				                  pkt->compr.wire);
		}

		compr = &pkt->compr;
531
	}
532

533 534 535
	uint8_t *pos = pkt->wire + pkt->size;
	size_t maxlen = pkt_remaining(pkt);

536
	/* Write RRSet to wireformat. */
537
	ret = knot_rrset_to_wire_rotate(rr, pos, maxlen, rotate, compr);
538
	if (ret < 0) {
539
		/* Truncate packet if required. */
540
		if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) {
541
			knot_wire_set_tc(pkt->wire);
542 543
		}
		return ret;
544 545
	}

546 547 548
	size_t len = ret;
	uint16_t rr_added = rr->rrs.rr_count;

549 550 551 552 553
	/* Keep reference to special types. */
	if (rr->type == KNOT_RRTYPE_OPT) {
		pkt->opt_rr = &pkt->rr[pkt->rrset_count];
	}

554
	if (rr_added > 0) {
555 556
		pkt->rrset_count += 1;
		pkt->sections[pkt->current].count += 1;
557 558 559
		pkt->size += len;
		pkt_rr_wirecount_add(pkt, pkt->current, rr_added);
	}
560

561
	return KNOT_EOK;
562 563
}

564
_public_
565
int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags)
566
{
567
	if (pkt == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
568
		return KNOT_EINVAL;
569
	}
Jan Včelák's avatar
Jan Včelák committed
570

571
	/* Reset parse state. */
Daniel Salzman's avatar
Daniel Salzman committed
572
	sections_reset(pkt);
573

574 575 576
	int ret = knot_pkt_parse_question(pkt);
	if (ret == KNOT_EOK) {
		ret = knot_pkt_parse_payload(pkt, flags);
577
	}
Jan Včelák's avatar
Jan Včelák committed
578

579
	return ret;
580 581
}

582
_public_
583
int knot_pkt_parse_question(knot_pkt_t *pkt)
584
{
585 586 587
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}
588

589 590 591 592
	/* Check at least header size. */
	if (pkt->size < KNOT_WIRE_HEADER_SIZE) {
		return KNOT_EMALF;
	}
593

594 595 596
	/* We have at least some DNS header. */
	pkt->parsed = KNOT_WIRE_HEADER_SIZE;

597 598 599 600
	/* Check QD count. */
	uint16_t qd = knot_wire_get_qdcount(pkt->wire);
	if (qd > 1) {
		return KNOT_EMALF;
601 602
	}

603 604 605 606
	/* No question. */
	if (qd == 0) {
		pkt->qname_size = 0;
		return KNOT_EOK;
607 608
	}

609 610 611
	/* Process question. */
	int len = knot_dname_wire_check(pkt->wire + pkt->parsed,
	                                pkt->wire + pkt->size,
612
	                                NULL /* No compression in QNAME. */);
613 614
	if (len <= 0) {
		return KNOT_EMALF;
615
	}
616

617 618 619 620 621
	/* Check QCLASS/QTYPE size. */
	uint16_t question_size = len + 2 * sizeof(uint16_t); /* QCLASS + QTYPE */
	if (pkt->parsed + question_size > pkt->size) {
		return KNOT_EMALF;
	}
622

623
	pkt->parsed += question_size;
624
	pkt->qname_size = len;
625

626
	return KNOT_EOK;
627 628
}

629 630 631 632 633
/*! \brief Check constraints (position, uniqueness, validity) for special types
 *         (TSIG, OPT).
 */
static int check_rr_constraints(knot_pkt_t *pkt, knot_rrset_t *rr, size_t rr_size,
                                unsigned flags)
634
{
635
	switch (rr->type) {
636
	case KNOT_RRTYPE_TSIG:
637 638 639 640
		if (pkt->current != KNOT_ADDITIONAL || pkt->tsig_rr != NULL ||
		    !knot_tsig_rdata_is_ok(rr)) {
			return KNOT_EMALF;
		}
641 642 643 644 645

		/* Strip TSIG RR from wireformat and decrease ARCOUNT. */
		if (!(flags & KNOT_PF_KEEPWIRE)) {
			pkt->parsed -= rr_size;
			pkt->size -= rr_size;
646 647
			pkt->tsig_wire.pos = pkt->wire + pkt->parsed;
			pkt->tsig_wire.len = rr_size;
648 649
			knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1);
		}
650 651

		pkt->tsig_rr = rr;
652 653
		break;
	case KNOT_RRTYPE_OPT:
654 655 656 657 658
		if (pkt->current != KNOT_ADDITIONAL || pkt->opt_rr != NULL ||
		    knot_edns_get_options(rr, &pkt->edns_opts, &pkt->mm) != KNOT_EOK) {
			return KNOT_EMALF;
		}

659
		pkt->opt_rr = rr;
660 661 662 663 664 665 666 667
		break;
	default:
		break;
	}

	return KNOT_EOK;
}

668
_public_
669
int knot_pkt_parse_rr(knot_pkt_t *pkt, unsigned flags)
Lubos Slovak's avatar
Lubos Slovak committed
670
{
671 672 673 674
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

675 676
	if (pkt->parsed >= pkt->size) {
		return KNOT_EFEWDATA;
677 678
	}

679 680 681 682 683 684
	/* Reserve memory for RR descriptors. */
	int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1);
	if (ret != KNOT_EOK) {
		return ret;
	}

685 686 687 688
	/* Initialize RR info. */
	memset(&pkt->rr_info[pkt->rrset_count], 0, sizeof(knot_rrinfo_t));
	pkt->rr_info[pkt->rrset_count].pos = pkt->parsed;
	pkt->rr_info[pkt->rrset_count].flags = KNOT_PF_FREE;
Lubos Slovak's avatar
Lubos Slovak committed
689

690
	/* Parse wire format. */
691
	size_t rr_size = pkt->parsed;
692
	knot_rrset_t *rr = &pkt->rr[pkt->rrset_count];
693
	ret = knot_rrset_rr_from_wire(pkt->wire, &pkt->parsed, pkt->size,
694
	                              rr, &pkt->mm, !(flags & KNOT_PF_NOCANON));
695 696
	if (ret != KNOT_EOK) {
		return ret;
Lubos Slovak's avatar
Lubos Slovak committed
697
	}
698

699 700
	/* Calculate parsed RR size from before/after parsing. */
	rr_size = (pkt->parsed - rr_size);
Lubos Slovak's avatar
Lubos Slovak committed
701

702
	/* Update packet RRSet count. */
703 704 705
	++pkt->rrset_count;
	++pkt->sections[pkt->current].count;

706 707
	/* Check special RRs (OPT and TSIG). */
	return check_rr_constraints(pkt, rr, rr_size, flags);
Lubos Slovak's avatar
Lubos Slovak committed
708 709
}

710
static int parse_section(knot_pkt_t *pkt, unsigned flags)
711
{
712
	assert(pkt);
713

714 715 716 717 718
	uint16_t rr_parsed = 0;
	uint16_t rr_count = pkt_rr_wirecount(pkt, pkt->current);

	/* Parse all RRs belonging to the section. */
	for (rr_parsed = 0; rr_parsed < rr_count; ++rr_parsed) {
719
		int ret = knot_pkt_parse_rr(pkt, flags);
720
		if (ret != KNOT_EOK) {
721
			return ret;
722
		}
723 724
	}

Lubos Slovak's avatar
Lubos Slovak committed
725
	return KNOT_EOK;
726 727
}

728
_public_
729
int knot_pkt_parse_payload(knot_pkt_t *pkt, unsigned flags)
730
{
731 732 733 734
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

735 736
	assert(pkt->wire != NULL);
	assert(pkt->size > 0);
737

738 739 740 741
	/* Reserve memory in advance to avoid resizing. */
	size_t rr_count = knot_wire_get_ancount(pkt->wire) +
	                  knot_wire_get_nscount(pkt->wire) +
	                  knot_wire_get_arcount(pkt->wire);
742 743 744 745 746

	if (rr_count > pkt->size / KNOT_WIRE_RR_MIN_SIZE) {
		return KNOT_EMALF;
	}

747 748 749 750
	int ret = pkt_rr_array_alloc(pkt, rr_count);
	if (ret != KNOT_EOK) {
		return ret;
	}
751

752 753 754 755 756
	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
		ret = knot_pkt_begin(pkt, i);
		if (ret != KNOT_EOK) {
			return ret;
		}
757
		ret = parse_section(pkt, flags);
758 759 760
		if (ret != KNOT_EOK) {
			return ret;
		}
761 762
	}

763 764 765
	/* TSIG must be last record of AR if present. */
	const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL);
	if (pkt->tsig_rr != NULL) {
766 767
		const knot_rrset_t *last_rr = knot_pkt_rr(ar, ar->count - 1);
		if (ar->count > 0 && pkt->tsig_rr->rrs.data != last_rr->rrs.data) {
768 769 770
			return KNOT_EMALF;
		}
	}
771

772 773
	/* Check for trailing garbage. */
	if (pkt->parsed < pkt->size) {
774
		return KNOT_ETRAIL;
775
	}
776

Lubos Slovak's avatar
Lubos Slovak committed
777
	return KNOT_EOK;
778
}
779

780
_public_
781
uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt)
782 783 784 785 786
{
	if (pkt == NULL) {
		return 0;
	}

787 788
	/* Get header RCODE. */
	uint16_t rcode = knot_wire_get_rcode(pkt->wire);
789

790 791
	/* Update to extended RCODE if EDNS is available. */
	if (pkt->opt_rr != NULL) {
792
		uint8_t opt_rcode = knot_edns_get_ext_rcode(pkt->opt_rr);
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
		rcode = knot_edns_whole_rcode(opt_rcode, rcode);
	}

	/* Return if not NOTAUTH. */
	if (rcode != KNOT_RCODE_NOTAUTH) {
		return rcode;
	}

	/* Get TSIG RCODE. */
	uint16_t tsig_rcode = KNOT_RCODE_NOERROR;
	if (pkt->tsig_rr != NULL) {
		tsig_rcode = knot_tsig_rdata_error(pkt->tsig_rr);
	}

	/* Return proper RCODE. */
	if (tsig_rcode != KNOT_RCODE_NOERROR) {
		return tsig_rcode;
810 811 812 813
	} else {
		return rcode;
	}
}
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

_public_
const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt)
{
	if (pkt == NULL) {
		return "";
	}

	uint16_t rcode = knot_pkt_ext_rcode(pkt);

	const knot_lookup_t *item = NULL;
	if (pkt->tsig_rr != NULL) {
		item = knot_lookup_by_id(knot_tsig_rcode_names, rcode);
	}
	if (item == NULL) {
		item = knot_lookup_by_id(knot_rcode_names, rcode);
	}

	return (item != NULL) ? item->name : "";
}