diff --git a/apps/epp_proxy/include/epp_proxy.hrl b/apps/epp_proxy/include/epp_proxy.hrl
index 44dc9ea..f1b0b0b 100644
--- a/apps/epp_proxy/include/epp_proxy.hrl
+++ b/apps/epp_proxy/include/epp_proxy.hrl
@@ -9,3 +9,14 @@
}).
-type epp_request() :: #epp_request{}.
+
+
+-define(XMLErrorCode, <<"2001">>).
+
+-define(XMLErrorMessage, <<"Command syntax error.">>).
+
+-define(UnknownCommandErrorCode, <<"2000">>).
+
+-define(UnknownCommandErrorMessage, <<"Unknown command.">>).
+
+-define(DefaultTimeout, 120000).
diff --git a/apps/epp_proxy/src/epp_router.erl b/apps/epp_proxy/src/epp_router.erl
index d7a8da7..a6b9777 100644
--- a/apps/epp_proxy/src/epp_router.erl
+++ b/apps/epp_proxy/src/epp_router.erl
@@ -1,6 +1,7 @@
-module(epp_router).
--export([request_method/1, route_request/1]).
+-export([is_valid_command/1, request_method/1,
+ route_request/1]).
-define(validCommands,
["hello", "login", "logout", "check", "info", "poll",
@@ -9,14 +10,22 @@
%% 47 is the character code
-define(forwardSlash, 47).
-%% request method: GET for greeting, POST for everything else.
+%% request method: GET for greeting and error, POST for everything else.
request_method("hello") -> get;
request_method(<<"hello">>) -> get;
request_method("error") -> get;
request_method(<<"error">>) -> get;
request_method(_) -> post.
+is_valid_command(Command) when is_binary(Command) ->
+ CommandAsList = binary_to_list(Command),
+ lists:member(CommandAsList, ?validCommands);
+is_valid_command(Command) when is_list(Command) ->
+ lists:member(Command, ?validCommands);
+is_valid_command(_) -> false.
+
%% Base router
+route_request(undefined) -> url_map("error");
route_request(Command) when is_binary(Command) ->
List = binary_to_list(Command), url_map(List);
route_request(Command) when is_list(Command) ->
diff --git a/apps/epp_proxy/src/epp_tcp_worker.erl b/apps/epp_proxy/src/epp_tcp_worker.erl
index e04894e..5fb2579 100644
--- a/apps/epp_proxy/src/epp_tcp_worker.erl
+++ b/apps/epp_proxy/src/epp_tcp_worker.erl
@@ -18,12 +18,6 @@
-record(state, {socket, session_id, headers}).
--define(XMLErrorCode, <<"2001">>).
-
--define(XMLErrorMessage, <<"Command syntax error.">>).
-
--define(DefaultTimeout, 120000).
-
%% Initialize process
%% Assign an unique session id that will be passed on to http server as a cookie
init(Socket) ->
@@ -177,8 +171,15 @@ parse_frame(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};
+ case epp_router:is_valid_command(Command) of
+ true ->
+ #valid_frame{command = Command, cl_trid = ClTRID,
+ raw_frame = Frame};
+ false ->
+ #invalid_frame{code = ?UnknownCommandErrorCode,
+ message = ?UnknownCommandErrorMessage,
+ cl_trid = ClTRID}
+ end;
{error, _} ->
#invalid_frame{code = ?XMLErrorCode,
message = ?XMLErrorMessage, cl_trid = ClTRID}
diff --git a/apps/epp_proxy/src/epp_tls_worker.erl b/apps/epp_proxy/src/epp_tls_worker.erl
index ec80b74..bb3c84d 100644
--- a/apps/epp_proxy/src/epp_tls_worker.erl
+++ b/apps/epp_proxy/src/epp_tls_worker.erl
@@ -18,12 +18,6 @@
-record(state, {socket, session_id, headers}).
--define(XMLErrorCode, <<"2001">>).
-
--define(XMLErrorMessage, <<"Command syntax error.">>).
-
--define(DefaultTimeout, 120000).
-
%% Initialize process
%% Assign an unique session id that will be passed on to http server as a cookie
init(Socket) ->
@@ -197,8 +191,15 @@ parse_frame(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};
+ case epp_router:is_valid_command(Command) of
+ true ->
+ #valid_frame{command = Command, cl_trid = ClTRID,
+ raw_frame = Frame};
+ false ->
+ #invalid_frame{code = ?UnknownCommandErrorCode,
+ message = ?UnknownCommandErrorMessage,
+ cl_trid = ClTRID}
+ end;
{error, _} ->
#invalid_frame{code = ?XMLErrorCode,
message = ?XMLErrorMessage, cl_trid = ClTRID}
diff --git a/apps/epp_proxy/test/tcp_client_SUITE.erl b/apps/epp_proxy/test/tcp_client_SUITE.erl
index 68cee2c..6853448 100644
--- a/apps/epp_proxy/test/tcp_client_SUITE.erl
+++ b/apps/epp_proxy/test/tcp_client_SUITE.erl
@@ -11,6 +11,7 @@
valid_command_test_case/1,
long_message_test_case/1,
invalid_command_test_case/1,
+ missing_command_test_case/1,
error_test_case/1]).
all() ->
@@ -20,6 +21,7 @@ all() ->
valid_command_test_case,
long_message_test_case,
invalid_command_test_case,
+ missing_command_test_case,
error_test_case].
init_per_suite(Config) ->
@@ -133,8 +135,7 @@ long_message_test_case(Config) ->
"Command completed successfully; no messages"),
ok.
-%% Sending an invalid command frame should close the connection.
-%% It also crashes the process.
+%% Sending an invalid command frame should return a canned response.
invalid_command_test_case(Config) ->
Options = proplists:get_value(tcp_options, Config),
{ok, Socket} = gen_tcp:connect("localhost", 1180, Options, 2000),
@@ -150,7 +151,26 @@ invalid_command_test_case(Config) ->
"\n"
"\n">>,
ok = send_data(InvalidCommand, Socket),
- {error, closed} = receive_data(Socket),
+ ErrorResponse = receive_data(Socket),
+ match_data(ErrorResponse,
+ "Unknown command."),
+ ok.
+
+%% Sending a valid XML without command frame should return a canned response.
+missing_command_test_case(Config) ->
+ Options = proplists:get_value(tcp_options, Config),
+ {ok, Socket} = gen_tcp:connect("localhost", 1180, Options, 2000),
+ _Data = receive_data(Socket),
+ ok = send_data(login_command(), Socket),
+ _LoginResponse = receive_data(Socket),
+ InvalidCommand =
+ <<"\n"
+ "\n"
+ "\n">>,
+ ok = send_data(InvalidCommand, Socket),
+ ErrorResponse = receive_data(Socket),
+ match_data(ErrorResponse,
+ "Unknown command."),
ok.
error_test_case(Config) ->
diff --git a/apps/epp_proxy/test/tls_client_SUITE.erl b/apps/epp_proxy/test/tls_client_SUITE.erl
index ae86826..297fffe 100644
--- a/apps/epp_proxy/test/tls_client_SUITE.erl
+++ b/apps/epp_proxy/test/tls_client_SUITE.erl
@@ -11,6 +11,7 @@
valid_command_test_case/1,
long_message_test_case/1,
invalid_command_test_case/1,
+ missing_command_test_case/1,
error_test_case/1,
revoked_cert_test_case/1]).
@@ -21,6 +22,7 @@ all() ->
valid_command_test_case,
long_message_test_case,
invalid_command_test_case,
+ missing_command_test_case,
error_test_case,
revoked_cert_test_case].
@@ -142,8 +144,7 @@ long_message_test_case(Config) ->
"Command completed successfully; no messages"),
ok.
-%% Sending an invalid command frame should close the connection.
-%% It also crashes the process.
+%% Sending an invalid command frame returns a canned response.
invalid_command_test_case(Config) ->
Options = proplists:get_value(ssl_options, Config),
{ok, Socket} = ssl:connect("localhost", 1443, Options, 2000),
@@ -159,7 +160,26 @@ invalid_command_test_case(Config) ->
"\n"
"\n">>,
ok = send_data(InvalidCommand, Socket),
- {error, closed} = receive_data(Socket),
+ ErrorResponse = receive_data(Socket),
+ match_data(ErrorResponse,
+ "Unknown command."),
+ ok.
+
+%% Sending a missing command frame should return a canned response.
+missing_command_test_case(Config) ->
+ Options = proplists:get_value(ssl_options, Config),
+ {ok, Socket} = ssl:connect("localhost", 1443, Options, 2000),
+ _Data = receive_data(Socket),
+ ok = send_data(login_command(), Socket),
+ _LoginResponse = receive_data(Socket),
+ InvalidCommand =
+ <<"\n"
+ "\n"
+ "\n">>,
+ ok = send_data(InvalidCommand, Socket),
+ ErrorResponse = receive_data(Socket),
+ match_data(ErrorResponse,
+ "Unknown command."),
ok.
error_test_case(Config) ->