ext-DnsD administrator manual


ext-DnsD Introduction: how it works, basic description

ext-DnsD works as a caching forward dns server for a network, passing all requests to child process, called resolvers, to know how to handle them. The basic working diagram is show as follow:

+------------+ (1) request dns +------------+ (4) ask external +---------------------+
| dns client | --------------> | ext-DnsD | --------------------> | external dns server |
+------------+ resolution +------------+ server to resolv +---------------------+
| ^
(2) ask child | | (3) child resolver signals
how to handle | | how to handle request: reject,
request | | discard, rewrite or forward
v |
| child resolver |

As the diagrama shows, the server implements all DNS protocol required to handle incoming requests, asking the child what to do on each case, to then reply to the dns client with the value request either because it was reject, rewritten or the particular value reported by the external server.

This diagram doesn't reflect other details like how ext-DnsD caches or how it handle bad requests (blacklisting dns clients), but it shows the basic concept to understand how a child resolver works.

ext-DnsD server configuration

By default, ext-DnsD configuration is found at /etc/ext-dnsd.conf, but can also be located under a different location using –config flag (-c short option). That configuration has the following form:

   <!--  -*- nxml -*-  -->
   <!--  Addresses where the server will listen for DNS requests.  By
        default all address are used on the standard port. The proto
        declaration defines which protocol is enabled on that listen
        address.   -->
   <listen proto="udp" value="" />
   <!--  <listen value="" proto="udp"/>  -->
   <!--  Which DNS server will be used by ext-dnsd to resolve requests
        received. You can configure the following values:
	- ip addresses like:
	- use resolv.conf to guess address like: resolveconf
   <relay-dns-server value="" />
   <!--  Path to the application that will be called by ext-dns to
        notify various events while doing DNS resolution. The
        application working as a child has to accept from the stdin
        notifications due by the server, and in return the application
        may be required to report what to do.   -->
   <!--  <child-resolver value="/home/acinom/core-admin/app-builder/apps/internet_access_manager/iam-resolver.py" />    -->
   <!--  Number of childs to create for operation.

	value: number of childs to create to resolve queries

	command-timeout: command timeout in seconds. Beyond that, the
	child is killed and the ext-dns server reports DNS unknown
	error. If nothing is configured, 15 will be used by
	default. You can disable it by configuring "disable" as value.
   <child-number command-timeout="15" value="10" />
   <!--  By default, should ext-dnsd server to resolve names found from
        /etc/hosts  -->
   <resolve-from-etc-hosts value="yes" />
   <!--  What to do when a failure (wrong signal) is received. By
        default is to hold the process to debug it. Allowed values
	- hold : hold the process 
	- abort : terminate the process
	- continue : continue normal processing

        If nothing is configured, hold is assumed.
   <failure-action value="hold" />
   <!--  allows to configure the cache size used by ext-Dns  -->
   <cache-size value="1000" />

As we see, ext-DnsD configuration is pretty straightforward. It's got a sections to declare what is the DNS server we will use to forward request (as requested by child resolvers), the location of the child resolver script, how many childs to create and other easy to see settings.

Checking ext-Dnsd server status

After successfully starting ext-Dnsd server, you can check its stats by reading the file located at /var/run/ext-dns.status as follows. It will give you lots of useful information about server status:

>> cat /var/run/ext-dnsd.status
Stamp: 1362599140
Child status: 0/10
Requests received: 1
Requests served: 1
Failures found: 0
Pending requests: 0
Cache stats: 0/1000 (used/max) 0/1 (hits/access) 0.00% (ratio)
Command timeout: 15 secs

How to write a ext-Dnsd child resolver (child resolver protocol)

A child resolver must be essentially a loop that receives on the standard input a single line when the ext-DnsD wants to ask something, and in turn the child resolver reports one or more lines.

Child resolver receives (note the use of \n to signify the carry return):

When ext-dnsd starts a child resolver, it sends a INIT\n
command so the can start internal databases and in turn, the
child must return OK\n
RESOLVE [source_ip] [record_name] [record_type] [record_class]\n
When a child resolver receives this command, the ext-dns is asking to resolve
this request. The command includes from where the request is comming and
what kind of DNS query it is. Some examples about this command are:
- RESOLVE www.google.com A IN\n
- RESOLVE google.com MX IN\n
To this command, the child must reply with some of this options:
- FORWARD\n : make the ext-dnsd to resolver the request with the
external dns server, passing the result directly to the
dns client requesting this data.
- DISCARD\n : silently ignore the request
- REJECT\n : send a reject reply (Rcode = 5, refused)
- UNKNOWN\n : send a reply as if it were unknown the value requested
- BLACKLIST [permanent] [seconds]\n
: silently ignore the request, and blacklists the dns client during
the provided amount of seconds. During that period all requests from
that source will be silently ignored.
Here are some examples:
- BLACKLIST permanent
- REPLY ipv4:[value] [ttl] [, more replies] [nocache]\n
- REPLY (name:|cname:)[value] [ttl] [norecurse] [, more replies] [nocache]\n
- REPLY mx:[mailer] [preference] [ttl] [, more replies] [nocache]\n
- REPLY ns:[dns server] [ttl] [, more replies] [nocache]\n
- REPLY soa:[primary server] [mail contact] [serial] [refresh] [retry] [expire] [minimum] [, more replies] [nocache]\n
: in this case the reply is directly handled by the child resolver
providing a cname as reply to the request (in the case name:/cname: is used)
or a particular ipv4 value if ipv4: is used.
The reply also includes the ttl to be used and optionally, an
indication about caching the result reported. In the case nocache string
is provided, ext-DnsD will not cache the value.
In the case of name: or cname: reply is received,
you can also especify norecurse option to enforce
ext-dns to avoid recursing to get the IP value associated to that name.
In the case you want multiple replies (answers) to the same question,
just add them separated by comma (,).
Here are some examples:
- REPLY ipv4: 3600 nocache
- REPLY ipv4: 3600
- REPLY ipv4: 3600, ipv4: 3600, ipv4: 3600 nocache
- REPLY name:www.aspl.es 3600
- REPLY name:www.google.com 3600, name:www.aspl.es 3600, name:www.asplhosting.com 3600 nocache
- REPLY mx:mail3.aspl.es 10 3600, mx:mail4.aspl.es 20 3600
- REPLY mx:mail3.aspl.es 10 3600
- REPLY ns:nsserver01.aspl.es 3600, ns:nsserver02.aspl.es 3600
- REPLY ns:test01.aspl.es 3600
- REPLY soa:ns1.account.aspl.es soporte@aspl.es 2012120400 10800 3600 604800 3600

A python child resolver skeleton

Full source code for this child resolver can be found at: https://dolphin.aspl.es/svn/publico/ext-dns/server/child-resolver-skel.py

1 #!/usr/bin/python
3 import sys
5 while True:
6  # get command from ext-dns
7  sys.stdout.flush ()
8  command = raw_input ().strip ()
10  # sys.stderr.write ("COMMAND RECEIVED: %s\n" % command)
12  if command == "INIT":
13  # sys.stderr.write ("REPLYING INIT OK\n")
14  # ext-dnsd is requesting to start this resolver, we have to
15  # reply OK to satisfy its inquire anyway
16  print "OK"
17  elif command[0:7] == "RESOLVE":
18  query_items = command.split (" ")
19  source_ip = query_items[1]
20  name = query_items[2]
21  dns_record = query_items[3]
22  dns_class = query_items[4]
24  if dns_class != "IN":
25  # we only resolve in IN
26  print "FORWARD"
27  # forward query but ask to not cache result
28  # print "FORWARD nocache"
29  continue
31  # example about letting the server to do the resolution
32  if dns_record != "A":
33  # we only resolve in IN
34  print "FORWARD"
35  continue
37  # MX reply example
38  if name == "aspl.es" and dns_record == "MX":
39  print "REPLY mx:mail3.aspl.es 10 3600, mx:mail4.aspl.es 20 3600"
40  continue
42  # NS reply example
43  if name == "aspl.es" and dns_record == "NS":
44  print "REPLY ns:nsserver01.aspl.es 3600, ns:nsserver02.aspl.es 3600"
45  continue
47  # SOA example
48  if name == "aspl.es" and dns_record == "SOA":
49  print "REPLY soa:ns1.account.aspl.es soporte@aspl.es 2012120400 10800 3600 604800 3600"
50  continue
52  # example about rewriting a request into another name
53  if name == "www.google.com":
54  print "REPLY name:www.aspl.es 3600"
55  continue
57  # example about giving an ip as a reply
58  print "REPLY ipv4: 3600"
60  else:
61  # by default, if command is not handled, reply forward to make
62  # the server to forward the reply to the forward dns server
63  # configured and reply to the user with the result
64  print "FORWARD"
65  # end if
67 # end while