forked from NLnetLabs/simdzone
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathzone.h
617 lines (578 loc) · 17.6 KB
/
zone.h
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
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
/*
* zone.h -- (DNS) zone parser
*
* Copyright (c) 2022-2023, NLnet Labs. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef ZONE_H
#define ZONE_H
/**
* @file
* @brief simdzone main header
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "zone/attributes.h"
#include "zone/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/**
* @defgroup class_codes Class codes
*
* @{
*/
/** Internet @rfc{1035} */
#define ZONE_IN (1u)
/** CSNET @rfc{1035} @obsolete */
#define ZONE_CS (2u)
/** CHAOS @rfc{1035} */
#define ZONE_CH (3u)
/** Hesiod @rfc{1035} */
#define ZONE_HS (4u)
/** @} */
/**
* @defgroup type_codes Type codes
*
* @{
*/
/** Host address @rfc{1035} */
#define ZONE_A (1u)
/** Authoritative name server @rfc{1035} */
#define ZONE_NS (2u)
/** Mail destination @rfc{1035} @obsolete */
#define ZONE_MD (3u)
/** Mail forwarder @rfc{1035} @obsolete */
#define ZONE_MF (4u)
/** Canonical name for an alias @rfc{1035} */
#define ZONE_CNAME (5u)
/** Marks the start of authority @rfc{1035} */
#define ZONE_SOA (6u)
/** Mailbox domain name @rfc{1035} @experimental */
#define ZONE_MB (7u)
/** Mail group member @rfc{1035} @experimental */
#define ZONE_MG (8u)
/** Mail rename domain name @rfc{1035} @experimental */
#define ZONE_MR (9u)
/** Anything @rfc{883} @obsolete */
#define ZONE_NULL (10u)
/** Well known service description @rfc{1035} */
#define ZONE_WKS (11u)
/** Domain name pointer @rfc{1035} */
#define ZONE_PTR (12u)
/** Host information @rfc{1035} */
#define ZONE_HINFO (13u)
/** Mailbox or mail list information @rfc{1035} */
#define ZONE_MINFO (14u)
/** Mail exchange @rfc{1035} */
#define ZONE_MX (15u)
/** Text strings @rfc{1035} */
#define ZONE_TXT (16u)
/** Responsible person @rfc{1035} */
#define ZONE_RP (17u)
/** AFS Data Base location @rfc{1183} @rfc{5864} */
#define ZONE_AFSDB (18u)
/** X.25 PSDN address @rfc{1183} */
#define ZONE_X25 (19u)
/** ISDN address @rfc{1183} */
#define ZONE_ISDN (20u)
/** Route Through @rfc{1183} */
#define ZONE_RT (21u)
/** NSAP address, NSAP style A record @rfc{1706} */
#define ZONE_NSAP (22u)
/** Domain name pointer, NSAP style @rfc{1348} @rfc{1637} */
#define ZONE_NSAP_PTR (23u)
/** Signature @rfc{2535} */
#define ZONE_SIG (24u)
/** Public key @rfc{2535} @rfc{2930} */
#define ZONE_KEY (25u)
/** X.400 mail mapping information @rfc{2163} */
#define ZONE_PX (26u)
/** Geographical Position @rfc{1712} */
#define ZONE_GPOS (27u)
/** IPv6 Address @rfc{3596} */
#define ZONE_AAAA (28u)
/** Location Information @rfc{1876} */
#define ZONE_LOC (29u)
/** Next domain @rfc{3755} @rfc{2535} @obsolete */
#define ZONE_NXT (30u)
/** Server Selection @rfc{2782} */
#define ZONE_SRV (33u)
/** Naming Authority Pointer @rfc{2915} @rfc{2168} @rfc{3403} */
#define ZONE_NAPTR (35u)
/** Key Exchanger @rfc{2230} */
#define ZONE_KX (36u)
/** CERT [RFC4398] */
#define ZONE_CERT (37u)
/** IPv6 Address @rfc{3226} @rfc{2874} @rfc{6563} @obsolete */
#define ZONE_A6 (38u)
/** DNAME @rfc{6672} */
#define ZONE_DNAME (39u)
/** Address Prefix List @rfc{3123} */
#define ZONE_APL (42u)
/** Delegation Signer @rfc{4034} @rfc{3658} */
#define ZONE_DS (43u)
/** SSH Key Fingerprint @rfc{4255} */
#define ZONE_SSHFP (44u)
/** IPsec public key @rfc{4025} */
#define ZONE_IPSECKEY (45u)
/** Resource Record Signature @rfc{4034} @rfc{3755} */
#define ZONE_RRSIG (46u)
/** Next Secure @rfc{4034} @rfc{3755} */
#define ZONE_NSEC (47u)
/** DNS Public Key @rfc{4034} @rfc{3755} */
#define ZONE_DNSKEY (48u)
/** DHCID [RFC4701] */
#define ZONE_DHCID (49u)
/** NSEC3 [RFC5155] */
#define ZONE_NSEC3 (50u)
/** NSEC3PARAM [RFC5155] */
#define ZONE_NSEC3PARAM (51u)
/** TLSA @rfc{6698} */
#define ZONE_TLSA (52u)
/** S/MIME cert association @rfc{8162} */
#define ZONE_SMIMEA (53u)
/** Host Identity Protocol @rfc{8005} */
#define ZONE_HIP (55u)
/** Child DS @rfc{7344} */
#define ZONE_CDS (59u)
/** DNSKEY(s) the Child wants reflected in DS @rfc{7344} */
#define ZONE_CDNSKEY (60u)
/** OpenPGP Key @rfc{7929} */
#define ZONE_OPENPGPKEY (61u)
/** Child-To-Parent Synchronization @rfc{7477} */
#define ZONE_CSYNC (62u)
/** Zone message digest @rfc{8976} */
#define ZONE_ZONEMD (63u)
/** Service binding @rfc{9460} */
#define ZONE_SVCB (64u)
/** Service binding @rfc{9460} */
#define ZONE_HTTPS (65u)
/** Sender Policy Framework @rfc{7208} */
#define ZONE_SPF (99u)
/** Node Identifier @rfc{6742} */
#define ZONE_NID (104u)
/** 32-bit Locator for ILNPv4-capable nodes @rfc{6742} */
#define ZONE_L32 (105u)
/** 64-bit Locator for ILNPv6-capable nodes @rfc{6742} */
#define ZONE_L64 (106u)
/** Name of an ILNP subnetwork @rfc{6742} */
#define ZONE_LP (107u)
/** EUI-48 address @rfc{7043} */
#define ZONE_EUI48 (108u)
/** EUI-64 address @rfc{7043} */
#define ZONE_EUI64 (109u)
/** Uniform Resource Identifier @rfc{7553} */
#define ZONE_URI (256u)
/** Certification Authority Restriction @rfc{6844} */
#define ZONE_CAA (257u)
/** DNS Authoritative Source (DNS-AS) */
#define ZONE_AVC (258u)
/** DNSSEC Lookaside Validation @rfc{4431} */
#define ZONE_DLV (32769u)
/** @} */
typedef struct zone_string zone_string_t;
struct zone_string {
size_t length;
const char *data;
};
// FIXME: probably best to rename this to mnemonic to stay with DNS terminology?
typedef struct zone_symbol zone_symbol_t;
struct zone_symbol {
struct {
char data[24]; // zero padded for convenient vectorized comparison
size_t length;
} key;
uint32_t value;
};
typedef struct zone_table zone_table_t;
struct zone_table {
size_t length;
const zone_symbol_t *symbols; // sorted for use with bsearch
};
/**
* @brief Type of value defined by field
*
* Fields are defined by their binary representation, NOT their textual
* representation. e.g. time-to-live and timestamp fields are encoded as
* 32-bit integers on the wire. @ref type_qualifiers are used to complement
* the type information. e.g. @ref ZONE_TTL and @ref ZONE_TIME can be used to
* provide extra information regarding the aforementioned types.
*/
typedef enum {
ZONE_INT8,
ZONE_INT16,
ZONE_INT32,
ZONE_IP4,
ZONE_IP6,
ZONE_NAME,
ZONE_STRING,
// (B)inary (L)arge (Ob)ject. Inspired by relational database terminology.
// Must be last.
ZONE_BLOB,
ZONE_ILNP64,
// hex fields
// ZONE_EUI48 (ZONE_HEX6?)
// ZONE_EUI64 (ZONE_HEX8?)
// miscellaneous fields
ZONE_SVC_PARAM, /**< SVCB service parameter */
ZONE_TYPE_BITMAP, /**< NSEC type bitmap */
ZONE_SERVICE_BITMAP /**< WKS service bitmap */
} zone_type_t;
/**
* @defgroup type_qualifiers Type qualifiers
*
* Type qualifiers provide additional information for RDATA fields. Types
* indicate the binary representation of an RDATA field, qualifier(s) can be
* used to communicate semantics. e.g. a time-to-live is presented on the
* wire as a 32-bit integer, ZONE_TTL can be used to signal the field
* represents a time-to-live value.
*
* @note Some types allow for more than one qualifier to be specified, hence
* each qualifier is assigned a separate bit.
*
* @{
*/
/**
* @brief Type code (#ZONE_INT16)
*
* Type codes may appear in text by name or generic type notation @rfc{3597}.
*/
#define ZONE_TYPE (1u << 0)
/**
* @brief Class code (#ZONE_INT16)
*
* Class codes may appear in text by name or generic class notation @rfc{3597}.
*/
#define ZONE_CLASS (1u << 1)
/**
* @brief Time-to-live (TTL) (#ZONE_INT32)
*
* Time-to-live values may appear in text as numeric value (seconds) or in
* "1h2m3s" notation (@e extension).
*/
#define ZONE_TTL (1u << 2)
/**
* @brief Timestamp (#ZONE_INT32)
*
* Timestamps must be presented in text in "YYYYMMDDHHmmSS" notation.
*/
#define ZONE_TIME (1u << 3)
/** @brief Text representation is base16 (#ZONE_STRING or #ZONE_BLOB) */
#define ZONE_BASE16 (1u << 4)
/** @brief Text representation is base32 (#ZONE_BLOB) */
#define ZONE_BASE32 (1u << 5)
/** @brief Text representation is base64 (#ZONE_BLOB) */
#define ZONE_BASE64 (1u << 6)
/** @brief Name is compressed (#ZONE_NAME) */
#define ZONE_COMPRESSED (1u << 7)
/** @brief Name represents a mailbox (#ZONE_NAME) */
#define ZONE_MAILBOX (1u << 8)
/** @brief Name is converted to lower case for DNSSEC validation (#ZONE_NAME) */
#define ZONE_LOWER_CASE (1u << 9)
/** @brief Optional (#ZONE_NAME) */
#define ZONE_OPTIONAL (1u << 10)
/**
* @brief May occur multiple times (#ZONE_STRING or #ZONE_SVC_PARAM)
*
* Field may occur multiple times. e.g. #ZONE_STRING in #ZONE_TXT or
* #ZONE_SVC_PARAM in #ZONE_SVCB. Sequences must be the last field in the
* record.
*/
#define ZONE_SEQUENCE (1u << 11)
#define ZONE_CAA_TAG (1u << 12)
/** @} */
typedef struct zone_field_info zone_field_info_t;
struct zone_field_info {
zone_string_t name;
uint32_t type;
uint32_t qualifiers;
zone_table_t symbols;
};
/**
* @defgroup options Type options
* @brief Options for record types
*
* @{
*/
#define ZONE_ANY (1<<2)
#define ZONE_EXPERIMENTAL (1<<3)
#define ZONE_OBSOLETE (1<<4)
/** @} */
typedef struct zone_type_info zone_type_info_t;
struct zone_type_info {
zone_symbol_t name;
uint32_t options;
struct {
size_t length;
const zone_field_info_t *fields;
} rdata;
};
#define ZONE_BLOCK_SIZE (64)
#define ZONE_WINDOW_SIZE (256 * ZONE_BLOCK_SIZE) // 16KB
// tape capacity must be large enough to hold every token from a single
// worst-case read (e.g. 64 consecutive line feeds). in practice a single
// block will never contain 64 tokens, therefore, to optimize throughput,
// allocate twice the size so consecutive index operations can be done
#define ZONE_TAPE_SIZE ((100 * ZONE_BLOCK_SIZE) + ZONE_BLOCK_SIZE)
#define ZONE_RDATA_SIZE (65535)
#define ZONE_NAME_SIZE (255)
#define ZONE_PADDING_SIZE (ZONE_BLOCK_SIZE)
typedef struct zone_name_buffer zone_name_buffer_t;
struct zone_name_buffer {
size_t length; /**< Length of domain name stored in buffer */
uint8_t octets[ ZONE_NAME_SIZE + ZONE_PADDING_SIZE ];
};
typedef struct zone_rdata_buffer zone_rdata_buffer_t;
struct zone_rdata_buffer {
size_t length; /**< Length of RDATA stored in buffer */
uint8_t octets[ ZONE_RDATA_SIZE + 4096 /* nsec padding */ ];
};
// @private
// non-delimiting tokens may contain (escaped) newlines. tracking newlines
// within tokens by taping them makes the lex operation more complex, resulting
// in a significantly larger binary and slower operation, and may introduce an
// infinite loop if the tape may not be sufficiently large enough. tokens
// containing newlines is very much an edge case, therefore the scanner
// implements an unlikely slow path that tracks the number of escaped newlines
// during tokenization and registers them with each consecutive newline token.
// this mode of operation nicely isolates location tracking in the scanner and
// accommodates parallel processing should that ever be desired
// @private
typedef struct zone_file zone_file_t;
struct zone_file {
zone_file_t *includer;
zone_name_buffer_t origin, owner;
uint16_t last_type;
uint32_t last_ttl, default_ttl;
uint16_t last_class;
// non-terminating line feeds, i.e. escaped line feeds, line feeds in quoted
// sections or within parentheses, are counted, but deferred for consistency
// in error reports
size_t span; /**< number of lines spanned by record */
size_t line; /**< starting line of record */
char *name;
char *path;
FILE *handle;
bool grouped;
bool start_of_line;
enum { ZONE_HAVE_DATA, ZONE_READ_ALL_DATA, ZONE_NO_MORE_DATA } end_of_file;
struct {
size_t index, length, size;
char *data;
} buffer;
// indexer state is kept per-file
struct {
uint64_t in_comment;
uint64_t in_quoted;
uint64_t is_escaped;
uint64_t follows_contiguous;
} state;
// vector(s) of tokens generated by the indexer. guaranteed to be large
// enough to hold every token for a single read + terminators
struct { const char **head, **tail, *tape[ZONE_TAPE_SIZE + 2]; } fields;
struct { const char **head, **tail, *tape[ZONE_TAPE_SIZE + 1]; } delimiters;
struct { uint16_t *head, *tail, tape[ZONE_TAPE_SIZE + 1]; } lines;
};
typedef struct zone_parser zone_parser_t;
struct zone_parser;
/**
* @defgroup log_categories Log categories.
*
* @note No direct relation between log categories and error codes exists.
* Log categories communicate the importance of the log message, error
* codes communicate what went wrong to the caller.
* @{
*/
/** Error condition. */
#define ZONE_ERROR (1u<<1)
/** Warning condition. */
#define ZONE_WARNING (1u<<2)
/** Informational message. */
#define ZONE_INFO (1u<<3)
/** @} */
typedef void(*zone_log_t)(
zone_parser_t *,
const char *, // file
size_t, // line
const char *, // function
uint32_t, // category
const char *, // message
void *); // user data
/**
* @brief Write error message to active log handler.
*
* @note Direct use is discouraged. Use of #ZONE_LOG instead.
*
* @param[in] parser Zone parser
* @param[in] file Name of source file
* @param[in] line Line number in source file
* @param[in] function Name of function
* @param[in] category Log category
* @param[in] format Format string compatible with printf
* @param[in] ... Variadic arguments corresponding to #format
*/
ZONE_EXPORT void zone_log(
zone_parser_t *parser,
const char *file,
size_t line,
const char *function,
uint32_t category,
const char *format,
...)
zone_nonnull((1,2,4,6))
zone_format_printf(6,7);
/**
* @brief Write log message to active log handler.
*
* The zone parser operates on a per-record base and therefore cannot detect
* errors that span records. e.g. SOA records being specified more than once.
* The user may print a message using the active log handler, keeping the
* error message format consistent.
*
* @param[in] parser Zone parser
* @param[in] category Log category
* @param[in] format Format string compatible with printf
* @param[in] ... Variadic arguments corresponding to @ref format
*/
#define ZONE_LOG(parser, category, ...) \
zone_log(parser, __FILE__, __LINE__, __func__, category, __VA_ARGS__)
typedef struct zone_name zone_name_t;
struct zone_name {
uint8_t length;
uint8_t *octets;
};
// invoked for each record (host order). header (owner, type, class and ttl)
// fields are passed individually for convenience. rdata fields can be visited
// individually by means of the iterator
typedef int32_t(*zone_add_t)(
zone_parser_t *,
const zone_type_info_t *, // type information
const zone_name_t *, // owner (length + octets)
uint16_t, // type
uint16_t, // class
uint32_t, // ttl
uint16_t, // rdlength
const uint8_t *, // rdata
void *); // user data
typedef struct {
/** Lax mode of operation. */
/** Authoritative servers may choose to be more lenient when operating as
as a secondary as data may have been transferred over AXFR/IXFR that
would have triggered an error otherwise. */
bool secondary;
/** Disable $INCLUDE directive. */
/** Useful in setups where untrusted input may be offered. */
bool no_includes;
/** Enable 1h2m3s notations for TTLS. */
bool pretty_ttls;
const char *origin;
uint32_t default_ttl;
uint16_t default_class;
struct {
/** Message categories to write out. */
/** All categories are printed if no categories are selected and no
custom callback was specified. */
uint32_t categories;
/** Callback used to write out log messages. */
zone_log_t write;
} log;
struct {
zone_add_t add;
// FIXME: more callbacks to be added at a later stage to support efficient
// (de)serialization of AXFR/IXFR in text representation.
//zone_delete_t remove;
} accept;
} zone_options_t;
/**
* @brief Buffer space reserved for parser
*
* Depending on the use case, parsing resource records and committing the data
* are disjunct operations. Specifically, authoritative name servers may want
* to parse and commit in parallel to cut load times. Allocate multiple buffers
* to allow for asynchronous operation.
*
* Synchronization between submission and completion is the responsibility of
* the application. The return code of the accept operation indicates which
* rdata buffer to use next. Rotation of name buffers is controlled by the
* parser.
*/
typedef struct zone_buffers zone_buffers_t;
struct zone_buffers {
size_t size; /**< Number of name and rdata buffers available */
zone_name_buffer_t *owner;
zone_rdata_buffer_t *rdata;
};
struct zone_parser {
zone_options_t options;
void *user_data;
struct {
size_t size;
struct {
size_t serial;
zone_name_buffer_t *blocks;
} owner;
struct {
zone_rdata_buffer_t *blocks;
} rdata;
} buffers;
zone_name_buffer_t *owner;
zone_rdata_buffer_t *rdata;
zone_file_t *file, first;
};
/**
* @defgroup return_codes Return codes
*
* @{
*/
/** Success */
#define ZONE_SUCCESS (0)
/** Syntax error */
#define ZONE_SYNTAX_ERROR (-1)
/** Semantic error */
#define ZONE_SEMANTIC_ERROR (-2)
/** Operation failed due to lack of memory */
#define ZONE_OUT_OF_MEMORY (-3)
/** Bad parameter value */
#define ZONE_BAD_PARAMETER (-4)
/** Error reading zone file */
#define ZONE_IO_ERROR (-5)
/** Control directive or support for record type is not implemented */
#define ZONE_NOT_IMPLEMENTED (-6)
/** Specified file does not exist */
#define ZONE_NOT_A_FILE (-6)
/** Access to specified file is not allowed */
#define ZONE_NOT_PERMITTED (-7)
/** @} */
/**
* @brief Parse zone file
*/
ZONE_EXPORT int32_t
zone_parse(
zone_parser_t *parser,
const zone_options_t *options,
zone_buffers_t *buffers,
const char *path,
void *user_data)
zone_nonnull((1,2,3,4));
/**
* @brief Parse zone from string
*/
ZONE_EXPORT int32_t
zone_parse_string(
zone_parser_t *parser,
const zone_options_t *options,
zone_buffers_t *buffers,
const char *string,
size_t length,
void *user_data)
zone_nonnull((1,2,3,4));
#if defined(__cplusplus)
}
#endif
#endif // ZONE_H