Current section
Files
Jump to
Current section
Files
src/rcon.erl
-module(rcon).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([execute/2, dial/5]).
-export_type([connection/0]).
-type connection() :: {connection, mug:socket(), integer()}.
-spec validate_auth_response(packet:packet(), integer()) -> {ok, nil} |
{error, errors:error()}.
validate_auth_response(Pkt, Auth_id) ->
case erlang:element(4, Pkt) of
2 ->
case erlang:element(3, Pkt) of
-1 ->
{error, auth_failed};
_ when erlang:element(3, Pkt) =:= Auth_id ->
{ok, nil};
Got ->
{error, {wrong_response_id, Auth_id, Got}}
end;
Got@1 ->
{error, {wrong_response_type, 2, Got@1}}
end.
-spec write(connection(), packet:packet_type(), integer(), binary()) -> {ok,
nil} |
{error, errors:error()}.
write(Conn, Packet_type, Packet_id, Cmd) ->
case packet:new(Packet_type, Packet_id, Cmd) of
{ok, Pkt} ->
Bytes = packet:to_bytes(Pkt),
case mug:send(erlang:element(2, Conn), Bytes) of
{ok, _} ->
{ok, nil};
{error, E} ->
{error, {socket_error, E}}
end;
{error, E@1} ->
{error, E@1}
end.
-spec read(connection()) -> {ok, packet:packet()} | {error, errors:error()}.
read(Conn) ->
case mug:'receive'(erlang:element(2, Conn), erlang:element(3, Conn)) of
{ok, Bytes} ->
packet:from_bytes(Bytes);
{error, E} ->
{error, {socket_error, E}}
end.
-spec execute(connection(), binary()) -> {ok, binary()} |
{error, errors:error()}.
execute(Conn, Cmd) ->
Cmd_len = gleam@string:length(Cmd),
case Cmd_len of
0 ->
{error, body_empty};
_ when Cmd_len > 4096 ->
{error, {body_too_large, Cmd_len}};
_ ->
case write(Conn, server_data_exec_command, 3, Cmd) of
{ok, _} ->
case read(Conn) of
{ok, Pkt} ->
case gleam@bit_array:to_string(
erlang:element(5, Pkt)
) of
{ok, Body} ->
{ok, Body};
{error, _} ->
{error,
{body_not_utf8, erlang:element(5, Pkt)}}
end;
{error, E} ->
{error, E}
end;
{error, E@1} ->
{error, E@1}
end
end.
-spec auth(connection(), binary()) -> {ok, nil} | {error, errors:error()}.
auth(Conn, Password) ->
case write(Conn, server_data_auth, 2, Password) of
{ok, _} ->
case read(Conn) of
{ok, Pkt} ->
case erlang:element(4, Pkt) of
0 ->
case read(Conn) of
{ok, P} ->
validate_auth_response(P, 2);
{error, E} ->
{error, E}
end;
_ ->
validate_auth_response(Pkt, 2)
end;
{error, E@1} ->
{error, E@1}
end;
{error, E@2} ->
{error, E@2}
end.
-spec dial(
binary(),
integer(),
binary(),
gleam@option:option(integer()),
gleam@option:option(integer())
) -> {ok, connection()} | {error, errors:error()}.
dial(Host, Port, Password, Connect_timeout_ms, Rw_timeout_ms) ->
Sock_result = begin
_pipe = mug:new(Host, Port),
_pipe@1 = mug:timeout(
_pipe,
gleam@option:unwrap(Connect_timeout_ms, 5000)
),
mug:connect(_pipe@1)
end,
case Sock_result of
{ok, Sock} ->
Conn = {connection, Sock, gleam@option:unwrap(Rw_timeout_ms, 5000)},
case auth(Conn, Password) of
{ok, _} ->
{ok, Conn};
{error, E} ->
{error, E}
end;
{error, E@1} ->
{error, {socket_error, E@1}}
end.