mirror of
https://github.com/internetee/epp_proxy.git
synced 2025-08-15 12:03:47 +02:00
Working base loop
This commit is contained in:
parent
da3fa72432
commit
c2c91667e7
12 changed files with 355 additions and 120 deletions
|
@ -6,12 +6,13 @@
|
||||||
common_name_from_subject/1, certificate_to_pem/1,
|
common_name_from_subject/1, certificate_to_pem/1,
|
||||||
der_certificate/1, headers_from_cert/1]).
|
der_certificate/1, headers_from_cert/1]).
|
||||||
|
|
||||||
% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SLL_CLIENT_CERT}
|
% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT}
|
||||||
headers_from_cert(Der) ->
|
headers_from_cert(Der) ->
|
||||||
OTPCertificate = der_certificate(Der),
|
OTPCertificate = der_certificate(Der),
|
||||||
Subject = subject_from_otp_certificate(OTPCertificate),
|
Subject = subject_from_otp_certificate(OTPCertificate),
|
||||||
CommonName = common_name_from_subject(Subject),
|
CommonName = common_name_from_subject(Subject),
|
||||||
PEM = certificate_to_pem(OTPCertificate),
|
logger:info("~p~n", [CommonName]),
|
||||||
|
PEM = certificate_to_pem(Der),
|
||||||
{CommonName, PEM}.
|
{CommonName, PEM}.
|
||||||
|
|
||||||
%% Read certificate from the wire and return back an otp type record
|
%% Read certificate from the wire and return back an otp type record
|
||||||
|
|
|
@ -36,11 +36,15 @@ init([]) ->
|
||||||
type => worker,
|
type => worker,
|
||||||
modules => [epp_tcp_acceptor],
|
modules => [epp_tcp_acceptor],
|
||||||
start => {epp_tcp_acceptor, start_link, [3333]}},
|
start => {epp_tcp_acceptor, start_link, [3333]}},
|
||||||
PoolSupervisor = #{id => pool_supervisor,
|
TLSAcceptor = #{id => epp_tls_acceptor,
|
||||||
|
type => worker,
|
||||||
|
modules => [epp_tls_acceptor],
|
||||||
|
start => {epp_tls_acceptor, start_link, [4444]}},
|
||||||
|
PoolSupervisor = #{id => epp_pool_supervisor,
|
||||||
type => supervisor,
|
type => supervisor,
|
||||||
modules => [pool_supervisor],
|
modules => [epp_pool_supervisor],
|
||||||
start => {pool_supervisor, start_link, []}},
|
start => {epp_pool_supervisor, start_link, []}},
|
||||||
{ok, {SupFlags, [TCPAcceptor, PoolSupervisor]}}.
|
{ok, {SupFlags, [TCPAcceptor, TLSAcceptor, PoolSupervisor]}}.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
-module(epp_tcp).
|
|
||||||
|
|
||||||
-behaviour(supervisor).
|
|
||||||
-define(SERVER, ?MODULE).
|
|
||||||
|
|
||||||
-export([start_link/1, init/1]).
|
|
||||||
|
|
||||||
start_link(Port) ->
|
|
||||||
supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
|
|
||||||
|
|
||||||
init([Port]) ->
|
|
||||||
process_flag(trap_exit, true),
|
|
||||||
SupFlags = #{strategy => one_for_one, intensity => 3, period => 60},
|
|
||||||
ChildSpecs = [#{id => epp_tcp_acceptor,
|
|
||||||
start => {epp_tcp_acceptor, start_link, [Port]}}],
|
|
||||||
log_message(Port),
|
|
||||||
{ok, {SupFlags, ChildSpecs}}.
|
|
||||||
|
|
||||||
log_message(Port) ->
|
|
||||||
logger:info("TCP listening on Port: ~p", [Port]).
|
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
-define(POOL_SUPERVISOR, pool_supervisor).
|
-define(POOL_SUPERVISOR, epp_pool_supervisor).
|
||||||
-define(WORKER, epp_tcp_worker).
|
-define(WORKER, epp_tcp_worker).
|
||||||
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_cast/2, handle_call/3, start_link/1]).
|
-export([init/1, handle_cast/2, handle_call/3, start_link/1]).
|
||||||
|
|
||||||
|
|
|
@ -5,76 +5,93 @@
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-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([terminate/2, code_change/3]).
|
-export([code_change/3]).
|
||||||
|
|
||||||
|
-export([request/3]).
|
||||||
|
|
||||||
-record(state,{socket, length, session_id}).
|
-record(state,{socket, length, session_id}).
|
||||||
|
-record(request,{method, url, body, cookies, headers}).
|
||||||
|
|
||||||
init(Socket) ->
|
init(Socket) ->
|
||||||
logger:info("Created a worker process"),
|
logger:info("Created a worker process"),
|
||||||
{ok, #state{socket=Socket}}.
|
SessionId = session_id(),
|
||||||
|
{ok, #state{socket=Socket, session_id=SessionId}}.
|
||||||
|
|
||||||
start_link(Socket) ->
|
start_link(Socket) ->
|
||||||
gen_server:start_link(?MODULE, Socket, []).
|
gen_server:start_link(?MODULE, Socket, []).
|
||||||
|
|
||||||
handle_cast(serve, State = #state{socket=Socket}) ->
|
handle_cast(serve, State = #state{socket=Socket}) ->
|
||||||
{noreply, State#state{socket=Socket}};
|
{noreply, State#state{socket=Socket}};
|
||||||
handle_cast(greeting, State = #state{socket=Socket}) ->
|
handle_cast(greeting, State = #state{socket=Socket, session_id=SessionId}) ->
|
||||||
SessionId = session_id(),
|
Request = request("hello", SessionId, ""),
|
||||||
Cookie = hackney_cookie:setcookie("session", SessionId, []),
|
logger:info("Request: ~p~n", [Request]),
|
||||||
|
|
||||||
{_Status, _StatusCode, _Headers, ClientRef} =
|
{_Status, _StatusCode, _Headers, ClientRef} =
|
||||||
hackney:request(get, epp_router:route_request("hello"), [], "",
|
hackney:request(Request#request.method, Request#request.url,
|
||||||
[{cookie, Cookie}, insecure]),
|
Request#request.headers, Request#request.body,
|
||||||
|
[{cookie, Request#request.cookies}, insecure]),
|
||||||
|
|
||||||
{ok, Body} = hackney:body(ClientRef),
|
{ok, Body} = hackney:body(ClientRef),
|
||||||
|
|
||||||
Length = byte_size(Body) + 4,
|
frame_to_socket(Body, Socket),
|
||||||
ByteSize = << Length:32/big >>,
|
gen_server:cast(self(), process_command),
|
||||||
io:format("State: ~p~n", [State]),
|
|
||||||
write_line(Socket, ByteSize),
|
|
||||||
write_line(Socket, Body),
|
|
||||||
gen_server:cast(self(), read_length),
|
|
||||||
{noreply, State#state{socket=Socket, session_id=SessionId}};
|
{noreply, State#state{socket=Socket, session_id=SessionId}};
|
||||||
handle_cast(read_length, State = #state{socket=Socket}) ->
|
|
||||||
case read_length(Socket) of
|
handle_cast(process_command, State = #state{socket=Socket, session_id=SessionId}) ->
|
||||||
|
Length = case read_length(Socket) of
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
NewState = State#state{length=Data},
|
Data;
|
||||||
gen_server:cast(self(), read_frame),
|
|
||||||
{noreply, NewState};
|
|
||||||
{error, _Details} ->
|
{error, _Details} ->
|
||||||
{stop, normal, State}
|
{stop, normal, State}
|
||||||
end;
|
end,
|
||||||
handle_cast(read_frame, State = #state{socket=Socket, length=Length}) ->
|
|
||||||
case read_frame(Socket, Length) of
|
Frame = case read_frame(Socket, Length) of
|
||||||
{ok, Data} ->
|
{ok, FrameData} ->
|
||||||
ByteLength = byte_size(Data) + 4,
|
io:format("~p~n", [FrameData]),
|
||||||
ByteSize = << ByteLength:32/big >>,
|
FrameData;
|
||||||
write_line(Socket, ByteSize),
|
{error, _FrameDetails} ->
|
||||||
write_line(Socket, Data),
|
|
||||||
gen_server:cast(self(), read_length),
|
|
||||||
{noreply, State};
|
|
||||||
{error, _Details} ->
|
|
||||||
{stop, normal, State}
|
{stop, normal, State}
|
||||||
end;
|
end,
|
||||||
handle_cast(Message, State) ->
|
|
||||||
logger:info("Received message ~p.~n", [Message]),
|
{ok, XMLRecord} = epp_xml:parse(Frame),
|
||||||
{noreply, State}.
|
Command = epp_xml:get_command(XMLRecord),
|
||||||
|
|
||||||
|
Request = request(Command, SessionId, Frame),
|
||||||
|
logger:info("Request: ~p~n", [Request]),
|
||||||
|
|
||||||
|
{_Status, _StatusCode, _Headers, ClientRef} =
|
||||||
|
hackney:request(Request#request.method, Request#request.url,
|
||||||
|
Request#request.headers, Request#request.body,
|
||||||
|
[{cookie, Request#request.cookies}, insecure]),
|
||||||
|
|
||||||
|
{ok, Body} = hackney:body(ClientRef),
|
||||||
|
|
||||||
|
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};
|
||||||
|
true ->
|
||||||
|
gen_server:cast(self(), process_command),
|
||||||
|
{noreply, State#state{socket=Socket, session_id=SessionId}}
|
||||||
|
end.
|
||||||
|
|
||||||
handle_call(_E, _From, State) -> {noreply, State}.
|
handle_call(_E, _From, State) -> {noreply, State}.
|
||||||
terminate(_Reason, _State) ->
|
|
||||||
ok.
|
|
||||||
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
||||||
|
|
||||||
%% Private function
|
%% Private function
|
||||||
write_line(Socket, Line) ->
|
write_line(Socket, Line) ->
|
||||||
gen_tcp:send(Socket, Line).
|
ok = gen_tcp:send(Socket, Line).
|
||||||
|
|
||||||
read_length(Socket) ->
|
read_length(Socket) ->
|
||||||
case gen_tcp:recv(Socket, 4) of
|
case gen_tcp:recv(Socket, 4) of
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
Length = binary:decode_unsigned(Data, big),
|
Length = binary:decode_unsigned(Data, big),
|
||||||
io:format("Preparing to receive: ~p~n", [Length]),
|
LengthToReceive = epp_util:frame_length_to_receive(Length),
|
||||||
{ok, Length - 4};
|
{ok, LengthToReceive};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
io:format("Error: ~p~n", [Reason]),
|
io:format("Error: ~p~n", [Reason]),
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
|
@ -94,3 +111,33 @@ session_id() ->
|
||||||
UniqueMap = epp_util:create_map(self()),
|
UniqueMap = epp_util:create_map(self()),
|
||||||
BinaryHash = epp_util:create_session_id(UniqueMap),
|
BinaryHash = epp_util:create_session_id(UniqueMap),
|
||||||
BinaryHash.
|
BinaryHash.
|
||||||
|
|
||||||
|
%% Map request and return values
|
||||||
|
request(Command, SessionId, RawFrame) ->
|
||||||
|
URL = epp_router:route_request(Command),
|
||||||
|
RequestMethod = request_method(Command),
|
||||||
|
Cookie = hackney_cookie:setcookie("session", SessionId, []),
|
||||||
|
case Command of
|
||||||
|
"hello" ->
|
||||||
|
Body = "";
|
||||||
|
_ ->
|
||||||
|
Body = {multipart, [{<<"raw_frame">>, RawFrame}]}
|
||||||
|
end,
|
||||||
|
Headers = [],
|
||||||
|
#request{url=URL, method=RequestMethod, body=Body, cookies=[Cookie],
|
||||||
|
headers=Headers}.
|
||||||
|
|
||||||
|
%% request method: GET for greeting, POST for everything else.
|
||||||
|
request_method("hello") ->
|
||||||
|
get;
|
||||||
|
request_method(<<"hello">>) ->
|
||||||
|
get;
|
||||||
|
request_method(_) ->
|
||||||
|
post.
|
||||||
|
|
||||||
|
%% 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).
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
-module(epp_tls).
|
|
||||||
|
|
||||||
-behaviour(supervisor).
|
|
||||||
-define(SERVER, ?MODULE).
|
|
||||||
|
|
||||||
-export([start_link/1, init/1, serve/1]).
|
|
||||||
|
|
||||||
start_link(Port) ->
|
|
||||||
supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
|
|
||||||
|
|
||||||
init([Port]) ->
|
|
||||||
process_flag(trap_exit, true),
|
|
||||||
SupFlags = #{strategy => one_for_one, intensity => 3, period => 60},
|
|
||||||
ChildSpecs = [],
|
|
||||||
log_message(Port),
|
|
||||||
accept(Port),
|
|
||||||
{ok, {SupFlags, ChildSpecs}}.
|
|
||||||
|
|
||||||
accept(Port) ->
|
|
||||||
Options = [binary,
|
|
||||||
{packet, raw},
|
|
||||||
{active, false},
|
|
||||||
{reuseaddr, true},
|
|
||||||
{verify, verify_peer},
|
|
||||||
{depth, 1},
|
|
||||||
{cacertfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/ca.crt.pem"},
|
|
||||||
{certfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/apache.crt"},
|
|
||||||
{keyfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/private/apache.key"}],
|
|
||||||
|
|
||||||
{ok, ListenSocket} = ssl:listen(Port, Options),
|
|
||||||
spawn_monitor(fun () -> loop_acceptor(ListenSocket) end).
|
|
||||||
|
|
||||||
loop_acceptor(ListenSocket) ->
|
|
||||||
case ssl:transport_accept(ListenSocket) of
|
|
||||||
{error, closed} -> exit(shutdown);
|
|
||||||
{ok, Client} ->
|
|
||||||
Pid = spawn_link(?MODULE, serve, [Client]),
|
|
||||||
logger:info("PID: ~p", [Pid]),
|
|
||||||
ok = ssl:controlling_process(Client, Pid)
|
|
||||||
end,
|
|
||||||
loop_acceptor(ListenSocket).
|
|
||||||
|
|
||||||
serve(Client) ->
|
|
||||||
{ok, TSocket} = ssl:handshake(Client),
|
|
||||||
{ok, ClientCert} = ssl:peercert(TSocket),
|
|
||||||
logger:info("Client cert ~s", [ClientCert]),
|
|
||||||
timer:sleep(100000),
|
|
||||||
exit(shutdown).
|
|
||||||
|
|
||||||
log_message(Port) ->
|
|
||||||
logger:info("SSL Listening on Port: ~p", [Port]).
|
|
67
apps/epp_proxy/src/epp_tls_acceptor.erl
Normal file
67
apps/epp_proxy/src/epp_tls_acceptor.erl
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
-module(epp_tls_acceptor).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-define(SERVER, ?MODULE).
|
||||||
|
-define(POOL_SUPERVISOR, epp_pool_supervisor).
|
||||||
|
-define(WORKER, epp_tls_worker).
|
||||||
|
|
||||||
|
-define(CaCertFile,
|
||||||
|
case application:get_env(epp_proxy, epp_cacertfile_path) of
|
||||||
|
undefined -> undefined;
|
||||||
|
{ok, Value} -> Value
|
||||||
|
end).
|
||||||
|
|
||||||
|
-define(CertFile,
|
||||||
|
case application:get_env(epp_proxy, epp_certfile_path) of
|
||||||
|
undefined -> undefined;
|
||||||
|
{ok, Value} -> Value
|
||||||
|
end).
|
||||||
|
-define(KeyFile,
|
||||||
|
case application:get_env(epp_proxy, epp_keyfile_path) of
|
||||||
|
undefined -> undefined;
|
||||||
|
{ok, Value} -> Value
|
||||||
|
end).
|
||||||
|
|
||||||
|
%% gen_server callbacks
|
||||||
|
-export([init/1, handle_cast/2, handle_call/3, 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},
|
||||||
|
{verify, verify_peer},
|
||||||
|
{depth, 1},
|
||||||
|
{cacertfile, ?CaCertFile},
|
||||||
|
{certfile, ?CertFile},
|
||||||
|
{keyfile, ?KeyFile}],
|
||||||
|
|
||||||
|
{ok, ListenSocket} = ssl:listen(Port, Options),
|
||||||
|
gen_server:cast(self(), accept),
|
||||||
|
|
||||||
|
{ok, #state{socket=ListenSocket, port=Port, options=Options}}.
|
||||||
|
|
||||||
|
handle_cast(accept, 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),
|
||||||
|
gen_server:cast(NewOwner, serve),
|
||||||
|
gen_server:cast(NewOwner, greeting),
|
||||||
|
gen_server:cast(self(), accept),
|
||||||
|
{noreply, State#state{socket=ListenSocket, port=Port, 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]}},
|
||||||
|
supervisor:start_child(?POOL_SUPERVISOR, ChildSpec).
|
155
apps/epp_proxy/src/epp_tls_worker.erl
Normal file
155
apps/epp_proxy/src/epp_tls_worker.erl
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
-module(epp_tls_worker).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
|
%% gen_server callbacks
|
||||||
|
-export([init/1, handle_cast/2, handle_call/3, start_link/1]).
|
||||||
|
-export([code_change/3]).
|
||||||
|
|
||||||
|
-export([request/5]).
|
||||||
|
|
||||||
|
-record(state,{socket, length, session_id, common_name, client_cert}).
|
||||||
|
-record(request,{method, url, body, cookies, headers}).
|
||||||
|
|
||||||
|
init(Socket) ->
|
||||||
|
logger:info("Created a worker process"),
|
||||||
|
SessionId = session_id(),
|
||||||
|
{ok, #state{socket=Socket, session_id=SessionId}}.
|
||||||
|
|
||||||
|
start_link(Socket) ->
|
||||||
|
gen_server:start_link(?MODULE, Socket, []).
|
||||||
|
|
||||||
|
handle_cast(serve, State = #state{socket=Socket}) ->
|
||||||
|
{ok, SecureSocket} = ssl:handshake(Socket),
|
||||||
|
{ok, PeerCert} = ssl:peercert(SecureSocket),
|
||||||
|
{SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} =
|
||||||
|
epp_certs:headers_from_cert(PeerCert),
|
||||||
|
|
||||||
|
{noreply, State#state{socket=SecureSocket, common_name=SSL_CLIENT_S_DN_CN,
|
||||||
|
client_cert=SSL_CLIENT_CERT}};
|
||||||
|
handle_cast(greeting, State = #state{socket=Socket, common_name=SSL_CLIENT_S_DN_CN,
|
||||||
|
client_cert=SSL_CLIENT_CERT,
|
||||||
|
session_id=SessionId}) ->
|
||||||
|
Request = request("hello", SessionId, "", SSL_CLIENT_S_DN_CN,
|
||||||
|
SSL_CLIENT_CERT),
|
||||||
|
logger:info("Request: ~p~n", [Request]),
|
||||||
|
|
||||||
|
{_Status, _StatusCode, _Headers, ClientRef} =
|
||||||
|
hackney:request(Request#request.method, Request#request.url,
|
||||||
|
Request#request.headers, Request#request.body,
|
||||||
|
[{cookie, Request#request.cookies}, insecure]),
|
||||||
|
|
||||||
|
{ok, Body} = hackney:body(ClientRef),
|
||||||
|
|
||||||
|
frame_to_socket(Body, Socket),
|
||||||
|
gen_server:cast(self(), process_command),
|
||||||
|
{noreply, State#state{socket=Socket, session_id=SessionId}};
|
||||||
|
handle_cast(process_command, State = #state{socket=Socket,
|
||||||
|
common_name=SSL_CLIENT_S_DN_CN,
|
||||||
|
client_cert=SSL_CLIENT_CERT,
|
||||||
|
session_id=SessionId}) ->
|
||||||
|
Length = case read_length(Socket) of
|
||||||
|
{ok, Data} ->
|
||||||
|
Data;
|
||||||
|
{error, _Details} ->
|
||||||
|
{stop, normal, State}
|
||||||
|
end,
|
||||||
|
|
||||||
|
Frame = case read_frame(Socket, Length) of
|
||||||
|
{ok, FrameData} ->
|
||||||
|
io:format("~p~n", [FrameData]),
|
||||||
|
FrameData;
|
||||||
|
{error, _FrameDetails} ->
|
||||||
|
{stop, normal, State}
|
||||||
|
end,
|
||||||
|
|
||||||
|
{ok, XMLRecord} = epp_xml:parse(Frame),
|
||||||
|
Command = epp_xml:get_command(XMLRecord),
|
||||||
|
|
||||||
|
Request = request(Command, SessionId, Frame, SSL_CLIENT_S_DN_CN,
|
||||||
|
SSL_CLIENT_CERT),
|
||||||
|
logger:info("Request: ~p~n", [Request]),
|
||||||
|
|
||||||
|
{_Status, _StatusCode, _Headers, ClientRef} =
|
||||||
|
hackney:request(Request#request.method, Request#request.url,
|
||||||
|
Request#request.headers, Request#request.body,
|
||||||
|
[{cookie, Request#request.cookies}, insecure]),
|
||||||
|
|
||||||
|
{ok, Body} = hackney:body(ClientRef),
|
||||||
|
|
||||||
|
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};
|
||||||
|
true ->
|
||||||
|
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 function
|
||||||
|
write_line(Socket, Line) ->
|
||||||
|
ok = ssl:send(Socket, Line).
|
||||||
|
|
||||||
|
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} ->
|
||||||
|
io:format("Error: ~p~n", [Reason]),
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
read_frame(Socket, FrameLength) ->
|
||||||
|
case ssl:recv(Socket, FrameLength) of
|
||||||
|
{ok, Data} ->
|
||||||
|
io:format("Frame: ~p~n", [Data]),
|
||||||
|
{ok, Data};
|
||||||
|
{error, Reason} ->
|
||||||
|
io:format("Error: ~p~n", [Reason]),
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
session_id() ->
|
||||||
|
UniqueMap = epp_util:create_map(self()),
|
||||||
|
BinaryHash = epp_util:create_session_id(UniqueMap),
|
||||||
|
BinaryHash.
|
||||||
|
|
||||||
|
%% Map request and return values
|
||||||
|
request(Command, SessionId, RawFrame, CommonName, ClientCert) ->
|
||||||
|
URL = epp_router:route_request(Command),
|
||||||
|
RequestMethod = request_method(Command),
|
||||||
|
Cookie = hackney_cookie:setcookie("session", SessionId, []),
|
||||||
|
case Command of
|
||||||
|
"hello" ->
|
||||||
|
Body = "";
|
||||||
|
_ ->
|
||||||
|
Body = {multipart, [{<<"raw_frame">>, RawFrame}]}
|
||||||
|
end,
|
||||||
|
Headers = [{"SSL_CLIENT_CERT", ClientCert},
|
||||||
|
{"SSL_CLIENT_S_DN_CN", CommonName}],
|
||||||
|
#request{url=URL, method=RequestMethod, body=Body, cookies=[Cookie],
|
||||||
|
headers=Headers}.
|
||||||
|
|
||||||
|
%% request method: GET for greeting, POST for everything else.
|
||||||
|
request_method("hello") ->
|
||||||
|
get;
|
||||||
|
request_method(<<"hello">>) ->
|
||||||
|
get;
|
||||||
|
request_method(_) ->
|
||||||
|
post.
|
||||||
|
|
||||||
|
%% 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).
|
|
@ -1,6 +1,9 @@
|
||||||
-module(epp_util).
|
-module(epp_util).
|
||||||
|
|
||||||
-export([create_map/1, create_session_id/1]).
|
-export([create_map/1, create_session_id/1, frame_length/1,
|
||||||
|
frame_length_to_receive/1, frame_length_to_send/1]).
|
||||||
|
|
||||||
|
-define(OFFSET, 4).
|
||||||
|
|
||||||
% Give me a process id, I'll create a random map for you.
|
% Give me a process id, I'll create a random map for you.
|
||||||
-spec create_map(pid()) -> #{string() => pid(), string() => float(),
|
-spec create_map(pid()) -> #{string() => pid(), string() => float(),
|
||||||
|
@ -24,3 +27,16 @@ create_session_id(#{"pid" := Pid, "random" := Random, "timestamp" := Timestamp})
|
||||||
BinaryHash = crypto:hash(sha512, ListOfGlyphs),
|
BinaryHash = crypto:hash(sha512, ListOfGlyphs),
|
||||||
String = lists:flatten([integer_to_list(X,16) || <<X>> <= BinaryHash]),
|
String = lists:flatten([integer_to_list(X,16) || <<X>> <= BinaryHash]),
|
||||||
String.
|
String.
|
||||||
|
|
||||||
|
frame_length_to_receive(Size) when Size >= 0 ->
|
||||||
|
Size - ?OFFSET.
|
||||||
|
|
||||||
|
frame_length_to_send(Frame) ->
|
||||||
|
Length = frame_length(Frame),
|
||||||
|
Length + ?OFFSET.
|
||||||
|
|
||||||
|
frame_length(Frame) when is_binary(Frame) ->
|
||||||
|
byte_size(Frame);
|
||||||
|
frame_length(Frame) when is_list(Frame) ->
|
||||||
|
Bin = unicode:characters_to_binary(Frame),
|
||||||
|
byte_size(Bin).
|
||||||
|
|
|
@ -15,7 +15,7 @@ wder_certificate_test() ->
|
||||||
subject_from_otp_certificate_test() ->
|
subject_from_otp_certificate_test() ->
|
||||||
Certificate = test_certificate(),
|
Certificate = test_certificate(),
|
||||||
Subject = epp_certs:subject_from_otp_certificate(Certificate),
|
Subject = epp_certs:subject_from_otp_certificate(Certificate),
|
||||||
{rdnSequence, ListOfAttributes} = Subject.
|
{rdnSequence, _ListOfAttributes} = Subject.
|
||||||
|
|
||||||
common_name_from_subject_test() ->
|
common_name_from_subject_test() ->
|
||||||
Certificate = test_certificate(),
|
Certificate = test_certificate(),
|
||||||
|
|
|
@ -17,3 +17,17 @@ create_session_id_test() ->
|
||||||
Hash = epp_util:create_session_id(Map),
|
Hash = epp_util:create_session_id(Map),
|
||||||
?assert(is_list(Hash)),
|
?assert(is_list(Hash)),
|
||||||
?assertEqual("88F49C2B1BDD6F2355BF4424A67C928DA7C5616B30F7C5B35F17747348AF61EF8723ED6BE6012D879CB3D6A7EC7A187660A56910ED896AE67DE533C212D", Hash).
|
?assertEqual("88F49C2B1BDD6F2355BF4424A67C928DA7C5616B30F7C5B35F17747348AF61EF8723ED6BE6012D879CB3D6A7EC7A187660A56910ED896AE67DE533C212D", Hash).
|
||||||
|
|
||||||
|
frame_length_test() ->
|
||||||
|
?assertEqual(2, epp_util:frame_length("aa")),
|
||||||
|
?assertEqual(2, epp_util:frame_length(<<"aa">>)),
|
||||||
|
?assertEqual(2, epp_util:frame_length(<<"OÜ">>)).
|
||||||
|
|
||||||
|
frame_length_to_receive_test() ->
|
||||||
|
?assertEqual(2, epp_util:frame_length_to_receive(6)),
|
||||||
|
?assertEqual(0, epp_util:frame_length_to_receive(4)),
|
||||||
|
?assertError(function_clause, epp_util:frame_length_to_receive(-22)).
|
||||||
|
|
||||||
|
frame_length_to_send_test() ->
|
||||||
|
?assertEqual(18, epp_util:frame_length_to_send("<epp><command>")),
|
||||||
|
?assertEqual(4, epp_util:frame_length_to_send("")).
|
||||||
|
|
|
@ -2,5 +2,8 @@
|
||||||
{epp_proxy, [{tcp_port, 3333},
|
{epp_proxy, [{tcp_port, 3333},
|
||||||
{tls_port, 4444},
|
{tls_port, 4444},
|
||||||
{epp_session_url, "https://registry.test/epp/session/"},
|
{epp_session_url, "https://registry.test/epp/session/"},
|
||||||
{epp_command_url, "https://registry.test/epp/command/"}]}
|
{epp_command_url, "https://registry.test/epp/command/"},
|
||||||
|
{epp_cacertfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/ca.crt.pem"},
|
||||||
|
{epp_certfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/apache.crt"},
|
||||||
|
{epp_keyfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/private/apache.key"}]}
|
||||||
].
|
].
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue