Erlang/OTP DNS server
erlang-dns is an authorative non recursive DNS server I am writing for my site http://www.domain-name-registration.co.za.
The idea is to have a simple DNS server that can be configured with arbitrary Zones via simple Erlang modules. It's up to you whether the Zone is defined in some file or a DB for example. As long as the module returns a zone as a list of resource records erlang-dns can serve it up.
In addition, erlang-dns supports custom extensions to enhance or entirely change the server's behaviour.
- 2014-01-11
v0.1-alpha
- First version that conforms to RFC1034 examples
On Ubuntu please install Erlang base and source with sudo apt install erlang-base erlang-src
.
The sources are required since this server depends on kernel/src/inet_dns.hrl
.
Run the end to end tests with make e2e
to confirm that everything works.
-module(my_zone_provider).
-export([get_zone/1]).
-include_lib("kernel/src/inet_dns.hrl").
get_zone(_Args) ->
%% Fetch the Zone from a file, the DB, ...
%% Here we just hardcode it
{ok, [
#dns_rr{domain="bot.co.za", type=soa, data={
"ns1.bot.co.za",
"hc.vst.io",
870611, %serial
1800, %refresh every 30 min
300, %retry every 5 min
604800, %expire after a week
86400 %minimum of a day
}
},
#dns_rr{domain="bot.co.za", type=ns, data="ns1.bot.co.za"},
#dns_rr{domain="bot.co.za", type=ns, data="ns2.bot.co.za"},
#dns_rr{domain="www.bot.co.za", type=cname, data="bot.co.za"},
#dns_rr{domain="bot.co.za", type=a, data={127,0,0,1}}
]}.
Then, bring up the server with erl -pa ./ebin -s edns
and register your zone
provider.
edns:register_zone_provider("bot.co.za", {my_zone_provider, get_zone, []}).
If you change and recompile my_zone_provider
you can flush the zone with
edns:flush("bot.co.za").
without restarting the server.
Also, please have a look at the-end-to end test scenario for an example of
how to setup the included src/simple_backend.erl
zone provider module.
The simple_backend
consists essentially of only the following two lines:
get_zone(Zone) ->
{ok, Zone}.
Instead of writing the my_zone_provider
above, you could have achieved the same
result with:
Eshell V5.10.2 (abort with ^G)
1> edns:register_zone_provider("bot.co.za", {simple_backend, get_zone, [
#dns_rr{domain="bot.co.za", type=soa, data={
"ns1.bot.co.za",
"hc.vst.io",
870611, %serial
1800, %refresh every 30 min
300, %retry every 5 min
604800, %expire after a week
86400 %minimum of a day
}
},
#dns_rr{domain="bot.co.za", type=ns, data="ns1.bot.co.za"},
#dns_rr{domain="bot.co.za", type=ns, data="ns2.bot.co.za"},
#dns_rr{domain="www.bot.co.za", type=cname, data="bot.co.za"},
#dns_rr{domain="bot.co.za", type=a, data={127,0,0,1}}
]}).
The example above requires the record defintion for #dns_rr
to be loaded.
This can be done with rr("/usr/lib/erlang/lib/kernel-6.5.1/src/inet_dns.hrl").
.
Note that the server listens by default on port 1053. You may wish to forward 53 to it using socat.
sudo socat UDP4-RECVFROM:53,fork UDP4-SENDTO:localhost:1053
Extensions live in the priv/extensions
folder. Have a look at the simple_stats
extension that counts the number of DNS requests.
Extensions can consist of custom resolvers and OTP servers/supervisors.
By default, queries are processed by ed_query_resolver
, however you can
implement a custom resolver to augment or swap out ed_query_resolver
entirely.
edns.app.src
specifies what resolver(s) to use:
{env, [
{port, 1051},
{resolvers, [ed_query_resolver, simple_stats_resolver]}
]}
ed_udp_handler_server
foldl
s over all resolvers to arrive at a DNS query
response.
The simple_stats_resolver
is a passthrough resolver that does not modify the repsonse.
All it does is to notify simple_stats_server
that another query has arrived.
Another example usecase of a custom resolver is one that looks up the synopsis of a Wikipedia article and returns it in a TXT record.
Modules in the extensions/...
directory that implement either the gen_server
or
supervisor
behaviour are automatically started and supervised by ed_extension_sup
.
This is useful if your custom resolvers need to maintain state.
To run the end to end test execute make e2e
.
Please see the wiki https://github.com/hcvst/erlang-dns/wiki/Architecture
- sanity-check the zone provided by a callback module
- use inet_dns ADT helper functions in the resolver rather than pattern matching.
Section 4.3.2 Algorithm in http://tools.ietf.org/html/rfc1034