summaryrefslogtreecommitdiff
path: root/src/dnssecport.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/dnssecport.erl')
-rw-r--r--src/dnssecport.erl130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/dnssecport.erl b/src/dnssecport.erl
new file mode 100644
index 0000000..804e727
--- /dev/null
+++ b/src/dnssecport.erl
@@ -0,0 +1,130 @@
+%%% Copyright (c) 2016, NORDUnet A/S.
+%%% See LICENSE for licensing information.
+
+-module(dnssecport).
+-behaviour(gen_server).
+-export([start_link/0, stop/0]).
+-export([validate/2]).
+%% gen_server callbacks.
+-export([init/1, handle_call/3, terminate/2, handle_cast/2, handle_info/2,
+ code_change/3]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE,
+ [code:priv_dir(catlfish) ++ "/dnssecport"], []).
+
+stop() ->
+ gen_server:call(?MODULE, stop).
+
+validate(ValidateRR, SupportRRs) ->
+ gen_server:call(?MODULE, {validate, [ValidateRR, SupportRRs]}).
+
+-record(state, {port :: port()}).
+
+init(Program) ->
+ lager:debug("starting dnssec service"),
+ Port = create_port(Program, []), % TODO: Pass path to dir with trust root.
+ {ok, #state{port = Port}}.
+
+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]),
+ 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>>}},
+ 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}
+ end;
+ {Port, {exit_status, ExitStatus}} ->
+ lager:error("dnssec port ~p exiting with status ~p",
+ [Port, ExitStatus]),
+ {stop, portexit, State#state{port = undefined}}
+ after
+ 3000 ->
+ lager:error("dnssec port timeout"),
+ {stop, timeout, State}
+ end
+ end.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+terminate(Reason, _State) ->
+ lager:info("dnssec port terminating: ~p", [Reason]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%
+create_port(Program, Args) ->
+ open_port({spawn_executable, Program},
+ [{args, Args},
+ exit_status, % Let us know if process dies.
+ {packet, 4}]).
+
+stop_port(State) ->
+ Port = State#state.port,
+ Port ! {self(), close},
+ receive
+ {Port, closed} ->
+ {stop, closed, State#state{port = undefined}};
+ {Port, Msg} ->
+ lager:debug("message received from dying port: ~p", [Msg]),
+ {stop, unknown, State#state{port = undefined}}
+ after
+ 2000 ->
+ lager:error("dnssec port ~p refuses to die", [Port]),
+ {stop, timeout, State}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Unit tests.
+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}.