Tutorial 1: Querying for MX records

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

ldns-mx is a simple tool that queries your default caching forwarder for the MX (Mail exchange) record of the given domain.

    % ldns-mx nlnetlabs.nl
    nlnetlabs.nl.   86400   IN      MX      100 omval.tednet.nl.
    nlnetlabs.nl.   86400   IN      MX      50 open.nlnetlabs.nl.

First of all, we need to include the correct header files, so that all functions are available to us:

#include "config.h"
#include <ldns/ldns.h>
config.h
ldns.h
Including this file will include all ldns files, and define some lookup tables.

In this case we have used a configure script to generate a config.h file that does all our inclusions for us, so that it can be compiled on multiple platforms. If your platform supports the include files `stdint.h` and `stdlib.h`, you can include these instead of using a configure script.

The first included files are prerequisites that ldns needs to function. The last one, of course, includes the functions of ldns itself.

In our main function, we declare some variables that we are going to use:

ldns_resolver *res;
ldns_struct_resolver
DNS stub resolver structure.
Definition resolver.h:60
ldns_rdf *domain;
ldns_pkt *p;
ldns_rr_list *mx;
ldns_status s;
ldns_status
enum ldns_enum_status ldns_status
Definition error.h:148
ldns_struct_pkt
DNS packet.
Definition packet.h:235
ldns_struct_rdf
Resource record data field.
Definition rdata.h:197
ldns_struct_rr_list
List or Set of Resource Records.
Definition rr.h:346

- The `ldns_resolver` structure keeps a list of nameservers, and can perform queries for us
- An `ldns_rdf` is a basic data type of dns, the RDATA. See Design for a description about the building blocks of DNS. In this case, `domain` will be used to store the name the user specifies when calling the program
- An `ldns_pkt` is a DNS packet, for instance a complete query, or an answer
- The `ldns_rr_list` structure contains a list of DNS Resource Records (RRs). In this case, we will store the MX records we find in the list.
- `ldns_status` is the basic type for status messages in ldns. Most functions will return a value of this type.

First, we parse the command line argument (checks omitted on this page, see full source code), and store it in our `domain` variable:

domain = ldns_dname_new_frm_str(argv[1]);
ldns_dname_new_frm_str
ldns_rdf * ldns_dname_new_frm_str(const char *str)
creates a new dname rdf from a string.
Definition dname.c:268

This function takes a string containing a domain name (like "nlnetlabs.nl") and returns an `ldns_rdf` representing that name. If somehow the given string can not be parsed it returns NULL.

Then, we create the resolver structure:

s = ldns_resolver_new_frm_file(&res, NULL);
ldns_resolver_new_frm_file
ldns_status ldns_resolver_new_frm_file(ldns_resolver **r, const char *filename)
Configure a resolver by means of a resolv.conf file The file may be NULL in which case there will be ...
Definition resolver.c:967

Most of the functions work like this, the first argument is a pointer to the structure where ldns should store its results (which is also a pointer). The function returns a status code indicating success (LDNS_STATUS_OK) or an error number. Remember that these types of functions allocate memory that you should free later (using the ldns_free_<type> functions).

The second argument is the filename that contains information about the resolver structure that is to be created. If this argument is NULL, /etc/resolv.conf is used. The syntax of the file is like that of /etc/resolv.conf.

We tell the resolver to query for our domain, type MX, of class IN:

p = ldns_resolver_search(res,
ldns_resolver_search
ldns_pkt * ldns_resolver_search(const ldns_resolver *r, const ldns_rdf *rdf, ldns_rr_type t, ldns_rr_class c, uint16_t flags)
Send the query for using the resolver and take the search list into account The search algorithm is a...
Definition resolver.c:1095
domain,
LDNS_RR_TYPE_MX,
LDNS_RR_CLASS_IN,
LDNS_RD);
LDNS_RD
#define LDNS_RD
Definition packet.h:30
LDNS_RR_TYPE_MX
@ LDNS_RR_TYPE_MX
mail exchange
Definition rr.h:108
LDNS_RR_CLASS_IN
@ LDNS_RR_CLASS_IN
the Internet
Definition rr.h:47

The last argument contains flags to influence the type of query the resolver structure sends. In this case, we want the nameserver to use recursion, so that we'll get the final answer. Therefore, we specify the LDNS_RD (Recursion Desired) flag.

This should return a packet if everything goes well.

We get all RRs of type MX from the answer packet and store them in our list:

mx = ldns_pkt_rr_list_by_type(p,
ldns_pkt_rr_list_by_type
ldns_rr_list * ldns_pkt_rr_list_by_type(const ldns_pkt *p, ldns_rr_type t, ldns_pkt_section s)
return all the rr with a specific type from a packet.
Definition packet.c:304
LDNS_RR_TYPE_MX,
LDNS_SECTION_ANSWER);
LDNS_SECTION_ANSWER
@ LDNS_SECTION_ANSWER
Definition packet.h:279

If this list is not empty, we sort and print it:

ldns_rr_list_sort(mx);
ldns_rr_list_sort
void ldns_rr_list_sort(ldns_rr_list *unsorted)
sorts an rr_list (canonical wire format).
Definition rr.c:1520
ldns_rr_list_print(stdout, mx);
ldns_rr_list_print
void ldns_rr_list_print(FILE *output, const ldns_rr_list *list)
print a rr_list to output
Definition host2str.c:3454

And finally, just to be proper, we free our allocated data:

ldns_rr_list_deep_free(mx);
ldns_rr_list_deep_free
void ldns_rr_list_deep_free(ldns_rr_list *rr_list)
frees an rr_list structure and all rrs contained therein.
Definition rr.c:1024
}
}
ldns_pkt_free(p);
ldns_resolver_deep_free(res);
ldns_pkt_free
void ldns_pkt_free(ldns_pkt *packet)
frees the packet structure and all data that it contains.
Definition packet.c:897
ldns_resolver_deep_free
void ldns_resolver_deep_free(ldns_resolver *res)
Frees the allocated space for this resolver and all it's data.
Definition resolver.c:1004

For structures that can contain other ldns structures, there are two types of free() function available

- `ldns_free_<type>` frees only the allocated data for the structure itself.
- `ldns_deep_free_<type>` frees the structure, and ALL structures that are nested in it. For example, of you `deep_free` an ldns_rr_list, all `ldns_rr` structures that were present in the list are also freed.