Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
miltertest(8)		     System Manager's Manual		   miltertest(8)

NAME
     miltertest - milter unit test utility

SYNOPSIS
     miltertest [-D name[=value]] [-s script] [-u] [-v] [-V] [-w]

DESCRIPTION
     miltertest  simulates the MTA side of an MTA-milter interaction for testing
     a milter-aware filter application.  It takes as input a  script  using  the
     Lua  language,  and  by exporting some utility functions, makes it possible
     for users to write scripts that exercise a filter.

     See documentation on Lua (e.g. http://www.lua.org) for the  syntax  of  the
     language  in general.  The documentation below describes functions that are
     added to Lua by this application to make testing possible.

     Documentation on milter can be found at http://www.milter.org.  A	particu-
     lar transaction must follow a series of steps to be completed, namely nego-
     tiate,  connection  information,  envelope  sender,  envelope recipient(s),
     header field(s), end-of-header, body chunk(s), end-of-message.  To make the
     work of writing tests with miltertest simpler, any of these steps prior  to
     end-of-message  that  is skipped will be filled in using arbitrary, but le-
     gal, data.

     Interspersed with these protocol phases are optional macro (key/value)  de-
     liveries from the MTA.  miltertest will never send these automatically.  If
     they  are	needed	for  your tests, you must send them as part of your test
     script.

OPTIONS
     -D name[=value]
	    Defines a global variable called name to the Lua interpreter.  If  a
	    value  is  provided,  the global variable is set to that value (as a
	    string, although Lua can convert strings to numbers internally).  If
	    no value is provided, the global variable is set to 1.

     -s script
	    Use the contents of file script as the Lua script  to  be  executed.
	    The default is to read from standard input.

     -u     After  the filter being tested is terminated, report user and system
	    time consumed.  See getrusage(2).

     -v     Increase verbose output.  May be specified multiple times to request
	    more and more information.

     -V     Print version number and exit.

     -w     Don't wait for child status to be returned when testing is complete.

FUNCTIONS
     The following functions are made available to Lua scripts for exercising  a
     filter.   All  functions  return  Lua constant "nil" on success or an error
     string on failure, unless otherwise indicated.

     mt.abort(conn)
	    Aborts the transaction in progress on the specified connection.

     mt.bodyfile(conn, file)
	    Sends the contents of the named file to the connection as body data.
	    If there is any error opening file for reading, the test aborts.

     mt.bodyrandom(conn, n)
	    Sends at least n bytes of random-length lines  of  random  printable
	    ASCII data as body chunks to the specified connection.

     mt.bodystring(conn, str)
	    Sends str as a chunk of body text on the specified connection.

     mt.chdir(directory)
	    Changes the current working directory to the named directory.

     mt.connect(sockinfo[, count, interval])
	    Makes  a connection to a filter listening at the socket described by
	    sockinfo.  Returns a handle referring to that connection, or the Lua
	    constant "nil" on error.  If count and interval are  included,  they
	    specify  the number of times to try to connect to the filter and the
	    delay between each connection in seconds (with floating point values
	    permitted).  If the environment variable MILTERTEST_RETRY_SPEED_FAC-
	    TOR is set and appears to contain an integer, the value of	interval
	    (if  set)  will be multiplied by the value found in that environment
	    variable.  This is included to allow tests in a large test suite  to
	    be	easily adjusted on slow systems without reconfiguring the entire
	    test suite.

     mt.conninfo(conn, host, ip)
	    Sends information about a new SMTP connection  to  the  MTA,  repre-
	    sented by connection conn, from the host named host at IP address ip
	    (both  strings).  If host is the Lua constant "nil", the string "lo-
	    calhost" is assumed.  If ip is the Lua constant "nil", a  DNS  query
	    will be made for the IP address matching host; if none is found, the
	    test  will	abort.	 The ip may also be the special string "unspec",
	    which will tell the filter that a connection came in from an unknown
	    protocol family.

     mt.data(conn)
	    Announces the DATA command on the specified connection, which occurs
	    between the last RCPT TO command and the  beginning  of  the  header
	    block.

     mt.disconnect(conn[, polite]))
	    Sends  a  "quit" message to the specified connection and then closes
	    that connection.  The specified conn  handle  should  no  longer  be
	    used.  If polite is defined, it must be a Boolean indicating whether
	    a  normal  disconnect  should be done (true) or an abrupt disconnect
	    should be done (false).  An abrupt disconnect skips standard  proto-
	    col shutdown steps.

     mt.echo(string)
	    Prints the specified string on standard output.  Returns nothing.

     mt.eoh(conn)
	    Announces end-of-header on the specified connection.

     mt.eom(conn)
	    Announces  end-of-message  on  the	specified connection, and begins
	    capturing any other operations the	filter	might  perform	in  that
	    phase.

     mt.eom_check(conn, op, param[, ...])
	    Checks  the  captured set of EOM operations (see above) to determine
	    whether or not specific milter actions were requested by the filter.
	    Returns a Boolean value (true or false).  See the EOM CHECKS section
	    for details.

     mt.getheader(conn, hdr, n)
	    Retrieves the value of the nth instance of header  field  named  hdr
	    added  during end-of-message processing on the specified connection.
	    This can be used by the script to verify that the header thus  added
	    contains the right thing.  Returns the value as a string, or the Lua
	    constant "nil" on error.

     mt.getcwd()
	    Returns the current working directory as a string.

     mt.getreply(conn)
	    Returns  the  last	milter reply received from the specified connec-
	    tion, as an integer.  This can be compared to  any	of  the  SMFIR_*
	    constants  defined	by  milter to see if the filter responded as ex-
	    pected.  This value is initially set to the NULL character.

     mt.header(conn, name, value)
	    Sends the header with the given name and value to the specified con-
	    nection.

     mt.helo(conn, name)
	    Sends HELO/EHLO information using the specified name as the  parame-
	    ter given.

     mt.macro(conn, type, name, value[, name2, value2[, ...]])
	    Declares  a  macro	called	name whose value is value and whose type
	    (matching protocol element) is type.  Valid types are SMFIC_CONNECT,
	    SMFIC_HELO, SMFIC_MAIL and SMFIC_RCPT.   Multiple  macro  names  and
	    values can be provided, but they must appear in pairs.

     mt.mailfrom(conn, envfrom[, ...])
	    Announces  envfrom	as  the envelope sender of a new message.  ESMTP
	    parameters as additional arguments are permitted.

     mt.negotiate(conn, version, actions, steps)
	    Performs milter option negotiation with the connection conn,  adver-
	    tising  that  the  specified  protocol version, protocol actions and
	    protocol steps are offered by the MTA.   Returns  the  Lua	constant
	    "nil"  on success or an error string on failure.  If any of the pro-
	    tocol parameters are "nil", the current defaults (defined in libmil-
	    ter/mfdef.h, provided with the milter source code) will be used.

     mt.rcptto(conn, envrcpt[, ...])
	    Announces envrcpt as an envelope recipient of a message.  ESMTP  pa-
	    rameters as additional arguments are permitted.

     mt.set_timeout(n)
	    Sets  the  read  timeout  to n seconds.  The default is ten seconds.
	    Returns nothing.

     mt.sleep(n)
	    Sleeps for n seconds.  The value may be an integer (for  whole  sec-
	    onds) or a floating-point value (for partial seconds).

     mt.signal(n)
	    Sends the specified signal number n to the running filter.

     mt.startfilter(path, arg1, arg2, ...)
	    Starts the filter whose binary is located at path with argument vec-
	    tor  comprised  of strings path, arg1, arg2, etc.  Basically this is
	    almost the same syntax as execl(3) except that miltertest also  does
	    the  fork  for you, and will remember the process ID in order to re-
	    quest a clean shutdown using SIGTERM and wait(2) at the end  of  the
	    test  script.   If	the filter could not be started, an exception is
	    generated with an error message returned.

     mt.test_action(conn, action)
	    Tests whether or not the connection represented  by  conn  requested
	    the  specified  milter protocol action, specified by an SMFIF_* con-
	    stant, during option negotiation.  (See the libmilter  documentation
	    and/or include files for details.)

     mt.test_option(conn, option)
	    Tests  whether  or	not the connection represented by conn requested
	    the specified milter protocol option, specified by an  SMFIP_*  con-
	    stant,  during option negotiation.	(See the libmilter documentation
	    and/or include files for details.)

     mt.unknown(conn, str)
	    Announces that the unknown SMTP command str arrived over the connec-
	    tion represented by conn.

EOM CHECKS
     The mt.eom_check() function is used to determine what changes to  the  mes-
     sage  the filter requested during its EOM callback.  The changes can be re-
     quested in any order.  The first parameter, op, indicates what operation is
     of interest, and it also dictates what  the  possible  parameter  list  is.
     Valid values and corresponding parameters for op are as follows:

     MT_HDRADD
	    Checks to see if a header field was added to the message.  If no pa-
	    rameters  are  given,  the function returns true if any header field
	    was added.	If one parameter was given, the  function  returns  true
	    only  if the named header field was added (regardless of its value).
	    If two parameters are given, the function returns true only  if  the
	    named header field was added with the specified value.

     MT_HDRCHANGE
	    Checks  to see if an existing header field was changed.  If no para-
	    meters are given, the function returns true if any header field  was
	    modified.	If  one  parameter  was given, the function returns true
	    only if the named header field was modified (regardless of	its  new
	    value).  If two parameters are given, the function returns true only
	    if	the  named  header  field was modified to have the specified new
	    value.

     MT_HDRDELETE
	    Checks to see if an existing header field was deleted.  If no  para-
	    meters  are given, the function returns true if any header field was
	    deleted.  If one parameter was given, the function returns true only
	    if the named header field was deleted.  If two parameters are given,
	    the function returns true only if the named header field was deleted
	    at the specified index.

     MT_HDRINSERT
	    Checks to see if a header field was inserted into the  message.   If
	    no	parameters  are  given,  the function returns true if any header
	    field was added.  If one parameter was given, the  function  returns
	    true  only	if  the  named header field was added (regardless of its
	    value).  If two parameters are given, the function returns true only
	    if the named header field was added with the  specified  value.   If
	    three  parameters  are  given, the function returns true only if the
	    named header field was added with the specified value at the  speci-
	    fied index.

     MT_RCPTADD
	    Checks  to	see  if an envelope recipient was added.  Currently only
	    one parameter may be provided.

     MT_RCPTDELETE
	    Checks to see if an envelope recipient was deleted.  Currently  only
	    one parameter may be provided.

     MT_BODYCHANGE
	    Checks  to	see if the message's body was replaced by other content.
	    With no parameters, the function returns true only if the  body  was
	    changed  (regardless  of  the new content).  With one parameter, the
	    function returns true only if the body was changed to the  specified
	    new content.

     MT_QUARANTINE
	    Checks  to	see if the filter requested quarantining of the message.
	    With no parameters, the function returns true only if quarantine was
	    requested.	With one parameter, the function returns  true	only  if
	    quarantine was requested with the specified reason string.

     MT_SMTPREPLY
	    Checks to see if the filter requested a specific SMTP reply message.
	    With no parameters, the function returns true only if a specific re-
	    ply  was  requested.   With one parameter, the function returns true
	    only if a specific reply was requested with the specified SMTP code.
	    With two parameters, the function returns true only  if  a	specific
	    reply was requested with the specified SMTP code and enhanced status
	    code.   With  three  parameters, the function returns true only if a
	    specific reply was requested with the specified SMTP code,	enhanced
	    status code, and text.

EXAMPLE
     -- Echo that the test is starting
     mt.echo("*** begin test")
     -- start the filter
     mt.startfilter("../myfilter", "-p", "inet:12345@localhost")
     mt.sleep(2)

     -- try to connect to it
     conn = mt.connect("inet:12345@localhost")
     if conn == nil then
	  error "mt.connect() failed"
     end

     -- send connection information
     -- mt.negotiate() is called implicitly
     if mt.conninfo(conn, "localhost", "127.0.0.1") ~= nil then
	  error "mt.conninfo() failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.conninfo() unexpected reply"
     end

     -- send envelope macros and sender data
     -- mt.helo() is called implicitly
     mt.macro(conn, SMFIC_MAIL, "i", "test-id")
     if mt.mailfrom(conn, "user@example.com") ~= nil then
	  error "mt.mailfrom() failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.mailfrom() unexpected reply"
     end

     -- send headers
     -- mt.rcptto() is called implicitly
     if mt.header(conn, "From", "user@example.com") ~= nil then
	  error "mt.header(From) failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.header(From) unexpected reply"
     end
     if mt.header(conn, "Date", "Tue, 22 Dec 2009 13:04:12 -0800") ~= nil then
	  error "mt.header(Date) failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.header(Date) unexpected reply"
     end
     if mt.header(conn, "Subject", "Signing test") ~= nil then
	  error "mt.header(Subject) failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.header(Subject) unexpected reply"
     end
     -- send EOH
     if mt.eoh(conn) ~= nil then
	  error "mt.eoh() failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.eoh() unexpected reply"
     end

     -- send body
     if mt.bodystring(conn, "This is a test!\r\n") ~= nil then
	  error "mt.bodystring() failed"
     end
     if mt.getreply(conn) ~= SMFIR_CONTINUE then
	  error "mt.bodystring() unexpected reply"
     end
     -- end of message; let the filter react
     if mt.eom(conn) ~= nil then
	  error "mt.eom() failed"
     end
     if mt.getreply(conn) ~= SMFIR_ACCEPT then
	  error "mt.eom() unexpected reply"
     end

     -- verify that a test header field got added
     if not mt.eom_check(conn, MT_HDRINSERT, "Test-Header") then
	  error "no header added"
     end

     -- wrap it up!
     mt.disconnect(conn)

NOTES
     If  a  filter  negotiates	one  of the SMFIP_NO* protocol option bits and a
     script attempts to perform one of those protocol steps,  an  error  is  re-
     turned.   It  is  up to the test author to use mt.test_option() function to
     see if performing a protocol step has been explicitly disabled by the  fil-
     ter.

MILTER NOTES
     When mt.macro() is called, it replaces all previous macros of the same type
     with the ones provided in the argument list.  Thus, one call should be made
     that  lists  the  complete  set  rather  than one call per name-value pair.
     Also, as each stage in the milter process is executed,  all  macros  corre-
     sponding  stages after the current one are discarded.  For example, calling
     mt.helo(), which corresponds to SMFIC_HELO, will cause all prior macros  of
     type  SMFIC_MAIL  and SMFIC_RCPT to be discarded as they represent a milter
     stage that comes later than SMFIC_HELO.

     Since the milter protocol and the internals of  libmilter	itself	are  not
     formally documented, there are myriad other subtleties of the milter proto-
     col  and  implementation  that are not documented here and may not be docu-
     mented elsewhere, and could change without notice.  Caveat emptor.

VERSION
     This man page covers version 1.5.0 of miltertest.

COPYRIGHT
     Copyright (c) 2009-2014, The Trusted Domain Project.  All rights reserved.

SEE ALSO
     Milter -- http://www.milter.org

     Lua -- http://www.lua.org

			   The Trusted Domain Project		   miltertest(8)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=miltertest&sektion=8&manpath=FreeBSD+Ports+15.1.quarterly>

home | help