Packages
erldns
11.0.2
11.1.0
11.0.3
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/zones/load/erldns_zone_loader_worker.erl
-module(erldns_zone_loader_worker).
-moduledoc false.
-include_lib("dns_erlang/include/dns.hrl").
-export([load_file/4, start_link/4, init/4]).
-ifdef(TEST).
-export([do_load_file/3]).
-endif.
-spec load_file(erldns_zone_loader_getter:tag(), file:filename(), file:name(), boolean()) ->
reference().
load_file(Tag, File, KeysPath, Strict) ->
{ok, Pid} = erldns_zone_loader_worker_sup:start_child([Tag, File, KeysPath, Strict]),
AliasMon = erlang:monitor(process, Pid, [{alias, reply_demonitor}]),
Pid ! {?MODULE, AliasMon},
AliasMon.
-spec start_link(erldns_zone_loader_getter:tag(), file:filename(), file:name(), boolean()) ->
dynamic().
start_link(Tag, File, KeysPath, Strict) ->
proc_lib:start_link(?MODULE, init, [Tag, File, KeysPath, Strict]).
-spec init(erldns_zone_loader_getter:tag(), file:filename(), file:name(), boolean()) -> no_return().
init(Tag, File, KeysPath, Strict) ->
proc_lib:init_ack({ok, self()}),
receive
{?MODULE, AliasMon} ->
run(Tag, File, KeysPath, Strict, AliasMon)
after 1000 ->
exit(timeout)
end.
run(Tag, File, KeysPath, Strict, AliasMon) ->
try
Zones = do_load_file(File, KeysPath, Strict),
lists:map(fun erldns_zone_cache:put_zone/1, Zones),
AliasMon ! {Tag, AliasMon, length(Zones)}
catch
throw:Reason ->
AliasMon ! {Tag, AliasMon, error, Reason};
error:unexpected_end = Error ->
AliasMon ! {Tag, AliasMon, error, {json_error, #{error => Error, file => File}}};
error:{invalid_byte, _} = Error ->
AliasMon ! {Tag, AliasMon, error, {json_error, #{error => Error, file => File}}};
error:{unexpected_sequence, _} = Error ->
AliasMon ! {Tag, AliasMon, error, {json_error, #{error => Error, file => File}}};
error:Reason:StackTrace ->
AliasMon ! {Tag, AliasMon, error, {Reason, StackTrace}}
end.
-spec do_load_file(file:filename(), file:name(), boolean()) -> [erldns:zone()].
do_load_file(File, KeysPath, Strict) ->
case filename:extension(File) of
".json" ->
load_json_file(File, Strict);
".zone" ->
load_zone_file(File, KeysPath, Strict)
end.
-spec load_json_file(file:filename(), boolean()) -> [erldns:zone()].
load_json_file(File, Strict) ->
maybe
{ok, Content} ?= file:read_file(File, [raw]),
{ok, List} ?= safe_json_decode_list(Content),
Zones = ensure_zones(List, Strict),
[erldns_zone_codec:decode(Zone) || Zone <- Zones]
else
[] ->
[];
{json_error, Reason} ->
Strict andalso erlang:throw({invalid_zone_file, Reason}),
[];
{error, Reason} ->
Strict andalso erlang:throw({file_read_error, File, Reason}),
[]
end.
-spec ensure_zones([json:decode_value()], boolean()) -> [json:decode_value()] | no_return().
ensure_zones([#{~"name" := _, ~"records" := _} = H | T], Strict) ->
[H | ensure_zones(T, Strict)];
ensure_zones([_ | T], Strict) ->
Strict andalso erlang:throw(invalid_zone_file),
ensure_zones(T, Strict);
ensure_zones([], _) ->
[].
-spec load_zone_file(file:filename(), file:name(), boolean()) -> [erldns:zone()].
load_zone_file(File, KeysPath, Strict) ->
maybe
{ok, Records} ?= dns_zone:parse_file(File),
Soa = #dns_rr{name = ZoneName} ?= lists:keyfind(?DNS_TYPE_SOA, #dns_rr.type, Records),
Sha = crypto:hash(sha256, term_to_binary(Soa)),
Keys = load_private_keys(ZoneName, KeysPath),
[erldns_zone_codec:build_zone(ZoneName, Sha, Records, Keys)]
else
false ->
Strict andalso erlang:throw({invalid_zone_file, File}),
[];
{error, Reason} ->
Strict andalso erlang:throw({file_read_error, File, Reason}),
[]
end.
%% Finds and loads private keys for every DNSKEY record in the list.
-spec load_private_keys(dns:dname(), file:name()) -> [erldns:keyset()].
load_private_keys(ZoneName, KeysDir) ->
Trimmed = string:trim(ZoneName, trailing, "."),
FileName = <<Trimmed/binary, ".private">>,
FullPath = filename:join(KeysDir, FileName),
maybe
{ok, Content} ?= file:read_file(FullPath, [raw]),
{ok, Json} ?= safe_json_decode_list(Content),
erldns_zone_decoder:parse_keysets(Json)
else
{json_error, Reason} ->
erlang:throw({invalid_key_file, FullPath, Reason});
{error, _} ->
%% File not found - This is normal for public keys we don't own
%% (or if we only have the ZSK but the KSK is held offline)
[]
end.
-spec safe_json_decode_list(binary()) -> {ok, [json:decode_value()]} | {json_error, atom()}.
safe_json_decode_list(Binary) ->
case json:decode(Binary) of
List when is_list(List) ->
{ok, List};
_ ->
{json_error, invalid_zone_file}
end.