mirror of
https://github.com/internetee/epp_proxy.git
synced 2025-08-18 05:23:48 +02:00
Reformat Erlang code, write more documentation
This commit is contained in:
parent
558e1f0ef7
commit
771cc06232
13 changed files with 323 additions and 330 deletions
|
@ -3,11 +3,11 @@
|
|||
-include_lib("public_key/include/public_key.hrl").
|
||||
|
||||
-export([certificate_to_pem/1,
|
||||
common_name_from_subject/1, der_certificate/1,
|
||||
headers_from_cert/1, pem_certificate/1,
|
||||
subject_from_otp_certificate/1]).
|
||||
common_name_from_subject/1, der_certificate/1,
|
||||
headers_from_cert/1, pem_certificate/1,
|
||||
subject_from_otp_certificate/1]).
|
||||
|
||||
% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT}
|
||||
% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT}
|
||||
headers_from_cert(Der) ->
|
||||
OTPCertificate = der_certificate(Der),
|
||||
Subject = subject_from_otp_certificate(OTPCertificate),
|
||||
|
@ -26,14 +26,14 @@ certificate_to_pem(Certificate) ->
|
|||
PemEntry = {'Certificate', Certificate, not_encrypted},
|
||||
PemString = public_key:pem_encode([PemEntry]),
|
||||
CleanBinary = binary:replace(PemString, <<"\n">>,
|
||||
<<" ">>, [global]),
|
||||
<<" ">>, [global]),
|
||||
CleanBinary.
|
||||
|
||||
%% Read only a specific type of certificate, otherwise fail.
|
||||
subject_from_otp_certificate(Certificate)
|
||||
when is_record(Certificate, 'OTPCertificate') ->
|
||||
when is_record(Certificate, 'OTPCertificate') ->
|
||||
Subject =
|
||||
(Certificate#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subject,
|
||||
(Certificate#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subject,
|
||||
Subject.
|
||||
|
||||
%% Take a subject rdnSequence that can be set into
|
||||
|
@ -41,7 +41,7 @@ subject_from_otp_certificate(Certificate)
|
|||
common_name_from_subject(Subject) ->
|
||||
CommonName = (?'id-at-commonName'),
|
||||
{_Type, Field} = field_from_subject(Subject,
|
||||
CommonName),
|
||||
CommonName),
|
||||
Field.
|
||||
|
||||
%% Only used for local development test, is not required for the application.
|
||||
|
@ -49,7 +49,7 @@ pem_certificate(PathToCert) ->
|
|||
{ok, PemBin} = file:file(PathToCert),
|
||||
PemEntries = public_key:pem_decode(PemBin),
|
||||
{value, CertEntry} = lists:keysearch('Certificate', 1,
|
||||
PemEntries),
|
||||
PemEntries),
|
||||
{_, DerCert, _} = CertEntry,
|
||||
Decoded = public_key:pkix_decode_cert(DerCert, otp),
|
||||
Decoded.
|
||||
|
@ -57,8 +57,8 @@ pem_certificate(PathToCert) ->
|
|||
field_from_subject({rdnSequence, Attributes}, Field) ->
|
||||
FlatList = lists:flatten(Attributes),
|
||||
ValidAttrs = lists:filter(fun (X) ->
|
||||
X#'AttributeTypeAndValue'.type =:= Field
|
||||
end,
|
||||
FlatList),
|
||||
X#'AttributeTypeAndValue'.type =:= Field
|
||||
end,
|
||||
FlatList),
|
||||
Attribute = lists:last(ValidAttrs),
|
||||
Attribute#'AttributeTypeAndValue'.value.
|
||||
|
|
|
@ -5,99 +5,98 @@
|
|||
-behaviour(epp_http_client_behaviour).
|
||||
|
||||
-export([request/1, request_builder/1]).
|
||||
|
||||
-define(errorCommand, "error").
|
||||
|
||||
-define(helloCommand, "hello").
|
||||
|
||||
%% Callback API
|
||||
request(#epp_request{} = Request) ->
|
||||
HackneyArgs = handle_args(Request),
|
||||
case apply(hackney, request, HackneyArgs) of
|
||||
{error, Error} -> log_and_return_canned(Error, Request);
|
||||
{Status, _StatusCode, _Headers, ClientRef} ->
|
||||
{ok, Body} = hackney:body(ClientRef), {Status, Body}
|
||||
{error, Error} -> log_and_return_canned(Error, Request);
|
||||
{Status, _StatusCode, _Headers, ClientRef} ->
|
||||
{ok, Body} = hackney:body(ClientRef), {Status, Body}
|
||||
end.
|
||||
|
||||
request_builder(Map) -> request_from_map(Map).
|
||||
|
||||
%% Private API
|
||||
-spec handle_args(epp_request()) -> list().
|
||||
%% For hello command, we ignore the payload, and send an empty body over the wire.
|
||||
handle_args(#epp_request{method=get,
|
||||
url = URL,
|
||||
headers = Headers,
|
||||
cookies = Cookies,
|
||||
epp_verb = ?helloCommand}) ->
|
||||
[get, URL, Headers, "", [{cookie, Cookies}, insecure]];
|
||||
|
||||
%% For hello command, we ignore the payload, and send an empty body over the wire.
|
||||
handle_args(#epp_request{method = get, url = URL,
|
||||
headers = Headers, cookies = Cookies,
|
||||
epp_verb = ?helloCommand}) ->
|
||||
[get, URL, Headers, "", [{cookie, Cookies}, insecure]];
|
||||
%% For error command, we convert the message and code into query parameters,
|
||||
%% and append them to the original URL.
|
||||
handle_args(#epp_request{method=get,
|
||||
url = URL,
|
||||
payload = Payload,
|
||||
headers = Headers,
|
||||
cookies = Cookies,
|
||||
epp_verb = ?errorCommand}) ->
|
||||
handle_args(#epp_request{method = get, url = URL,
|
||||
payload = Payload, headers = Headers,
|
||||
cookies = Cookies, epp_verb = ?errorCommand}) ->
|
||||
QueryString = hackney_url:qs(Payload),
|
||||
CompleteURL = [URL, <<"?">>, QueryString],
|
||||
[get, CompleteURL, Headers, "", [{cookie, Cookies}, insecure]];
|
||||
[get, CompleteURL, Headers, "",
|
||||
[{cookie, Cookies}, insecure]];
|
||||
%% For valid commands, we set the multipart body earlier, now we just pass it on.
|
||||
handle_args(#epp_request{method=post,
|
||||
url = URL,
|
||||
payload = Payload,
|
||||
headers = Headers,
|
||||
cookies = Cookies}) ->
|
||||
[post, URL, Headers, Payload, [{cookie, Cookies}, insecure]].
|
||||
handle_args(#epp_request{method = post, url = URL,
|
||||
payload = Payload, headers = Headers,
|
||||
cookies = Cookies}) ->
|
||||
[post, URL, Headers, Payload,
|
||||
[{cookie, Cookies}, insecure]].
|
||||
|
||||
%% Map request and return values.
|
||||
request_from_map(#{command := ?errorCommand,
|
||||
session_id := SessionId, code := Code,
|
||||
message := Message, headers := Headers,
|
||||
cl_trid := ClTRID}) ->
|
||||
session_id := SessionId, code := Code,
|
||||
message := Message, headers := Headers,
|
||||
cl_trid := ClTRID}) ->
|
||||
URL = epp_router:route_request(?errorCommand),
|
||||
RequestMethod = epp_router:request_method(?errorCommand),
|
||||
RequestMethod =
|
||||
epp_router:request_method(?errorCommand),
|
||||
Cookie = hackney_cookie:setcookie("session", SessionId,
|
||||
[]),
|
||||
[]),
|
||||
QueryParams = query_params(Code, Message, ClTRID),
|
||||
Headers = Headers,
|
||||
Request = #epp_request{url = URL,
|
||||
method = RequestMethod,
|
||||
payload = QueryParams, cookies = [Cookie],
|
||||
headers = Headers, epp_verb= ?errorCommand},
|
||||
method = RequestMethod, payload = QueryParams,
|
||||
cookies = [Cookie], headers = Headers,
|
||||
epp_verb = ?errorCommand},
|
||||
lager:info("Error Request from map: [~p]~n", [Request]),
|
||||
Request;
|
||||
request_from_map(#{command := Command,
|
||||
session_id := SessionId, raw_frame := RawFrame,
|
||||
headers := Headers, cl_trid := ClTRID}) ->
|
||||
session_id := SessionId, raw_frame := RawFrame,
|
||||
headers := Headers, cl_trid := ClTRID}) ->
|
||||
URL = epp_router:route_request(Command),
|
||||
RequestMethod = epp_router:request_method(Command),
|
||||
Cookie = hackney_cookie:setcookie("session", SessionId,
|
||||
[]),
|
||||
[]),
|
||||
Body = request_body(Command, RawFrame, ClTRID),
|
||||
Headers = Headers,
|
||||
Request = #epp_request{url = URL,
|
||||
method = RequestMethod, payload = Body,
|
||||
cookies = [Cookie], headers = Headers,
|
||||
epp_verb = Command},
|
||||
method = RequestMethod, payload = Body,
|
||||
cookies = [Cookie], headers = Headers,
|
||||
epp_verb = Command},
|
||||
lager:info("Request from map: [~p]~n", [Request]),
|
||||
Request;
|
||||
request_from_map(#{command := Command,
|
||||
session_id := SessionId, raw_frame := RawFrame,
|
||||
common_name := CommonName, client_cert := ClientCert,
|
||||
peer_ip := PeerIp, cl_trid := ClTRID}) ->
|
||||
session_id := SessionId, raw_frame := RawFrame,
|
||||
common_name := CommonName, client_cert := ClientCert,
|
||||
peer_ip := PeerIp, cl_trid := ClTRID}) ->
|
||||
URL = epp_router:route_request(Command),
|
||||
RequestMethod = epp_router:request_method(Command),
|
||||
Cookie = hackney_cookie:setcookie("session", SessionId,
|
||||
[]),
|
||||
[]),
|
||||
Body = request_body(Command, RawFrame, ClTRID),
|
||||
Headers = [{"SSL_CLIENT_CERT", ClientCert},
|
||||
{"SSL_CLIENT_S_DN_CN", CommonName},
|
||||
{"User-Agent", <<"EPP proxy">>},
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
{"SSL_CLIENT_S_DN_CN", CommonName},
|
||||
{"User-Agent", <<"EPP proxy">>},
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
Request = #epp_request{url = URL,
|
||||
method = RequestMethod, payload = Body,
|
||||
cookies = [Cookie], headers = Headers,
|
||||
epp_verb = Command},
|
||||
lager:info("Unified Request from map: [~p]~n", [Request]),
|
||||
method = RequestMethod, payload = Body,
|
||||
cookies = [Cookie], headers = Headers,
|
||||
epp_verb = Command},
|
||||
lager:info("Unified Request from map: [~p]~n",
|
||||
[Request]),
|
||||
Request.
|
||||
|
||||
%% Return form data or an empty list.
|
||||
|
@ -121,7 +120,7 @@ query_params(Code, Message, ClTRID) ->
|
|||
log_and_return_canned(Error, Request) ->
|
||||
lager:alert("Registry cannot be reached!"),
|
||||
lager:alert("Error contacting registry: [~p, ~p]~n",
|
||||
[Error, Request]),
|
||||
[Error, Request]),
|
||||
{2400, canned_response()}.
|
||||
|
||||
%% In case registry is not accessible, return this response.
|
||||
|
|
|
@ -7,4 +7,5 @@
|
|||
%% Abstract module for http client behaviour. It should call EPP HTTP server
|
||||
%% and return a response back to the caller.
|
||||
-callback request(epp_request()) -> http_response().
|
||||
|
||||
-callback request_builder(map()) -> epp_request().
|
||||
|
|
|
@ -13,5 +13,5 @@ start_link() ->
|
|||
|
||||
init([]) ->
|
||||
SupFlags = #{strategy => one_for_one, intensity => 3,
|
||||
period => 60},
|
||||
period => 60},
|
||||
{ok, {SupFlags, []}}.
|
||||
|
|
|
@ -16,19 +16,19 @@
|
|||
-define(SERVER, ?MODULE).
|
||||
|
||||
-define(DevMode,
|
||||
application:get_env(epp_proxy, dev_mode)).
|
||||
application:get_env(epp_proxy, dev_mode)).
|
||||
|
||||
-define(TCPPort,
|
||||
case application:get_env(epp_proxy, tcp_port) of
|
||||
undefined -> undefined;
|
||||
{ok, Value} -> Value
|
||||
end).
|
||||
case application:get_env(epp_proxy, tcp_port) of
|
||||
undefined -> undefined;
|
||||
{ok, Value} -> Value
|
||||
end).
|
||||
|
||||
-define(TLSPort,
|
||||
case application:get_env(epp_proxy, tls_port) of
|
||||
undefined -> undefined;
|
||||
{ok, Val} -> Val
|
||||
end).
|
||||
case application:get_env(epp_proxy, tls_port) of
|
||||
undefined -> undefined;
|
||||
{ok, Val} -> Val
|
||||
end).
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
|
@ -47,21 +47,21 @@ start_link() ->
|
|||
%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
|
||||
init([]) ->
|
||||
SupFlags = #{strategy => one_for_one, intensity => 3,
|
||||
period => 60},
|
||||
period => 60},
|
||||
TCPAcceptor = #{id => epp_tcp_acceptor, type => worker,
|
||||
modules => [epp_tcp_acceptor],
|
||||
start => {epp_tcp_acceptor, start_link, [?TCPPort]}},
|
||||
modules => [epp_tcp_acceptor],
|
||||
start => {epp_tcp_acceptor, start_link, [?TCPPort]}},
|
||||
TLSAcceptor = #{id => epp_tls_acceptor, type => worker,
|
||||
modules => [epp_tls_acceptor],
|
||||
start => {epp_tls_acceptor, start_link, [?TLSPort]}},
|
||||
modules => [epp_tls_acceptor],
|
||||
start => {epp_tls_acceptor, start_link, [?TLSPort]}},
|
||||
PoolSupervisor = #{id => epp_pool_supervisor,
|
||||
type => supervisor, modules => [epp_pool_supervisor],
|
||||
start => {epp_pool_supervisor, start_link, []}},
|
||||
type => supervisor, modules => [epp_pool_supervisor],
|
||||
start => {epp_pool_supervisor, start_link, []}},
|
||||
ChildrenSpec = case ?DevMode of
|
||||
{ok, true} ->
|
||||
[TCPAcceptor, TLSAcceptor, PoolSupervisor];
|
||||
_ -> [TLSAcceptor, PoolSupervisor]
|
||||
end,
|
||||
{ok, true} ->
|
||||
[TCPAcceptor, TLSAcceptor, PoolSupervisor];
|
||||
_ -> [TLSAcceptor, PoolSupervisor]
|
||||
end,
|
||||
{ok, {SupFlags, ChildrenSpec}}.
|
||||
|
||||
%%====================================================================
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
-export([request_method/1, route_request/1]).
|
||||
|
||||
-define(validCommands,
|
||||
["hello", "login", "logout", "check", "info", "poll",
|
||||
"create", "delete", "renew", "update", "transfer"]).
|
||||
["hello", "login", "logout", "check", "info", "poll",
|
||||
"create", "delete", "renew", "update", "transfer"]).
|
||||
|
||||
%% 47 is the character code
|
||||
-define(forwardSlash, 47).
|
||||
|
@ -25,80 +25,80 @@ route_request(Command) when is_list(Command) ->
|
|||
%% Actually route to places
|
||||
url_map(Command) when is_list(Command) ->
|
||||
case Command of
|
||||
%% Session commands
|
||||
"hello" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
"login" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
"logout" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
%% Poll commands
|
||||
"check" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"info" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"poll" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
%% Transform commands
|
||||
"create" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"delete" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"renew" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"update" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
% Transfer is both poll and query
|
||||
"transfer" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
% Error route
|
||||
"error" ->
|
||||
base_error_url() % Anything else should fail.
|
||||
%% Session commands
|
||||
"hello" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
"login" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
"logout" ->
|
||||
unicode:characters_to_list([base_session_url(),
|
||||
Command]);
|
||||
%% Poll commands
|
||||
"check" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"info" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"poll" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
%% Transform commands
|
||||
"create" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"delete" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"renew" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
"update" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
% Transfer is both poll and query
|
||||
"transfer" ->
|
||||
unicode:characters_to_list([base_command_url(),
|
||||
Command]);
|
||||
% Error route
|
||||
"error" ->
|
||||
base_error_url() % Anything else should fail.
|
||||
end.
|
||||
|
||||
%% This allows the person who configures proxy to not care about trailing
|
||||
%% slashes in HTTP.
|
||||
appendable_route(Route) ->
|
||||
case lists:last(Route) of
|
||||
?forwardSlash -> Route;
|
||||
_ -> unicode:characters_to_list([Route, "/"])
|
||||
?forwardSlash -> Route;
|
||||
_ -> unicode:characters_to_list([Route, "/"])
|
||||
end.
|
||||
|
||||
%% This allows the person who configures proxy to not care about trailing
|
||||
%% slashes in HTTP.
|
||||
parametrizable_route(Route) ->
|
||||
case lists:last(Route) of
|
||||
?forwardSlash -> lists:droplast(Route);
|
||||
_ -> Route
|
||||
?forwardSlash -> lists:droplast(Route);
|
||||
_ -> Route
|
||||
end.
|
||||
|
||||
%% Every time a request is made, this will go to ETS to check what's the route,
|
||||
%% But that is fast enough to not be noticed by anyone.
|
||||
base_session_url() ->
|
||||
case application:get_env(epp_proxy, epp_session_url) of
|
||||
undefined -> "https://registry.test/epp/session/";
|
||||
{ok, Value} -> appendable_route(Value)
|
||||
undefined -> "https://registry.test/epp/session/";
|
||||
{ok, Value} -> appendable_route(Value)
|
||||
end.
|
||||
|
||||
base_command_url() ->
|
||||
case application:get_env(epp_proxy, epp_command_url) of
|
||||
undefined -> "https://registry.test/epp/command/";
|
||||
{ok, Value} -> appendable_route(Value)
|
||||
undefined -> "https://registry.test/epp/command/";
|
||||
{ok, Value} -> appendable_route(Value)
|
||||
end.
|
||||
|
||||
base_error_url() ->
|
||||
case application:get_env(epp_proxy, epp_error_url) of
|
||||
undefined -> "https://registry.test/epp/error";
|
||||
{ok, Value} -> parametrizable_route(Value)
|
||||
undefined -> "https://registry.test/epp/error";
|
||||
{ok, Value} -> parametrizable_route(Value)
|
||||
end.
|
||||
|
|
|
@ -15,41 +15,41 @@
|
|||
|
||||
%% gen_server callbacks
|
||||
-export([handle_call/3, handle_cast/2, init/1,
|
||||
start_link/1]).
|
||||
start_link/1]).
|
||||
|
||||
-record(state, {socket, port, options}).
|
||||
|
||||
start_link(Port) ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, Port,
|
||||
[]).
|
||||
[]).
|
||||
|
||||
init(Port) ->
|
||||
Options = [binary, {packet, raw}, {active, false},
|
||||
{reuseaddr, true}],
|
||||
{reuseaddr, true}],
|
||||
{ok, ListenSocket} = gen_tcp:listen(Port, Options),
|
||||
gen_server:cast(self(), accept),
|
||||
{ok,
|
||||
#state{socket = ListenSocket, port = Port,
|
||||
options = Options}}.
|
||||
options = Options}}.
|
||||
|
||||
handle_cast(accept,
|
||||
State = #state{socket = ListenSocket, port = Port,
|
||||
options = Options}) ->
|
||||
State = #state{socket = ListenSocket, port = Port,
|
||||
options = Options}) ->
|
||||
{ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
|
||||
{ok, NewOwner} = create_worker(AcceptSocket),
|
||||
ok = gen_tcp:controlling_process(AcceptSocket,
|
||||
NewOwner),
|
||||
NewOwner),
|
||||
gen_server:cast(NewOwner, serve),
|
||||
gen_server:cast(NewOwner, greeting),
|
||||
gen_server:cast(self(), accept),
|
||||
{noreply,
|
||||
State#state{socket = ListenSocket, port = Port,
|
||||
options = Options}}.
|
||||
options = Options}}.
|
||||
|
||||
handle_call(_E, _From, State) -> {noreply, State}.
|
||||
|
||||
create_worker(Socket) ->
|
||||
ChildSpec = #{id => rand:uniform(), type => worker,
|
||||
modules => [?WORKER], restart => temporary,
|
||||
start => {?WORKER, start_link, [Socket]}},
|
||||
modules => [?WORKER], restart => temporary,
|
||||
start => {?WORKER, start_link, [Socket]}},
|
||||
supervisor:start_child(?POOL_SUPERVISOR, ChildSpec).
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
%% gen_server callbacks
|
||||
-export([handle_call/3, handle_cast/2, init/1,
|
||||
start_link/1]).
|
||||
start_link/1]).
|
||||
|
||||
-export([code_change/3]).
|
||||
|
||||
|
@ -45,14 +45,14 @@ handle_cast(serve, State = #state{socket = Socket}) ->
|
|||
%% When this succeeds, send "process_command" to self and await further
|
||||
%% commands.
|
||||
handle_cast(greeting,
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => "hello",
|
||||
session_id => SessionId,
|
||||
raw_frame => "",
|
||||
headers => Headers,
|
||||
cl_trid => nomatch}),
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
"hello",
|
||||
session_id => SessionId,
|
||||
raw_frame => "",
|
||||
headers => Headers,
|
||||
cl_trid => nomatch}),
|
||||
{_Status, Body} = epp_http_client:request(Request),
|
||||
frame_to_socket(Body, Socket),
|
||||
gen_server:cast(self(), process_command),
|
||||
|
@ -68,38 +68,39 @@ handle_cast(greeting,
|
|||
%%
|
||||
%% Otherwise send "process_command" again to self to repeat the process.
|
||||
handle_cast(process_command,
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
RawFrame = frame_from_socket(Socket, State),
|
||||
case parse_frame(RawFrame) of
|
||||
#valid_frame{command = Command, cl_trid = ClTRID} ->
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => Command,
|
||||
session_id => SessionId,
|
||||
raw_frame => RawFrame,
|
||||
headers => Headers,
|
||||
cl_trid => ClTRID});
|
||||
#invalid_frame{message = Message, code = Code, cl_trid = ClTRID} ->
|
||||
Command = "error",
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => Command,
|
||||
session_id => SessionId,
|
||||
headers => Headers,
|
||||
code => Code,
|
||||
message => Message,
|
||||
cl_trid => ClTRID})
|
||||
#valid_frame{command = Command, cl_trid = ClTRID} ->
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
Command,
|
||||
session_id => SessionId,
|
||||
raw_frame => RawFrame,
|
||||
headers => Headers,
|
||||
cl_trid => ClTRID});
|
||||
#invalid_frame{message = Message, code = Code,
|
||||
cl_trid = ClTRID} ->
|
||||
Command = "error",
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
Command,
|
||||
session_id => SessionId,
|
||||
headers => Headers,
|
||||
code => Code,
|
||||
message => Message,
|
||||
cl_trid => ClTRID})
|
||||
end,
|
||||
{_Status, Body} = epp_http_client:request(Request),
|
||||
frame_to_socket(Body, Socket),
|
||||
%% On logout, close the socket.
|
||||
%% Else, go back to the beginning of the loop.
|
||||
if Command =:= "logout" ->
|
||||
ok = gen_tcp:shutdown(Socket, read_write),
|
||||
{stop, normal, State};
|
||||
ok = gen_tcp:shutdown(Socket, read_write),
|
||||
{stop, normal, State};
|
||||
true ->
|
||||
gen_server:cast(self(), process_command),
|
||||
{noreply,
|
||||
State#state{socket = Socket, session_id = SessionId}}
|
||||
gen_server:cast(self(), process_command),
|
||||
{noreply,
|
||||
State#state{socket = Socket, session_id = SessionId}}
|
||||
end.
|
||||
|
||||
handle_call(_E, _From, State) -> {noreply, State}.
|
||||
|
@ -112,20 +113,20 @@ write_line(Socket, Line) ->
|
|||
|
||||
read_length(Socket) ->
|
||||
case gen_tcp:recv(Socket, 4) of
|
||||
{ok, Data} ->
|
||||
Length = binary:decode_unsigned(Data, big),
|
||||
LengthToReceive =
|
||||
epp_util:frame_length_to_receive(Length),
|
||||
{ok, LengthToReceive};
|
||||
{error, Reason} ->
|
||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
||||
{ok, Data} ->
|
||||
Length = binary:decode_unsigned(Data, big),
|
||||
LengthToReceive =
|
||||
epp_util:frame_length_to_receive(Length),
|
||||
{ok, LengthToReceive};
|
||||
{error, Reason} ->
|
||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
||||
end.
|
||||
|
||||
read_frame(Socket, FrameLength) ->
|
||||
case gen_tcp:recv(Socket, FrameLength) of
|
||||
{ok, Data} -> {ok, Data};
|
||||
{error, Reason} ->
|
||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
||||
{ok, Data} -> {ok, Data};
|
||||
{error, Reason} ->
|
||||
io:format("Error: ~p~n", [Reason]), {error, Reason}
|
||||
end.
|
||||
|
||||
%% Wrap a message in EPP frame, and then send it to socket.
|
||||
|
@ -139,24 +140,24 @@ frame_to_socket(Message, Socket) ->
|
|||
state_from_socket(Socket, State) ->
|
||||
{ok, {PeerIp, _PeerPort}} = inet:peername(Socket),
|
||||
Headers = [{"User-Agent", <<"EPP proxy">>},
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
NewState = State#state{socket = Socket,
|
||||
headers = Headers},
|
||||
headers = Headers},
|
||||
lager:info("Established connection with: [~p]~n",
|
||||
[NewState]),
|
||||
[NewState]),
|
||||
NewState.
|
||||
|
||||
%% First, listen for 4 bytes, then listen until the declared length.
|
||||
%% Return the frame binary at the very end.
|
||||
frame_from_socket(Socket, State) ->
|
||||
Length = case read_length(Socket) of
|
||||
{ok, Data} -> Data;
|
||||
{error, _Details} -> {stop, normal, State}
|
||||
end,
|
||||
{ok, Data} -> Data;
|
||||
{error, _Details} -> {stop, normal, State}
|
||||
end,
|
||||
Frame = case read_frame(Socket, Length) of
|
||||
{ok, FrameData} -> FrameData;
|
||||
{error, _FrameDetails} -> {stop, normal, State}
|
||||
end,
|
||||
{ok, FrameData} -> FrameData;
|
||||
{error, _FrameDetails} -> {stop, normal, State}
|
||||
end,
|
||||
Frame.
|
||||
|
||||
%% Get status, XML record, command and clTRID if defined.
|
||||
|
@ -164,11 +165,11 @@ frame_from_socket(Socket, State) ->
|
|||
parse_frame(Frame) ->
|
||||
ClTRID = epp_xml:find_cltrid(Frame),
|
||||
case epp_xml:parse(Frame) of
|
||||
{ok, XMLRecord} ->
|
||||
Command = epp_xml:get_command(XMLRecord),
|
||||
#valid_frame{command = Command, cl_trid = ClTRID,
|
||||
raw_frame = Frame};
|
||||
{error, _} ->
|
||||
#invalid_frame{code = ?XMLErrorCode,
|
||||
message = ?XMLErrorMessage, cl_trid = ClTRID}
|
||||
{ok, XMLRecord} ->
|
||||
Command = epp_xml:get_command(XMLRecord),
|
||||
#valid_frame{command = Command, cl_trid = ClTRID,
|
||||
raw_frame = Frame};
|
||||
{error, _} ->
|
||||
#invalid_frame{code = ?XMLErrorCode,
|
||||
message = ?XMLErrorMessage, cl_trid = ClTRID}
|
||||
end.
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
%% gen_server callbacks
|
||||
-export([handle_call/3, handle_cast/2, init/1,
|
||||
start_link/1]).
|
||||
start_link/1]).
|
||||
|
||||
-export([crl_file/0]).
|
||||
|
||||
|
@ -18,21 +18,21 @@
|
|||
|
||||
start_link(Port) ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, Port,
|
||||
[]).
|
||||
[]).
|
||||
|
||||
init(Port) ->
|
||||
Options = [binary, {packet, raw}, {active, false},
|
||||
{reuseaddr, true}, {verify, verify_peer}, {depth, 1},
|
||||
{cacertfile, ca_cert_file()}, {certfile, cert_file()},
|
||||
{keyfile, key_file()}, {crl_check, peer},
|
||||
{crl_cache,
|
||||
{ssl_crl_cache, {internal, [{http, 5000}]}}}],
|
||||
{reuseaddr, true}, {verify, verify_peer}, {depth, 1},
|
||||
{cacertfile, ca_cert_file()}, {certfile, cert_file()},
|
||||
{keyfile, key_file()}, {crl_check, peer},
|
||||
{crl_cache,
|
||||
{ssl_crl_cache, {internal, [{http, 5000}]}}}],
|
||||
ssl_crl_cache:insert({file, crl_file()}),
|
||||
{ok, ListenSocket} = ssl:listen(Port, Options),
|
||||
gen_server:cast(self(), accept),
|
||||
{ok,
|
||||
#state{socket = ListenSocket, port = Port,
|
||||
options = Options}}.
|
||||
options = Options}}.
|
||||
|
||||
%% Acceptor has only one state that goes in a loop:
|
||||
%% 1. Listen for a connection from anyone.
|
||||
|
@ -41,8 +41,8 @@ init(Port) ->
|
|||
%% the socket.
|
||||
%% 4. Go back to listening.
|
||||
handle_cast(accept,
|
||||
State = #state{socket = ListenSocket, port = Port,
|
||||
options = Options}) ->
|
||||
State = #state{socket = ListenSocket, port = Port,
|
||||
options = Options}) ->
|
||||
{ok, AcceptSocket} = ssl:transport_accept(ListenSocket),
|
||||
{ok, NewOwner} = create_worker(AcceptSocket),
|
||||
ok = ssl:controlling_process(AcceptSocket, NewOwner),
|
||||
|
@ -51,7 +51,7 @@ handle_cast(accept,
|
|||
gen_server:cast(self(), accept),
|
||||
{noreply,
|
||||
State#state{socket = ListenSocket, port = Port,
|
||||
options = Options}}.
|
||||
options = Options}}.
|
||||
|
||||
handle_call(_E, _From, State) -> {noreply, State}.
|
||||
|
||||
|
@ -59,32 +59,32 @@ handle_call(_E, _From, State) -> {noreply, State}.
|
|||
%% but for the purpose of order we should put them in a supervision tree.
|
||||
create_worker(Socket) ->
|
||||
ChildSpec = #{id => rand:uniform(), type => worker,
|
||||
modules => [?WORKER], restart => temporary,
|
||||
start => {?WORKER, start_link, [Socket]}},
|
||||
modules => [?WORKER], restart => temporary,
|
||||
start => {?WORKER, start_link, [Socket]}},
|
||||
supervisor:start_child(?POOL_SUPERVISOR, ChildSpec).
|
||||
|
||||
%% Private functions for returning paths to files. It costs almost nothing
|
||||
%% to query them from ETS.
|
||||
ca_cert_file() ->
|
||||
case application:get_env(epp_proxy, cacertfile_path) of
|
||||
undefined -> undefined;
|
||||
{ok, CaCertFile} -> CaCertFile
|
||||
undefined -> undefined;
|
||||
{ok, CaCertFile} -> CaCertFile
|
||||
end.
|
||||
|
||||
cert_file() ->
|
||||
case application:get_env(epp_proxy, certfile_path) of
|
||||
undefined -> undefined;
|
||||
{ok, CertFile} -> CertFile
|
||||
undefined -> undefined;
|
||||
{ok, CertFile} -> CertFile
|
||||
end.
|
||||
|
||||
key_file() ->
|
||||
case application:get_env(epp_proxy, keyfile_path) of
|
||||
undefined -> undefined;
|
||||
{ok, KeyFile} -> KeyFile
|
||||
undefined -> undefined;
|
||||
{ok, KeyFile} -> KeyFile
|
||||
end.
|
||||
|
||||
crl_file() ->
|
||||
case application:get_env(epp_proxy, crlfile_path) of
|
||||
undefined -> undefined;
|
||||
{ok, CrlFile} -> CrlFile
|
||||
undefined -> undefined;
|
||||
{ok, CrlFile} -> CrlFile
|
||||
end.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
%% gen_server callbacks
|
||||
-export([handle_call/3, handle_cast/2, init/1,
|
||||
start_link/1]).
|
||||
start_link/1]).
|
||||
|
||||
-export([code_change/3]).
|
||||
|
||||
|
@ -51,14 +51,14 @@ handle_cast(serve, State = #state{socket = Socket}) ->
|
|||
%% client. When this succeeds, send "process_command" to self and
|
||||
%% await further commands.
|
||||
handle_cast(greeting,
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => "hello",
|
||||
session_id => SessionId,
|
||||
raw_frame => "",
|
||||
headers => Headers,
|
||||
cl_trid => nomatch}),
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
"hello",
|
||||
session_id => SessionId,
|
||||
raw_frame => "",
|
||||
headers => Headers,
|
||||
cl_trid => nomatch}),
|
||||
{_Status, Body} = epp_http_client:request(Request),
|
||||
frame_to_socket(Body, Socket),
|
||||
gen_server:cast(self(), process_command),
|
||||
|
@ -74,89 +74,80 @@ handle_cast(greeting,
|
|||
%%
|
||||
%% Otherwise send "process_command" again to self to repeat the process.
|
||||
handle_cast(process_command,
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
State = #state{socket = Socket, session_id = SessionId,
|
||||
headers = Headers}) ->
|
||||
RawFrame = frame_from_socket(Socket, State),
|
||||
case parse_frame(RawFrame) of
|
||||
#valid_frame{command = Command, cl_trid = ClTRID} ->
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => Command,
|
||||
session_id => SessionId,
|
||||
raw_frame => RawFrame,
|
||||
headers => Headers,
|
||||
cl_trid => ClTRID});
|
||||
#invalid_frame{message = Message, code = Code,
|
||||
cl_trid = ClTRID} ->
|
||||
Command = "error",
|
||||
Request =
|
||||
epp_http_client:request_builder(#{command => Command,
|
||||
session_id => SessionId,
|
||||
headers => Headers,
|
||||
code => Code,
|
||||
message => Message,
|
||||
cl_trid => ClTRID})
|
||||
#valid_frame{command = Command, cl_trid = ClTRID} ->
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
Command,
|
||||
session_id => SessionId,
|
||||
raw_frame => RawFrame,
|
||||
headers => Headers,
|
||||
cl_trid => ClTRID});
|
||||
#invalid_frame{message = Message, code = Code,
|
||||
cl_trid = ClTRID} ->
|
||||
Command = "error",
|
||||
Request = epp_http_client:request_builder(#{command =>
|
||||
Command,
|
||||
session_id => SessionId,
|
||||
headers => Headers,
|
||||
code => Code,
|
||||
message => Message,
|
||||
cl_trid => ClTRID})
|
||||
end,
|
||||
{_Status, Body} = epp_http_client:request(Request),
|
||||
frame_to_socket(Body, Socket),
|
||||
%% On logout, close the socket.
|
||||
%% Else, go back to the beginning of the loop.
|
||||
if Command =:= "logout" ->
|
||||
ok = ssl:shutdown(Socket, read_write),
|
||||
{stop, normal, State};
|
||||
ok = ssl:shutdown(Socket, read_write),
|
||||
{stop, normal, State};
|
||||
true ->
|
||||
gen_server:cast(self(), process_command),
|
||||
{noreply,
|
||||
State#state{socket = Socket, session_id = SessionId}}
|
||||
gen_server:cast(self(), process_command),
|
||||
{noreply,
|
||||
State#state{socket = Socket, session_id = SessionId}}
|
||||
end.
|
||||
|
||||
handle_call(_E, _From, State) -> {noreply, State}.
|
||||
|
||||
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
||||
|
||||
%% Private functions
|
||||
read_length(Socket) ->
|
||||
case ssl:recv(Socket, 4) of
|
||||
{ok, Data} ->
|
||||
Length = binary:decode_unsigned(Data, big),
|
||||
LengthToReceive =
|
||||
epp_util:frame_length_to_receive(Length),
|
||||
{ok, LengthToReceive};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
read_frame(Socket, FrameLength) ->
|
||||
case ssl:recv(Socket, FrameLength, ?DefaultTimeout) of
|
||||
{ok, Data} -> {ok, Data};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
%% Wrap a message in EPP frame, and then send it to socket.
|
||||
frame_to_socket(Message, Socket) ->
|
||||
Length = epp_util:frame_length_to_send(Message),
|
||||
ByteSize = <<Length:32/big>>,
|
||||
write_line(Socket, ByteSize),
|
||||
write_line(Socket, Message).
|
||||
CompleteMessage = <<ByteSize/binary, Message/binary>>,
|
||||
write_line(Socket, CompleteMessage).
|
||||
|
||||
write_line(Socket, Line) -> ok = ssl:send(Socket, Line).
|
||||
|
||||
%% First, listen for 4 bytes, then listen until the declared length.
|
||||
%% Return the frame binary at the very end.
|
||||
%% If the client closes connection abruptly, then kill the process
|
||||
frame_from_socket(Socket, State) ->
|
||||
{ok, CompleteFrame} = ssl:recv(Socket, 0, ?DefaultTimeout),
|
||||
EPPEnvelope = binary:part(CompleteFrame, {0, 4}),
|
||||
ReportedLength = binary:decode_unsigned(EPPEnvelope, big),
|
||||
case ssl:recv(Socket, 0, ?DefaultTimeout) of
|
||||
{ok, Data} ->
|
||||
EPPEnvelope = binary:part(Data, {0, 4}),
|
||||
ReportedLength = binary:decode_unsigned(EPPEnvelope,
|
||||
big),
|
||||
read_until_exhausted(Socket, ReportedLength, Data);
|
||||
{error, closed} -> log_and_exit(State);
|
||||
{error, timeout} -> log_on_timeout(State)
|
||||
end.
|
||||
|
||||
read_until_exhausted(Socket, CompleteFrame, ReportedLength).
|
||||
|
||||
read_until_exhausted(Socket, Frame, ExpectedLength) ->
|
||||
if
|
||||
ExpectedLength =:= byte_size(Frame) ->
|
||||
binary:part(Frame, {byte_size(Frame), 4 - ExpectedLength});
|
||||
ExpectedLength > byte_size(Frame) ->
|
||||
{ok, NextFrame} = ssl:recv(Socket, 0, ?DefaultTimeout),
|
||||
NewFrame = <<Frame/binary, NextFrame/binary>>,
|
||||
read_until_exhausted(Socket, NewFrame, ExpectedLength)
|
||||
%% When an EPP message is long, it will be received in smaller chunks.
|
||||
%% For example, first 4 bytes equal 800 000, but we'd receive only 200 000
|
||||
%% in the first chunk. In such a case, we should listen for more.
|
||||
%%
|
||||
%% Note that there is no case for messages the exceed the reported length of a
|
||||
%% frame. Those cases are invalid from the perspective of EPP protocol and
|
||||
%% should not be supported.
|
||||
read_until_exhausted(Socket, ExpectedLength, Frame) ->
|
||||
if ExpectedLength =:= byte_size(Frame) ->
|
||||
binary:part(Frame,
|
||||
{byte_size(Frame), 4 - ExpectedLength});
|
||||
ExpectedLength > byte_size(Frame) ->
|
||||
{ok, NextFrame} = ssl:recv(Socket, 0, ?DefaultTimeout),
|
||||
NewFrame = <<Frame/binary, NextFrame/binary>>,
|
||||
read_until_exhausted(Socket, ExpectedLength, NewFrame)
|
||||
end.
|
||||
|
||||
log_and_exit(State) ->
|
||||
|
@ -172,15 +163,15 @@ state_from_socket(Socket, State) ->
|
|||
{ok, PeerCert} = ssl:peercert(Socket),
|
||||
{ok, {PeerIp, _PeerPort}} = ssl:peername(Socket),
|
||||
{SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} =
|
||||
epp_certs:headers_from_cert(PeerCert),
|
||||
epp_certs:headers_from_cert(PeerCert),
|
||||
Headers = [{"SSL-CLIENT-CERT", SSL_CLIENT_CERT},
|
||||
{"SSL-CLIENT-S-DN-CN", SSL_CLIENT_S_DN_CN},
|
||||
{"User-Agent", <<"EPP proxy">>},
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
{"SSL-CLIENT-S-DN-CN", SSL_CLIENT_S_DN_CN},
|
||||
{"User-Agent", <<"EPP proxy">>},
|
||||
{"X-Forwarded-for", epp_util:readable_ip(PeerIp)}],
|
||||
NewState = State#state{socket = Socket,
|
||||
headers = Headers},
|
||||
headers = Headers},
|
||||
lager:info("Established connection with: [~p]~n",
|
||||
[NewState]),
|
||||
[NewState]),
|
||||
NewState.
|
||||
|
||||
%% Get status, XML record, command and clTRID if defined.
|
||||
|
@ -188,11 +179,11 @@ state_from_socket(Socket, State) ->
|
|||
parse_frame(Frame) ->
|
||||
ClTRID = epp_xml:find_cltrid(Frame),
|
||||
case epp_xml:parse(Frame) of
|
||||
{ok, XMLRecord} ->
|
||||
Command = epp_xml:get_command(XMLRecord),
|
||||
#valid_frame{command = Command, cl_trid = ClTRID,
|
||||
raw_frame = Frame};
|
||||
{error, _} ->
|
||||
#invalid_frame{code = ?XMLErrorCode,
|
||||
message = ?XMLErrorMessage, cl_trid = ClTRID}
|
||||
{ok, XMLRecord} ->
|
||||
Command = epp_xml:get_command(XMLRecord),
|
||||
#valid_frame{command = Command, cl_trid = ClTRID,
|
||||
raw_frame = Frame};
|
||||
{error, _} ->
|
||||
#invalid_frame{code = ?XMLErrorCode,
|
||||
message = ?XMLErrorMessage, cl_trid = ClTRID}
|
||||
end.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
-module(epp_util).
|
||||
|
||||
-export([create_map/1, create_session_id/1,
|
||||
frame_length/1, frame_length_to_receive/1,
|
||||
frame_length_to_send/1, readable_ip/1, session_id/1]).
|
||||
frame_length/1, frame_length_to_receive/1,
|
||||
frame_length_to_send/1, readable_ip/1, session_id/1]).
|
||||
|
||||
-define(OFFSET, 4).
|
||||
|
||||
|
@ -16,7 +16,7 @@ session_id(Pid) ->
|
|||
|
||||
%% Give me a process id, I'll create a random map for you.
|
||||
-spec create_map(pid()) -> #{string() => pid(),
|
||||
string() => float(), string() => string()}.
|
||||
string() => float(), string() => string()}.
|
||||
|
||||
create_map(Pid) when is_pid(Pid) ->
|
||||
Now = erlang:system_time(second),
|
||||
|
@ -26,21 +26,21 @@ create_map(Pid) when is_pid(Pid) ->
|
|||
%% Given the special data structure, return back a binary hash to pass to the
|
||||
%% application server.
|
||||
-spec create_session_id(#{string() => pid(),
|
||||
string() => float(),
|
||||
string() => string()}) -> [char()].
|
||||
string() => float(),
|
||||
string() => string()}) -> [char()].
|
||||
|
||||
create_session_id(#{"pid" := Pid, "random" := Random,
|
||||
"timestamp" := Timestamp}) ->
|
||||
"timestamp" := Timestamp}) ->
|
||||
Map = #{"pid" => pid_to_list(Pid),
|
||||
"random" => float_to_list(Random),
|
||||
"timestamp" => Timestamp},
|
||||
"random" => float_to_list(Random),
|
||||
"timestamp" => Timestamp},
|
||||
ListOfTuples = maps:to_list(Map),
|
||||
ListOfLists = [[X, ",", Y] || {X, Y} <- ListOfTuples],
|
||||
NestedList = lists:join(",", ListOfLists),
|
||||
ListOfGlyphs = lists:flatten(NestedList),
|
||||
BinaryHash = crypto:hash(sha512, ListOfGlyphs),
|
||||
String = lists:flatten([integer_to_list(X, 16)
|
||||
|| <<X>> <= BinaryHash]),
|
||||
|| <<X>> <= BinaryHash]),
|
||||
String.
|
||||
|
||||
frame_length_to_receive(Size) when Size >= 0 ->
|
||||
|
@ -57,13 +57,13 @@ frame_length(Frame) when is_list(Frame) ->
|
|||
|
||||
%% Pass a tuple of IP address, return a binary for sending over the wire.
|
||||
-spec readable_ip({integer(), integer(), integer(),
|
||||
integer()}) -> binary().
|
||||
integer()}) -> binary().
|
||||
|
||||
readable_ip({FirstOctet, SecondOctet, ThirdOctet,
|
||||
FourthOctet}) ->
|
||||
FourthOctet}) ->
|
||||
List = [integer_to_list(FirstOctet), ".",
|
||||
integer_to_list(SecondOctet), ".",
|
||||
integer_to_list(ThirdOctet), ".",
|
||||
integer_to_list(FourthOctet)],
|
||||
integer_to_list(SecondOctet), ".",
|
||||
integer_to_list(ThirdOctet), ".",
|
||||
integer_to_list(FourthOctet)],
|
||||
Binary = list_to_binary(List),
|
||||
Binary.
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
%% Get command from an xmlElement. Otherwise return undefined.
|
||||
get_command(Record)
|
||||
when is_record(Record, xmlElement) ->
|
||||
when is_record(Record, xmlElement) ->
|
||||
case xmerl_xpath:string("name(/epp/command/*[1])",
|
||||
Record)
|
||||
of
|
||||
{xmlObj, string, []} -> undefined;
|
||||
{xmlObj, string, Command} -> Command
|
||||
Record)
|
||||
of
|
||||
{xmlObj, string, []} -> undefined;
|
||||
{xmlObj, string, Command} -> Command
|
||||
end;
|
||||
get_command(_) -> undefined.
|
||||
|
||||
|
@ -25,10 +25,10 @@ parse(_) -> {error, {fatal, {expected_binary_or_list}}}.
|
|||
%% Parse a record that came from the wire and return a xmlElement record.
|
||||
parse_list(List) when is_list(List) ->
|
||||
try xmerl_scan:string(List, [{quiet, true}]) of
|
||||
{Record, []} when is_record(Record, xmlElement) ->
|
||||
{ok, Record}
|
||||
{Record, []} when is_record(Record, xmlElement) ->
|
||||
{ok, Record}
|
||||
catch
|
||||
exit:X -> {error, X}
|
||||
exit:X -> {error, X}
|
||||
end.
|
||||
|
||||
%% The idea is that even when XML command is invalid,
|
||||
|
@ -43,7 +43,7 @@ find_cltrid(_) -> nomatch.
|
|||
run_regex(Text) ->
|
||||
{ok, MP} = re:compile("<clTRID>(?<cltrid>.+)</clTRID>"),
|
||||
case re:run(Text, MP, [{capture, ["cltrid"], binary}])
|
||||
of
|
||||
{match, [Binary]} -> Binary;
|
||||
nomatch -> nomatch
|
||||
of
|
||||
{match, [Binary]} -> Binary;
|
||||
nomatch -> nomatch
|
||||
end.
|
||||
|
|
|
@ -48,4 +48,5 @@
|
|||
{test, [{deps, [proper]}]}]
|
||||
}.
|
||||
|
||||
{plugins, [rebar3_fmt]}.
|
||||
{plugins, [rebar3_auto,
|
||||
{erl_tidy_prv_fmt, ".*", {git, "git://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}]}.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue