summaryrefslogtreecommitdiff
path: root/src/libknot/packet/packet.h
blob: 1bf74a91127027466f8bef09cce00479a8215525 (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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*!
 * \file packet.h
 *
 * \author Lubos Slovak <lubos.slovak@nic.cz>
 *
 * \brief Structure for holding DNS packet data and metadata.
 *
 * \addtogroup libknot
 * @{
 */
/*  Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
 */

#ifndef _KNOT_PACKET_H_
#define _KNOT_PACKET_H_

#include <stdint.h>
#include <string.h>

#include "dname.h"
#include "rrset.h"
#include "edns.h"

/*----------------------------------------------------------------------------*/
/*!
 * \brief Structure for holding information needed for compressing domain names.
 *
 * It's a simple table of domain names and their offsets in wire format of the
 * packet.
 *
 * \todo Consider using some better lookup structure, such as skip-list.
 */
struct knot_compressed_dnames {
	const knot_dname_t **dnames;  /*!< Domain names present in packet. */
	size_t *offsets;          /*!< Offsets of domain names in the packet. */
	short count;              /*!< Count of items in the previous arrays. */
	short max;                /*!< Capacity of the structure (allocated). */
};

typedef struct knot_compressed_dnames knot_compressed_dnames_t;

/*----------------------------------------------------------------------------*/
/*!
 * \brief Structure representing the DNS packet header.
 */
struct knot_header {
	uint16_t id;      /*!< ID stored in host byte order. */
	uint8_t flags1;   /*!< First octet of header flags. */
	uint8_t flags2;   /*!< Second octet of header flags. */
	uint16_t qdcount; /*!< Number of Question RRs, in host byte order. */
	uint16_t ancount; /*!< Number of Answer RRs, in host byte order. */
	uint16_t nscount; /*!< Number of Authority RRs, in host byte order. */
	uint16_t arcount; /*!< Number of Additional RRs, in host byte order. */
};

typedef struct knot_header knot_header_t;

/*!
 * \brief Structure representing one Question entry in the DNS packet.
 */
struct knot_question {
	knot_dname_t *qname;  /*!< Question domain name. */
	uint16_t qtype;         /*!< Question TYPE. */
	uint16_t qclass;        /*!< Question CLASS. */
};

typedef struct knot_question knot_question_t;

enum knot_packet_prealloc_type {
	KNOT_PACKET_PREALLOC_NONE,
	KNOT_PACKET_PREALLOC_QUERY,
	KNOT_PACKET_PREALLOC_RESPONSE
};

typedef enum knot_packet_prealloc_type knot_packet_prealloc_type_t;

/*----------------------------------------------------------------------------*/
/*!
 * \brief Structure representing a DNS packet.
 *
 * \note QNAME, Answer, Authority and Additonal sections are by default put to
 *       preallocated space after the structure with default sizes. If the
 *       space is not enough, more space is allocated dynamically.
 */
struct knot_packet {
	/*! \brief DNS header. */
	knot_header_t header;

	/*!
	 * \brief Question section.
	 *
	 * \note Only one Question is supported!
	 */
	knot_question_t question;

	uint8_t *owner_tmp;  /*!< Allocated space for RRSet owner wire format.*/

	const knot_rrset_t **answer;      /*!< Answer RRSets. */
	const knot_rrset_t **authority;   /*!< Authority RRSets. */
	const knot_rrset_t **additional;  /*!< Additional RRSets. */

	short an_rrsets;     /*!< Count of Answer RRSets in the response. */
	short ns_rrsets;     /*!< Count of Authority RRSets in the response. */
	short ar_rrsets;     /*!< Count of Additional RRSets in the response. */

	short max_an_rrsets; /*!< Allocated space for Answer RRsets. */
	short max_ns_rrsets; /*!< Allocated space for Authority RRsets. */
	short max_ar_rrsets; /*!< Allocated space for Additional RRsets. */

	knot_opt_rr_t opt_rr;     /*!< OPT RR included in the packet. */

	uint8_t *wireformat;  /*!< Wire format of the packet. */

	short free_wireformat;
	size_t parsed;

	size_t size;      /*!< Current wire size of the packet. */
	size_t max_size;  /*!< Maximum allowed size of the packet. */

	/*! \brief Information needed for compressing domain names in packet. */
	knot_compressed_dnames_t compression;

	/*! \brief RRSets to be destroyed with the packet structure. */
	const knot_rrset_t **tmp_rrsets;
	short tmp_rrsets_count;  /*!< Count of temporary RRSets. */
	short tmp_rrsets_max;    /*!< Allocated space for temporary RRSets. */

	struct knot_packet *query; /*!< Associated query. */

	knot_packet_prealloc_type_t prealloc_type;
	
	size_t tsig_size;	/*!< Space to reserve for the TSIG RR. */
};

typedef struct knot_packet knot_packet_t;

/*----------------------------------------------------------------------------*/
/*!
 * \brief Default sizes for response structure parts and steps for increasing
 *        them.
 */
enum {
	DEFAULT_ANCOUNT = 6,         /*!< Default count of Answer RRSets. */
	DEFAULT_NSCOUNT = 8,         /*!< Default count of Authority RRSets. */
	DEFAULT_ARCOUNT = 28,        /*!< Default count of Additional RRSets. */

	DEFAULT_ANCOUNT_QUERY = 1,   /*!< Default count of Answer RRSets. */
	DEFAULT_NSCOUNT_QUERY = 0,   /*!< Default count of Authority RRSets. */
	DEFAULT_ARCOUNT_QUERY = 1,  /*!< Default count of Additional RRSets. */
	/*!
	 * \brief Default count of all domain names in response.
	 *
	 * Used for compression table.
	 */
	DEFAULT_DOMAINS_IN_RESPONSE = 22,

	/*! \brief Default count of temporary RRSets stored in response. */
	DEFAULT_TMP_RRSETS = 5,

	/*! \brief Default count of temporary RRSets stored in query. */
	DEFAULT_TMP_RRSETS_QUERY = 2,

	STEP_ANCOUNT = 6, /*!< Step for increasing space for Answer RRSets. */
	STEP_NSCOUNT = 8, /*!< Step for increasing space for Authority RRSets.*/
	STEP_ARCOUNT = 8,/*!< Step for increasing space for Additional RRSets.*/
	STEP_DOMAINS = 10,   /*!< Step for resizing compression table. */
	STEP_TMP_RRSETS = 5  /*!< Step for increasing temorary RRSets count. */
};

/*----------------------------------------------------------------------------*/
#define PREALLOC_RRSETS(count) (count * sizeof(knot_rrset_t *))

/*! \brief Sizes for preallocated space in the response structure. */
enum {
	/*! \brief Size of the response structure itself. */
	PREALLOC_PACKET = sizeof(knot_packet_t),
	/*! \brief Space for QNAME dname structure. */
	PREALLOC_QNAME_DNAME = sizeof(knot_dname_t),
	/*! \brief Space for QNAME name (maximum domain name size). */
	PREALLOC_QNAME_NAME = 256,
	/*! \brief Space for QNAME labels (maximum label count). */
	PREALLOC_QNAME_LABELS = 127,
	/*! \brief Total space for QNAME. */
	PREALLOC_QNAME = PREALLOC_QNAME_DNAME
	                 + PREALLOC_QNAME_NAME
	                 + PREALLOC_QNAME_LABELS,
	/*!
	 * \brief Space for RR owner wire format.
	 *
	 * Temporary buffer, used when putting RRSets to the response.
	 */
	PREALLOC_RR_OWNER = 256,

//	/*! \brief Space for Answer RRSets. */
//	PREALLOC_ANSWER = DEFAULT_ANCOUNT * sizeof(knot_dname_t *),
//	/*! \brief Space for Authority RRSets. */
//	PREALLOC_AUTHORITY = DEFAULT_NSCOUNT * sizeof(knot_dname_t *),
//	/*! \brief Space for Additional RRSets. */
//	PREALLOC_ADDITIONAL = DEFAULT_ARCOUNT * sizeof(knot_dname_t *),
//	/*! \brief Total size for Answer, Authority and Additional RRSets. */
//	PREALLOC_RRSETS = PREALLOC_ANSWER
//	                  + PREALLOC_AUTHORITY
//	                  + PREALLOC_ADDITIONAL,
	/*! \brief Space for one part of the compression table (domain names).*/
	PREALLOC_DOMAINS =
		DEFAULT_DOMAINS_IN_RESPONSE * sizeof(knot_dname_t *),
	/*! \brief Space for other part of the compression table (offsets). */
	PREALLOC_OFFSETS =
		DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t),
	PREALLOC_COMPRESSION = PREALLOC_DOMAINS + PREALLOC_OFFSETS,

//	/*! \brief Space for temporary RRSets. */
//	PREALLOC_TMP_RRSETS =
//		DEFAULT_TMP_RRSETS * sizeof(knot_rrset_t *),

	PREALLOC_QUERY = PREALLOC_PACKET
	                 + PREALLOC_QNAME
	                 + PREALLOC_RRSETS(DEFAULT_ANCOUNT_QUERY)
	                 + PREALLOC_RRSETS(DEFAULT_NSCOUNT_QUERY)
	                 + PREALLOC_RRSETS(DEFAULT_ARCOUNT_QUERY)
	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS_QUERY),

	/*! \brief Total preallocated size for the response. */
	PREALLOC_RESPONSE = PREALLOC_PACKET
	                 + PREALLOC_QNAME
	                 + PREALLOC_RR_OWNER
	                 + PREALLOC_RRSETS(DEFAULT_ANCOUNT)
	                 + PREALLOC_RRSETS(DEFAULT_NSCOUNT)
	                 + PREALLOC_RRSETS(DEFAULT_ARCOUNT)
	                 + PREALLOC_COMPRESSION
	                 + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS)
};

/*----------------------------------------------------------------------------*/
/*!
 * \brief Creates new empty packet structure.
 *
 * \param prealloc What space should be preallocated in the structure.
 *
 * \return New packet structure or NULL if an error occured.
 */
knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc);

/*!
 * \brief Parses the DNS packet from wire format.
 *
 * \param packet Packet structure to parse into.
 * \param wireformat Wire format of the DNS packet.
 * \param size Size of the wire format in bytes.
 * \param question_only Set to <> 0 if you do not want to parse the whole
 *                      packet. In such case the parsing will end after the
 *                      Question section. Set to 0 to parse the whole packet.
 *
 * \retval KNOT_EOK
 */
int knot_packet_parse_from_wire(knot_packet_t *packet,
                                  const uint8_t *wireformat, size_t size,
                                  int question_only);

int knot_packet_parse_rest(knot_packet_t *packet);

int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
                                       knot_rrset_t **rr);

int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
                                         knot_rrset_t **rr);

size_t knot_packet_size(const knot_packet_t *packet);

/*! \brief Returns size of the wireformat of Header and Question sections. */
size_t knot_packet_question_size(const knot_packet_t *packet);

size_t knot_packet_parsed(const knot_packet_t *packet);

/*!
 * \brief Sets the maximum size of the packet and allocates space for wire
 *        format (if needed).
 *
 * This function also allocates space for the wireformat of the packet, if
 * the given max size is larger than the current maximum size of the packet
 * and copies the current wireformat over to the new space.
 *
 * \warning Do not call this function if you are not completely sure that the
 *          current wire format of the packet fits into the new space.
 *          It does not update the current size of the wire format, so the
 *          produced packet may be larger than the given max size.
 *
 * \param packet Packet to set the maximum size of.
 * \param max_size Maximum size of the packet in bytes.
 *
 * \retval KNOT_EOK
 * \retval KNOT_EBADARG
 * \retval KNOT_ENOMEM
 *
 * \todo Needs test.
 */
int knot_packet_set_max_size(knot_packet_t *packet, int max_size);

uint16_t knot_packet_id(const knot_packet_t *packet);

void knot_packet_set_id(knot_packet_t *packet, uint16_t id);

void knot_packet_set_random_id(knot_packet_t *packet);

/*!
 * \brief Returns the OPCODE of the packet.
 *
 * \param packet Packet (with parsed query) to get the OPCODE from.
 *
 * \return OPCODE stored in the packet.
 */
uint8_t knot_packet_opcode(const knot_packet_t *packet);

/*!
 * \brief Returns the QNAME from the packet.
 *
 * \param packet Packet (with parsed query) to get the QNAME from.
 *
 * \return QNAME stored in the packet.
 */
const knot_dname_t *knot_packet_qname(const knot_packet_t *packet);

/*!
 * \brief Returns the QTYPE from the packet.
 *
 * \param packet Packet (with parsed query) to get the QTYPE from.
 *
 * \return QTYPE stored in the packet.
 */
uint16_t knot_packet_qtype(const knot_packet_t *packet);

/*!
 * \brief Set the QTYPE of the packet.
 *
 * \param packet Packet containing question.
 * \param qtype New QTYPE for question.
 */
void knot_packet_set_qtype(knot_packet_t *packet, knot_rr_type_t qtype);


/*!
 * \brief Returns the QCLASS from the packet.
 *
 * \param response Packet (with parsed query) to get the QCLASS from.
 *
 * \return QCLASS stored in the packet.
 */
uint16_t knot_packet_qclass(const knot_packet_t *packet);

int knot_packet_is_query(const knot_packet_t *packet);

const knot_packet_t *knot_packet_query(const knot_packet_t *packet);

int knot_packet_rcode(const knot_packet_t *packet);

int knot_packet_tc(const knot_packet_t *packet);

int knot_packet_qdcount(const knot_packet_t *packet);

int knot_packet_ancount(const knot_packet_t *packet);

int knot_packet_nscount(const knot_packet_t *packet);

int knot_packet_arcount(const knot_packet_t *packet);

void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size);

/*!
 * \brief Returns number of RRSets in Answer section of the packet.
 *
 * \param response Packet to get the Answer RRSet count from.
 */
short knot_packet_answer_rrset_count(const knot_packet_t *packet);

/*!
 * \brief Returns number of RRSets in Authority section of the packet.
 *
 * \param response Packet to get the Authority RRSet count from.
 */
short knot_packet_authority_rrset_count(const knot_packet_t *packet);

/*!
 * \brief Returns number of RRSets in Additional section of the packet.
 *
 * \param response Packet to get the Additional RRSet count from.
 */
short knot_packet_additional_rrset_count(const knot_packet_t *packet);

/*!
 * \brief Returns the requested Answer RRset.
 *
 * \param packet Packet to get the RRSet from.
 * \param pos Position of the RRSet in the Answer section (RRSets are stored
 *            in the order they were added to the response or parsed from the
 *            query).
 *
 * \return The RRSet on position \a pos in the Answer section of \a packet
 *         or NULL if there is no such RRSet.
 */
const knot_rrset_t *knot_packet_answer_rrset(
	const knot_packet_t *packet, short pos);

/*!
 * \brief Returns the requested Authority RRset.
 *
 * \param packet Packet to get the RRSet from.
 * \param pos Position of the RRSet in the Authority section (RRSets are stored
 *            in the order they were added to the response or parsed from the
 *            query).
 *
 * \return The RRSet on position \a pos in the Authority section of \a packet
 *         or NULL if there is no such RRSet.
 */
const knot_rrset_t *knot_packet_authority_rrset(
	knot_packet_t *packet, short pos);

/*!
 * \brief Returns the requested Additional RRset.
 *
 * \param packet Packet to get the RRSet from.
 * \param pos Position of the RRSet in the Additional section (RRSets are stored
 *            in the order they were added to the response or parsed from the
 *            query).
 *
 * \return The RRSet on position \a pos in the Additional section of \a packet
 *         or NULL if there is no such RRSet.
 */
const knot_rrset_t *knot_packet_additional_rrset(
	knot_packet_t *packet, short pos);

/*!
 * \brief Checks if the packet already contains the given RRSet.
 *
 * It searches for the RRSet in the three lists of RRSets corresponding to
 * Answer, Authority and Additional sections of the packet.
 *
 * \note Only pointers are compared, i.e. two instances of knot_rrset_t with
 * the same data will be considered different.
 *
 * \param packet Packet to look for the RRSet in.
 * \param rrset RRSet to look for.
 *
 * \retval 0 if \a resp does not contain \a rrset.
 * \retval <> 0 if \a resp does contain \a rrset.
 */
int knot_packet_contains(const knot_packet_t *packet,
                           const knot_rrset_t *rrset,
                           knot_rrset_compare_type_t cmp);

/*!
 * \brief Adds RRSet to the list of temporary RRSets.
 *
 * Temporary RRSets are fully freed when the response structure is destroyed.
 *
 * \param response Response to which the temporary RRSet should be added.
 * \param tmp_rrset Temporary RRSet to be stored in the response.
 *
 * \retval KNOT_EOK
 * \retval KNOT_ENOMEM
 */
int knot_packet_add_tmp_rrset(knot_packet_t *response,
                                knot_rrset_t *tmp_rrset);

void knot_packet_free_tmp_rrsets(knot_packet_t *pkt);

/*!
 * \brief Converts the header structure to wire format.
 *
 * \note This function also adjusts the position (\a pos) according to
 *       the size of the converted wire format.
 *
 * \param[in] header DNS header structure to convert.
 * \param[out] pos Position where to put the converted header. The space has
 *                 to be allocated before calling this function.
 * \param[out] size Size of the wire format of the header in bytes.
 */
void knot_packet_header_to_wire(const knot_header_t *header,
                                  uint8_t **pos, size_t *size);

int knot_packet_question_to_wire(knot_packet_t *packet);

/*!
 * \brief Converts the stored response OPT RR to wire format and adds it to
 *        the response wire format.
 *
 * \param resp Response structure.
 */
int knot_packet_edns_to_wire(knot_packet_t *packet);

/*!
 * \brief Converts the packet to wire format.
 *
 * \param packet Packet to be converted to wire format.
 * \param wire Here the wire format of the packet will be stored.
 *             Space for the packet will be allocated. *resp_wire must
 *             be set to NULL (to avoid leaks).
 * \param wire_size The size of the packet in wire format will be stored here.
 *
 * \retval KNOT_EOK
 * \retval KNOT_EBADARG
 */
int knot_packet_to_wire(knot_packet_t *packet, uint8_t **wire,
                          size_t *wire_size);

const uint8_t *knot_packet_wireformat(const knot_packet_t *packet);

/*!
 * \brief Properly destroys the packet structure.
 *
 * \param response Packet to be destroyed.
 */
void knot_packet_free(knot_packet_t **packet);

/*!
 * \brief Dumps the whole packet in human-readable form.
 *
 * \note This function is empty unless KNOT_PACKET_DEBUG is defined.
 *
 * \param resp Packet to dump.
 */
void knot_packet_dump(const knot_packet_t *packet);

#endif /* _KNOT_PACKET_H_ */

/*! @} */