-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) ).