pkt.c 20.1 KB
Newer Older
1
/*  Copyright (C) 2011 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/>.
 */

Lubos Slovak's avatar
Lubos Slovak committed
17
#include <assert.h>
18
#include <stdlib.h>
19
#include <stdbool.h>
Lubos Slovak's avatar
Lubos Slovak committed
20

Marek Vavrusa's avatar
Marek Vavrusa committed
21
#include "libknot/packet/pkt.h"
22

23
#include "common/debug.h"
24
#include "common/log.h"
25
#include "libknot/common.h"
26
#include "libknot/descriptor.h"
27
#include "libknot/packet/wire.h"
28
#include "libknot/rrtype/tsig.h"
29
#include "libknot/tsig-op.h"
Lubos Slovak's avatar
Lubos Slovak committed
30

31
/*! \brief Scan packet for RRSet existence. */
32
static bool pkt_contains(const knot_pkt_t *packet,
33
			 const knot_rrset_t *rrset)
34
{
35 36
	assert(packet);
	assert(rrset);
37

38
	for (int i = 0; i < packet->rrset_count; ++i) {
39 40 41
		const uint16_t type = packet->rr[i].type;
		const uint8_t *data = packet->rr[i].rrs.data;
		if (type == rrset->type && data == rrset->rrs.data) {
42
			return true;
43 44
		}
	}
45

46
	return false;
47 48
}

49
/*! \brief Free all RRSets and reset RRSet count. */
50
static void pkt_free_data(knot_pkt_t *pkt)
51
{
52 53
	assert(pkt);

54 55 56
	/* Free RRSets if applicable. */
	for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
		if (pkt->rr_info[i].flags & KNOT_PF_FREE) {
57
			knot_rrset_clear(&pkt->rr[i], &pkt->mm);
58
		}
59
	}
Jan Včelák's avatar
Jan Včelák committed
60

61 62
	/* Reset RR count. */
	pkt->rrset_count = 0;
63 64 65 66

	/* Reset special types. */
	pkt->opt_rr = NULL;
	pkt->tsig_rr = NULL;
67
}
Jan Včelák's avatar
Jan Včelák committed
68

69
/*! \brief Allocate new wireformat of given length. */
70 71
static int pkt_wire_alloc(knot_pkt_t *pkt, uint16_t len)
{
72
	assert(pkt);
Marek Vavruša's avatar
Marek Vavruša committed
73
	assert(len >= KNOT_WIRE_HEADER_SIZE);
74

75 76 77
	pkt->wire = pkt->mm.alloc(pkt->mm.ctx, len);
	if (pkt->wire == NULL) {
		return KNOT_ENOMEM;
78
	}
Jan Včelák's avatar
Jan Včelák committed
79

80 81 82
	pkt->flags |= KNOT_PF_FREE;
	pkt->max_size = len;
	knot_pkt_clear(pkt);
83
	return KNOT_EOK;
84 85
}

86
/*! \brief Set packet wireformat to an existing memory. */
87 88
static void pkt_wire_set(knot_pkt_t *pkt, void *wire, uint16_t len)
{
89 90
	assert(pkt);

91 92 93 94
	pkt->wire = wire;
	pkt->size = pkt->max_size = len;
	pkt->parsed = 0;
}
95

96
/*! \brief Calculate remaining size in the packet. */
97
static uint16_t pkt_remaining(knot_pkt_t *pkt)
98
{
99 100
	assert(pkt);

101
	return pkt->max_size - pkt->size - pkt->reserved;
102
}
103

104 105 106 107 108 109 110 111
/*! \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);
112
	default: assert(0);   return 0;
113
	}
114
}
Lubos Slovak's avatar
Lubos Slovak committed
115

116 117 118 119 120 121 122 123 124
/*! \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) {
	case KNOT_ANSWER:     return knot_wire_add_ancount(pkt->wire, val);
	case KNOT_AUTHORITY:  return knot_wire_add_nscount(pkt->wire, val);
	case KNOT_ADDITIONAL: return knot_wire_add_arcount(pkt->wire, val);
Lubos Slovak's avatar
Lubos Slovak committed
125
	}
126
}
Lubos Slovak's avatar
Lubos Slovak committed
127

128
/*! \brief Clear the packet and switch wireformat pointers (possibly allocate new). */
129
static int pkt_reset(knot_pkt_t *pkt, void *wire, uint16_t len)
130
{
131
	assert(pkt);
132 133 134 135 136 137 138

	/* Free allocated data. */
	pkt_free_data(pkt);

	/* NULL everything up to 'sections' (not the large data fields). */
	int ret = KNOT_EOK;
	mm_ctx_t mm = pkt->mm;
139
	memset(pkt, 0, offsetof(knot_pkt_t, rr_info));
140 141 142 143 144 145 146 147 148 149 150 151 152
	pkt->mm = mm;

	/* Initialize wire. */
	if (wire == NULL) {
		ret = pkt_wire_alloc(pkt, len);
	} else {
		pkt_wire_set(pkt, wire, len);
	}

	return ret;
}

/*! \brief Clear packet payload and free allocated data. */
153
static void pkt_clear_payload(knot_pkt_t *pkt)
154 155
{
	dbg_packet("%s(%p)\n", __func__, pkt);
156
	assert(pkt);
157 158 159

	/* Keep question. */
	pkt->parsed = 0;
160
	pkt->size = KNOT_WIRE_HEADER_SIZE + knot_pkt_question_size(pkt);
161 162 163 164 165 166 167 168 169 170 171 172 173 174
	knot_wire_set_ancount(pkt->wire, 0);
	knot_wire_set_nscount(pkt->wire, 0);
	knot_wire_set_arcount(pkt->wire, 0);

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

	/* Reset section. */
	pkt->current = KNOT_ANSWER;
	pkt->sections[pkt->current].rr = pkt->rr;
	pkt->sections[pkt->current].rrinfo = pkt->rr_info;
}

/*! \brief Allocate new packet using memory context. */
175 176
static knot_pkt_t *pkt_new_mm(void *wire, uint16_t len, mm_ctx_t *mm)
{
177 178
	assert(mm);

179
	knot_pkt_t *pkt = mm_alloc(mm, sizeof(knot_pkt_t));
180
	if (pkt == NULL) {
181 182 183
		return NULL;
	}

184 185
	/* No data to free, set memory context. */
	pkt->rrset_count = 0;
186
	memcpy(&pkt->mm, mm, sizeof(mm_ctx_t));
187
	if (pkt_reset(pkt, wire, len) != KNOT_EOK) {
188
		mm_free(mm, pkt);
189
		return NULL;
Lubos Slovak's avatar
Lubos Slovak committed
190
	}
Jan Včelák's avatar
Jan Včelák committed
191

192 193
	return pkt;
}
Jan Včelák's avatar
Jan Včelák committed
194

195 196 197 198 199 200 201 202
knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, mm_ctx_t *mm)
{
	/* Default memory allocator if NULL. */
	dbg_packet("%s(%p, %hu, %p)\n", __func__, wire, len, mm);
	mm_ctx_t _mm;
	if (mm == NULL) {
		mm_ctx_init(&_mm);
		mm = &_mm;
203 204
	}

205
	return pkt_new_mm(wire, len, mm);
206 207
}

Marek Vavruša's avatar
Marek Vavruša committed
208
int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src)
209
{
Marek Vavruša's avatar
Marek Vavruša committed
210 211 212
	dbg_packet("%s(%p, %p)\n", __func__, dst, src);
	if (dst == NULL || src == NULL) {
		return KNOT_EINVAL;
213 214
	}

Marek Vavruša's avatar
Marek Vavruša committed
215 216
	if (dst->max_size < src->size) {
		return KNOT_ESPACE;
217 218
	}

Marek Vavruša's avatar
Marek Vavruša committed
219 220
	dst->size = src->size;
	memcpy(dst->wire, src->wire, dst->size);
221 222

	/* Copy TSIG RR back to wire. */
Marek Vavruša's avatar
Marek Vavruša committed
223 224 225
	if (src->tsig_rr) {
		int ret = knot_tsig_append(dst->wire, &dst->size, dst->max_size,
		                           src->tsig_rr);
226
		if (ret != KNOT_EOK) {
Marek Vavruša's avatar
Marek Vavruša committed
227
			return ret;
228 229 230 231
		}
	}

	/* @note This could be done more effectively if needed. */
Marek Vavruša's avatar
Marek Vavruša committed
232
	return knot_pkt_parse(dst, 0);
233 234
}

235
int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query)
236
{
237 238 239
	dbg_packet("%s(%p, %p)\n", __func__, pkt, query);
	if (pkt == NULL || query == NULL) {
		return KNOT_EINVAL;
240
	}
241

242
	/* Header + question size. */
243 244 245
	size_t base_size = KNOT_WIRE_HEADER_SIZE + knot_pkt_question_size(query);
	if (base_size > pkt->max_size) {
		dbg_packet("%s: can't fit HEADER + QUESTION\n", __func__);
246
		return KNOT_ESPACE;
247
	}
248

249
	pkt->size = base_size;
250
	pkt->qname_size = query->qname_size;
251
	memcpy(pkt->wire, query->wire, base_size);
252

253 254 255 256 257
	/* Update size and flags. */
	knot_wire_set_qr(pkt->wire);
	knot_wire_clear_tc(pkt->wire);
	knot_wire_clear_ad(pkt->wire);
	knot_wire_clear_ra(pkt->wire);
258
	knot_wire_clear_aa(pkt->wire);
259

260
	/* Clear payload. */
261
	pkt_clear_payload(pkt);
Lubos Slovak's avatar
Lubos Slovak committed
262
	return KNOT_EOK;
263 264
}

265
void knot_pkt_clear(knot_pkt_t *pkt)
266
{
267 268 269 270
	dbg_packet("%s(%p)\n", __func__, pkt);
	if (pkt == NULL) {
		return;
	}
271

272
	/* Clear payload. */
273
	pkt_clear_payload(pkt);
Lubos Slovak's avatar
Lubos Slovak committed
274

275 276 277 278
	/* Reset to header size. */
	pkt->size = KNOT_WIRE_HEADER_SIZE;
	memset(pkt->wire, 0, pkt->size);
}
Lubos Slovak's avatar
Lubos Slovak committed
279

280
void knot_pkt_free(knot_pkt_t **pkt)
281 282
{
	dbg_packet("%s(%p)\n", __func__, pkt);
283
	if (pkt == NULL || *pkt == NULL) {
284
		return;
Lubos Slovak's avatar
Lubos Slovak committed
285 286
	}

287
	/* Free temporary RRSets. */
288
	pkt_free_data(*pkt);
289

290
	// free the space for wireformat
291 292
	if ((*pkt)->flags & KNOT_PF_FREE) {
		(*pkt)->mm.free((*pkt)->wire);
Lubos Slovak's avatar
Lubos Slovak committed
293 294
	}

295
	dbg_packet("Freeing packet structure\n");
296 297
	(*pkt)->mm.free(*pkt);
	*pkt = NULL;
298
}
Lubos Slovak's avatar
Lubos Slovak committed
299

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size)
{
	dbg_packet("%s(%p, %hu)\n", __func__, pkt, 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;
	}
}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size)
{
	dbg_packet("%s(%p, %hu)\n", __func__, pkt, size);
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

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

}
331

332
uint16_t knot_pkt_type(const knot_pkt_t *pkt)
333 334
{
	dbg_packet("%s(%p)\n", __func__, pkt);
335 336 337 338
	if (pkt == NULL) {
		return 0;
	}

339
	bool is_query = (knot_wire_get_qr(pkt->wire) == 0);
340
	uint16_t ret = KNOT_QUERY_INVALID;
341 342
	uint8_t opcode = knot_wire_get_opcode(pkt->wire);
	uint8_t query_type = knot_pkt_qtype(pkt);
Lubos Slovak's avatar
Lubos Slovak committed
343

344 345 346 347 348 349 350
	switch (opcode) {
	case KNOT_OPCODE_QUERY:
		switch (query_type) {
		case 0 /* RESERVED */: /* INVALID */ break;
		case KNOT_RRTYPE_AXFR: ret = KNOT_QUERY_AXFR; break;
		case KNOT_RRTYPE_IXFR: ret = KNOT_QUERY_IXFR; break;
		default:               ret = KNOT_QUERY_NORMAL; break;
Lubos Slovak's avatar
Lubos Slovak committed
351
		}
352 353 354 355
		break;
	case KNOT_OPCODE_NOTIFY: ret = KNOT_QUERY_NOTIFY; break;
	case KNOT_OPCODE_UPDATE: ret = KNOT_QUERY_UPDATE; break;
	default: break;
Lubos Slovak's avatar
Lubos Slovak committed
356 357
	}

358 359
	if (!is_query) {
		ret = ret|KNOT_RESPONSE;
Lubos Slovak's avatar
Lubos Slovak committed
360 361
	}

362
	return ret;
Lubos Slovak's avatar
Lubos Slovak committed
363 364
}

365 366
/*----------------------------------------------------------------------------*/

367
uint16_t knot_pkt_question_size(const knot_pkt_t *pkt)
368
{
369
	dbg_packet("%s(%p)\n", __func__, pkt);
370
	if (pkt == NULL || pkt->qname_size == 0) {
371 372 373
		return 0;
	}

374
	return pkt->qname_size + 2 * sizeof(uint16_t);
375 376
}

377
/*----------------------------------------------------------------------------*/
378

379
const knot_dname_t *knot_pkt_qname(const knot_pkt_t *pkt)
380 381
{
	dbg_packet("%s(%p)\n", __func__, pkt);
382
	if (pkt == NULL || pkt->qname_size == 0) {
383 384
		return NULL;
	}
385

386
	return pkt->wire + KNOT_WIRE_HEADER_SIZE;
387 388 389 390
}

/*----------------------------------------------------------------------------*/

391
uint16_t knot_pkt_qtype(const knot_pkt_t *pkt)
392
{
393
	dbg_packet("%s(%p)\n", __func__, pkt);
394
	if (pkt == NULL || pkt->qname_size == 0) {
395
		return 0;
396 397
	}

398 399
	unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size;
	return knot_wire_read_u16(pkt->wire + off);
400 401 402
}

/*----------------------------------------------------------------------------*/
403

404
uint16_t knot_pkt_qclass(const knot_pkt_t *pkt)
405
{
406
	dbg_packet("%s(%p)\n", __func__, pkt);
407
	if (pkt == NULL || pkt->qname_size == 0) {
408
		return 0;
Lubos Slovak's avatar
Lubos Slovak committed
409
	}
Lubos Slovak's avatar
Lubos Slovak committed
410

411 412
	unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size + sizeof(uint16_t);
	return knot_wire_read_u16(pkt->wire + off);
413 414
}

415 416
int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id)
{
417 418 419 420
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

421 422 423 424
	/* Cannot step to lower section. */
	dbg_packet("%s(%p, %u)\n", __func__, pkt, section_id);
	assert(section_id >= pkt->current);
	pkt->current = section_id;
425

426 427
	/* Remember watermark. */
	pkt->sections[section_id].rr = pkt->rr + pkt->rrset_count;
428
	pkt->sections[section_id].rrinfo = pkt->rr_info + pkt->rrset_count;
Lubos Slovak's avatar
Lubos Slovak committed
429
	return KNOT_EOK;
430 431
}

432
int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
Lubos Slovak's avatar
Lubos Slovak committed
433
{
434 435
	dbg_packet("%s(%p, %p, %hu, %hu)\n", __func__, pkt, qname, qclass, qtype);
	if (pkt == NULL || qname == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
436
		return KNOT_EINVAL;
437 438
	}

439 440
	assert(pkt->size == KNOT_WIRE_HEADER_SIZE);
	assert(pkt->rrset_count == 0);
441

442 443 444
	/* Copy name wireformat. */
	uint8_t *dst = pkt->wire + KNOT_WIRE_HEADER_SIZE;
	int qname_len = knot_dname_to_wire(dst, qname, pkt->max_size - pkt->size);
445 446 447
	if (qname_len < 0) {
		return qname_len;
	}
448

449
	/* Check size limits. */
450
	size_t question_len = 2 * sizeof(uint16_t) + qname_len;
451
	if (qname_len < 0 || pkt->size + question_len > pkt->max_size) {
452
		return KNOT_ESPACE;
453
	}
Jan Včelák's avatar
Jan Včelák committed
454

455 456 457 458 459
	/* Copy QTYPE & QCLASS */
	dst += qname_len;
	knot_wire_write_u16(dst, qtype);
	dst += sizeof(uint16_t);
	knot_wire_write_u16(dst, qclass);
460

461 462 463 464
	/* Update question count and sizes. */
	knot_wire_set_qdcount(pkt->wire, 1);
	pkt->size += question_len;
	pkt->qname_size = qname_len;
465

466 467
	/* Start writing ANSWER. */
	return knot_pkt_begin(pkt, KNOT_ANSWER);
Lubos Slovak's avatar
Lubos Slovak committed
468 469
}

Lubos Slovak's avatar
Lubos Slovak committed
470 471
int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
                 uint16_t flags)
472
{
473
	dbg_packet("%s(%p, %u, %p, %u)\n", __func__, pkt, compr_hint, rr, flags);
474 475 476
	if (pkt == NULL || rr == NULL) {
		return KNOT_EINVAL;
	}
477

478 479 480 481
	knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count];
	memset(rrinfo, 0, sizeof(knot_rrinfo_t));
	rrinfo->pos = pkt->size;
	rrinfo->flags = flags;
482
	rrinfo->compress_ptr[0] = compr_hint;
483
	pkt->rr[pkt->rrset_count] = *rr;
484

485 486
	/* Check for double insertion. */
	if ((flags & KNOT_PF_CHECKDUP) &&
487
	    pkt_contains(pkt, rr)) {
488
		return KNOT_EOK; /*! \todo return rather a number of added RRs */
Lubos Slovak's avatar
Lubos Slovak committed
489 490
	}

491 492 493 494
	uint8_t *pos = pkt->wire + pkt->size;
	uint16_t rr_added = 0;
	size_t maxlen = pkt_remaining(pkt);
	size_t len = maxlen;
495

496
	/* Create compression context. */
497 498 499 500 501 502 503
	knot_compr_t compr;
	compr.wire = pkt->wire;
	compr.wire_pos = pkt->size;
	compr.rrinfo = rrinfo;
	compr.suffix.pos = KNOT_WIRE_HEADER_SIZE;
	compr.suffix.labels = knot_dname_labels(compr.wire + compr.suffix.pos,
	                                        compr.wire);
504

505
	/* Write RRSet to wireformat. */
506 507
	int ret = knot_rrset_to_wire(rr, pos, &len, maxlen, &rr_added, &compr);
	if (ret != KNOT_EOK) {
508
		dbg_packet("%s: rr_to_wire = %s\n,", __func__, knot_strerror(ret));
509

510
		/* Truncate packet if required. */
511
		if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) {
512 513 514 515
				dbg_packet("%s: set TC=1\n", __func__);
				knot_wire_set_tc(pkt->wire);
		}
		return ret;
516 517
	}

518 519 520 521 522
	/* Keep reference to special types. */
	if (rr->type == KNOT_RRTYPE_OPT) {
		pkt->opt_rr = &pkt->rr[pkt->rrset_count];
	}

523
	if (rr_added > 0) {
524 525
		pkt->rrset_count += 1;
		pkt->sections[pkt->current].count += 1;
526 527 528
		pkt->size += len;
		pkt_rr_wirecount_add(pkt, pkt->current, rr_added);
	}
529

Jan Kadlec's avatar
Jan Kadlec committed
530
	dbg_packet("%s: added %u RRs (@%zu, len=%zu), pktsize=%zu\n",
Jan Kadlec's avatar
Jan Kadlec committed
531
	           __func__, rr_added, pkt->size - len, len, pkt->size);
532

533
	return KNOT_EOK;
534 535
}

536
const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt, knot_section_t section_id)
Lubos Slovak's avatar
Lubos Slovak committed
537
{
538 539 540
	dbg_packet("%s(%p, %u)\n", __func__, pkt, section_id);
	if (pkt == NULL) {
		return NULL;
541 542
	}

543
	return &pkt->sections[section_id];
Lubos Slovak's avatar
Lubos Slovak committed
544 545
}

546
int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags)
547
{
548 549
	dbg_packet("%s(%p, %u)\n", __func__, pkt, flags);
	if (pkt == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
550
		return KNOT_EINVAL;
551
	}
Jan Včelák's avatar
Jan Včelák committed
552

553 554 555
	int ret = knot_pkt_parse_question(pkt);
	if (ret == KNOT_EOK) {
		ret = knot_pkt_parse_payload(pkt, flags);
556
	}
Jan Včelák's avatar
Jan Včelák committed
557

558
	return ret;
559 560
}

561
int knot_pkt_parse_question(knot_pkt_t *pkt)
Lubos Slovak's avatar
Lubos Slovak committed
562
{
563
	dbg_packet("%s(%p)\n", __func__, pkt);
564 565 566
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}
Jan Včelák's avatar
Jan Včelák committed
567

568 569
	/* Check at least header size. */
	if (pkt->size < KNOT_WIRE_HEADER_SIZE) {
570
		dbg_packet("%s: smaller than DNS header, NOREPLY\n", __func__);
571 572
		return KNOT_EMALF;
	}
Lubos Slovak's avatar
Lubos Slovak committed
573

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

577 578 579 580 581
	/* Check QD count. */
	uint16_t qd = knot_wire_get_qdcount(pkt->wire);
	if (qd > 1) {
		dbg_packet("%s: QD(%u) > 1, FORMERR\n", __func__, qd);
		return KNOT_EMALF;
582 583
	}

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

590 591 592
	/* Process question. */
	int len = knot_dname_wire_check(pkt->wire + pkt->parsed,
	                                pkt->wire + pkt->size,
593
	                                NULL /* No compression in QNAME. */);
594 595
	if (len <= 0) {
		return KNOT_EMALF;
596
	}
Jan Včelák's avatar
Jan Včelák committed
597

598 599 600 601 602 603
	/* Check QCLASS/QTYPE size. */
	uint16_t question_size = len + 2 * sizeof(uint16_t); /* QCLASS + QTYPE */
	if (pkt->parsed + question_size > pkt->size) {
		dbg_packet("%s: missing QCLASS/QTYPE, FORMERR\n", __func__);
		return KNOT_EMALF;
	}
604

Jan Včelák's avatar
Jan Včelák committed
605
	pkt->parsed += question_size;
606
	pkt->qname_size = len;
607

608
	return KNOT_EOK;
609 610
}

611 612 613 614
/*! \note Legacy code, mainly for transfers and updates.
 *        RRSets should use packet memory context for allocation and
 *        should be copied if they are supposed to be stored in zone permanently.
 */
615 616
static int knot_pkt_rr_from_wire(const uint8_t *wire, size_t *pos,
                                 size_t size, mm_ctx_t *mm, knot_rrset_t *rrset)
617
{
618
	dbg_packet("%s(%p, %zu, %zu)\n", __func__, wire, *pos, size);
619 620 621
	assert(wire);
	assert(pos);

622
	knot_dname_t *owner = knot_dname_parse(wire, pos, size, mm);
623
	if (owner == NULL) {
624
		return KNOT_EMALF;
625
	}
626
	knot_dname_to_lower(owner);
627

628 629
	if (size - *pos < KNOT_RR_HEADER_SIZE) {
		dbg_packet("%s: not enough data to parse RR HEADER\n", __func__);
630
		knot_dname_free(&owner, mm);
631
		return KNOT_EMALF;
632
	}
633

634 635
	uint16_t type = knot_wire_read_u16(wire + *pos);
	uint16_t rclass = knot_wire_read_u16(wire + *pos + sizeof(uint16_t));
636
	uint32_t ttl = knot_wire_read_u32(wire + *pos + 2 * sizeof(uint16_t));
637
	uint16_t rdlength = knot_wire_read_u16(wire + *pos + 4 * sizeof(uint16_t));
638

639
	*pos += KNOT_RR_HEADER_SIZE;
640

641
	if (size - *pos < rdlength) {
642
		dbg_packet("%s: not enough data to parse RDATA\n", __func__);
Marek Vavruša's avatar
Marek Vavruša committed
643
		knot_dname_free(&owner, mm);
644
		return KNOT_EMALF;
645
	}
646

Marek Vavruša's avatar
Marek Vavruša committed
647
	knot_rrset_init(rrset, owner, type, rclass);
648
	int ret = knot_rrset_rdata_from_wire_one(rrset, wire, pos, size, ttl,
Jan Kadlec's avatar
Jan Kadlec committed
649
	                                         rdlength, mm);
650 651
	if (ret != KNOT_EOK) {
		dbg_packet("%s: couldn't parse RDATA (%d)\n", __func__, ret);
652
		knot_rrset_clear(rrset, mm);
653
		return ret;
654 655
	}

656
	return KNOT_EOK;
657 658
}

659
int knot_pkt_parse_rr(knot_pkt_t *pkt, unsigned flags)
Lubos Slovak's avatar
Lubos Slovak committed
660
{
661
	dbg_packet("%s(%p, %u)\n", __func__, pkt, flags);
662 663 664 665
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

666 667 668
	if (pkt->parsed >= pkt->size) {
		dbg_packet("%s: parsed %zu/%zu data\n", __func__, pkt->parsed, pkt->size);
		return KNOT_EFEWDATA;
669 670
	}

671 672 673 674 675
	/* Initialize RR info. */
	int ret = KNOT_EOK;
	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
676

677
	/* Parse wire format. */
678
	size_t rr_size = pkt->parsed;
679 680 681 682
	knot_rrset_t *rr = &pkt->rr[pkt->rrset_count];
	ret = knot_pkt_rr_from_wire(pkt->wire, &pkt->parsed, pkt->max_size,
	                           &pkt->mm, rr);
	if (ret != KNOT_EOK) {
683
		dbg_packet("%s: failed to parse RR\n", __func__);
684
		return ret;
Lubos Slovak's avatar
Lubos Slovak committed
685
	}
Jan Včelák's avatar
Jan Včelák committed
686

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

690
	/* Update packet RRSet count. */
691 692 693 694 695 696
	++pkt->rrset_count;

	/* Update section RRSet count. */
	++pkt->sections[pkt->current].count;

	/* Check RR constraints. */
697
	switch(rr->type) {
698 699 700 701 702 703 704 705 706 707 708
	case KNOT_RRTYPE_TSIG:
		// if there is some TSIG already, treat as malformed
		if (pkt->tsig_rr != NULL) {
			dbg_packet("%s: found 2nd TSIG\n", __func__);
			return KNOT_EMALF;
		} else if (!tsig_rdata_is_ok(rr)) {
			dbg_packet("%s: bad TSIG RDATA\n", __func__);
			return KNOT_EMALF;
		}

		/* Strip TSIG RR from wireformat and decrease ARCOUNT. */
709 710 711 712 713 714
		if (!(flags & KNOT_PF_KEEPWIRE)) {
			pkt->parsed -= rr_size;
			pkt->size -= rr_size;
			knot_wire_set_id(pkt->wire, tsig_rdata_orig_id(rr));
			knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1);
		}
715 716 717 718 719

		/* Remember TSIG RR. */
		pkt->tsig_rr = rr;
		break;
	case KNOT_RRTYPE_OPT:
720
		/* If there is some OPT already, treat as malformed. */
Lubos Slovak's avatar
Lubos Slovak committed
721 722 723
		if (pkt->opt_rr != NULL) {
			dbg_packet("%s: found 2nd OPT\n", __func__);
			return KNOT_EMALF;
Lubos Slovak's avatar
Lubos Slovak committed
724
		}
Lubos Slovak's avatar
Lubos Slovak committed
725

726 727 728 729 730 731
		/* Semantic checks for the OPT. */
		if (!knot_edns_check_record(rr)) {
			dbg_packet("%s: OPT RR check failed\n", __func__);
			return KNOT_EMALF;
		}

Lubos Slovak's avatar
Lubos Slovak committed
732
		pkt->opt_rr = rr;
733 734 735
		break;
	default:
		break;
Lubos Slovak's avatar
Lubos Slovak committed
736 737
	}

738
	return ret;
Lubos Slovak's avatar
Lubos Slovak committed
739 740
}

741
int knot_pkt_parse_section(knot_pkt_t *pkt, unsigned flags)
742
{
743
	dbg_packet("%s(%p, %u)\n", __func__, pkt, flags);
744 745 746
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}
747

748 749 750 751 752 753 754 755 756 757
	int ret = KNOT_EOK;
	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) {
		ret = knot_pkt_parse_rr(pkt, flags);
		if (ret != KNOT_EOK) {
			dbg_packet("%s: couldn't parse RR %u/%u = %d\n",
			           __func__, rr_parsed, rr_count, ret);
758
			return ret;
759
		}
760 761
	}

Lubos Slovak's avatar
Lubos Slovak committed
762
	return KNOT_EOK;
763 764
}

765
int knot_pkt_parse_payload(knot_pkt_t *pkt, unsigned flags)
766
{
767
	dbg_packet("%s(%p, %u)\n", __func__, pkt, flags);
768 769 770 771
	if (pkt == NULL) {
		return KNOT_EINVAL;
	}

772 773
	assert(pkt->wire != NULL);
	assert(pkt->size > 0);
774

775
	int ret = KNOT_ERROR;
776

777 778 779 780 781 782 783 784 785 786 787 788 789
	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
		ret = knot_pkt_begin(pkt, i);
		if (ret != KNOT_EOK) {
			dbg_packet("%s: couldn't begin section %u = %d\n",
			           __func__, i, ret);
			return ret;
		}
		ret = knot_pkt_parse_section(pkt, flags);
		if (ret != KNOT_EOK) {
			dbg_packet("%s: couldn't parse section %u = %d\n",
			           __func__, i, ret);
			return ret;
		}
790 791
	}

792 793 794
	/* 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) {
795
		if (ar->count > 0 && pkt->tsig_rr->rrs.data != ar->rr[ar->count - 1].rrs.data) {
796 797 798 799
			dbg_packet("%s: TSIG not last RR in AR.\n", __func__);
			return KNOT_EMALF;
		}
	}
800

801 802 803 804 805 806
	/* Check for trailing garbage. */
	if (pkt->parsed < pkt->size) {
		dbg_packet("%s: %zu bytes of trailing garbage\n",
		           __func__, pkt->size - pkt->parsed);
		return KNOT_EMALF;
	}
807

Lubos Slovak's avatar
Lubos Slovak committed
808
	return KNOT_EOK;
809
}