From f41fb570d6811a785a4bf9dd82820c93d9c2b93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mickae=CC=88l=20Re=CC=81mond?= Date: Wed, 11 Feb 2015 13:10:50 +0100 Subject: [PATCH] Migrated to https://github.com/processone/dns-tools --- dns/src/dns.erl | 381 ------------------------------------------------ 1 file changed, 381 deletions(-) delete mode 100644 dns/src/dns.erl diff --git a/dns/src/dns.erl b/dns/src/dns.erl deleted file mode 100644 index 5712b92..0000000 --- a/dns/src/dns.erl +++ /dev/null @@ -1,381 +0,0 @@ -%%%------------------------------------------------------------------- -%% @copyright Process One 2008-2009 -%% @author Geoff Cant -%% @author Geoff Cant -%% @version {@vsn}, {@date} {@time} -%% @doc DNS Lookup and utility functions. -%% -%% Provides a programmer-friendly API for a number of undocumented OTP -%% dns lookup, resolution, caching and configuration functions. -%% -%% Also provides utility functions for performing lookups while -%% bypassing local resolver caching, finding closest parent zones, -%% parsing `resolv.conf' files, simplifying #dns_rec{} answers and more. -%% @end -%%%------------------------------------------------------------------- --module(dns). - --include_lib("eunit/include/eunit.hrl"). - -%% API --export([lookup/2 - ,lookup/3 - ,lookup/4 - ,lookup/5 - ,lookup/7 - ,lookup_cache/3 - ,cache_lookup/1 - ,simplify/1]). - --export([find_soa/1 - ,nameservers/1 - ,nameservers/2 - ,nameserver_address/1 - ,direct_lookup/3 - ,info/2 - ,to_proplist/1 - ,parse_resolv/1 - ,domain_exists/1 - ,expand_options/1 - ,resolvers/0 - ]). - --include_lib("kernel/src/inet_dns.hrl"). --include_lib("kernel/include/inet.hrl"). - -%% @type query_class() = in. -%% An atom with the name of a DNS query class. `in' is the only class -%% used (and probably tested) in the OTP lookup code. - -%% @type query_type() = a | cname | soa | mx | ns | srv | any. -%% An atom with the name of a DNS query type. This list is not -%% exhaustive. - -%% @type simple_rr() = {Name::string(), query_type(), RRdata::term(), [simple_rr_info()]}. -%% A simplified version of #dns_rr{} resource records. - -%% @type simple_rr_info() = {ttl, TimeToLive::integer()} | {class, query_class()}. - -%% @type ip_address() = {integer(),integer(),integer(),integer()}. - -%% @type address() = ip_address() | {ip_address(), port_no()} | -%% string() | #dns_rr{}. - -%% @type port_no() = integer(). - -%% @type resolver() = {ip_address(), port_no()}. - -%% @type query_options() = [query_option()]. -%% A proplist of configuration options for lookup behaviour. - -%% @type query_option() = {read_cache, bool()} | -%% {write_cache, bool()} | -%% {timeout, TimeOut::integer()} | -%% {servers, [address()]} | -%% {class, query_class()} | -%% defaults | read_cached. -%%
    -%%
  • `read_cache' - if `true' (default: `false'), return results from the `inet_db' cache -%% before making a query over the network.
  • -%%
  • `write_cache' - if `true' (default: `false'), write successful query result RRs to -%% the `inet_db' cache
  • -%%
  • `timeout' - number of milliseconds (default: 5000) to wait for a response from a -%% nameserver.
  • -%%
  • `servers' - a list of nameservers to query for the RRs. Defaults -%% to `inet_db:res_option(nameserver)' which is usually the nameservers -%% specified in '/etc/resolv.conf'
  • -%%
  • `class' - record class to query for (default: `in').
  • -%%
  • `defaults' - shorthand for -%% -%% [{servers, dns:resolvers()}, -%% {read_cache, false}, {write_cache, false}, -%% {timeout, timer:seconds(5)}, {class, in}] -%% -%%
  • -%%
  • `read_cached' - shorthand for -%% -%% [{servers, dns:resolvers()}, -%% {read_cache, true}, {write_cache, false}, -%% {timeout, timer:seconds(5)}, {class, in}] -%% -%%
  • -%%
- -%%==================================================================== -%% RR lookup API -%%==================================================================== - -%% @spec lookup(Name::string(), query_type()) -> #dns_res{} -%% @doc Query for a DNS record of Type for Name. Uses reasonable default -%% options for class (`in') timeouts (5s), caching (no caching) and -%% nameservers (`inet_db' defaults). -%% @end -%% @equiv lookup(Name, Type, [defaults]) -lookup(Name, Type) - when is_list(Name), is_atom(Type) -> - lookup(Name, Type, [defaults]). - -%% @spec lookup(Name::string(), query_type(), query_options()) -> #dns_res{} -%% @doc Query for a DNS record of Type for Name. Takes a variety of -%% query options. -%% @end -lookup(Name, Type, Options) - when is_list(Name), is_atom(Type), - is_list(Options) -> - lookup2(Name, Type, expand_options(Options)). - -%% @private -lookup2(Name, Type, Options) -> - Timeout = proplists:get_value(timeout, Options, timer:seconds(5)), - Servers = [nameserver_address(R) - || R <- proplists:get_value(servers, Options, resolvers())], - Class = proplists:get_value(class, Options, in), - ReadCache = proplists:get_value(read_cache, Options, false), - WriteCache = proplists:get_value(write_cache, Options, false), - lookup(Name, Class, Type, Servers, Timeout, ReadCache, WriteCache). - -%% @spec lookup(Name::string(), query_type(), -%% NameServers, Timeout::integer()) -> #dns_res{} -%% where NameServers = [resolver()] -%% @doc Query the given Nameservers for a DNS record of Type (class `in') for -%% Name. Takes a Timeout in milliseconds. Doesn't read or write the -%% `inet_db' RR cache. -%% @equiv lookup(Name, in, Type, Servers, Timeout, false, false) -lookup(Name, Type, Servers, Timeout) - when is_list(Name), is_atom(Type), - is_list(Servers), is_integer(Timeout) -> - lookup(Name, in, Type, Servers, Timeout). - -%% @spec lookup(Name::string(), query_class(), query_type(), -%% [resolver()], Timeout::integer()) -> #dns_res{} -%% @doc Query the given Nameservers for a DNS record of Class, Type for -%% Name. Takes a Timeout in milliseconds. Doesn't read or write the -%% `inet_db' RR cache. -%% @equiv lookup(Name, Class, Type, Servers, Timeout, false, false) -lookup(Name, Class, Type, Servers, Timeout) - when is_list(Name), is_atom(Class), is_atom(Type), - is_list(Servers), is_integer(Timeout) -> - lookup(Name, Class, Type, Servers, Timeout, false, false). - -%% @spec lookup(Name::string(), class(), query_type(), -%% Servers::[address()], Timeout::integer(), -%% ReadCache::bool(), WriteCache::bool()) -> #dns_res{} -%% @doc Query the given Nameservers for a DNS record of Class, Type for -%% Name. Takes a Timeout in milliseconds. Returns results from the -%% `inet_db' RR cache if available and ReadCache is `true'. Writes -%% successful query answers to the cache if WriteCache is `true'. -%% @end -%% Query inet_db cache. -lookup(Name, Class = in, Type, Servers, Timeout, - _ReadCache = true, WriteCache) -> - case lookup_cache(Class, Name, Type) of - {ok, Answer} -> {ok, Answer}; - {error, nxdomain} -> - lookup(Name, Class, Type, Servers, Timeout, false, WriteCache) - end; -%% Perform DNS lookup and write results to inet_db cache -lookup(Name, Class, Type, Servers, Timeout, - _ReadCache, _WriteCache = true) -> - case lookup(Name, Class, Type, Servers, Timeout, false, false) of - {ok, Rec} -> cache_lookup(Rec), Rec; - Else -> Else - end; -%% Lookup via DNS -lookup(Name, Class, Type, Servers, Timeout, - _ReadCache = false, _WriteCache = false) -> - inet_res:nnslookup(Name, Class, Type, Servers, Timeout). - -%% @private -%% Process options list -expand_options(Opts) -> - proplists:expand(option_expansions(), Opts). -%% @private -option_expansions() -> - [{defaults, [{servers, resolvers()}, - {timeout, timer:seconds(5)}, - {read_cache, false}, - {write_cache, false}]} - ,{read_cached, [{servers, resolvers()}, - {timeout, timer:seconds(5)}, - {read_cache, true}, - {write_cache, false}]} - ]. - -%% @spec simplify(Result) -> {error, Reason::term()} | [simple_rr()] -%% where Result = {ok, #dns_rec{}} | {error, Reason::term()} -%% @doc Simplify the records returned from lookup to fixed-format -%% tuples instead of records. -%% @end -simplify({ok, #dns_rec{anlist=A}}) -> - {ok, [{Name, Type, Data, [{ttl, TTL},{class,Class}]} - || #dns_rr{domain=Name,class=Class, - type=Type,data=Data, - ttl=TTL} <- A]}; -simplify(E) -> E. - -%%==================================================================== -%% Cache API -%%==================================================================== - -%% @spec lookup_cache(query_class(), Name::string(), query_type()) -> -%% {ok, #dns_rec{}} | {error, Reason::term()} -%% @doc Query `inet_db' RR cache. Converts the `inet_db' `#hostent{}' respone -%% into a fake `#dns_rec{}'. -lookup_cache(Class, Name, Type) -> - case inet_db:getbyname(Name, Type) of - {ok, #hostent{h_addr_list=Answers}} -> - %% Convert hostents to fake dns records - RRs = [#dns_rr{domain=Name, class=Class, - type=Type, ttl=cached, - data=D} - || D <- Answers], - {ok, #dns_rec{header=cached, anlist=RRs}}; - Else -> Else - end. - -%% @spec cache_lookup(#dns_rec{}) -> ok -%% @doc Store dns answer resource records in inet_db cache -cache_lookup(#dns_rec{anlist=RRs}) -> - lists:foreach(fun inet_db:add_rr/1, RRs), - ok. - -%%==================================================================== -%% Utility API -%%==================================================================== - -%% @spec resolvers() -> [resolver()] -%% @doc Returns a list of name servers that can be used as recursive -%% resolvers. Resolvers taken from the `inet_db' `namserver' setting. -resolvers() -> - inet_db:res_option(nameserver). - -%% @spec find_soa(Name::string()) -> {ok, {SoaName::string(), #dns_rr{}}} | -%% {error, Reason::term()} -%% @doc Recursively climb the ancestry of a domain name to find the -%% most specific SOA. (Closest domain delegation/authority) -%% Given `foo.bar.baz.com' would try `foo.bar.baz.com' then `bar.baz.com' -%% then `baz.com' and so on. -find_soa(Name) -> - Components = string:tokens(Name, "."), - find_soa2(Components). - -%% @private -find_soa2([]) -> - {error, nxdomain}; -find_soa2(Components) -> - Name = string:join(Components,"."), - case lookup(Name, soa) of - {ok, #dns_rec{anlist=[RR = #dns_rr{domain=Name,type=soa}]}} -> - {ok, {Name, RR}}; - {error, nxdomain} -> - find_soa2(tl(Components)); - Else -> Else - end. - -%% @spec domain_exists(DomainName::string()) -> bool() -%% @doc Returns `true' if a name exists and has an SOA record. -domain_exists(Name) -> - case lookup(Name, soa) of - {ok, #dns_rec{anlist=[#dns_rr{domain=Name,type=soa}]}} -> - true; - {error, nxdomain} -> - false; - Else -> erlang:error(Else) - end. - -%% @spec parse_resolv(FileName::string()) -> [ip_address()] -%% @doc Returns a list of nameservers from a POSIX style `resolv.conf' file. -parse_resolv(File) -> - {ok, Rs} = inet_parse:resolv(File), - [NS || {nameserver,NS} <- Rs]. - -%% @spec nameservers(Domain::string()) -> [resolver()] -%% @equiv nameservers(Domain, [read_cached]) -nameservers(Domain) -> nameservers(Domain, [read_cached]). - -%% @spec nameservers(Domain::string(), query_options()) -> [resolver()] -%% @doc Retrieve the names and addresses of the authoritative -%% nameservers for Domain. Will perform one NS lookup if the -%% resolving nameserver returns the address records for the queried -%% records, otherwise will perform 1 + N lookups (N = number of NS -%% records for Domain). Takes a list of query option that will be used -%% for NS lookups. -nameservers(Domain, Options) -> - case lookup(Domain, ns, Options) of - E = {error, _} -> E; - {ok, #dns_rec{anlist=RRs, arlist=AR}} -> - Names = [Name - || #dns_rr{data=Name,type=ns,domain=D} <- RRs, - D =:= Domain], - Additional = [Addr || #dns_rr{data=Addr,type=a,domain=Name} <- AR, - lists:member(Name, Names)], - if length(Additional) > 0 -> - [nameserver_address(Addr) || Addr <- Additional]; - true -> - [nameserver_address(RR) || RR <- RRs] - end - end. - -%% @spec nameserver_address(address()) -> resolver() | error -%% @doc -%% Convert a variety of nameserver address specifications into a -%% host-port tuple for use with `gen_udp' and `inet_res'. -%% @end -%% @todo Should take/respect query options instead of calling inet:getaddr/2. -nameserver_address(IP = {_, _, _, _}) -> {IP,53}; -nameserver_address(Addr = {{_, _, _, _}, _}) -> Addr; -nameserver_address(Name) when is_list(Name) -> - case inet:getaddr(Name, inet) of - {ok, IP} -> {IP, 53}; - _ -> error - end; -nameserver_address(#dns_rr{data=Name,type=ns}) -> - nameserver_address(Name). - -%% @spec direct_lookup(Name::string(), query_type(), Domain::string()) -%% -> #dns_res{} -%% @doc Lookup the given Name/Type against the authoritative NSs for -%% Domain. This will bypass nxdomain caching and should result in -%% quicker recognition of changed/added records. -%% This is mainly intended for checking if people have setup a set of -%% DNS records correctly (say for a white-label hosting service). -direct_lookup(Name, Type, Domain) -> - Servers = [{A,53} || {_NS, A} <- nameservers(Domain)], - lookup(Name, Type, [{servers, Servers}]). - -%%==================================================================== -%% inet record manipulation and access API -%%==================================================================== - -%% @private -info(Field, Rec) -> - Fields = fields(Rec), - FieldIdx = lists:zip(Fields, lists:seq(2,length(Fields)+1)), - element(proplists:get_value(Field,FieldIdx), Rec). - -%% @private -fields(#dns_rec{}) -> fields(dns_rec); -fields(dns_rec) -> record_info(fields, dns_rec); -fields(#dns_rr{}) -> fields(dns_rr); -fields(dns_rr) -> record_info(fields, dns_rr). - -%% @private -to_proplist(R) -> - Keys = fields(R), - Values = tl(tuple_to_list(R)), - lists:zip(Keys,Values). - -%%==================================================================== -%% Unit Tests -%%==================================================================== -%% @todo Add more unit tests. - -%% @private -nameserver_address_test_() -> - T = [{{1,2,3,4}, {{1,2,3,4},53}}, - {{{1,2,3,4},53}, {{1,2,3,4},53}}, - {#dns_rr{data={1,2,3,4},type=ns}, {{1,2,3,4},53}}, - {"localhost", {{127,0,0,1},53}}, - {#dns_rr{data="localhost",type=ns}, {{127,0,0,1},53}}], - [ ?_assertMatch(C when C =:= B, nameserver_address(A)) - || {A,B} <- T].