Objective Caml API
To dive directly into the API, the fully documented API is available at this url: http://odns.tuxfamily.org/doc/dns/html/ (older APIs are available from here if needed: http://odns.tuxfamily.org/doc/).
Compile Your Program with ODNS
Once ODNS is installed,
ocamlfind “knows” where is installed the library.
Hence the output to
ocamlfind -list will return you a list of installed library. One of them will be called “dns” (this is actually the ocamlfind name for ODNS):
$ ocamlfind -list
bigarray (version: [distributed with Ocaml])
camlp4 (version: [distributed with Ocaml])
[... other libraries ...]
dns (version: 0.2)
dynlink (version: [distributed with Ocaml])
findlib (version: 1.2.6)
[... other libraries ...]
Thanks to this, you can easily compile and link your program with just the name of the library (which is “dns” for ocamlfind, I remind).
For instance if you usually compile your program like this:
ocamlc -c file1.ml
Then the ocamlfind way is:
ocamlfind ocamlc -package dns -c file1.ml
ocamlfind command will add all necessary path for the compiler to find the library.
The same syntax works when linking. More information here.
ODNS provides very a generic resolver class for many common type of DNS resource records.
The currently supported resource records are:
- Host Address
- Authoritative Name Server
- Mail Destination (obsolete)
- Mail Forwared (obsolete)
- Canonical Name for an Alias
- Start Of Zone of Authority
- Mailbox Domain Name
- Mail Group Member
- Mail Rename Domain Name
- Null resource record
- Well Known Service Description
- Domain Name Pointer
- Host Information
- Mailbox or Mail List information
- Mail Exchange
- Text Strings
- Location of Service (defined in rfc2782)
- IP version 6 Host Address (defined in rfc3596)
Additionally the following pseudo-records are available as requests (they are not actual records but implies several kind of records as response):
- Transfer of an entire zone
- mailbox-related records (MB, MG or MR)
- mail agent RRs (Obsolete – see MX)
- * or ANY
- All records
It also provides helpers for additional processing that some resource records may require. In particular currently SRV, A, AAA and any address helpers.
Finally it provides lower level access to DNS data through the resolver, which allows you to construct your own queries, even with unsupported types and class if necessary, and to process the return DNS messages yourself, for instance if you want to check the header fields, or do additional processing if you fall in a case it is only possible from such a resolver.
Classic Generic DNS Lookups
As you can see in the API documentation, ODNS provides a powerful resolver, implemented as a class: Dns.query.
What does interest us the most here are the first 2 sections: High Level Methods and Resolver Configuration.
High Level Methods
The methods in the first section in particular are used to run queries. As a good example is better than any text:
let q = Dns.resolver in q#add_query ~qclass:"IN" ~qtype:"SRV" ~qname="_xmpp-client._tcp.xmpp.org"; q#query; let rec display_records = function |  -> () | rr::l -> print_string (Dnsprint.pretty_record rr); print_newline; display_records l (* Display the records in human format *) in display_records q#answers;
There is only one record for this domain, and the result, as of today, would be:
xmpp.org. has a xmpp-client service record on tcp: priority = 0 weight = 0 port = 9222 target = athena.jabber.org.
We may want to hack a little into the resolver configuration to change its internal behavior. Basically right now, this means:
- use other nameservers (
q#set_nameservers [ip1; ip2];) than the ones configured by the system (either by yourself personally, but nowaydays it is most often configured automatically by the DHCP client in the same time it gets an IP for you).
- Change the timeout (
q#set_timeout 2;) other than the default 5 seconds. The timeout indicates the time the resolver will wait for an answer before considering the query having failed.
- Change the number of retries (
q#set_retries 3;) it will do if all queries on all name servers failed. The default is to retry only once (1).
The current algorithm is to loop over all the list of name servers until you get an answer (which can be an empty answer or even be an error telling you that the domain does not exist, as long as it is authoritative answer). If you times-out or get an error, the next name server is tried. When all name servers have been tried, all in error or timeout, it will retry the whole list again, as many times as “retries” is set.
Obviously all these methods must be run before
q#action; if you want them to have any effet.
Dns.resolver is the basic resolver and returns you all the resource records returned in response of a given query (either as direct answers, or as additional information, or as authorities name servers information). This is basically what do all resolvers out there.
But often the more annoying part of such query is what comes “after”. This is the contextual work which must be done after you got the information and which requires the logics and semantics of the specific resource record you are requesting.
For instance, let’s take our example above. We are requesting a XMPP client service for a domain. Right. But what interest us is not a stupid list of records with many raw fields (domain, priority, weight and port for SRV). No we just want to connect to this server as a XMPP client.
Just to understand how a helper is useful, here is the post-processing you should normally do to the resolver’s result:
- Check that the data is what you asked, which means that the returned records are indeed service records, and for the right domain (you never know, the name server might have gone crazy, and that would be dangerous to connect to another XMPP server).
Be careful, I don’t speak about checking that someone is not trying to fool you (for instance using DNS cache poisining), but just that the fields of the answers are consistent with your query. No big deal, but still needs to be done.
- Each service record has a priority AND a weight field. This is used to order the result by some semi-randomization (“semi”, because this is a randomization using probability), hence making some kind of cheap load balancing (though not dynamic, hence not perfect, but better than nothing) system for a service using many servers.
- Finally what interests you are IP addresses, not domains. This means typically that after doing all previous still, you will still have to get the addresses of each of these new domain names, hence checking they are not already provided in the additional section (which is a best practice), or else make yourself additional A and AAAA queries.
- Alternatively if there are no resource records, you must query the addresses of the main target and use the default port for such service (if you know it).
And so all these complicated stuffs are taken care by a single function in ODNS:
It returns you a list of IPs (IPv4 and IPv6) and ports, in the right order, ready to be used for connecting your XMPP client.
ODNS provides currently 3 other helpers:
a_lookup to get the list of IPv4 of a domain,
aaaa_lookup for the IPv6, and
adress_lookup for both IPv4 and IPv6.
Low Level Interface and Resolver
Sometimes you might want more control over your query.
For instance, you might want to make queries that are not supported by ODNS, or even inexisting, experimental queries that you are testing (with the goal to make a RFC?).
ODNS provides very easy ways to do this.
TODO (to be continued)