Tutorial 2: Reading a zone file

The full source code can be found in examples/ldns-read-zone.c

ldns-read-zone reads a zone file, and prints it to stdout, with 1 resource record per line.

    % cat example.zone
    $ORIGIN example.
    $TTL 600

    example.        IN SOA  example. op.example. (
                                    2004022501 ; serial
                                    28800      ; refresh (8 hours)
                                    7200       ; retry (2 hours)
                                    604800     ; expire (1 week)
                                    18000      ; minimum (5 hours)
                                    )

    @       IN      MX      10 mail.example.
    @       IN      NS      ns1
    @       IN      NS      ns2
    @       IN      A       123.123.123.123

    % ldns-read-zone example.zone
    example.        600     IN      SOA     example. op.example. 2004022501 28800 7200 604800 18000
    example.        600     IN      MX      10 mail.example.
    example.        600     IN      NS      ns1.example.
    example.        600     IN      NS      ns2.example.
    example.        600     IN      A       123.123.123.123

Again, let's start with including some necessary header files:

#include "config.h"
config.h
#include <unistd.h>
#include <stdlib.h>
#include <ldns/ldns.h>
#include <ldns/host2str.h>
#include <errno.h>
host2str.h
host2str.h - txt presentation of RRs
ldns.h
Including this file will include all ldns files, and define some lookup tables.

In this example, we are going to open a file, if that fails, we'll need errno.h to display an error message.

Okay, let's declare the variables we are going to need today:

char *filename;
FILE *fp;
ldns_zone *z;
int line_nr = 0;
int c;
bool canonicalize = false;
bool sort = false;
bool print_soa = true;
ldns_status s;
ldns_status
enum ldns_enum_status ldns_status
Definition error.h:148
ldns_zone
struct ldns_struct_zone ldns_zone
Definition zone.h:50

The only two ldns-specific types here are `ldns_zone` and `ldns_status`.

- `ldns_zone` is the structure that can contain a complete zone
- `ldns_status` again is used to check return values of ldns functions

If we get no filename, we'll read standard input, otherwise, we'll try to open the given filename:

if (argc == 0) {
fp = stdin;
} else {
filename = argv[0];
fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
}

With the `FILE` pointer in our hands, we visit ldns to pour it into a zone structure:

s = ldns_zone_new_frm_fp_l(&z, fp, NULL, 0, LDNS_RR_CLASS_IN, &line_nr);
LDNS_RR_CLASS_IN
@ LDNS_RR_CLASS_IN
the Internet
Definition rr.h:47
ldns_zone_new_frm_fp_l
ldns_status ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t ttl, ldns_rr_class c, int *line_nr)
Create a new zone from a file, keep track of the line numbering.

There is also a `ldns_zone_new_frm_fp`, but this one also remembers the line number it was on, so we can use that if we encounter a parse error.

Just like in Tutorial 1: Querying for MX records, the first argument is a pointer to the place ldns should store its creation in, and again, the return value is the status code.

The second argument is the file pointer where our zone data should reside.

The third argument, if not NULL, is a `dname` that contains the zones origin. It will place this dname after every name in the file that is not a fully qualified domain name.

The fourth argument, if not 0, is the default TTL to use.

Both these values can be specified in the zone file by setting `$ORIGIN` and `$TTL`.

The fifth argument specifies the default class, which defaults to IN (LDNS_RR_CLASS_IN).

And finally, every time `ldns_zone_new_frm_fp_l` reads a line from the input file pointer, it will increment the value pointed to by the last argument with 1.

Okay, with that, we should have a nice zone structure. Of course we need to check whether it has succeeded.

if (s != LDNS_STATUS_OK) {
LDNS_STATUS_OK
@ LDNS_STATUS_OK
Definition error.h:26
fprintf(stderr, "%s at line %d\n",
ldns_get_errorstr_by_id(s),
line_nr);
exit(EXIT_FAILURE);
}
if (show_types) {
if (print_soa)
print_soa = ldns_nsec_bitmap_covers_type(show_types,
LDNS_RR_TYPE_SOA);
stripped_list = ldns_rr_list_new();
while ((cur_rr = ldns_rr_list_pop_rr(ldns_zone_rrs(z))))
if (ldns_nsec_bitmap_covers_type(show_types,
ldns_rr_get_type(cur_rr)))
ldns_rr_list_push_rr(stripped_list, cur_rr);
else
ldns_rr_free(cur_rr);
ldns_rr_list_free(ldns_zone_rrs(z));
ldns_zone_set_rrs(z, stripped_list);
}
if (canonicalize) {
ldns_rr2canonical(ldns_zone_soa(z));
for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z)); i++) {
ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z), i));
}
}
if (sort) {
ldns_zone_sort(z);
}
if (print_soa && ldns_zone_soa(z)) {
if (soa_serial_increment_func) {
ldns_rr_soa_increment_func_int(
ldns_zone_soa(z)
, soa_serial_increment_func
, soa_serial_increment_func_data
);
}
ldns_rr_print_fmt(stdout, fmt, ldns_zone_soa(z));
}
ldns_rr_list_print_fmt(stdout, fmt, ldns_zone_rrs(z));
ldns_zone_deep_free(z);
ldns_nsec_bitmap_covers_type
signed char ldns_nsec_bitmap_covers_type(const ldns_rdf *bitmap, ldns_rr_type type)
Check if RR type t is enumerated and set in the RR type bitmap rdf.
Definition dnssec.c:1393
ldns_get_errorstr_by_id
const char * ldns_get_errorstr_by_id(ldns_status err)
look up a descriptive text by each error.
Definition error.c:196
ldns_rr_list_print_fmt
void ldns_rr_list_print_fmt(FILE *output, const ldns_output_format *fmt, const ldns_rr_list *list)
print a rr_list to output
Definition host2str.c:3444
ldns_rr_print_fmt
void ldns_rr_print_fmt(FILE *output, const ldns_output_format *fmt, const ldns_rr *rr)
Prints the data in the resource record to the given file stream (in presentation format).
Definition host2str.c:3406
ldns_rr_list_free
void ldns_rr_list_free(ldns_rr_list *rr_list)
frees an rr_list structure.
Definition rr.c:1015
ldns_rr_free
void ldns_rr_free(ldns_rr *rr)
frees an RR structure
Definition rr.c:81
ldns_rr_list_new
ldns_rr_list * ldns_rr_list_new(void)
creates a new rr_list structure.
Definition rr.c:1004
ldns_rr_list_pop_rr
ldns_rr * ldns_rr_list_pop_rr(ldns_rr_list *rr_list)
pops the last rr from an rrlist.
Definition rr.c:1181
LDNS_RR_TYPE_SOA
@ LDNS_RR_TYPE_SOA
marks the start of a zone of authority
Definition rr.h:90
ldns_rr_list_push_rr
signed char ldns_rr_list_push_rr(ldns_rr_list *rr_list, const ldns_rr *rr)
pushes an rr to an rrlist.
Definition rr.c:1136
ldns_rr2canonical
void ldns_rr2canonical(ldns_rr *rr)
converts each dname in a rr to its canonical form.
Definition rr.c:1785
ldns_rr_list_rr_count
size_t ldns_rr_list_rr_count(const ldns_rr_list *rr_list)
returns the number of rr's in an rr_list.
Definition rr.c:961
ldns_rr_get_type
ldns_rr_type ldns_rr_get_type(const ldns_rr *rr)
returns the type of the rr.
Definition rr.c:947
ldns_rr_list_rr
ldns_rr * ldns_rr_list_rr(const ldns_rr_list *rr_list, size_t nr)
returns a specific rr of an rrlist.
Definition rr.c:994
ldns_rr_soa_increment_func_int
void ldns_rr_soa_increment_func_int(ldns_rr *soa, ldns_soa_serial_increment_func_t f, int data)
Increment the serial number of the given SOA with the given function using data as an argument for th...
Definition rr_functions.c:426
ldns_zone_soa
ldns_rr * ldns_zone_soa(const ldns_zone *z)
Return the soa record of a zone.
Definition zone.c:17
ldns_zone_set_rrs
void ldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist)
Set the zone's contents.
Definition zone.c:41
ldns_zone_rrs
ldns_rr_list * ldns_zone_rrs(const ldns_zone *z)
Get a list of a zone's content.
Definition zone.c:35
ldns_zone_deep_free
void ldns_zone_deep_free(ldns_zone *zone)
Frees the allocated memory for the zone, the soa rr in it, and the rr_list structure in it,...
Definition zone.c:376
ldns_zone_sort
void ldns_zone_sort(ldns_zone *zone)
Sort the rrs in a zone, with the current impl.
Definition zone.c:359

If everything went well, we sort the zone if necessary, print it, and free it.

Since `ldns_zone` contains other ldns structures, we use `ldns_deep_free` so that every `ldns_rr_list`, `ldns_rr` et cetera are freed too.

If something went wrong, we use `ldns_get_errorstr_by_id()` to get a nice error string instead of just a status integer.

And of course, we should play nice and close the file: