Packages

TLS/SSL OpenSSL-based NIF for Erlang

Current section

Files

Jump to
erltls src erltls_options.erl
Raw

src/erltls_options.erl

-module(erltls_options).
-include("erltls.hrl").
-export([
get_inet_names/1,
get_inet_options/1,
get_options/1,
emulated_for_socket/1,
default_inet_options/0,
emulated_list2record/1,
emulated_list2record/2,
emulated_record2list/1,
emulated_by_names/2,
use_session_ticket/1,
normalize_options/1
]).
get_inet_names(Opt) ->
get_inet_names(Opt, [], []).
get_inet_names([H|T], InetNames, EmulatedNames) ->
case is_emulated_option(H) of
true ->
get_inet_names(T, InetNames, [H|EmulatedNames]);
_ ->
get_inet_names(T, [H|InetNames], EmulatedNames)
end;
get_inet_names([], InetNames, EmulatedNames) ->
{ok, InetNames, EmulatedNames}.
get_inet_options(Opt) ->
try
get_inet_options(normalize_options(Opt), [], [])
catch
_:Error ->
Error
end.
get_inet_options([H|T], TcpOpt, EmulatedOpt) ->
OptionKey = get_option_key(H),
case is_emulated_option(OptionKey) of
true ->
{K, V} = H,
validate_emulated_option(K, V),
get_inet_options(T, TcpOpt, [H|EmulatedOpt]);
_ ->
get_inet_options(T, [H|TcpOpt], EmulatedOpt)
end;
get_inet_options([], TcpOpt, EmulatedOpt) ->
{ok, TcpOpt, EmulatedOpt}.
get_options(Options) ->
try
get_options(normalize_options(Options), [], [], [])
catch
_:Error ->
Error
end.
get_options([H|T], TcpOpt, TlsOpt, EmulatedOpt) ->
OptionKey = get_option_key(H),
case is_tls_option(OptionKey) of
true ->
% validation for the other tls options are in nif code
get_options(T, TcpOpt, [H|TlsOpt], EmulatedOpt);
_ ->
case is_emulated_option(OptionKey) of
false ->
get_options(T, [H|TcpOpt], TlsOpt, EmulatedOpt);
_ ->
{K, V} = H,
validate_emulated_option(K, V),
get_options(T, TcpOpt, TlsOpt, [H|EmulatedOpt])
end
end;
get_options([], TcpOpt, TlsOpt, EmulatedOpt) ->
{ok, TcpOpt, TlsOpt, EmulatedOpt}.
get_option_key({K, _V}) ->
K;
get_option_key(Key) when is_atom(Key) ->
Key;
get_option_key(El) when is_tuple(El) ->
element(1, El).
is_tls_option(Key) ->
lists:member(Key, [
%options available in both ssl and erltls
certfile, keyfile, password, cacertfile, dhfile, ciphers, verify, depth, fail_if_no_peer_cert,
%options available only in erltls
use_session_ticket, reuse_sessions_ttl, protocol,
% todo: implement the following options:
verify_fun, cert, key,
cacerts, dh, user_lookup_fun, psk_identity, srp_identity, ssl_imp,
hibernate_after, reuse_sessions, reuse_session, alpn_advertised_protocols,
alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols,
log_alert, server_name_indication, sni_hosts, sni_fun
]).
is_emulated_option(mode) ->
true;
is_emulated_option(header) ->
true;
is_emulated_option(packet) ->
true;
is_emulated_option(packet_size) ->
true;
is_emulated_option(_) ->
false.
validate_emulated_option(packet, Value) when not (is_atom(Value) orelse is_integer(Value)) ->
throw({error, {options, {packet,Value}}});
validate_emulated_option(packet_size, Value) when not is_integer(Value) ->
throw({error, {options, {packet_size,Value}}});
validate_emulated_option(header, Value) when not is_integer(Value) ->
throw({error, {options, {header,Value}}});
validate_emulated_option(_, _) ->
ok.
emulated_for_socket(TcpSocket) ->
case inet:getopts(TcpSocket, [packet, packet_size, header, mode]) of
{ok, Opt} ->
Opt;
Error ->
Error
end.
default_inet_options() -> [
{mode, binary},
{packet, 0},
{packet_size, 0},
{header, 0}
].
emulated_list2record(OptionsList) ->
emulated_list2record(OptionsList, #emulated_opts{}).
emulated_list2record([{K, V} | T], Rc) ->
case K of
packet ->
emulated_list2record(T, Rc#emulated_opts{packet = V});
packet_size ->
emulated_list2record(T, Rc#emulated_opts{packet_size = V});
header ->
emulated_list2record(T, Rc#emulated_opts{header = V});
mode ->
emulated_list2record(T, Rc#emulated_opts{mode = V})
end;
emulated_list2record([], Rc) ->
Rc.
emulated_record2list(#emulated_opts{packet = Packet, packet_size = PkSize, header = Header, mode = Mode}) -> [
{packet, Packet},
{packet_size, PkSize},
{header, Header},
{mode, Mode}
].
emulated_by_names(Names, Emul) ->
emulated_by_names(Names, Emul, []).
emulated_by_names([H|T], Emul, Acc) ->
case H of
packet ->
emulated_by_names(T, Emul, [{packet, Emul#emulated_opts.packet} | Acc]);
packet_size ->
emulated_by_names(T, Emul, [{packet_size, Emul#emulated_opts.packet_size} | Acc]);
header ->
emulated_by_names(T, Emul, [{header, Emul#emulated_opts.header} | Acc]);
mode ->
emulated_by_names(T, Emul, [{mode, Emul#emulated_opts.mode} | Acc])
end;
emulated_by_names([], _Emul, Acc) ->
Acc.
use_session_ticket({UseSessionKey, _Key}) when is_boolean(UseSessionKey) ->
UseSessionKey;
use_session_ticket(UseSessionKey) when is_boolean(UseSessionKey) ->
UseSessionKey;
use_session_ticket(_) ->
false.
normalize_options(Options) ->
try
proplists:expand([{binary, [{mode, binary}]}, {list, [{mode, list}]}], Options)
catch
_:_ ->
throw({error, {options, {not_a_proplist, Options}}})
end.