mirror of
https://github.com/internetee/epp_proxy.git
synced 2025-08-15 12:03:47 +02:00
Document EPP TLS worker
This commit is contained in:
parent
4b5c90912d
commit
4566e14b31
1 changed files with 40 additions and 77 deletions
|
@ -9,8 +9,6 @@
|
||||||
-export([init/1, handle_cast/2, handle_call/3, start_link/1]).
|
-export([init/1, handle_cast/2, handle_call/3, start_link/1]).
|
||||||
-export([code_change/3]).
|
-export([code_change/3]).
|
||||||
|
|
||||||
-export([request_from_map/1]).
|
|
||||||
|
|
||||||
-record(valid_frame, {command,
|
-record(valid_frame, {command,
|
||||||
cl_trid,
|
cl_trid,
|
||||||
raw_frame}).
|
raw_frame}).
|
||||||
|
@ -21,6 +19,11 @@
|
||||||
session_id,
|
session_id,
|
||||||
headers }).
|
headers }).
|
||||||
|
|
||||||
|
-define(XMLErrorCode, <<"2001">>).
|
||||||
|
-define(XMLErrorMessage, <<"Command syntax error.">>).
|
||||||
|
|
||||||
|
%% Initialize process
|
||||||
|
%% Assign an unique session id that will be passed on to http server as a cookie
|
||||||
init(Socket) ->
|
init(Socket) ->
|
||||||
lager:info("Created a worker process: [~p]", [self()]),
|
lager:info("Created a worker process: [~p]", [self()]),
|
||||||
SessionId = epp_util:session_id(self()),
|
SessionId = epp_util:session_id(self()),
|
||||||
|
@ -29,13 +32,24 @@ init(Socket) ->
|
||||||
start_link(Socket) ->
|
start_link(Socket) ->
|
||||||
gen_server:start_link(?MODULE, Socket, []).
|
gen_server:start_link(?MODULE, Socket, []).
|
||||||
|
|
||||||
|
%% First step after spinning off the process:
|
||||||
|
%% Perform an TLS handshake and gather data that we use a headers for
|
||||||
|
%% http request:
|
||||||
|
%% Common name
|
||||||
|
%% Client certificate
|
||||||
|
%% Client IP address
|
||||||
|
%% If certificate is revoked, this will fail right away here.
|
||||||
|
%% mod_epp does exactly the same thing.
|
||||||
handle_cast(serve, State = #state{socket=Socket}) ->
|
handle_cast(serve, State = #state{socket=Socket}) ->
|
||||||
%% If certificate is revoked, this will fail right away here.
|
|
||||||
%% mod_epp does exactly the same thing.
|
|
||||||
{ok, SecureSocket} = ssl:handshake(Socket),
|
{ok, SecureSocket} = ssl:handshake(Socket),
|
||||||
NewState = state_from_socket(SecureSocket, State),
|
NewState = state_from_socket(SecureSocket, State),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
|
|
||||||
|
%% Step two:
|
||||||
|
%% Using the state of the connection, get the hello route from http server.
|
||||||
|
%% Send the response from HTTP server back to EPP client.
|
||||||
|
%% When this succeeds, send "process_command" to self and await further
|
||||||
|
%% commands.
|
||||||
handle_cast(greeting, State = #state{socket=Socket,
|
handle_cast(greeting, State = #state{socket=Socket,
|
||||||
session_id=SessionId,
|
session_id=SessionId,
|
||||||
headers=Headers}) ->
|
headers=Headers}) ->
|
||||||
|
@ -52,8 +66,15 @@ handle_cast(greeting, State = #state{socket=Socket,
|
||||||
gen_server:cast(self(), process_command),
|
gen_server:cast(self(), process_command),
|
||||||
{noreply, State#state{socket=Socket, session_id=SessionId}};
|
{noreply, State#state{socket=Socket, session_id=SessionId}};
|
||||||
|
|
||||||
%% Main loop of processing commands. Ends the connection when command is logout.
|
%% Step three to N:
|
||||||
|
%% Await input from client. Parse it, and perform an appropriate http request.
|
||||||
|
%% Commands go to commands, invalid XML goes to error.
|
||||||
|
%% Send the response from HTTP server back to EPP client.
|
||||||
%%
|
%%
|
||||||
|
%% When the command from client was logout, close the connection and quit the
|
||||||
|
%% process.
|
||||||
|
%%
|
||||||
|
%% Otherwise send "process_command" again to self to repeat the process.
|
||||||
handle_cast(process_command,
|
handle_cast(process_command,
|
||||||
State = #state{socket=Socket,session_id=SessionId,
|
State = #state{socket=Socket,session_id=SessionId,
|
||||||
headers=Headers}) ->
|
headers=Headers}) ->
|
||||||
|
@ -96,9 +117,6 @@ handle_call(_E, _From, State) -> {noreply, State}.
|
||||||
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
||||||
|
|
||||||
%% Private functions
|
%% Private functions
|
||||||
write_line(Socket, Line) ->
|
|
||||||
ok = ssl:send(Socket, Line).
|
|
||||||
|
|
||||||
read_length(Socket) ->
|
read_length(Socket) ->
|
||||||
case ssl:recv(Socket, 4) of
|
case ssl:recv(Socket, 4) of
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
|
@ -106,7 +124,7 @@ read_length(Socket) ->
|
||||||
LengthToReceive = epp_util:frame_length_to_receive(Length),
|
LengthToReceive = epp_util:frame_length_to_receive(Length),
|
||||||
{ok, LengthToReceive};
|
{ok, LengthToReceive};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
io:format("Error: ~p~n", [Reason]),
|
lager:error("Error: ~p~n", [Reason]),
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -115,72 +133,10 @@ read_frame(Socket, FrameLength) ->
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
{ok, Data};
|
{ok, Data};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
io:format("Error: ~p~n", [Reason]),
|
lager:error("Error: ~p~n", [Reason]),
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Map request and return values.
|
|
||||||
request_from_map(#{command := "error", session_id := SessionId,
|
|
||||||
code := Code, message := Message, headers:=Headers,
|
|
||||||
cl_trid := ClTRID}) ->
|
|
||||||
URL = epp_router:route_request("error"),
|
|
||||||
RequestMethod = epp_router:request_method("error"),
|
|
||||||
Cookie = hackney_cookie:setcookie("session", SessionId, []),
|
|
||||||
QueryParams = query_params(Code, Message, ClTRID),
|
|
||||||
Headers=Headers,
|
|
||||||
Request = #epp_error_request{url=URL,
|
|
||||||
method=RequestMethod,
|
|
||||||
query_params=QueryParams,
|
|
||||||
cookies=[Cookie],
|
|
||||||
headers=Headers},
|
|
||||||
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}) ->
|
|
||||||
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,
|
|
||||||
body=Body,
|
|
||||||
cookies=[Cookie],
|
|
||||||
headers=Headers},
|
|
||||||
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}) ->
|
|
||||||
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)}],
|
|
||||||
Request = #epp_request{url=URL,
|
|
||||||
method=RequestMethod,
|
|
||||||
body=Body,
|
|
||||||
cookies=[Cookie],
|
|
||||||
headers=Headers},
|
|
||||||
lager:info("Request from map: [~p]~n", [Request]),
|
|
||||||
Request.
|
|
||||||
|
|
||||||
%% Return form data or an empty list.
|
|
||||||
request_body("hello", _, _) ->
|
|
||||||
"";
|
|
||||||
request_body(_Command, RawFrame, nomatch) ->
|
|
||||||
{multipart, [{<<"raw_frame">>, RawFrame}]};
|
|
||||||
request_body(_Command, RawFrame, ClTRID) ->
|
|
||||||
{multipart, [{<<"raw_frame">>, RawFrame}, {<<"clTRID">>, ClTRID}]}.
|
|
||||||
|
|
||||||
query_params(Code, Message, nomatch) ->
|
|
||||||
[{<<"code">>, Code}, {<<"msg">>, Message}];
|
|
||||||
query_params(Code, Message, ClTRID) ->
|
|
||||||
[{<<"code">>, Code}, {<<"msg">>, Message}, {<<"clTRID">>, ClTRID}].
|
|
||||||
|
|
||||||
%% Wrap a message in EPP frame, and then send it to socket.
|
%% Wrap a message in EPP frame, and then send it to socket.
|
||||||
frame_to_socket(Message, Socket) ->
|
frame_to_socket(Message, Socket) ->
|
||||||
Length = epp_util:frame_length_to_send(Message),
|
Length = epp_util:frame_length_to_send(Message),
|
||||||
|
@ -188,6 +144,9 @@ frame_to_socket(Message, Socket) ->
|
||||||
write_line(Socket, ByteSize),
|
write_line(Socket, ByteSize),
|
||||||
write_line(Socket, Message).
|
write_line(Socket, Message).
|
||||||
|
|
||||||
|
write_line(Socket, Line) ->
|
||||||
|
ok = ssl:send(Socket, Line).
|
||||||
|
|
||||||
%% First, listen for 4 bytes, then listen until the declared length.
|
%% First, listen for 4 bytes, then listen until the declared length.
|
||||||
%% Return the frame binary at the very end.
|
%% Return the frame binary at the very end.
|
||||||
frame_from_socket(Socket, State) ->
|
frame_from_socket(Socket, State) ->
|
||||||
|
@ -220,14 +179,18 @@ state_from_socket(Socket, State) ->
|
||||||
lager:info("Established connection with: [~p]~n", [NewState]),
|
lager:info("Established connection with: [~p]~n", [NewState]),
|
||||||
NewState.
|
NewState.
|
||||||
|
|
||||||
%% Get status, XML record, command and clTRID if defined
|
%% Get status, XML record, command and clTRID if defined.
|
||||||
|
%% Otherwise return an invalid frame with predefined error message and code.
|
||||||
parse_frame(Frame) ->
|
parse_frame(Frame) ->
|
||||||
ClTRID = epp_xml:find_cltrid(Frame),
|
ClTRID = epp_xml:find_cltrid(Frame),
|
||||||
case epp_xml:parse(Frame) of
|
case epp_xml:parse(Frame) of
|
||||||
{ok, XMLRecord} ->
|
{ok, XMLRecord} ->
|
||||||
Command = epp_xml:get_command(XMLRecord),
|
Command = epp_xml:get_command(XMLRecord),
|
||||||
#valid_frame{command=Command, cl_trid=ClTRID, raw_frame=Frame};
|
#valid_frame{command=Command,
|
||||||
|
cl_trid=ClTRID,
|
||||||
|
raw_frame=Frame};
|
||||||
{error, _} ->
|
{error, _} ->
|
||||||
ErrorMessage = <<"Command syntax error.">>,
|
#invalid_frame{code=?XMLErrorCode,
|
||||||
#invalid_frame{code=2001, message=ErrorMessage, cl_trid=ClTRID}
|
message=?XMLErrorMessage,
|
||||||
|
cl_trid=ClTRID}
|
||||||
end.
|
end.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue