-module(sendr_smtp_ffi). -export([get_hostname/0, get_host_by_name/1]). -export([tcp_close/1, tcp_connect/3, tcp_receive/2, tcp_send/2, ssl_close/1, ssl_connect/4, ssl_receive/2, ssl_send/2, upgrade/4]). -include_lib("kernel/include/inet.hrl"). get_hostname() -> {ok, Hostname} = inet:gethostname(), unicode:characters_to_binary(Hostname). get_host_by_name(Hostname) -> Host = unicode:characters_to_list(Hostname), case inet:gethostbyname(Host) of {ok, #hostent{h_name = FQDN}} -> {ok, unicode:characters_to_binary(FQDN)}; Error -> Error end. tcp_connect(Hostname, Port, Timeout) -> Host = unicode:characters_to_list(Hostname), Opts = [{mode, binary}, {active, false}, {packet, line}, {reuseaddr, true}], Resp = gen_tcp:connect(Host, Port, Opts, Timeout), normalise(Resp). tcp_close(Socket) -> gen_tcp:close(Socket), nil. tcp_receive(Socket, Timeout) -> Resp = gen_tcp:recv(Socket, 0, Timeout), normalise(Resp). tcp_send(Socket, Data) -> Resp = gen_tcp:send(Socket, Data), normalise(Resp). upgrade(Socket, Hostname, AllowInvalidCerts, Timeout) -> application:ensure_all_started(ssl), Host = unicode:characters_to_list(Hostname), TlsOptions = ssl_options(Host, AllowInvalidCerts), Resp = ssl:connect(Socket, TlsOptions, Timeout), normalise(Resp). ssl_connect(Hostname, Port, AllowInvalidCerts, Timeout) -> application:ensure_all_started(ssl), Host = unicode:characters_to_list(Hostname), TlsOptions = ssl_options(Host, AllowInvalidCerts), Resp = ssl:connect(Host, Port, TlsOptions, Timeout), normalise(Resp). ssl_options(Host, AllowInvalidCerts) -> Options = [{mode, binary}, {active, false}, {packet, line}], case AllowInvalidCerts of true -> [{verify, verify_none}, {server_name_indication, Host} | Options]; false -> [{verify, verify_peer}, {server_name_indication, Host}, {cacerts, public_key:cacerts_get()} | Options] end. ssl_close(Socket) -> ssl:close(Socket), nil. ssl_receive(Socket, Timeout) -> Resp = ssl:recv(Socket, 0, Timeout), normalise(Resp). ssl_send(Socket, Data) -> Resp = ssl:send(Socket, Data), normalise(Resp). normalise(ok) -> {ok, nil}; normalise({ok, Socket}) -> {ok, Socket}; normalise({ok, Socket, _Ext}) -> {ok, Socket}; normalise(closed) -> {error, closed}; normalise({error, closed} = E) -> E; normalise({error, timeout} = E) -> E; normalise({error, {timeout, _}}) -> {error, timeout}; % normalise({error, system_limit} = E) -> % E; normalise({error, ssl_not_started}) -> {error, ssl_not_started}; normalise({error, {tls_alert, {Alert, Description}}}) -> Desc = unicode:characters_to_binary(Description), {error, {tls_alert, Alert, Desc}}; normalise({error, Reason}) when is_atom(Reason) -> {error, {posix_error, Reason}}. % normalise({ok, {_Address, Port}}) -> % {ok, {port, Port}}; % normalise({error, Reason}) -> % Formatted = ssl:format_error(Reason), % Description = unicode:characters_to_binary(Formatted), % {error, {ssl_error, Description}}.