Packages

TLS client for Gleam on Erlang

Current section

Files

Jump to
kafein src kafein.erl
Raw

src/kafein.erl

-module(kafein).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/kafein.gleam").
-export([default_options/0, send_builder/2, send/2, shutdown/1, 'receive'/2, receive_exact/3, receive_next_packet_as_message/1, select_ssl_messages/2, unsafe_cast/1, wrap/2]).
-export_type([ssl_socket/0, tls_alert_kind/0, error/0, protocol_version/0, verification_type/0, certificate/0, wrap_options/0, ssl_message/0, connect_options/0, gen_tcp_option/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type ssl_socket() :: any().
-type tls_alert_kind() :: close_notify |
unexpected_message |
bad_record_mac |
record_overflow |
handshake_failure |
bad_certificate |
unsupported_certificate |
certificate_revoked |
certificate_expired |
certificate_unknown |
illegal_parameter |
unknown_ca |
access_denied |
decode_error |
decrypt_error |
export_restriction |
protocol_version |
insufficient_security |
internal_error |
inappropriate_fallback |
user_canceled |
no_renegotiation |
unsupported_extension |
certificate_unobtainable |
unrecognized_name |
bad_certificate_status_response |
bad_certificate_hash_value |
unknown_psk_identity |
no_application_protocol.
-type error() :: closed |
{options, gleam@dynamic:dynamic_()} |
{other, gleam@dynamic:dynamic_()} |
{tcp_error, mug:error()} |
{cipher_suite_not_recognized, binary()} |
{tls_alert, tls_alert_kind(), binary()}.
-type protocol_version() :: tlsv1 | tlsv1m1 | tlsv1m2 | tlsv1m3.
-type verification_type() :: verify_none | verify_peer.
-type certificate() :: {certificate,
binary(),
binary(),
gleam@option:option(fun(() -> binary()))}.
-type wrap_options() :: {wrap_options,
list(protocol_version()),
list(binary()),
gleam@option:option(binary()),
list(binary()),
integer(),
verification_type(),
list(certificate())}.
-type ssl_message() :: {packet, ssl_socket(), bitstring()} |
{socket_closed, ssl_socket()} |
{ssl_error, ssl_socket(), error()}.
-type connect_options() :: {versions, list(gleam@erlang@atom:atom_())} |
{cacerts, gleam@dynamic:dynamic_()} |
{depth, integer()} |
{verify, verification_type()} |
{ciphers, list(gleam@dynamic:dynamic_())} |
{alpn_advertised_protocols, list(binary())} |
{cacertfile, binary()} |
{certs_keys, list(gleam@dict:dict(binary(), gleam@dynamic:dynamic_()))}.
-type gen_tcp_option() :: {active, gleam@erlang@atom:atom_()}.
-file("src/kafein.gleam", 113).
?DOC(" Default options when wrapping\n").
-spec default_options() -> wrap_options().
default_options() ->
{wrap_options, [tlsv1m2, tlsv1m3], [], none, [], 100, verify_peer, []}.
-file("src/kafein.gleam", 182).
-spec send_builder(ssl_socket(), gleam@bytes_tree:bytes_tree()) -> {ok, nil} |
{error, mug:error()}.
send_builder(Socket, Data) ->
kafein_ffi:send(Socket, Data).
-file("src/kafein.gleam", 177).
-spec send(ssl_socket(), bitstring()) -> {ok, nil} | {error, mug:error()}.
send(Socket, Data) ->
kafein_ffi:send(Socket, gleam@bytes_tree:from_bit_array(Data)).
-file("src/kafein.gleam", 223).
-spec shutdown(ssl_socket()) -> {ok, nil} | {error, mug:error()}.
shutdown(Socket) ->
kafein_ffi:shutdown(Socket).
-file("src/kafein.gleam", 187).
-spec 'receive'(ssl_socket(), integer()) -> {ok, bitstring()} |
{error, mug:error()}.
'receive'(Socket, Timeout) ->
ssl:recv(Socket, 0, Timeout).
-file("src/kafein.gleam", 194).
-spec receive_exact(ssl_socket(), integer(), integer()) -> {ok, bitstring()} |
{error, mug:error()}.
receive_exact(Socket, Size, Timeout) ->
ssl:recv(Socket, Size, Timeout).
-file("src/kafein.gleam", 202).
-spec receive_next_packet_as_message(ssl_socket()) -> nil.
receive_next_packet_as_message(Socket) ->
ssl:setopts(
Socket,
[begin
_pipe = erlang:binary_to_atom(<<"once"/utf8>>),
{active, _pipe}
end]
),
nil.
-file("src/kafein.gleam", 250).
-spec optional(boolean(), fun(() -> HZJ)) -> list(HZJ).
optional(Cond, Value) ->
case Cond of
true ->
[Value()];
false ->
[]
end.
-file("src/kafein.gleam", 207).
-spec select_ssl_messages(
gleam@erlang@process:selector(HZB),
fun((ssl_message()) -> HZB)
) -> gleam@erlang@process:selector(HZB).
select_ssl_messages(Selector, Mapper) ->
Ssl = erlang:binary_to_atom(<<"ssl"/utf8>>),
Closed = erlang:binary_to_atom(<<"ssl_closed"/utf8>>),
Error = erlang:binary_to_atom(<<"ssl_error"/utf8>>),
Map_message = fun(Msg) -> Mapper(kafein_ffi:coerce_ssl_message(Msg)) end,
_pipe = Selector,
_pipe@1 = gleam@erlang@process:select_record(_pipe, Ssl, 2, Map_message),
_pipe@2 = gleam@erlang@process:select_record(
_pipe@1,
Closed,
1,
Map_message
),
gleam@erlang@process:select_record(_pipe@2, Error, 2, Map_message).
-file("src/kafein.gleam", 273).
-spec unsafe_cast(any()) -> any().
unsafe_cast(Value) ->
kafein_ffi:unsafe_cast(Value).
-file("src/kafein.gleam", 128).
?DOC(
" Upgrades a TCP connection to SSL connection.\n"
"\n"
" Returns an error if upgrading was failed.\n"
).
-spec wrap(mug:socket(), wrap_options()) -> {ok, ssl_socket()} |
{error, error()}.
wrap(Socket, Opts) ->
gleam@result:'try'(
kafein_ffi:strs_to_suites(erlang:element(5, Opts)),
fun(Ciphers) ->
Connect_options = lists:append(
[[{versions,
gleam@list:map(
erlang:element(2, Opts),
fun(Ver) -> erlang:binary_to_atom(case Ver of
tlsv1 ->
<<"tlsv1"/utf8>>;
tlsv1m1 ->
<<"tlsv1.1"/utf8>>;
tlsv1m2 ->
<<"tlsv1.2"/utf8>>;
tlsv1m3 ->
<<"tlsv1.3"/utf8>>
end) end
)},
{cacerts, public_key:cacerts_get()},
{depth, erlang:element(6, Opts)},
{verify, erlang:element(7, Opts)},
{certs_keys,
gleam@list:map(
erlang:element(8, Opts),
fun(Cert) -> _pipe = maps:new(),
_pipe@2 = gleam@dict:insert(
_pipe,
<<"certfile"/utf8>>,
begin
_pipe@1 = erlang:element(2, Cert),
gleam_stdlib:identity(_pipe@1)
end
),
_pipe@4 = gleam@dict:insert(
_pipe@2,
<<"keyfile"/utf8>>,
begin
_pipe@3 = erlang:element(3, Cert),
gleam_stdlib:identity(_pipe@3)
end
),
(fun(D) -> case erlang:element(4, Cert) of
{some, Func} ->
_pipe@5 = D,
gleam@dict:insert(
_pipe@5,
<<"password"/utf8>>,
begin
_pipe@6 = Func,
kafein_ffi:unsafe_cast(
_pipe@6
)
end
);
none ->
D
end end)(_pipe@4) end
)}],
optional(
gleam@option:is_some(erlang:element(4, Opts)),
fun() ->
Path@1 = case erlang:element(4, Opts) of
{some, Path} -> Path;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"kafein"/utf8>>,
function => <<"wrap"/utf8>>,
line => 165,
value => _assert_fail,
start => 4202,
'end' => 4241,
pattern_start => 4213,
pattern_end => 4223})
end,
{cacertfile, Path@1}
end
),
optional(
not gleam@list:is_empty(Ciphers),
fun() -> {ciphers, Ciphers} end
),
optional(
not gleam@list:is_empty(erlang:element(3, Opts)),
fun() ->
{alpn_advertised_protocols, erlang:element(3, Opts)}
end
)]
),
kafein_ffi:wrap(Socket, Connect_options)
end
).