pkt.c 18.7 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 47
		const knot_rdata_t *data = packet->rr[i].rrs.rdata;
		if (type == rrset->type && data == rrset->rrs.rdata) {
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
	size_t len = ret;
547
	uint16_t rr_added = rr->rrs.count;
548

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
static int parse_question(knot_pkt_t *pkt)
565
{
566
	assert(pkt);
567

568 569 570 571
	/* Check at least header size. */
	if (pkt->size < KNOT_WIRE_HEADER_SIZE) {
		return KNOT_EMALF;
	}
572

573 574 575
	/* We have at least some DNS header. */
	pkt->parsed = KNOT_WIRE_HEADER_SIZE;

576 577 578 579
	/* Check QD count. */
	uint16_t qd = knot_wire_get_qdcount(pkt->wire);
	if (qd > 1) {
		return KNOT_EMALF;
580 581
	}

582 583 584 585
	/* No question. */
	if (qd == 0) {
		pkt->qname_size = 0;
		return KNOT_EOK;
586 587
	}

588 589 590
	/* Process question. */
	int len = knot_dname_wire_check(pkt->wire + pkt->parsed,
	                                pkt->wire + pkt->size,
591
	                                NULL /* No compression in QNAME. */);
592 593
	if (len <= 0) {
		return KNOT_EMALF;
594
	}
595

596 597 598 599 600
	/* 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;
	}
601

602
	pkt->parsed += question_size;
603
	pkt->qname_size = len;
604

605
	return KNOT_EOK;
606 607
}

608 609 610 611 612
/*! \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)
613
{
614
	switch (rr->type) {
615
	case KNOT_RRTYPE_TSIG:
616 617 618 619
		if (pkt->current != KNOT_ADDITIONAL || pkt->tsig_rr != NULL ||
		    !knot_tsig_rdata_is_ok(rr)) {
			return KNOT_EMALF;
		}
620 621 622 623 624

		/* Strip TSIG RR from wireformat and decrease ARCOUNT. */
		if (!(flags & KNOT_PF_KEEPWIRE)) {
			pkt->parsed -= rr_size;
			pkt->size -= rr_size;
625 626
			pkt->tsig_wire.pos = pkt->wire + pkt->parsed;
			pkt->tsig_wire.len = rr_size;
627 628
			knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1);
		}
629 630

		pkt->tsig_rr = rr;
631 632
		break;
	case KNOT_RRTYPE_OPT:
633 634 635 636 637
		if (pkt->current != KNOT_ADDITIONAL || pkt->opt_rr != NULL ||
		    knot_edns_get_options(rr, &pkt->edns_opts, &pkt->mm) != KNOT_EOK) {
			return KNOT_EMALF;
		}

638
		pkt->opt_rr = rr;
639 640 641 642 643 644 645 646
		break;
	default:
		break;
	}

	return KNOT_EOK;
}

647
static int parse_rr(knot_pkt_t *pkt, unsigned flags)
Lubos Slovak's avatar
Lubos Slovak committed
648
{
649
	assert(pkt);
650

651 652
	if (pkt->parsed >= pkt->size) {
		return KNOT_EFEWDATA;
653 654
	}

655 656 657 658 659 660
	/* Reserve memory for RR descriptors. */
	int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1);
	if (ret != KNOT_EOK) {
		return ret;
	}

661 662 663 664
	/* 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
665

666
	/* Parse wire format. */
667
	size_t rr_size = pkt->parsed;
668
	knot_rrset_t *rr = &pkt->rr[pkt->rrset_count];
669
	ret = knot_rrset_rr_from_wire(pkt->wire, &pkt->parsed, pkt->size,
670
	                              rr, &pkt->mm, !(flags & KNOT_PF_NOCANON));
671 672
	if (ret != KNOT_EOK) {
		return ret;
Lubos Slovak's avatar
Lubos Slovak committed
673
	}
674

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

678
	/* Update packet RRSet count. */
679 680 681
	++pkt->rrset_count;
	++pkt->sections[pkt->current].count;

682 683
	/* Check special RRs (OPT and TSIG). */
	return check_rr_constraints(pkt, rr, rr_size, flags);
Lubos Slovak's avatar
Lubos Slovak committed
684 685
}

686
static int parse_section(knot_pkt_t *pkt, unsigned flags)
687
{
688
	assert(pkt);
689

690 691 692 693 694
	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) {
695
		int ret = parse_rr(pkt, flags);
696
		if (ret != KNOT_EOK) {
697
			return ret;
698
		}
699 700
	}

Lubos Slovak's avatar
Lubos Slovak committed
701
	return KNOT_EOK;
702 703
}

704
static int parse_payload(knot_pkt_t *pkt, unsigned flags)
705
{
706 707
	assert(pkt);
	assert(pkt->wire);
708
	assert(pkt->size > 0);
709

710 711 712 713
	/* 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);
714 715 716 717 718

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

719 720 721 722
	int ret = pkt_rr_array_alloc(pkt, rr_count);
	if (ret != KNOT_EOK) {
		return ret;
	}
723

724 725 726 727 728
	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
		ret = knot_pkt_begin(pkt, i);
		if (ret != KNOT_EOK) {
			return ret;
		}
729
		ret = parse_section(pkt, flags);
730 731 732
		if (ret != KNOT_EOK) {
			return ret;
		}
733 734
	}

735 736 737
	/* 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) {
738
		const knot_rrset_t *last_rr = knot_pkt_rr(ar, ar->count - 1);
739
		if (ar->count > 0 && pkt->tsig_rr->rrs.rdata != last_rr->rrs.rdata) {
740 741 742
			return KNOT_EMALF;
		}
	}
743

744 745
	/* Check for trailing garbage. */
	if (pkt->parsed < pkt->size) {
746
		return KNOT_ETRAIL;
747
	}
748

Lubos Slovak's avatar
Lubos Slovak committed
749
	return KNOT_EOK;
750
}
751

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
_public_
int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags)
{
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

	/* Reset parse state. */
	sections_reset(pkt);

	int ret = parse_question(pkt);
	if (ret == KNOT_EOK) {
		ret = parse_payload(pkt, flags);
	}

	return ret;
}

770
_public_
771
uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt)
772 773 774 775 776
{
	if (pkt == NULL) {
		return 0;
	}

777 778
	/* Get header RCODE. */
	uint16_t rcode = knot_wire_get_rcode(pkt->wire);
779

780 781
	/* Update to extended RCODE if EDNS is available. */
	if (pkt->opt_rr != NULL) {
782
		uint8_t opt_rcode = knot_edns_get_ext_rcode(pkt->opt_rr);
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
		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;
800 801 802 803
	} else {
		return rcode;
	}
}
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823

_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 : "";
}