summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2016-04-06 10:07:48 +0200
committerLinus Nordberg <linus@nordu.net>2016-04-06 10:07:48 +0200
commit28bc4ae4d9cf9f34f9dcd99da1c89bdb56a2bd38 (patch)
tree1466969b12989b1645f39aa7f742ff4f5880807d /src
parentba016c8b02006943198aa5bfb68cf13fccdc33ca (diff)
Add unit test for validation, from dnssecport:handle_call().
- The port now returns the RRset (DS, chain, trust root and all RRSIG's). This in preparatino for when this data will be normalised. - dnssecport decodes and encodes DNS data. - v1 stores the DS RR in the leaf and the rest, including the DS RRSIG, in the chain.
Diffstat (limited to 'src')
-rw-r--r--src/dns.erl69
-rw-r--r--src/dnssecport.erl113
-rw-r--r--src/v1.erl17
3 files changed, 153 insertions, 46 deletions
diff --git a/src/dns.erl b/src/dns.erl
new file mode 100644
index 0000000..24fb8fb
--- /dev/null
+++ b/src/dns.erl
@@ -0,0 +1,69 @@
+%%% Copyright (c) 2016, NORDUnet A/S.
+%%% See LICENSE for licensing information.
+
+-module(dns).
+-export([split_rrset/1, encode_rr/1, encode_rrset/1]).
+
+decode_name_label(Name) ->
+ <<Len:8/integer, Label:Len/binary, Rest/binary>> = Name,
+ {Label, Rest}.
+
+encode_name_label(Label) ->
+ Len = byte_size(Label),
+ <<Len:8/integer, Label/binary>>.
+
+decode_name(RR) ->
+ decode_name(RR, []).
+decode_name(<<0, Rest/binary>>, Acc) ->
+ {lists:reverse(Acc), Rest};
+decode_name(Name, Acc) ->
+ {Label, Rest} = decode_name_label(Name),
+ decode_name(Rest, [Label | Acc]).
+
+-spec encode_name(list()) -> binary().
+encode_name(Name) ->
+ encode_name(Name, []).
+encode_name([], Acc) ->
+ Bin = list_to_binary(lists:reverse(Acc)),
+ <<Bin/binary, 0>>;
+encode_name([H|T], Acc) ->
+ encode_name(T, [encode_name_label(H) | Acc]).
+
+-spec decode_rr(binary()) -> {list(), binary()}.
+decode_rr(RR) ->
+ {Name, RestRR} = decode_name(RR),
+ <<Type:2/binary,
+ Class:2/binary,
+ TTL:4/integer-unit:8,
+ RDLength:2/integer-unit:8,
+ RDATA:RDLength/binary,
+ Rest/binary>> = RestRR,
+ {[Name, Type, Class, TTL, RDATA], Rest}.
+
+-spec split_rrset(binary()) -> list().
+split_rrset(RRSet) ->
+ split_rrset(RRSet, []).
+split_rrset(<<>>, Acc) ->
+ lists:reverse(Acc);
+split_rrset(RRSet, Acc) ->
+ {RR, Rest} = decode_rr(RRSet),
+ split_rrset(Rest, [RR | Acc]).
+
+-spec encode_rr(list()) -> binary().
+encode_rr([Name, Type, Class, TTL, RDATA]) ->
+ EncodedName = encode_name(Name),
+ RDLength = byte_size(RDATA),
+ <<EncodedName/binary,
+ Type:2/binary,
+ Class:2/binary,
+ TTL:4/integer-unit:8,
+ RDLength:2/integer-unit:8,
+ RDATA/binary>>.
+
+-spec encode_rrset(list()) -> binary().
+encode_rrset(RRSet) ->
+ encode_rrset(RRSet, []).
+encode_rrset([], Acc) ->
+ list_to_binary(lists:reverse(Acc));
+encode_rrset([H|T], Acc) ->
+ encode_rrset(T, [encode_rr(H) | Acc]).
diff --git a/src/dnssecport.erl b/src/dnssecport.erl
index 804e727..02f919a 100644
--- a/src/dnssecport.erl
+++ b/src/dnssecport.erl
@@ -4,7 +4,7 @@
-module(dnssecport).
-behaviour(gen_server).
-export([start_link/0, stop/0]).
--export([validate/2]).
+-export([validate/1]).
%% gen_server callbacks.
-export([init/1, handle_call/3, terminate/2, handle_cast/2, handle_info/2,
code_change/3]).
@@ -18,36 +18,40 @@ start_link() ->
stop() ->
gen_server:call(?MODULE, stop).
-validate(ValidateRR, SupportRRs) ->
- gen_server:call(?MODULE, {validate, [ValidateRR, SupportRRs]}).
+validate(Data) ->
+ gen_server:call(?MODULE, {validate, Data}).
-record(state, {port :: port()}).
init(Program) ->
lager:debug("starting dnssec service"),
- Port = create_port(Program, []), % TODO: Pass path to dir with trust root.
+ Port = create_port(Program, []), % TODO: Pass path to trust root file.
{ok, #state{port = Port}}.
+decode_response(Response) ->
+ <<Status:16/integer, RRSet/binary>> = Response,
+ {ok, Status, dns:split_rrset(RRSet)}.
+
handle_call(stop, _From, State) ->
lager:debug("dnssec stop request received"),
stop_port(State);
-handle_call({validate, [ValidateRR, SupportRRs]}, _From, State) ->
- lager:debug("dnssec validate incoming request: ~p", [ValidateRR]),
+handle_call({validate, Data}, _From, State) ->
case State#state.port of
undefined ->
{error, noport};
Port when is_port(Port) ->
- S = list_to_binary(SupportRRs),
- Port ! {self(), {command, <<ValidateRR/binary, S/binary>>}},
+ Port ! {self(), {command, dns:encode_rrset(Data)}},
receive
{Port, {data, Response}} ->
- lager:debug("received response from dnssec port: ~p",
- [Response]),
- case Response of
- "valid" ->
- {reply, ok, State};
- Err ->
- {reply, {error, Err}, State}
+ case decode_response(list_to_binary(Response)) of
+ {ok, 400, [DS | Chain]} ->
+ {reply,
+ {ok, [dns:encode_rr(DS) | dns:encode_rrset(Chain)]},
+ State};
+ {ok, Error, _} ->
+ {reply, {error, Error}, State};
+ {error, Reason} ->
+ {stop, {protocolerror, Reason}, State}
end;
{Port, {exit_status, ExitStatus}} ->
lager:error("dnssec port ~p exiting with status ~p",
@@ -97,34 +101,67 @@ stop_port(State) ->
%%%%%%%%%%%%%%%%%%%%
%% Unit tests.
-start_test_port(TestType) ->
- Port = create_port("priv/dnssecport", ["--testmode", atom_to_list(TestType)]),
- ?debugFmt("Port: ~p", [Port]),
- Port.
+-define(TA_FILE, "test/testdata/dnssec/trust_anchors").
+-define(REQ1_FILE, "test/testdata/dnssec/req.1").
+
+start_test_port() ->
+ create_port("priv/dnssecport", [?TA_FILE]).
+
stop_test_port(Port) ->
{stop, closed, _State} = stop_port(#state{port = Port}),
ok.
-err_test_() ->
- {setup,
- fun() -> start_test_port(err) end,
- fun(Port) -> stop_test_port(Port) end,
- fun(Port) ->
- R = handle_call({validate, [<<"invalid-DS">>, []]},
- self(), #state{port = Port}),
- [
- ?_assertMatch({reply, {error, "err"}, _State}, R)
- ]
- end}.
+read_submission_from_file(Filename) ->
+ {ok, Data} = file:read_file(Filename),
+ dns:split_rrset(Data).
+
+read_dec_enc_test_() ->
+ DecodedRRset = read_submission_from_file(?REQ1_FILE),
+ {ok, FileContent} = file:read_file(?REQ1_FILE),
+ [?_assertEqual(FileContent, dns:encode_rrset(DecodedRRset))].
-ok_test_() ->
+full_test_() ->
{setup,
- fun() -> start_test_port(ok) end,
- fun(Port) -> stop_test_port(Port) end,
- fun(Port) ->
- R = handle_call({validate, [<<"invalid-DS">>, []]},
+ fun() ->
+ start_test_port() end,
+ fun(Port) ->
+ stop_test_port(Port) end,
+ fun(Port) ->
+ R = handle_call({validate, read_submission_from_file(?REQ1_FILE)},
self(), #state{port = Port}),
[
- ?_assertMatch({reply, ok, _State}, R)
- ]
- end}.
+ ?_assertMatch({reply, {ok, _}, _State}, R)
+ ] end
+ }.
+
+%% start_test_port(TestType) ->
+%% Port = create_port("priv/dnssecport", ["--testmode", atom_to_list(TestType)]),
+%% ?debugFmt("Port: ~p", [Port]),
+%% Port.
+%% stop_test_port(Port) ->
+%% {stop, closed, _State} = stop_port(#state{port = Port}),
+%% ok.
+
+%% err_test_() ->
+%% {setup,
+%% fun() -> start_test_port(err) end,
+%% fun(Port) -> stop_test_port(Port) end,
+%% fun(Port) ->
+%% R = handle_call({validate, [<<"invalid-DS">>, []]},
+%% self(), #state{port = Port}),
+%% [
+%% ?_assertMatch({reply, {error, "err"}, _State}, R)
+%% ]
+%% end}.
+
+%% ok_test_() ->
+%% {setup,
+%% fun() -> start_test_port(ok) end,
+%% fun(Port) -> stop_test_port(Port) end,
+%% fun(Port) ->
+%% R = handle_call({validate, [<<"invalid-DS">>, []]},
+%% self(), #state{port = Port}),
+%% [
+%% ?_assertMatch({reply, ok, _State}, R)
+%% ]
+%% end}.
diff --git a/src/v1.erl b/src/v1.erl
index 86cd799..bab77aa 100644
--- a/src/v1.erl
+++ b/src/v1.erl
@@ -156,8 +156,8 @@ add_ds(Input) ->
case decode_chain(List) of
{invalid, ErrText} ->
err400(io:format("add-ds-rr: ~p", [ErrText]), List);
- [DSRR, DSRRSIG | SupportRRs] ->
- add_ds_helper(DSRR, DSRRSIG, SupportRRs);
+ Data when is_list(Data) ->
+ add_ds_helper(Data);
_ ->
err400("add-ds-rr: missing one or more entries", List)
end;
@@ -173,10 +173,11 @@ decode_chain(List) ->
L
end.
-add_ds_helper(DSRR, DSRRSIG, Support) ->
- case dnssecport:dnssec_validate([DSRR, DSRRSIG], Support) of
- ok ->
- success(catlfish:add_chain(DSRR, [DSRRSIG | Support], normal));
- _ ->
- err400("add-ds-rr: invalid DS record", [DSRR, DSRRSIG | Support])
+add_ds_helper(Data) ->
+ case dnssecport:dnssec_validate(Data) of
+ {ok, [DS | Chain]} ->
+ success(catlfish:add_chain(DS, Chain, normal));
+ {error, ErrorCode} ->
+ err400(io:format("add-ds-rr: invalid DS record: ~p", [ErrorCode]),
+ Data)
end.