Current section
Files
Jump to
Current section
Files
src/support/mc_utils.erl
%%%-------------------------------------------------------------------
%%% @author tihon
%%% @copyright (C) 2014, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 30. Дек. 2014 22:54
%%%-------------------------------------------------------------------
-module(mc_utils).
-author("tihon").
-include_lib("kernel/include/inet.hrl").
-define(OLD_CRYPTO_API, true).
-ifdef(OTP_RELEASE).
-if(?OTP_RELEASE >= 23).
-undef(OLD_CRYPTO_API).
-endif.
-endif.
%% API
-export([
get_value/2,
get_value/3,
value_to_binary/1,
pw_key/3,
pw_hash/2,
get_timeout/0,
encode_name/1,
random_nonce/1,
hmac/2,
is_proplist/1,
to_binary/1,
get_srv_seeds/1]).
get_value(Key, List) -> get_value(Key, List, undefined).
get_value(Key, List, Default) ->
case lists:keyfind(Key, 1, List) of
{_, Value} -> Value;
false -> Default
end.
-spec is_proplist(list() | any()) -> boolean().
is_proplist(List) ->
Check = fun({X, _}) when is_atom(X) -> true;(_) -> false end,
lists:all(Check, List).
-spec encode_name(binary()) -> binary().
encode_name(Name) ->
Comma = re:replace(Name, <<"=">>, <<"=3D">>, [{return, binary}]),
re:replace(Comma, <<",">>, <<"=2C">>, [{return, binary}]).
-spec random_nonce(integer()) -> binary().
random_nonce(TextLength) ->
ByteLength = trunc(TextLength / 4 * 3),
RandBytes = crypto:strong_rand_bytes(ByteLength),
base64:encode(RandBytes).
value_to_binary(Value) when is_integer(Value) ->
bson:utf8(integer_to_list(Value));
value_to_binary(Value) when is_atom(Value) ->
atom_to_binary(Value, utf8);
value_to_binary(Value) when is_binary(Value) ->
Value;
value_to_binary(_Value) ->
<<>>.
get_timeout() ->
case application:get_env(mc_worker_call_timeout) of
{ok, Time} -> Time;
undefined -> infinity
end.
-ifdef(OLD_CRYPTO_API).
hmac(One, Two) -> crypto:hmac(sha, One, Two).
-else.
hmac(One, Two) -> crypto:mac(hmac, sha, One, Two).
-endif.
pw_key(Nonce, Username, Password) ->
bson:utf8(binary_to_hexstr(crypto:hash(md5, [Nonce, Username, pw_hash(Username, Password)]))).
pw_hash(Username, Password) ->
bson:utf8(binary_to_hexstr(crypto:hash(md5, [Username, <<":mongo:">>, Password]))).
-spec to_binary(string() | binary()) -> binary().
to_binary(Str) when is_list(Str) -> list_to_binary(Str);
to_binary(Str) when is_binary(Str) -> Str.
%% @private
binary_to_hexstr(Bin) ->
lists:flatten([io_lib:format("~2.16.0b", [X]) || X <- binary_to_list(Bin)]).
%% Returns a list of {Host, port} seeds for a given host. Performs
%% a DNS lookup of the SRV as described in the MongoDB reference manual
-spec get_srv_seeds(string() | binary()) -> {ok, [{Host :: string(), Port :: inet:port_number()}]} |
{error, srv_lookup_failed | srv_insecure_endpoints}.
get_srv_seeds(Host) when is_binary(Host) ->
get_srv_seeds(unicode:characters_to_list(Host));
get_srv_seeds(Host) ->
Srv = "_mongodb._tcp." ++ Host,
case inet_res:getbyname(Srv, srv) of
{ok, #hostent{h_addr_list = Endpoints}} ->
Seeds = [{H, P} || {_, _, P, H} <- Endpoints],
case lists:all(fun ({H, _P}) ->
valid_endpoint(Host, H)
end,
Seeds)
of
true ->
{ok, Seeds};
false ->
{error, srv_insecure_endpoints}
end;
{error, _Reason} ->
{error, srv_lookup_failed}
end.
valid_endpoint(Host, Srv) ->
[_ | HostBaseDomain] = string:split(Host, "."),
[_ | SrvBaseDomain] = string:split(Srv, "."),
HostBaseDomain == SrvBaseDomain.