Current section
Files
Jump to
Current section
Files
src/neon@ssl.erl
-module(neon@ssl).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/neon/ssl.gleam").
-export([rsa_private_key/1, ec_private_key/1, new/2, from_tcp/2, verify_none/1, verify_peer/1, connect_cacerts/2, timeout/2, connect/1, send/2, 'receive'/3, active/1, passive/1, controlling_process/2, select/2, shutdown/1, close/1, start/0, stop/0, port/1, handshake_options/2, handshake_cacerts/2, handshake_timeout/2, listen/2, accept/2, handshake/2, handshake_from_tcp/2]).
-export_type([ssl/0, private_key/0, tls_alert/0, ssl_error/0, ssl_message/0, verify_value/0, verify/0, connect/0, connect_options/0, handshake_options/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() :: any().
-opaque private_key() :: {rsa_private_key, bitstring()} |
{ec_private_key, bitstring()}.
-type tls_alert() :: 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 ssl_error() :: closed |
timeout |
{posix, neon@net:posix()} |
{tls_alert, tls_alert(), binary()} |
{ssl_error, binary()} |
ssl_not_started |
invalid_pid.
-type ssl_message() :: {packet, ssl(), bitstring()} |
{socket_closed, ssl()} |
{socket_error, ssl(), ssl_error()}.
-type verify_value() :: verify_none | verify_peer.
-type verify() :: {verify, verify_value()}.
-type connect() :: {open, neon@net:address(), neon@net:port_()} |
{upgrade, neon@tcp:tcp(), neon@net:address()}.
-opaque connect_options() :: {connect_options,
connect(),
verify(),
gleam@option:option(list(bitstring())),
neon@net:timeout_()}.
-opaque handshake_options() :: {handshake_options,
bitstring(),
private_key(),
gleam@option:option(list(bitstring())),
neon@net:timeout_()}.
-file("src/neon/ssl.gleam", 20).
?DOC(" Creates an RSA private key from a DER-encoded binary.\n").
-spec rsa_private_key(bitstring()) -> private_key().
rsa_private_key(Key) ->
{rsa_private_key, Key}.
-file("src/neon/ssl.gleam", 25).
?DOC(" Creates an EC private key from a DER-encoded binary.\n").
-spec ec_private_key(bitstring()) -> private_key().
ec_private_key(Key) ->
{ec_private_key, Key}.
-file("src/neon/ssl.gleam", 122).
?DOC(
" Creates connection options for a fresh SSL/TLS connection to the given\n"
" address and port.\n"
"\n"
" Defaults to `verify_peer` and an infinite timeout.\n"
).
-spec new(neon@net:address(), neon@net:port_()) -> connect_options().
new(Address, Port) ->
Connect = {open, Address, Port},
{connect_options, Connect, {verify, verify_peer}, none, infinity}.
-file("src/neon/ssl.gleam", 139).
?DOC(
" Creates connection options to upgrade an existing TCP socket to SSL/TLS.\n"
"\n"
" The provided address can be a hostname or an IP Address. If the address\n"
" is a hostname, that hostname is used for Server Name Indication. If the\n"
" address is an IP Address, SNI is disabled. Defaults to `verify_peer`\n"
" and an infinite timeout.\n"
).
-spec from_tcp(neon@tcp:tcp(), neon@net:address()) -> connect_options().
from_tcp(Socket, Address) ->
Connect = {upgrade, Socket, Address},
{connect_options, Connect, {verify, verify_peer}, none, infinity}.
-file("src/neon/ssl.gleam", 154).
?DOC(
" Disables certificate verification.\n"
"\n"
" This is insecure and should only be used for testing or when connecting\n"
" to hosts with self-signed certificates.\n"
).
-spec verify_none(connect_options()) -> connect_options().
verify_none(Opts) ->
{connect_options,
erlang:element(2, Opts),
{verify, verify_none},
erlang:element(4, Opts),
erlang:element(5, Opts)}.
-file("src/neon/ssl.gleam", 161).
?DOC(
" Enables certificate verification against the system CA store.\n"
"\n"
" This is the default.\n"
).
-spec verify_peer(connect_options()) -> connect_options().
verify_peer(Opts) ->
{connect_options,
erlang:element(2, Opts),
{verify, verify_peer},
erlang:element(4, Opts),
erlang:element(5, Opts)}.
-file("src/neon/ssl.gleam", 169).
?DOC(
" Sets the CA certificates to use for peer verification.\n"
"\n"
" When set, these certificates are used instead of the system CA store.\n"
" Only has an effect when `verify_peer` is enabled.\n"
).
-spec connect_cacerts(connect_options(), list(bitstring())) -> connect_options().
connect_cacerts(Opts, Certs) ->
{connect_options,
erlang:element(2, Opts),
erlang:element(3, Opts),
{some, Certs},
erlang:element(5, Opts)}.
-file("src/neon/ssl.gleam", 177).
?DOC(" Sets the connection timeout.\n").
-spec timeout(connect_options(), neon@net:timeout_()) -> connect_options().
timeout(Opts, Timeout) ->
{connect_options,
erlang:element(2, Opts),
erlang:element(3, Opts),
erlang:element(4, Opts),
Timeout}.
-file("src/neon/ssl.gleam", 187).
?DOC(
" Establishes an SSL/TLS connection using the given options.\n"
"\n"
" This either opens a new connection or upgrades an existing TCP socket,\n"
" depending on whether `new` or `from_tcp` was used to create the options.\n"
"\n"
" `start` must be called before this function.\n"
).
-spec connect(connect_options()) -> {ok, ssl()} | {error, ssl_error()}.
connect(Opts) ->
case erlang:element(2, Opts) of
{open, Address, Port} ->
_pipe = Address,
ssl_ffi:connect(
_pipe,
Port,
erlang:element(3, Opts),
erlang:element(4, Opts),
erlang:element(5, Opts)
);
{upgrade, Socket, Address@1} ->
ssl_ffi:upgrade(
Socket,
Address@1,
erlang:element(3, Opts),
erlang:element(4, Opts),
erlang:element(5, Opts)
)
end.
-file("src/neon/ssl.gleam", 199).
?DOC(" Sends data over an SSL/TLS socket.\n").
-spec send(ssl(), bitstring()) -> {ok, nil} | {error, ssl_error()}.
send(Socket, Payload) ->
ssl_ffi:send(Socket, Payload).
-file("src/neon/ssl.gleam", 207).
?DOC(
" Receives data from an SSL/TLS socket.\n"
"\n"
" The `length` parameter specifies the number of bytes to receive. Use `0`\n"
" to receive whatever data is available. Must be non-negative.\n"
).
-spec 'receive'(ssl(), integer(), neon@net:timeout_()) -> {ok, bitstring()} |
{error, ssl_error()}.
'receive'(Socket, Length, Timeout) ->
case Length >= 0 of
true ->
ssl_ffi:recv(Socket, Length, Timeout);
false ->
{error, {ssl_error, <<"Length must be non-negative"/utf8>>}}
end.
-file("src/neon/ssl.gleam", 222).
?DOC(
" Sets the socket to active mode.\n"
"\n"
" In active mode, incoming data is delivered as messages to the socket\n"
" owner's mailbox. Use `select` to handle these messages.\n"
).
-spec active(ssl()) -> {ok, ssl()} | {error, ssl_error()}.
active(Socket) ->
ssl_ffi:active(Socket).
-file("src/neon/ssl.gleam", 229).
?DOC(
" Sets the socket to passive mode.\n"
"\n"
" In passive mode, data must be read explicitly using `receive`.\n"
).
-spec passive(ssl()) -> {ok, ssl()} | {error, ssl_error()}.
passive(Socket) ->
ssl_ffi:passive(Socket).
-file("src/neon/ssl.gleam", 237).
?DOC(
" Change the controlling process of a socket.\n"
"\n"
" The controlling process is the process that the socket sends messages to.\n"
" Note that if the provided `Pid` is invalid, this function will no-op and return `Ok(Nil)`.\n"
).
-spec controlling_process(ssl(), gleam@erlang@process:pid_()) -> {ok, nil} |
{error, ssl_error()}.
controlling_process(Socket, Pid) ->
ssl_ffi:controlling_process(Socket, Pid).
-file("src/neon/ssl.gleam", 249).
?DOC(
" Adds SSL message handlers to a selector for use with active mode sockets.\n"
"\n"
" In active mode, incoming data, close notifications, and errors are\n"
" delivered as messages to the socket owner's mailbox. Use this function\n"
" to register handlers for these messages on a `Selector`.\n"
).
-spec select(gleam@erlang@process:selector(DZG), fun((ssl_message()) -> DZG)) -> gleam@erlang@process:selector(DZG).
select(Selector, Mapper) ->
Map = fun(Msg) -> Mapper(ssl_ffi:handle_ssl_message(Msg)) end,
_pipe = Selector,
_pipe@1 = gleam@erlang@process:select_record(
_pipe,
erlang:binary_to_atom(<<"ssl"/utf8>>),
2,
Map
),
_pipe@2 = gleam@erlang@process:select_record(
_pipe@1,
erlang:binary_to_atom(<<"ssl_closed"/utf8>>),
1,
Map
),
gleam@erlang@process:select_record(
_pipe@2,
erlang:binary_to_atom(<<"ssl_error"/utf8>>),
2,
Map
).
-file("src/neon/ssl.gleam", 266).
?DOC(" Shuts down the SSL/TLS connection for both reading and writing.\n").
-spec shutdown(ssl()) -> {ok, nil} | {error, ssl_error()}.
shutdown(Socket) ->
ssl_ffi:shutdown(Socket).
-file("src/neon/ssl.gleam", 271).
?DOC(" Closes an SSL/TLS socket.\n").
-spec close(ssl()) -> {ok, nil} | {error, ssl_error()}.
close(Socket) ->
ssl_ffi:close(Socket).
-file("src/neon/ssl.gleam", 280).
?DOC(
" Starts the SSL application and its dependencies.\n"
"\n"
" Must be called before any SSL/TLS operations. This function is\n"
" idempotent and can safely be called multiple times.\n"
).
-spec start() -> {ok, nil} | {error, ssl_error()}.
start() ->
ssl_ffi:start().
-file("src/neon/ssl.gleam", 287).
?DOC(
" Stops the SSL application.\n"
"\n"
" After calling this, SSL/TLS operations will fail with `SslNotStarted`\n"
" until `start` is called again.\n"
).
-spec stop() -> nil.
stop() ->
ssl_ffi:stop().
-file("src/neon/ssl.gleam", 290).
?DOC(" Returns the port number assigned to an SSL socket by the operating system.\n").
-spec port(ssl()) -> {ok, neon@net:port_()} | {error, ssl_error()}.
port(Socket) ->
ssl_ffi:port(Socket).
-file("src/neon/ssl.gleam", 311).
?DOC(
" Creates handshake options with the given certificate and private key.\n"
"\n"
" The certificate should be a DER-encoded binary. Defaults to no CA\n"
" certificates and an infinite timeout.\n"
).
-spec handshake_options(bitstring(), private_key()) -> handshake_options().
handshake_options(Cert, Key) ->
{handshake_options, Cert, Key, none, infinity}.
-file("src/neon/ssl.gleam", 316).
?DOC(" Sets the CA certificates for client certificate verification.\n").
-spec handshake_cacerts(handshake_options(), list(bitstring())) -> handshake_options().
handshake_cacerts(Opts, Certs) ->
{handshake_options,
erlang:element(2, Opts),
erlang:element(3, Opts),
{some, Certs},
erlang:element(5, Opts)}.
-file("src/neon/ssl.gleam", 324).
?DOC(" Sets the handshake timeout.\n").
-spec handshake_timeout(handshake_options(), neon@net:timeout_()) -> handshake_options().
handshake_timeout(Opts, Timeout) ->
{handshake_options,
erlang:element(2, Opts),
erlang:element(3, Opts),
erlang:element(4, Opts),
Timeout}.
-file("src/neon/ssl.gleam", 332).
?DOC(" Creates an SSL listen socket bound to the given port and IP address.\n").
-spec listen(neon@net:port_(), neon@net:ip_address()) -> {ok, ssl()} |
{error, ssl_error()}.
listen(Port, Ip_address) ->
ssl_ffi:listen(Port, Ip_address).
-file("src/neon/ssl.gleam", 343).
?DOC(
" Accepts an incoming connection on an SSL listen socket.\n"
"\n"
" Returns a transport socket that has not yet completed the TLS\n"
" handshake. Call `handshake` to complete the TLS negotiation.\n"
).
-spec accept(ssl(), neon@net:timeout_()) -> {ok, ssl()} | {error, ssl_error()}.
accept(Socket, Timeout) ->
ssl_ffi:transport_accept(Socket, Timeout).
-file("src/neon/ssl.gleam", 351).
?DOC(
" Performs the server-side TLS handshake on a transport socket\n"
" returned by `accept`.\n"
"\n"
" `start` must be called before this function.\n"
).
-spec handshake(ssl(), handshake_options()) -> {ok, ssl()} |
{error, ssl_error()}.
handshake(Socket, Opts) ->
ssl_ffi:handshake(
Socket,
erlang:element(2, Opts),
erlang:element(3, Opts),
erlang:element(4, Opts),
erlang:element(5, Opts)
).
-file("src/neon/ssl.gleam", 361).
?DOC(
" Performs a server-side TLS handshake on a raw TCP socket.\n"
"\n"
" This is the server-side counterpart to `from_tcp` and is used for\n"
" START-TLS upgrades where an existing TCP connection is promoted to TLS.\n"
"\n"
" `start` must be called before this function.\n"
).
-spec handshake_from_tcp(neon@tcp:tcp(), handshake_options()) -> {ok, ssl()} |
{error, ssl_error()}.
handshake_from_tcp(Socket, Opts) ->
ssl_ffi:handshake(
Socket,
erlang:element(2, Opts),
erlang:element(3, Opts),
erlang:element(4, Opts),
erlang:element(5, Opts)
).