Packages
erldns
1.0.0
11.0.2
11.0.1
11.0.0
10.6.0
10.5.6
10.5.5
10.5.4
10.5.3
10.5.2
10.5.1
10.5.0
10.4.4
10.4.3
10.4.2
10.4.1
10.4.0
10.3.0
10.2.1
10.2.0
10.1.0
10.0.0
10.0.0-rc4
10.0.0-rc3
10.0.0-rc2
10.0.0-rc1
9.1.0
9.0.0
9.0.0-rc3
9.0.0-rc2
9.0.0-rc1
8.1.0
8.0.0
8.0.0-rc6
8.0.0-rc5
8.0.0-rc4
8.0.0-rc3
8.0.0-rc2
8.0.0-rc1
7.0.0
7.0.0-rc9
7.0.0-rc8
7.0.0-rc7
7.0.0-rc6
7.0.0-rc5
7.0.0-rc4
7.0.0-rc3
7.0.0-rc2
7.0.0-rc12
7.0.0-rc11
7.0.0-rc10
7.0.0-rc1
6.0.2
6.0.1
6.0.0
5.0.0
4.3.1
4.3.0
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.2
4.1.1
4.1.0
4.0.0
3.0.0
1.0.0
Erlang Authoritative DNS Server
Current section
Files
Jump to
Current section
Files
src/erldns_zone_encoder.erl
%% Copyright (c) 2012-2018, DNSimple Corporation
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%% @doc A process that maintains a collection of encoders in its state
%% for encoding zones from their Erlang representation to JSON.
-module(erldns_zone_encoder).
-behavior(gen_server).
-include_lib("dns_erlang/include/dns.hrl").
-include("erldns.hrl").
-export([start_link/0]).
-export([zone_to_json/1, register_encoders/1, register_encoder/1]).
% Gen server hooks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-define(SERVER, ?MODULE).
-record(state, {encoders}).
% Public API
%% @doc Start the encoder process.
-spec start_link() -> any().
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% @doc Encode a Zone record into JSON.
-spec zone_to_json(#zone{}) -> binary().
zone_to_json(Zone) ->
gen_server:call(?SERVER, {encode_zone, Zone}).
%% @doc Register a list of encoder modules.
-spec register_encoders([module()]) -> ok.
register_encoders(Modules) ->
lager:info("Registering custom encoders (modules: ~p)", [Modules]),
gen_server:call(?SERVER, {register_encoders, Modules}).
%% @doc Register a single encoder module.
-spec register_encoder(module()) -> ok.
register_encoder(Module) ->
lager:info("Registering customer encoder (module: ~p)", [Module]),
gen_server:call(?SERVER, {register_encoder, Module}).
% Gen server hooks
init([]) ->
{ok, #state{encoders = []}}.
handle_call({encode_zone, Zone}, _From, State) ->
{reply, zone_to_json(Zone, State#state.encoders), State};
handle_call({register_encoders, Modules}, _From, State) ->
{reply, ok, State#state{encoders = State#state.encoders ++ Modules}};
handle_call({register_encoder, Module}, _From, State) ->
{reply, ok, State#state{encoders = State#state.encoders ++ [Module]}}.
handle_cast(_, State) ->
{noreply, State}.
handle_info(_, State) ->
{noreply, State}.
terminate(_, _State) ->
ok.
code_change(_, State, _) ->
{ok, State}.
% Internal API
zone_to_json(Zone, Encoders) ->
Records = records_to_json(Zone, Encoders),
FilteredRecords = lists:filter(record_filter(), Records),
jsx:encode([{<<"erldns">>,
[
{<<"zone">>, [
{<<"name">>, Zone#zone.name},
{<<"version">>, Zone#zone.version},
{<<"records">>, FilteredRecords}
% Note: Private key material is purposely omitted
]}
]
}]).
record_filter() ->
fun(R) ->
case R of
[] -> false;
{} -> false;
_ -> true
end
end.
records_to_json(Zone, Encoders) ->
lists:map(encode(Encoders), Zone#zone.records).
encode(Encoders) ->
fun(Record) ->
encode_record(Record, Encoders)
end.
encode_record(Record, Encoders) ->
lager:debug("Encoding record (record: ~p)", [Record]),
case encode_record(Record) of
[] ->
lager:debug("Trying custom encoders (encoders: ~p)", [Encoders]),
try_custom_encoders(Record, Encoders);
EncodedRecord -> EncodedRecord
end.
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_SOA, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_NS, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_A, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_AAAA, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_CNAME, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_MX, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_HINFO, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_TXT, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_SPF, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_SSHFP, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_SRV, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_NAPTR, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_CAA, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_DS, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_CDS, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_DNSKEY, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_CDNSKEY, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record({dns_rr, Name, _, Type = ?DNS_TYPE_RRSIG, Ttl, Data}) ->
encode_record(Name, Type, Ttl, Data);
encode_record(Record) ->
lager:warning("Unable to encode record (record: ~p)", [Record]),
[].
encode_record(Name, Type, Ttl, Data) ->
[
{<<"name">>, erlang:iolist_to_binary(io_lib:format("~s.", [Name]))},
{<<"type">>, dns:type_name(Type)},
{<<"ttl">>, Ttl},
{<<"content">>, encode_data(Data)}
].
try_custom_encoders(_Record, []) ->
{};
try_custom_encoders(Record, [Encoder|Rest]) ->
lager:debug("Trying custom encoder (encoder: ~p)", [Encoder]),
case Encoder:encode_record(Record) of
[] -> try_custom_encoders(Record, Rest);
EncodedData -> EncodedData
end.
encode_data({dns_rrdata_soa, Mname, Rname, Serial, Refresh, Retry, Expire, Minimum}) ->
erlang:iolist_to_binary(io_lib:format("~s. ~s. (~w ~w ~w ~w ~w)", [Mname, Rname, Serial, Refresh, Retry, Expire, Minimum]));
encode_data({dns_rrdata_ns, Dname}) ->
erlang:iolist_to_binary(io_lib:format("~s.", [Dname]));
encode_data({dns_rrdata_a, Address}) ->
list_to_binary(inet_parse:ntoa(Address));
encode_data({dns_rrdata_aaaa, Address}) ->
list_to_binary(inet_parse:ntoa(Address));
encode_data({dns_rrdata_caa, Flags, Tag, Value}) ->
erlang:iolist_to_binary(io_lib:format("~w ~s \"~s\"", [Flags, Tag, Value]));
encode_data({dns_rrdata_cname, Dname}) ->
erlang:iolist_to_binary(io_lib:format("~s.", [Dname]));
encode_data({dns_rrdata_mx, Preference, Dname}) ->
erlang:iolist_to_binary(io_lib:format("~w ~s.", [Preference, Dname]));
encode_data({dns_rrdata_hinfo, Cpu, Os}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w", [Cpu, Os]));
% RP
encode_data({dns_rrdata_txt, Text}) ->
erlang:iolist_to_binary(io_lib:format("~s", [Text]));
encode_data({dns_rrdata_spf, [Data]}) ->
erlang:iolist_to_binary(io_lib:format("~s", [Data]));
encode_data({dns_rrdata_sshfp, Alg, Fptype, Fp}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~s", [Alg, Fptype, Fp]));
encode_data({dns_rrdata_srv, Priority, Weight, Port, Dname}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~s.", [Priority, Weight, Port, Dname]));
encode_data({dns_rrdata_naptr, Order, Preference, Flags, Services, Regexp, Replacements}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~s ~s ~s ~s", [Order, Preference, Flags, Services, Regexp, Replacements]));
encode_data({dns_rrdata_ds, Keytag, Alg, DigestType, Digest}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~s", [Keytag, Alg, DigestType, Digest]));
encode_data({dns_rrdata_cds, Keytag, Alg, DigestType, Digest}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~s", [Keytag, Alg, DigestType, Digest]));
encode_data({dns_rrdata_dnskey, Flags, Protocol, Alg, Key, KeyTag}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~w ~w", [Flags, Protocol, Alg, Key, KeyTag]));
encode_data({dns_rrdata_cdnskey, Flags, Protocol, Alg, Key, KeyTag}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~w ~w", [Flags, Protocol, Alg, Key, KeyTag]));
encode_data({dns_rrdata_rrsig, TypeCovered, Alg, Labels, OriginalTtl, Expiration, Inception, KeyTag, SignersName, Signature}) ->
erlang:iolist_to_binary(io_lib:format("~w ~w ~w ~w ~w ~w ~w ~w ~s", [TypeCovered, Alg, Labels, OriginalTtl, Expiration, Inception, KeyTag, SignersName, Signature]));
encode_data(Data) ->
lager:debug("Unable to encode rrdata (data: ~p)", [Data]),
{}.