Encodes and decodes milter protocol packets (Sendmail's libmilter,
protocol version 6) in both directions: commands from the MTA, and the
milter's responses. Sovite.Milter uses it as the MTA side; a milter
written in Elixir can use it the other way round.
A packet is a 32-bit length in network byte order, a command byte, and the command's data. The length counts the command byte and the data. Strings are NUL-terminated; numbers are in network byte order.
Decoding never creates atoms: command bytes are mapped by fixed clauses, and unknown bytes are errors.
Flags
Actions and protocol flags are bit masks on the wire, and lists of
atoms in the rest of Sovite.Milter. action_mask/1, actions/1,
protocol_mask/1 and protocol/1 convert between them.
Actions (SMFIF_*): :add_headers, :change_body, :add_recipients,
:delete_recipients, :change_headers, :quarantine, :change_sender,
:add_recipients_with_args, :set_macros.
Protocol flags (SMFIP_*): steps the milter does not want,
:no_connect, :no_helo, :no_mail, :no_rcpt, :no_body,
:no_headers, :no_end_of_headers, :no_unknown, :no_data; steps it
will not reply to, :no_connect_reply, :no_helo_reply,
:no_mail_reply, :no_rcpt_reply, :no_data_reply,
:no_unknown_reply, :no_header_reply, :no_end_of_headers_reply,
:no_body_reply; and :skip (the milter may answer body chunks with
:skip), :rejected_recipients (it wants to see rejected recipients
too), :header_leading_space (header values are sent and taken with
their leading space).
Summary
Types
An action the MTA allows and the milter asks for.
A command from the MTA. :mail and :rcpt carry the address in angle
brackets followed by the ESMTP arguments. A :macro packet names the
command byte its macros belong to. A :connect with family :unknown
has no port and address.
Why a packet could not be decoded.
The address family of a connecting client.
A protocol flag: a step the milter does not want or will not reply to.
A response from the milter, as on the wire: addresses keep their angle
brackets and ESMTP arguments are one string (or nil).
Functions
Returns the bit mask of a list of actions.
Returns the actions in a bit mask. Unknown bits are dropped.
Takes one packet from the start of buffer.
Decodes the command byte and data of a packet from the MTA.
Decodes the command byte and data of a packet from the milter.
Encodes a command from the MTA as a framed packet.
Encodes a response from the milter as a framed packet.
Frames a packet: the length, the command byte, and the data.
Returns the protocol flags in a bit mask. Unknown bits are dropped.
Returns the bit mask of a list of protocol flags.
Types
@type action() ::
:add_headers
| :change_body
| :add_recipients
| :delete_recipients
| :change_headers
| :quarantine
| :change_sender
| :add_recipients_with_args
| :set_macros
An action the MTA allows and the milter asks for.
@type command() :: {:optneg, version :: non_neg_integer(), actions :: non_neg_integer(), protocol :: non_neg_integer()} | {:macro, command :: byte(), [{String.t(), String.t()}]} | {:connect, hostname :: String.t(), family(), :inet.port_number() | nil, address :: String.t() | nil} | {:helo, String.t()} | {:mail, [String.t(), ...]} | {:rcpt, [String.t(), ...]} | :data | {:header, name :: String.t(), value :: String.t()} | :end_of_headers | {:body, binary()} | :end_of_message | :abort | :quit | :quit_new_connection | {:unknown, String.t()}
A command from the MTA. :mail and :rcpt carry the address in angle
brackets followed by the ESMTP arguments. A :macro packet names the
command byte its macros belong to. A :connect with family :unknown
has no port and address.
@type error() :: :empty_packet | {:packet_too_large, non_neg_integer()} | {:unknown_command, byte()} | {:malformed, byte()}
Why a packet could not be decoded.
@type family() :: :inet | :inet6 | :unix | :unknown
The address family of a connecting client.
@type protocol_flag() ::
:no_connect
| :no_helo
| :no_mail
| :no_rcpt
| :no_body
| :no_headers
| :no_end_of_headers
| :no_header_reply
| :no_unknown
| :no_data
| :skip
| :rejected_recipients
| :no_connect_reply
| :no_helo_reply
| :no_mail_reply
| :no_rcpt_reply
| :no_data_reply
| :no_unknown_reply
| :no_end_of_headers_reply
| :no_body_reply
| :header_leading_space
A protocol flag: a step the milter does not want or will not reply to.
@type response() :: {:optneg, version :: non_neg_integer(), actions :: non_neg_integer(), protocol :: non_neg_integer(), [{stage :: non_neg_integer(), String.t()}]} | :continue | :accept | :reject | :tempfail | :discard | :skip | :progress | :connection_failure | :shutdown | {:reply_code, String.t()} | {:add_recipient, String.t()} | {:add_recipient_with_args, String.t(), String.t() | nil} | {:delete_recipient, String.t()} | {:replace_body, binary()} | {:add_header, String.t(), String.t()} | {:insert_header, non_neg_integer(), String.t(), String.t()} | {:change_header, non_neg_integer(), String.t(), String.t()} | {:change_sender, String.t(), String.t() | nil} | {:quarantine, String.t()} | {:set_macros, stage :: non_neg_integer(), String.t()}
A response from the milter, as on the wire: addresses keep their angle
brackets and ESMTP arguments are one string (or nil).
:optneg carries the macro requests, as stage numbers and
space-separated macro names. :connection_failure is SMFIR_CONN_FAIL.
Functions
@spec action_mask([action()]) :: non_neg_integer()
Returns the bit mask of a list of actions.
iex> Sovite.Milter.Packet.action_mask([:add_headers, :change_headers])
0x11
@spec actions(non_neg_integer()) :: [action()]
Returns the actions in a bit mask. Unknown bits are dropped.
iex> Sovite.Milter.Packet.actions(0x11)
[:add_headers, :change_headers]
@spec decode(binary(), pos_integer()) :: {:ok, {byte(), binary()}, binary()} | :more | {:error, error()}
Takes one packet from the start of buffer.
{:ok, {command, data}, rest}- a whole packet and the bytes after it.:more- the packet is not complete yet.{:error, reason}- an empty packet, or one longer thanmax_sizebytes (command byte and data), checked as soon as the length is in.
Decodes the command byte and data of a packet from the MTA.
Decodes the command byte and data of a packet from the milter.
Encodes a command from the MTA as a framed packet.
Encodes a response from the milter as a framed packet.
Frames a packet: the length, the command byte, and the data.
iex> Sovite.Milter.Packet.frame(?c, "") |> IO.iodata_to_binary()
<<0, 0, 0, 1, ?c>>
@spec protocol(non_neg_integer()) :: [protocol_flag()]
Returns the protocol flags in a bit mask. Unknown bits are dropped.
@spec protocol_mask([protocol_flag()]) :: non_neg_integer()
Returns the bit mask of a list of protocol flags.