summaryrefslogtreecommitdiffstats
path: root/src/libknot/packet/pkt.h
blob: f5e218fc73fdbf66b540d73c20ec743428f5c66f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    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 <https://www.gnu.org/licenses/>.
 */

/*!
 * \file
 *
 * \brief Structure for holding DNS packet data and metadata.
 *
 * \addtogroup pkt
 * @{
 */

#pragma once

#include <assert.h>
#include <stdint.h>

#include "libknot/consts.h"
#include "libknot/dname.h"
#include "libknot/mm_ctx.h"
#include "libknot/rrset.h"
#include "libknot/rrtype/opt.h"
#include "libknot/packet/wire.h"
#include "libknot/packet/compr.h"
#include "libknot/wire.h"

/* Number of packet sections (ANSWER, AUTHORITY, ADDITIONAL). */
#define KNOT_PKT_SECTIONS 3

/*!
 * \brief Packet flags.
 */
enum {
	KNOT_PF_NULL      = 0 << 0, /*!< No flags. */
	KNOT_PF_FREE      = 1 << 1, /*!< Free with packet. */
	KNOT_PF_NOTRUNC   = 1 << 2, /*!< Don't truncate. */
	KNOT_PF_CHECKDUP  = 1 << 3, /*!< Check for duplicates. */
	KNOT_PF_KEEPWIRE  = 1 << 4, /*!< Keep wireformat untouched when parsing. */
	KNOT_PF_NOCANON   = 1 << 5, /*!< Don't canonicalize rrsets during parsing. */
	KNOT_PF_ORIGTTL   = 1 << 6, /*!< Write RRSIGs with their original TTL. */
	KNOT_PF_SOAMINTTL = 1 << 7, /*!< Write SOA with its minimum-ttl as TTL. */
};

typedef struct knot_pkt knot_pkt_t;

/*!
 * \brief Packet section.
 * Points to RRSet and RRSet info arrays in the packet.
 * This structure is required for random access to packet sections.
 */
typedef struct {
	knot_pkt_t *pkt; /*!< Owner. */
	uint16_t pos;    /*!< Position in the rr/rrinfo fields in packet. */
	uint16_t count;  /*!< Number of RRSets in this section. */
} knot_pktsection_t;

/*!
 * \brief Structure representing a DNS packet.
 */
struct knot_pkt {
	uint8_t *wire;         /*!< Wire format of the packet. */
	size_t size;           /*!< Current wire size of the packet. */
	size_t max_size;       /*!< Maximum allowed size of the packet. */
	size_t parsed;         /*!< Parsed size. */
	uint16_t reserved;     /*!< Reserved space. */
	uint16_t qname_size;   /*!< QNAME size. */
	uint16_t rrset_count;  /*!< Packet RRSet count. */
	uint16_t flags;        /*!< Packet flags. */

	knot_rrset_t *opt_rr;   /*!< OPT RR included in the packet. */
	knot_rrset_t *tsig_rr;  /*!< TSIG RR stored in the packet. */

	/*! EDNS option positions in the wire (if parsed from wire). */
	knot_edns_options_t *edns_opts;

	/*! TSIG RR position in the wire (if parsed from wire). */
	struct {
		uint8_t *pos;
		size_t len;
	} tsig_wire;

	/* Packet sections. */
	knot_section_t current;
	knot_pktsection_t sections[KNOT_PKT_SECTIONS];

	/* Packet RRSet (meta)data. */
	size_t rrset_allocd;
	knot_rrinfo_t *rr_info;
	knot_rrset_t *rr;

	knot_mm_t mm; /*!< Memory allocation context. */

	knot_compr_t compr; /*!< Compression context. */

	/*! Lowercased QNAME. MUST BE LAST ITEM! */
	knot_dname_storage_t lower_qname;
};

/*!
 * \brief Create new packet over existing memory, or allocate new from memory context.
 *
 * \note Packet is allocated from given memory context.
 *
 * \param wire If NULL, memory of 'len' size shall be allocated.
 *        Otherwise pointer is used for the wire format of the packet.
 * \param len Wire format length.
 * \param mm Memory context (NULL for default).
 * \return New packet or NULL.
 */
knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm);

/*!
 * \brief Copy packet.
 *
 * \note Current implementation is not very efficient, as it re-parses the wire.
 *
 * \param dst Target packet.
 * \param src Source packet.
 *
 * \return new packet or NULL
 */
int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src);

/*!
 * \brief Initialized response from query packet.
 *
 * \note Question is not checked, it is expected to be checked already.
 *
 * \param pkt Given packet.
 * \param query Query.
 * \return KNOT_EOK, KNOT_EINVAL, KNOT_ESPACE
 */
int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query);

/*! \brief Reinitialize packet for another use. */
void knot_pkt_clear(knot_pkt_t *pkt);

/*! \brief Begone you foul creature of the underworld. */
void knot_pkt_free(knot_pkt_t *pkt);

/*!
 * \brief Reserve an arbitrary amount of space in the packet.
 *
 * \return KNOT_EOK
 * \return KNOT_ERANGE if size can't be reserved
 */
int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size);

/*!
 * \brief Reclaim reserved size.
 *
 * \return KNOT_EOK
 * \return KNOT_ERANGE if size can't be reclaimed
 */
int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size);

/*
 * Packet QUESTION accessors.
 */
static inline uint16_t knot_pkt_question_size(const knot_pkt_t *pkt)
{
	if (pkt == NULL || pkt->qname_size == 0) {
		return 0;
	}

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

static inline const knot_dname_t *knot_pkt_qname(const knot_pkt_t *pkt)
{
	if (pkt == NULL || pkt->qname_size == 0) {
		return NULL;
	}

	return pkt->lower_qname;
}

static inline const knot_dname_t *knot_pkt_wire_qname(const knot_pkt_t *pkt)
{
	if (pkt == NULL || pkt->qname_size == 0) {
		return NULL;
	}

	return pkt->wire + KNOT_WIRE_HEADER_SIZE;
}

static inline uint16_t knot_pkt_qtype(const knot_pkt_t *pkt)
{
	if (pkt == NULL || pkt->qname_size == 0) {
		return 0;
	}

	unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size;
	return knot_wire_read_u16(pkt->wire + off);
}

static inline uint16_t knot_pkt_qclass(const knot_pkt_t *pkt)
{
	if (pkt == NULL || pkt->qname_size == 0) {
		return 0;
	}

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

/*
 * Packet writing API.
 */

/*!
 * \brief Begin reading/writing packet section.
 *
 * \note You must proceed in the natural order (ANSWER, AUTHORITY, ADDITIONAL).
 *
 * \param pkt
 * \param section_id
 * \return KNOT_EOK or KNOT_EINVAL
 */
int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id);

/*!
 * \brief Put QUESTION in the packet.
 *
 * \note Since we support QD=1 only, QUESTION is a special type of packet section.
 * \note Must not be used after putting RRsets into the packet.
 *
 * \param pkt
 * \param qname
 * \param qclass
 * \param qtype
 * \return KNOT_EOK or various errors
 */
int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname,
                          uint16_t qclass, uint16_t qtype);

/*!
 * \brief Put RRSet into packet.
 *
 * \note See compr.h for description on how compression hints work.
 * \note Available flags: PF_FREE, KNOT_PF_CHECKDUP, KNOT_PF_NOTRUNC
 *
 * \param pkt
 * \param compr_hint  Compression hint, see enum knot_compr_hint or absolute
 *                    position.
 * \param rr          Given RRSet.
 * \param rotate      Rotate the RRSet order by this count.
 * \param flags       RRSet flags (set PF_FREE if you want RRSet to be freed
 *                    with the packet).
 *
 * \return KNOT_EOK, KNOT_ESPACE, various errors
 */
int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
                        uint16_t rotate, uint16_t flags);

/*! \brief Same as knot_pkt_put_rotate but without rrset rotation. */
static inline int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint,
                               const knot_rrset_t *rr, uint16_t flags)
{
	return knot_pkt_put_rotate(pkt, compr_hint, rr, 0, flags);
}

/*! \brief Get description of the given packet section. */
static inline const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt,
                                                        knot_section_t section_id)
{
	assert(pkt);
	return &pkt->sections[section_id];
}

/*! \brief Get RRSet from the packet section. */
static inline const knot_rrset_t *knot_pkt_rr(const knot_pktsection_t *section,
                                              uint16_t i)
{
	assert(section);
	return section->pkt->rr + section->pos + i;
}

/*! \brief Get RRSet offset in the packet wire. */
static inline uint16_t knot_pkt_rr_offset(const knot_pktsection_t *section,
                                          uint16_t i)
{
	assert(section);
	return section->pkt->rr_info[section->pos + i].pos;
}

/*
 * Packet parsing API.
 */

/*!
 * \brief Parse both packet question and payload.
 *
 * Parses both QUESTION and all packet sections,
 * includes semantic checks over specific RRs (TSIG, OPT).
 *
 * \note If KNOT_PF_KEEPWIRE is set, TSIG RR is not stripped from the wire
 *       and is processed as any other RR.
 *
 * \param  pkt Given packet.
 * \param  flags Parsing flags (allowed KNOT_PF_KEEPWIRE)
 *
 * \retval KNOT_EOK if success.
 * \retval KNOT_ETRAIL if success but with some trailing data.
 * \retval KNOT_EMALF and other errors.
 */
int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags);

/*!
 * \brief Parse packet header and a QUESTION section.
 */
int knot_pkt_parse_question(knot_pkt_t *pkt);

/*!
 * \brief Get packet extended RCODE.
 *
 * Extended RCODE is created by considering TSIG RCODE, EDNS RCODE and
 * DNS Header RCODE. (See RFC 6895, Section 2.3).
 *
 * \param pkt Packet to get the response code from.
 *
 * \return Whole extended RCODE (0 if pkt == NULL).
 */
uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt);

/*!
 * \brief Get packet extended RCODE name.
 *
 * The packet parameter is important as the name depends on TSIG.
 *
 * \param pkt Packet to get the response code from.
 *
 * \return RCODE name (or empty string if not known).
 */
const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt);

/*!
 * \brief Checks if there is an OPT RR in the packet.
 */
static inline bool knot_pkt_has_edns(const knot_pkt_t *pkt)
{
	assert(pkt);
	return pkt->opt_rr != NULL;
}

/*!
 * \brief Checks if TSIG is present.
 */
static inline bool knot_pkt_has_tsig(const knot_pkt_t *pkt)
{
	assert(pkt);
	return pkt->tsig_rr != NULL;
}

/*!
 * \brief Checks if DO bit is set in the packet's OPT RR.
 */
static inline bool knot_pkt_has_dnssec(const knot_pkt_t *pkt)
{
	assert(pkt);
	return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr);
}

/*!
 * \brief Get specific EDNS option from a parsed packet.
 */
static inline uint8_t *knot_pkt_edns_option(const knot_pkt_t *pkt, uint16_t code)
{
	assert(pkt);
	if (pkt->edns_opts != NULL && code <= KNOT_EDNS_MAX_OPTION_CODE) {
		return pkt->edns_opts->ptr[code];
	} else {
		return NULL;
	}
}

/*!
 * \brief Computes a reasonable Padding data length for a given packet and opt RR.
 *
 * \param pkt     DNS Packet prepared and otherwise ready to go, no OPT yet added.
 * \param opt_rr  OPT RR, not yet including padding.
 *
 * \return Required padding length or -1 if padding not required.
 */
static inline int knot_pkt_default_padding_size(const knot_pkt_t *pkt,
                                                const knot_rrset_t *opt_rr)
{
	if (knot_wire_get_qr(pkt->wire)) {
		return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr),
		                                KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT);
	} else {
		return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr),
		                                KNOT_EDNS_ALIGNMENT_QUERY_DEFAULT);
	}
}

static inline size_t knot_pkt_size(const knot_pkt_t *pkt)
{
	assert(pkt);
	return pkt->size + (knot_pkt_has_tsig(pkt) ? pkt->tsig_wire.len : 0);
}

/*! @} */