# smtpd-tables

## NAME



           **smtpd-tables** — table API for the smtpd daemon
    


## DESCRIPTION



           The _smtpd_(8) daemon provides a Simple Mail Transfer Protocol
           (SMTPD) implementation, which allows ordinary machines to become
           Mail eXchangers (MX).  Some features that are commonly used by MX,
           such as querying databases for user credentials, are outside of
           the scope of SMTP and too complex to fit in _smtpd_(8).
    
           Because an MX may need to provide these features, _smtpd_(8)
           provides an API to implement _table_(5) backends with a simple text-
           based protocol.
    


## DESIGN



           **smtpd-tables** are programs that run as unique standalone processes,
           they do not share _smtpd_(8) address space.  They are executed by
           _smtpd_(8) at startup and expected to run in an infinite loop,
           reading events and queries from standard input and writing
           responses to standard output.  They are not allowed to terminate.
    
           Because **smtpd-tables** are standalone programs that communicate with
           _smtpd_(8), they may run as different users than _smtpd_(8) and may be
           written in any language.  **smtpd-tables** must not use blocking I/O,
           they must support answering asynchronously to _smtpd_(8).
    


## PROTOCOL



           The protocol consist of human-readable lines exchanged between
           **smtpd-tables** and _smtpd_(8).
    
           The protocol begins with a handshake.  First, _smtpd_(8) provides
           **smtpd-tables** with general configuration information in the form of
           key-value lines, terminated by ‘config|ready’.  For example:
    
                 config|smtpd-version|7.7.0
                 config|protocol|0.1
                 config|tablename|devs
                 config|ready
    
           Then, **smtpd-tables** register the supported services, terminating
           with ‘register|ready’.  For example:
    
                 register|alias
                 register|credentials
                 register|ready
    
           Finally, _smtpd_(8) can start querying the table.  For example:
    
                 table|0.1|1713795082.354255|devs|lookup|alias|b72508d|op
    
           The “|” character is used to separate the fields and may only
           appear verbatim in the last field of the payload, in which case it
           should be considered a regular character and not a separator.  No
           other field may contain a “|”.
    
           Each request has a common set of fields, followed by some other
           fields that are operation-specific.  The common format consists of
           a protocol prefix ‘table’, the protocol version, the timestamp and
           the table name.  For example:
    
                 table|0.1|1713795091.202157|devs
    
           The protocol is inherently asynchronous, so multiple request may
           be sent without waiting for the table to reply.  All the replies
           have a common prefix, followed by the operation-specific response.
           The common format consist of a prefix with the operation name in
           followed by ‘-result’, and the unique ID of the request.  For
           example:
    
                 lookup-result|b72508d
    
           The list of operations, operation-specific parameters and
           responses are as follows:
    
           **update** _id_
                   Ask the table to reload its configuration.  The result is
                   either ‘ok’ on success or ‘error’ and a message upon a
                   failure to do so.
    
           **check** _service id query_
                   Check whether _query_ is present in the table.  The result
                   is ‘found’ if found, ‘not-found’ if not, or ‘error’ and a
                   message upon an error.
    
           **lookup** _service id query_
                   Look up a value in the table for given the _query_.  The
                   result is ‘found’ and the value if found, ‘not-found’ if
                   not found, or ‘error’ and a message upon an error.
    
           **fetch** _service id_
                   Fetch the next item from the table, eventually wrapping
                   around.  It is only supported for the **source** and **relayhost**
                   services.  The result is ‘found’ and the value if found,
                   ‘not-found’ if the table is empty, or ‘error’ and a
                   message upon an error.
    
           Each service has a specific format for the result.  The exact
           syntax for the values and eventually the keys are described in
           _table_(5).  The services and their result format are as follows:
    
           **alias** One or more aliases separated by a comma.
           **auth** Only usable for check.  Lookup key is username and
                        cleartext password separated by ‘:’.
           **domain** A domain name.
           **credentials** The user name, followed by ‘:’ and the encrypted
                        password as per _smtpctl_(8) **encrypt** subcommand.
           **netaddr** IPv4 and IPv6 address or netmask.
           **userinfo** The user id, followed by ‘:’ then the group id, then
                        ‘:’ and finally the home directory.
           **source** IPv4 and IPv6 address.
           **mailaddr** An username, a domain or a full email address.
           **addrname** Used to map IP addresses to hostnames.
    


## EXAMPLES



           Assuming the table is called “devs”, here's an example of a failed
           **update** transaction:
    
                 table|0.1|1713795097.394049|devs|update|478ff0d2
                 update-result|478ff0d2|error|failed to connect to the database
    
           A **check** request for the **netaddr** service for the 192.168.0.7 IPv4
           address which is not in the table:
    
                 table|0.1|1713795103.314423|devs|check|netaddr|e5862859|192.168.0.7
                 check-result|e5862859|not-found
    
           A successful **lookup** request for the **userinfo** service for the user
           ‘op’:
    
                 table|0.1|1713795110.354921|devs|lookup|userinfo|f993c74|op
                 lookup-result|f993c74|found|1000:1000:/home/op
    
           A series of **fetch** requests for the **source** service that wraps
           around:
    
                 table|0.1|1713795116.227321|devs|fetch|source|189bd3ee
                 lookup-result|189bd3ee|found|192.168.1.7
                 table|0.1|1713795120.162438|devs|fetch|source|9e4c56d4
                 lookup-result|9e4c56d4|found|10.0.0.8
                 table|0.1|1713795122.930928|devs|fetch|source|f2c8b906
                 lookup-result|f2c8b906|found|192.168.1.7
    


* * *