Asks a policy server what to do, over TCP, a Unix socket, or the standard input and output of a program.
{:ok, conn} = Client.connect({:inet, "127.0.0.1", 10023})
{:ok, "DEFER_IF_PERMIT Greylisted", conn} =
Client.request(conn, protocol_state: "RCPT", client_address: {192, 0, 2, 7}, ...)
:ok = Client.close(conn)check/3 connects, asks once, and closes. A connection can carry any
number of requests, one after the other: keep it to save the connection
setup, as Postfix does. Only the process that opened a connection may
use it.
The reply's action is returned as text: parse it with
Sovite.Policy.parse_action/1. Other attributes of the reply are
ignored. Requests are encoded with Sovite.Policy.encode_request/1.
Addresses
{:inet, host, port}- TCP.hostis a name, an IP address string, or an IP address tuple.{:unix, path}- a Unix socket.{:command, [program | args]}- a program, started for each connection with the protocol on its standard input and output, as Postfix's spawn(8) runs policyd-spf. It runs directly, never through a shell, with the environment of the VM; its standard error is not read.programis an absolute path or found inPATH. Closing the connection closes its standard input, which ends such programs.
parse_address/1 reads them from Postfix-style strings.
Errors
A failed request closes the connection; connect again for the next request. Reasons:
:closed- the server closed the connection or the program exited, maybe because it was idle for too long, or the connection was closed already. Reconnecting usually works.:timeout- no complete reply in time.:response_too_large- the reply is over:max_responsebytes.:invalid_response- the reply is malformed or has noaction.
connect/2 returns :timeout or the socket error, such as
:econnrefused or :enoent, and :enoent or :eacces when the
program cannot be run.
Options
For connect/2 and check/3; request/3 takes :timeout and
:max_response, overriding those given to connect/2.
:timeout- milliseconds for each request, to send it and get the whole reply. Defaults to 100 seconds, like Postfix'ssmtpd_policy_service_timeout.:connect_timeout- milliseconds. Defaults to 10 seconds.:max_response- bytes of a reply. Defaults to 4096.
Telemetry
[:sovite, :policy, :client, :request, :stop]-%{duration},%{address, action}, for each reply.actionis the text.[:sovite, :policy, :client, :error]-%{duration},%{address, reason}, when connecting or a request fails.reasonis an error above.
Summary
Functions
Closes the connection. Closing it again does nothing.
Connects to a policy server. See the module docs for options.
Parses an address string
Sends a request and waits for the reply's action. See
Sovite.Policy.encode_request/1 for attrs, and the module docs for
errors and options. After an error the connection is closed.
Types
@type address() :: {:inet, host :: String.t() | :inet.ip_address(), :inet.port_number()} | {:unix, Path.t()} | {:command, [String.t(), ...]}
Where the policy server is, see the module docs.
@type error() :: :closed | :timeout | :response_too_large | :invalid_response
Why a request failed, see the module docs.
@opaque t()
Functions
@spec check(address(), Sovite.Policy.attributes(), keyword()) :: {:ok, action :: String.t()} | {:error, term()}
Connects, sends one request, and closes. See connect/2 and
request/3.
{:ok, action} = Client.check({:unix, "/run/postgrey.sock"}, attrs)
@spec close(t()) :: :ok
Closes the connection. Closing it again does nothing.
Connects to a policy server. See the module docs for options.
Parses an address string:
inet:host:port, orinet:[ipv6]:portfor an IPv6 addressunix:pathspawn:program args...- the program and its arguments, split on whitespace (not a Postfix syntax)
Examples
iex> Sovite.Policy.Client.parse_address("inet:127.0.0.1:10023")
{:ok, {:inet, "127.0.0.1", 10023}}
iex> Sovite.Policy.Client.parse_address("inet:[::1]:9998")
{:ok, {:inet, "::1", 9998}}
iex> Sovite.Policy.Client.parse_address("spawn:/usr/bin/policyd-spf /etc/spf.conf")
{:ok, {:command, ["/usr/bin/policyd-spf", "/etc/spf.conf"]}}
@spec request(t(), Sovite.Policy.attributes(), keyword()) :: {:ok, action :: String.t(), t()} | {:error, error()}
Sends a request and waits for the reply's action. See
Sovite.Policy.encode_request/1 for attrs, and the module docs for
errors and options. After an error the connection is closed.