diff options
Diffstat (limited to 'src/dnssecport.erl')
-rw-r--r-- | src/dnssecport.erl | 130 |
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}. |