% $Id: pmilter.api.tex,v 1.15 2005/08/30 17:42:58 ca Exp $
%% this section should also be in README.
%% how can we do this?
%% put this into an include file and write a filter
%% that generates a format suitable for README?
%% or should we use \ifelse? that may cause problems with latex2html?
\section{Native Policy Milter API}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI}

Note: this API may evolve over time.

Naming conventions:
A {\it policy milter} (also called {\it pmilter})
is a program that uses the API provided by {\it libpmilter}.
The latter interacts with the SMTP servers via
an internal protocol, i.e., this protocol can be changed
without changing the visible API and should not directly be accessed
by a user application.

\subsection{Data Structures}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:DataStructures}

libpmilter itself uses three context structures all of which
must be treated by a milter as opaque.

\begin{enumerate}
\item
{\tt pmg\_ctx}: ``global'' libpmilter context
(only one per process).
\item
{\tt pmss\_ctx}: libpmilter context per SMTP server
that connects to this instance.
\item
{\tt pmse\_ctx}: libpmilter context per SMTP session.
\end{enumerate}

Any of the libpmilter functions takes one of these contexts as parameter;
e.g., all SMTP session oriented functions have a parameter of type
{\tt pmse\_ctx\_P}.

A milter can have its own contexts for each of these three environments,
see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:pmilterContexts}.

\subsection{Start and Stop}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:StartandStop}

The functions in this section return {\tt SM\_SUCCESS} (0) on success
and a negative value in case of an error.

First libpmilter must be initialized;
a pmilter must specify a variable
{\tt pmg\_ctx\_P pmg\_ctx;}
which is passed per reference to the initialization function:

{\tt sm\_ret\_T sm\_pmfi\_init(pmg\_ctx\_P *pmg\_ctx)}

The pmilter global context must be treated as opaque data structure,
it is passed to subsequent libpmilter function calls.

Next pmilter starts libpmilter by handing control over to the library;
the pmilter passes a description of its requirements and functionality:

{\tt sm\_ret\_T sm\_pmfi\_start(pmg\_ctx\_P pmg\_ctx, pmilter\_P pmilter)}

A milter can stop by calling:

{\tt sm\_ret\_T sm\_pmfi\_stop(pmg\_ctx\_P pmg\_ctx)}

There are various functions to set some options which can be called
after libpmilter is initialized but before it is started.
To set the path of the Unix domain socket over which the SMTP servers
(see Section \ref{ConfigurationforSMTPServer},
item \ref{ConfigurationforSMTPServer:pmilter})
and libpmilter communicate:

{\tt sm\_ret\_T sm\_pmfi\_setconn(pmg\_ctx\_P pmg\_ctx, const char *path)}

The backlog parameter of the {\tt listen(2)} function can be set:

{\tt sm\_ret\_T sm\_pmfi\_setbacklog(pmg\_ctx\_P pmg\_ctx, int backlog)}

The debug level of libpmilter might be set via
(this requires knowledge of the internals of the library
which can be acquired by looking at the source code):

{\tt sm\_ret\_T sm\_pmfi\_setdbg(pmg\_ctx\_P pmg\_ctx, int debuglevel)}

To set the communication timeout:

{\tt sm\_ret\_T sm\_pmfi\_settimeout(pmg\_ctx\_P pmg\_ctx, int timeout)}

\subsection{New SMTP Server}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:NewSMTPServer}

Whenever an SMTP server connects to a milter an option negotiation
is performed (similar to ESMTP itself).
% For details see Section \ref{func:Milter:SMTPSMilterProtocol:OptionNegotiation}.
A pmilter can 
check whether server capabilities are acceptable and return
the options that it wants:

{\tt sm\_ret\_T pmfi\_negotiate(pmss\_ctx\_P pmss\_ctx, uint32\_t srv\_cap, uint32\_t srv\_fct, uint32\_t srv\_feat, uint32\_t srv\_misc, uint32\_t *pm\_cap, uint32\_t *pm\_fct, uint32\_t *pm\_feat, uint32\_t *pm\_misc)}

Currently only the capabilities field is used:
{\tt srv\_cap} is set by the SMTP server to a list (implemented as bit field)
of phases of the ESMTP dialogue that can be passed to a pmilter.
In turn the pmilter must set
{\tt *pm\_cap}
to includes those
phases of the ESMTP dialogue that it wants to receive.
For details, see {\tt include/sm/pmilter.h}.
For each of those phases a callback is invoked (see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction})
which must be set by the pmilter in its description structure
{\tt struct pmilter\_S} (see {\tt include/sm/pmfapi.h}).

\subsection{SMTP Session and Transaction}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction}

The protocol steps from ESMTP are forwarded to
the policy milter which can decide to accept or reject them.

\begin{itemize}
\item
New SMTP session:

{\tt sfsistat\_T pmfi\_connect(pmse\_ctx\_P pmse\_ctx, const char *hostname, sm\_sock\_addr\_T *hostaddr)}

{\tt hostname}: host name, as determined by a reverse lookup
on the host IP address;
{\tt hostaddr}: host address, as determined by a {\tt getpeername(2)}
call on the SMTP socket.


\item
SMTP {\tt HELO}/{\tt EHLO} command:

{\tt sfsistat\_T pmfi\_helo(pmse\_ctx\_P pmse\_ctx, const char *helohost)}

{\tt helohost}: Value passed to HELO/EHLO command, which should be
the domain name of the sending host.
%(but is, in practice, anything the sending host wants to send).

\item
{\tt MAIL} (envelope sender):

{\tt sfsistat\_T pmfi\_mail(pmse\_ctx\_P pmse\_ctx, const char *mail, char **argv)}

{\tt mail}: envelope mail address;
{\tt argv}: null-terminated MAIL command arguments.

\item
{\tt RCPT} (envelope recipient):

{\tt sfsistat\_T pmfi\_rcpt(pmse\_ctx\_P pmse\_ctx, const char *rcpt, char **argv)}

{\tt rcpt}: envelope recipient address;
{\tt argv}: null-terminated RCPT command arguments.

\item
{\tt DATA}:

{\tt sfsistat\_T pmfi\_data(pmse\_ctx\_P pmse\_ctx)}

\item
unknown/not implemented SMTP command:

{\tt sfsistat\_T pmfi\_unknown(pmse\_ctx\_P pmse\_ctx, const char *cmd)}

{\tt cmd}: SMTP command.

\item
For each chunk of a message:

{\tt sfsistat\_T pmfi\_msg(pmse\_ctx\_P pmse\_ctx, unsigned char *msgp, size\_t msglen)}

{\tt msgp}: pointer to message data;
{\tt msglen}: length of message data.
There may be multiple message chunks passed to the filter.
End-of-lines are represented as received from SMTP
(normally Carriage-Return/Line-Feed; CRLF).
Notes:
\begin{itemize}
\item
the last message chunk contains the final dot of the SMTP transmission,
i.e., ``CRLF.CRLF''
\item
the message is {\it streamed} while being received.
That is, the mail is not first stored on disk and then sent to the filter,
but each part received from the client is sent directly to the filter
(at the same speed as received from the network which might be slow).
This may mean that the filter does not receive the entire message as
the transmission may get interrupted
or the SMTP server may decide to skip the rest of the message because
it exceeds the maximum size.
\item
the return code is ignored unless
{\tt SM\_SCAP\_PM\_MSG\_RC}
is set, see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities},
item
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMMSGRC}.
\end{itemize}

\item
End of message (final dot of message has been received):

{\tt sfsistat\_T pmfi\_eom(pmse\_ctx\_P pmse\_ctx)}

\item
Message is aborted outside of the control of the filter,
for example, if the SMTP client issues an RSET command.

{\tt sm\_ret\_T pmfi\_abort(pmse\_ctx\_P pmse\_ctx)}

If {\tt pmfi\_abort} is called, {\tt pmfi\_eom} will not be called
and vice versa.

\item
{\tt QUIT} (end of an SMTP session):

{\tt sm\_ret\_T pmfi\_close(pmse\_ctx\_P pmse\_ctx)}

This is called when an SMTP session ends.

\end{itemize}

\subsection{Set and Get pmilter Contexts}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:pmilterContexts}

As explained in Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:DataStructures}
a milter can have a ``global'' context {\tt pmilter\_g\_ctx},
a context per SMTP server {\tt pmilter\_ss\_ctx},
and a context per SMTP session {\tt pmilter\_se\_ctx}.
The following functions are provided to set and get these contexts.

Set the ``global'' context {\tt pmilter\_g\_ctx}:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_g(pmg\_ctx\_P pmg\_ctx, void *pmilter\_g\_ctx)}.

This must be done after libpmilter has been initialized
but before control is transferred to it.

To retrieve the ``global'' context invoke:

{\tt void *sm\_pmfi\_get\_ctx\_g(pmg\_ctx\_P pmg\_ctx)}

Note: this requires the ``global'' libpmilter context which is
not usually passed to pmilter functions in callbacks.
See below how to access the ``global'' context {\tt pmilter\_g\_ctx}
from other places.

To set the pmilter context per SMTP server {\tt pmilter\_ss\_ctx} use:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_ss(pmss\_ctx\_P pmss\_ctx, void *pmilter\_ss\_ctx)};

to retrieve it call:

{\tt void *sm\_pmfi\_get\_ctx\_ss(pmss\_ctx\_P pmss\_ctx)}

The ``global'' pmilter context {\tt pmilter\_g\_ctx} can be retrieved
from the libpmilter context per SMTP server:

{\tt void *sm\_pmfi\_get\_ctx\_g\_ss(pmss\_ctx\_P pmss\_ctx)}

At the lowest level a context per SMTP session {\tt pmilter\_se\_ctx}
can be set via:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_se(pmse\_ctx\_P pmse\_ctx, void *pmilter\_se\_ctx)}

and retrieved by:

{\tt void *sm\_pmfi\_get\_ctx\_se(pmse\_ctx\_P pmse\_ctx)}.

Just as before there is a function to retrieve
the pmilter context per SMTP server {\tt pmilter\_ss\_ctx}
from the libpmilter context per SMTP session:

{\tt void *sm\_pmfi\_get\_ctx\_ss\_se(pmse\_ctx\_P pmse\_ctx)}

Note: if a pmilter uses these contexts, then it is useful
that each ``lower level'' context contains a link
to its ``higher level'' context.
That is,
each pmilter context per SMTP session {\tt pmilter\_se\_ctx}
should have a pointer to its
pmilter context per SMTP server {\tt pmilter\_ss\_ctx}
which in turn should have a pointer to the
``global'' pmilter context {\tt pmilter\_g\_ctx}.
This allows access from a function that is specific to a SMTP session
to each relevant context.

\subsection{Accessing MTA Symbols}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:AccessingMTASymbols}

A pmilter can set a list of symbols it wants to receive from the MTA
by calling

{\tt sm\_pmfi\_setmaclist(pmss\_ctx\_P pmss\_ctx, uint where, ...)}

during the option negotiation, i.e., in {\tt pmfi\_negotiate()}.
The parameter {\tt where} denotes the stage of the ESMTP dialogue
when the value of the symbol should be sent.
It must be one of

\begin{tabular}{ll}
{\tt PM\_SMST\_CONNECT}	& Session start	\\
{\tt PM\_SMST\_EHLO}		& EHLO or HELO command \\
{\tt PM\_SMST\_MAIL}		& MAIL command	\\
{\tt PM\_SMST\_RCPT}		& RCPT command	\\
{\tt PM\_SMST\_DATA}		& DATA command	\\
{\tt PM\_SMST\_DOT}		& Final dot of mail body	\\
\end{tabular}

A sequence of up to {\tt PM\_MAX\_MACROS} macros can be requested which
must end with {\tt PMM\_END}.
Valid values are:

\begin{tabular}{lll}
{\tt PMM\_SRVHOSTNAME}	& hostname of SMTP server	\\
{\tt PMM\_SEID}		& session id			\\
% {\tt PMM\_CLIENT\_RESOLVE}	& result of client lookups	\\
{\tt PMM\_MAIL\_TAID}		& transaction id		\\
{\tt PMM\_DOT\_MSGID}		& Message-Id			\\
\end{tabular}

{\tt PMM\_MAIL\_TAID} cannot be requested before {\tt PM\_SMST\_MAIL}
and
{\tt PMM\_DOT\_MSGID} can only be requested at stage
{\tt PM\_SMST\_DOT}.

To retrieve the value of a symbol the function

{\tt sm\_pmfi\_getmac(pmse\_ctx\_P pmse\_ctx, uint32\_t macro, char **pvalue)}

can be used in the various callback functions of the ESMTP dialogue.
If the macro was not in the request list, an error will be returned.
If the macro has not yet been received, {\tt *pvalue} will be {\tt NULL}.
Otherwise {\tt *pvalue} will point to the value of the macro.
Note: the string to which {\tt *pvalue} points must {\em not} be changed.

\subsection{Further Capabilities}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities}

In addition to selecting which SMTP commands to send to pmilter
(see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:NewSMTPServer}),
there are some more capabilities available:

\begin{enumerate}
\item
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMRCPTST}
{\tt SM\_SCAP\_PM\_RCPT\_ST}
causes the MTA to send {\tt RCPT} information
even if the command has been rejected,
e.g., because the recipient is unknown, the recipient has been rejected
due to access map checks, or relaying has been denied.
Note: {\tt RCPT} commands that are rejected for other reasons, e.g.,
because the address is syntactically invalid, or some limit
(maximum number of recipients) is exceeded,
will not be sent to pmilter.
\item
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMMSGRC}
{\tt SM\_SCAP\_PM\_MSG\_RC}
allows a pmilter to return a reply code
as specified in
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:ReturnValues}
from {\tt pmfi\_msg()}.
This is useful if a pmilter can make a decision about the mail
without having to read the entire message.
If this capability is turned on,
{\tt pmfi\_msg()}
must return
{\tt SMTP\_R\_CONT}
for each message chunk by default to receive subsequent parts.
Otherwise {\tt pmfi\_eom()} will not be called but the return code from
{\tt pmfi\_msg()}
will be used at the end of the message (in response to the final dot).
\end{enumerate}

The function

{\tt sm\_ret\_T sm\_pmfi\_getstatus(pmse\_ctx\_P pmse\_ctx, sfsistat\_T *pstatus)}

should be used in that case to access the current SMTP reply code
for the command.
This functionality is useful for a pmilter that wants
to keep track of all recipients, not just those which are accepted,
e.g., to deal with dictionary attacks.

\subsection{Miscellaneous Functions}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:MiscellaneousFunctions}

To set a reply text in an SMTP session or transaction oriented callback
in addition to the reply code use:

{\tt sm\_ret\_T sm\_pmfi\_setreply(pmse\_ctx\_P pmse\_ctx, const char *reply)}

Note: the reply string {\em must} contain the full SMTP reply, i.e., it
must be of the form

{\tt XYZ D.S.N text\textbackslash r\textbackslash n}

where
{\tt XYZ} is a valid SMTP reply code (see RFC 2821 \cite{bib:rfc2821})
which {\em must} match the return code of the function from which
{\tt sm\_pmfi\_setreply()} is called,
{\tt D.S.N} is an enhanced status code as defined in RFC 3463
\cite{bib:rfc3463}
and the rest is an explanation of the status including CRLF
({\tt \textbackslash r\textbackslash n}).


Return version number of libpmilter:

{\tt sm\_ret\_T sm\_pmfi\_version(pmg\_ctx\_P pmg\_ctx, uint32\_t *major, uint32\_t *minor, uint32\_t *patchlevel)}

This can be used to compare the version number of the library against
which pmilter is linked
with the version number against which pmilter is compiled.
The major version numbers must match otherwise the program will not run.

Signal handler function:

{\tt sm\_ret\_T pmfi\_signal(pmg\_ctx\_P pmg\_ctx, int sig)}

This will be called when a USR1 or USR2 signal is received;
it is not called within a signal handler, i.e., the code does not have
to be signal-safe.
Note: this is not yet implemented.

\subsection{Return Values}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:ReturnValues}

SMTP Session and transaction oriented functions
% (\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction})
use 
{\tt sfsistat\_T}
as return type.
Allowed values for this type are (as defined in
{\tt include/sm/smreplycodes.h}):
\begin{itemize}
\item
{\tt SMTP\_R\_OK}: accept command.
\item
{\tt SMTP\_R\_ACCEPT}: accept entire transaction or session
depending on where this value is returned.
Note: this is just a shortcut for
{\tt SMTP\_R\_SET\_QUICK(SMTP\_R\_OK)}.
%not yet implemented
%\item
%{\tt SMTP\_R\_RELAY}: allow relaying for this transaction.
\item
{\tt SMTP\_R\_DISCARD}: discard effect of command.
\item
{\tt SMTP\_R\_CONT}: continue other checks.
%\item
%{\tt SMTP\_R\_DUNNO}: continue other checks.
%\item
%{\tt SMTP\_R\_SKIP}: continue other checks.
\item
{\tt SMTP\_R\_SSD}: shut down SMTP session.
\item
{\tt SMTP\_R\_TEMP}: reject command with a temporary error.
\item
{\tt SMTP\_R\_SYNTAX}: syntax error.
%\item
%{\tt SMTP\_R\_REJECT}: reject command.
\item
{\tt SMTP\_R\_PERM}: reject command with a permanent error.
\item
other valid SMTP reply codes
\cite{bib:rfc2821}.
\end{itemize}

Additionally return values can be modified by using
{\tt SMTP\_R\_SET\_QUICK(returnvalue)}.
See Section \ref{ConfigurationMapsforSMAR:access:quick}
for the effects of this.

%% what to do for: accept command and don't bother me anymore?
%% SMTP_R_SET_QUICK(SMTPR_OK)??

For functions that use {\tt sm\_ret\_T} as return type
a successful call returns {\tt SM\_SUCCESS} (0)
and a negative value in case of an error.
