Packages

An alternative NIF-based implementation of Erlang ssl module.

Current section

Files

Jump to
etls src etls_nif.erl
Raw

src/etls_nif.erl

%%%--------------------------------------------------------------------
%%% @author Konrad Zemek
%%% @copyright (C) 2015 ACK CYFRONET AGH
%%% This software is released under the MIT license
%%% cited in 'LICENSE.md'.
%%% @end
%%%--------------------------------------------------------------------
%%% @private
%%% @doc
%%% Erlang wrapper for etls NIF module.
%%% @end
%%%--------------------------------------------------------------------
-module(etls_nif).
-author("Konrad Zemek").
-on_load(init/0).
%% API
-export([connect/12, send/2, recv/2, listen/10, accept/2, handshake/2,
peername/2, sockname/2, acceptor_sockname/2, close/2,
certificate_chain/1, shutdown/3]).
-type str() :: binary() | string().
-type socket() :: term().
-type acceptor() :: term().
-export_type([socket/0, acceptor/0]).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Creates a native TCP socket, connects to the given host and port
%% and performs an TLS handshake.
%% When finished, sends {Ref, {ok, Socket} | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec connect(Ref :: reference(), Host :: str(), Port :: inet:port_number(),
CertPath :: str(), KeyPath :: str(), VerifyType :: str(),
FailIfNoPeerCert :: boolean(), VerifyClientOnce :: boolean(),
RFC2818Hostname :: str(), CAs :: [binary()], CRLs :: [binary()],
Chain :: [binary()]) ->
ok | {error, Reason :: atom()}.
connect(_Ref, _Host, _Port, _CertPath, _KeyPath, _VerifyType, _FailIfNoPeerCert,
_VerifyClientOnce, _RFC2818Hostname, _CAs, _CRLs, _Chain) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Sends a message through the Socket.
%% When finished, sends ok | {error, Reason} to the calling
%% process.
%% @end
%%--------------------------------------------------------------------
-spec send(Socket :: socket(), Data :: iodata()) ->
ok | {error, Reason :: atom()}.
send(_Sock, _Data) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Receives a message from the Socket.
%% When Size is 0, waits for any data to arrive on the socket.
%% When finished, sends {ok, Data :: binary()} | {error, Reason} to
%% the calling process.
%% @end
%%--------------------------------------------------------------------
-spec recv(Socket :: socket(), Size :: non_neg_integer()) ->
ok | {error, Reason :: atom()}.
recv(_Sock, _Size) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Creates an acceptor socket that listens on the given port.
%% @end
%%--------------------------------------------------------------------
-spec listen(Port :: inet:port_number(), CertPath :: str(), KeyPath :: str(),
VerifyType :: str(), FailIfNoPeerCert :: boolean(),
VerifyClientOnce :: boolean(), RFC2818Hostname :: str(),
CAs :: [binary()], CRLs :: [binary()], Chain :: [binary()]) ->
{ok, Acceptor :: acceptor()} |
{error, Reason :: atom()}.
listen(_Port, _CertPath, _KeyPath, _VerifyType, _FailIfNoPeerCert,
_VerifyClientOnce, _RFC2818Hostname, _CAs, _CRLs, _Chain) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Accepts an incoming TCP connection on the acceptor.
%% When finished, sends {Ref, {ok, Socket} | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec accept(Ref :: reference(), Acceptor :: acceptor()) ->
ok | {error, Reason :: atom()}.
accept(_Ref, _Acceptor) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Performs a TLS handshake on the new TCP connection.
%% When finished, sends {Ref, ok | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec handshake(Ref :: reference(), Socket :: socket()) ->
ok | {error, Reason :: atom()}.
handshake(_Ref, _Sock) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Returns a tuple {RemoteHostname, RemotePort} describing the peer.
%% When finished, sends {Ref, {ok, Result} | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec peername(Ref :: reference(), Socket :: socket()) ->
ok | {error, Reason :: atom()}.
peername(_Ref, _Sock) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Returns a tuple {LocalHostname, LocalPort} describing the local
%% socket.
%% When finished, sends {Ref, {ok, Result} | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec sockname(Ref :: reference(), Socket :: socket()) ->
ok | {error, Reason :: atom()}.
sockname(_Ref, _Sock) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Returns a tuple {LocalHostname, LocalPort} describing the acceptor
%% socket.
%% When finished, sends {Ref, {ok, Result} | {error, Reason}} to the
%% calling process.
%% @end
%%--------------------------------------------------------------------
-spec acceptor_sockname(Ref :: reference(), Socket :: acceptor()) ->
ok | {error, Reason :: atom()}.
acceptor_sockname(_Ref, _Acceptor) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Closes the socket.
%% When finished, sends {Ref, ok | {error, Reason}} to the calling
%% process.
%% @end
%%--------------------------------------------------------------------
-spec close(Ref :: reference(), Socket :: socket()) ->
ok | {error, Reason :: atom()}.
close(_Ref, _Sock) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Returns a certificate chain of the peer.
%% This method can only be used after connect / handshake.
%% @end
%%--------------------------------------------------------------------
-spec certificate_chain(Socket :: socket()) ->
{ok, [binary()]} | {error, Reason :: atom()}.
certificate_chain(_Sock) ->
erlang:nif_error(etls_nif_not_loaded).
%%--------------------------------------------------------------------
%% @doc
%% Shuts down socket communciation in a chosen direction.
%% When finished, sends {Ref, ok | {error, Reason}} to the calling
%% process.
%% @end
%%--------------------------------------------------------------------
-spec shutdown(Ref :: reference(), Socket :: socket(),
Type :: read | write | read_write) ->
ok | {error, Reason :: atom()}.
shutdown(_Ref, _Sock, _Type) ->
erlang:nif_error(etls_nif_not_loaded).
%%%===================================================================
%%% Internal functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initialization function for the module.
%% Loads the NIF native library. The library is first searched for
%% in application priv dir, and then under ../priv and ./priv .
%% @end
%%--------------------------------------------------------------------
-spec init() -> ok | {error, Reason :: atom()}.
init() ->
LibName = "libetls",
LibPath =
case code:priv_dir(etls) of
{error, bad_name} ->
case filelib:is_dir(filename:join(["..", priv])) of
true ->
filename:join(["..", priv, LibName]);
_ ->
filename:join([priv, LibName])
end;
Dir ->
filename:join(Dir, LibName)
end,
erlang:load_nif(LibPath, 0).