Getting started

simdzone is a high performance DNS presentation format parser. simdzone takes care of control entries and deserializes resource record (RR) entries to wire format. Handling of any special considerations that apply to the data are delegated to the application to allow for wider applicability and better performance.

For example, zone files must contain exactly one SOA RR at the top of the zone and all RRs must be of the same class (see RFC 1035#section-5.2). The application is responsible for enforcing these restrictions so that the parser itself is equally well suited to parse serialized zone transfers, etc.

The interface is purposely minimalistic and provides two parse functions:

  • zone_parse to parse files.

  • zone_parse_string to parse in-memory data.

To keep track of state, the functions require the application to pass a zone_parser_t, which is initialized on use. zone_options_t can be used to configure callbacks, defaults and behavior. Specifying at least an accept callback function (invoked for each resource record), origin, default ttl and default class is required.

To avoid memory leaks and improve performance the parser takes a set of pre-allocated buffers. A zone_name_buffer_t to store the owner and a zone_rdata_buffer_t to store RDATA. The user_data pointer is passed verbatim to the specified callback function and can be used to communicate application context.

Parsing zones

Let’s create a simple zone parser to demonstrate simdzone usage.

First, we define a function to receives RRs as they’re parsed.

 1#include <stdio.h>
 2#include <stdint.h>
 3#include <stdlib.h>
 4
 5#include <zone.h>
 6
 7struct zone {
 8  size_t count;
 9  uint16_t class;
10};
11
12static const int32_t accept_rr(
13  zone_parser_t *parser,
14  const zone_name_t *owner,
15  uint16_t type,
16  uint16_t class,
17  uint32_t ttl,
18  uint16_t rdlength,
19  const uint8_t *rdata,
20  void *user_data)
21{
22  struct zone *zone = user_data;
23  // require first record to be of type SOA
24  if (!zone->count) {
25    zone->class = class;
26    if (type != 6) {
27      // use log function to automatically print file and line number
28      zone_log(parser, ZONE_ERROR, "First record is not of type SOA");
29      return ZONE_SEMANTIC_ERROR;
30    }
31  } else {
32    // require records not to be of type SOA
33    if (type == 6) {
34      zone_log(parser, ZONE_ERROR, "Extra record of type SOA");
35      return ZONE_SEMANTIC_ERROR;
36    // require each record uses the same class
37    } else if (class != zone->class) {
38      zone_log(parser, ZONE_ERROR, "Record not in class " PRIu16, zone->class);
39      return ZONE_SEMANTIC_ERROR;
40    }
41  }
42
43  // authoritative servers would now store RR in a database or similar
44
45  zone->count++;
46  return ZONE_SUCCESS;
47}

zone_log is a convenience function that can be used to print location information with the error message. Returning ZONE_SEMANTIC_ERROR from the callback signals the parser an error has occurred and processing must be halted. simdzone defines a number of error codes, but any negative number will halt the parser. The error code is propagated and eventually returned as the result of zone_parse.

Next we define a main function that’s called on execution of the program.

 1int main(int argc, char *argv[])
 2{
 3  zone_parser_t parser;
 4  zone_name_buffer_t name;
 5  zone_rdata_buffer_t rdata;
 6  zone_buffers_t buffers = { 1, &name, &rdata };
 7  zone_options_t options = { 0 }; // must be properly initialized
 8
 9  if (argc != 3) {
10    fprintf(stderr, "Usage: %s zone-file origin\n", argv[0]);
11    exit(EXIT_FAILURE);
12  }
13
14  options.accept.callback = accept_rr;
15  options.origin = argv[2];
16  options.default_ttl = 3600;
17  options.default_class = 1; // IN
18
19  int32_t result;
20  struct zone zone = { 0 };
21
22  result = zone_parse(&parser, &options, &buffers, argv[1], &zone);
23  if (result < 0) {
24    fprintf(stderr, "Could not parse %s\n", argv[1]);
25    exit(EXIT_FAILURE);
26  }
27
28  printf("parsed %zu records in %s", zone->count, argv[1]);
29
30  return 0;
31}