@c This file is part of the Mailfromd manual. @c Copyright (C) 2007 Sergey Poznyakoff @c See file mailfromd.texi for copying conditions. @c ******************************************************************* @cindex @command{mtasim}, described The @command{mtasim} utility is a @acronym{MTA} simulator for testing @command{mailfromd} filter scripts. By default it operates in @dfn{stdio} mode, similar to that of @command{sendmail -bs}. In this mode it reads @acronym{SMTP} commands from standard input and sends its responses to the standard output. There is also another mode, called @dfn{daemon}, where @command{mtasim} opens a @acronym{TCP} socket and listens on it much like any @acronym{MTA} does. In both modes no actual delivery is performed, the tool only simulates the actions an @acronym{MTA} would do and responses it would give. This tool is derived from the program @command{mta}, which I wrote for GNU Anubis test suite. @menu * interactive mode:: * expect commands:: * traces:: * daemon mode:: * option summary:: @end menu @node interactive mode @section @command{mtasim} interactive mode mode If you start @command{mtasim} without options, you will see the following: @smallexample @group 220 mtasim (mailfromd @value{VERSION}) ready (mtasim) _ @end group @end smallexample @cindex readline @cindex GNU Readline The first line is an usual @acronym{RFC} 2821 reply. The second one is a prompt, indicating that @command{mtasim} is in interactive mode and ready for input. The prompt appears only if the package is compiled with GNU Readline and @command{mtasim} determines that its standard input is connected to the terminal. This is called @dfn{interactive mode} and is intended to save the human user some typing by offering line editing and history facilities (@pxref{Command Line Editing, , Command Line Editing, readline, GNU Readline Library}). If the package is compiled without GNU Readline, you will see: @smallexample @group 220 mtasim (mailfromd @value{VERSION}) ready _ @end group @end smallexample @noindent where @samp{_} represents the cursor. Whatever the mode, @command{mtasim} will wait for further input. @cindex @code{HELP}, @command{mtasim} statement The input is expected to consist of valid @acronym{SMTP} commands and special @command{mtasim} statements. The utility will act exactly like a @acronym{RFC} 2821-compliant @acronym{MTA}, except that it will not do actual message delivery or relaying. Try typing @code{HELP} to get the list of supported commands. You will see something similar to: @smallexample 250-mtasim (mailfromd @value{VERSION}); supported SMTP commands: 250- EHLO 250- HELO 250- MAIL 250- RCPT 250- DATA 250- HELP 250- QUIT 250- HELP 250 RSET @end smallexample You can try a simple @acronym{SMTP} session now: @smallexample 220 mtasim (mailfromd @value{VERSION}) ready (mtasim) @kbd{ehlo localhost} 250-pleased to meet you 250 HELP (mtasim) @kbd{mail from: } 250 Sender OK (mtasim) @kbd{rcpt to: } 250 Recipient OK (mtasim) @kbd{data} 354 Enter mail, end with `.' on a line by itself (mtasim) @kbd{.} 250 Mail accepted for delivery (mtasim) @kbd{quit} 221 Done @end smallexample Notice, that @command{mtasim} does no domain checking, so such thing as @samp{rcpt to: } was eaten without complaints. @anchor{mtasim milter port} @mtasimopt{port, described} So far so good, but what all this has to do with @command{mailfromd}? Well, that's what we are going to explain. To make @command{mtasim} consult any milter, use @option{--port} (@option{-X}) command line option. This option takes a single argument that specifies the milter port to use. The port can be given either in the usual Milter format (@xref{milter port specification}, for a short description), or as a full @file{sendmail.cf} style @code{X} command, in which case it allows to set timeouts as well: @smallexample $ @kbd{mtasim --port=inet:999@@localhost} # @r{This is also valid}: $ @kbd{mtasim --port='mailfrom, S=inet:999@@localhost, F=T, T=C:100m;R:180s'} @end smallexample If the milter is actually listening on this port, @command{mtasim} will connect to it and you will get the following initial prompt: @smallexample @group 220-mtasim (mailfromd @value{VERSION}) ready 220 Connected to milter inet:999@@localhost (mtasim) @end group @end smallexample Notice, that it makes no difference what implementation is listening on that port, it may well be some other filter, not necessarily @command{mailfromd}. @cindex @command{mtasim} auto mode However, let's return to @command{mailfromd}. If you do not want to connect to an existing @command{mailfromd} instance, but prefer instead to create a new one and run your tests with it (a preferred way, if you already have a stable filter running but wish to test a new script without disturbing it), use @option{--port=auto}. This option instructs @command{mtasim} to do the following: @enumerate 1 @item Create a unique temporary directory in @file{/tmp} and create a communication socket within it. @item Spawn a new instance of @command{mailfromd}. The arguments and options for that instance may be given in the invocation of @command{mtasim} after a double-dash marker (@samp{--}) @item Connect to that filter. @end enumerate When @command{mtasim} exits, it terminates the subsidiary @command{mailfromd} process and removes the temporary directory it has created. For example, the following command will start @command{mailfromd -I. -I../mflib test.rc}: @smallexample @group $ @kbd{mtasim -Xauto -- -I. -I../mflib test.rc} 220-mtasim (mailfromd @value{VERSION}) ready 220 Connected to milter unix:/tmp/mtasim-j6tRLC/socket (mtasim) @end group @end smallexample @anchor{statedir mtasim option} @mtasimopt{statedir, described} The @file{/tmp/mtasim-j6tRLC} directory and any files within it will exist as long as @command{mtasim} is running and will be removed when you exit from it.@footnote{However, this is true only if the program is exited the usual way (via @code{QUIT} or end-of-file). If it is aborted with a signal like @code{SIGINTR}, the temporary directory is not removed.} You can also instruct the subsidiary @command{mailfromd} to use this directory as its state directory (@pxref{statedir}). This is done by @option{--statedir} command line option: @smallexample $ @kbd{mtasim -Xauto --statedir -- -I. -I../mflib test.rc} @end smallexample @noindent (notice that @option{--statedir} is the @command{mtasim} option, therefore it must appear @emph{before} @samp{--}) Now, let's try @code{HELP} command again: @smallexample 250-mtasim (mailfromd @value{VERSION}); supported SMTP commands: 250- EHLO 250- HELO 250- MAIL 250- RCPT 250- DATA 250- HELP 250- QUIT 250- HELP 250- RSET 250-Supported administrative commands: 250- \Dname=value [name=value...] Define Sendmail macros 250- \Ecode Expect given SMTP reply code 250- \L[name] [name...] List macros 250 \Uname [name...] Undefine Sendmail macros @end smallexample @cindex @command{mtasim} administrative commands While the @acronym{SMTP} commands do not need any clarification, some words about the @dfn{administrative commands} are surely in place. These commands allow to define, undefine and list arbitrary Sendmail macros. Each administrative command consists of a backslash followed by a command letter. Just like @acronym{SMTP} ones, administrative commands are case-insensitive. If a command takes arguments, the first argument must follow the command letter without intervening whitespace. Subsequent arguments can be delimited by arbitrary amount of whitespace. @anchor{D command} @cindex D, @code{\D}, a @command{mtasim} command @cindex @command{mtasim}, defining Sendmail macros For example, the @code{\D} command defines Sendmail macros: @smallexample (mtasim) @kbd{\Dclient_addr=192.168.10.1 f=sergiusz@@localhost i=testmsg} (mtasim) @end smallexample @noindent Notice that @command{mailfromd} does not send any response to the command, except if there was some syntactic error, in which case it will return a @samp{502} response. @cindex L, @code{\L}, a @command{mtasim} command @cindex @command{mtasim}, listing Sendmail macros Now, you can list all available macros: @smallexample (mtasim) @kbd{\L} 220-client_addr=192.168.10.1 220-f=sergiusz@@localhost 220 i=testmsg (mtasim) @end smallexample @noindent or just some of them: @smallexample (mtasim) @kbd{\Lclient_addr} 220 client_addr=192.168.10.1 (mtasim) @end smallexample @cindex U, @code{\U}, a @command{mtasim} command @cindex @command{mtasim}, undefining Sendmail macros To undefine a macro, use @command{\U} command: @smallexample (mtasim) @kbd{\Ui} (mtasim) @kbd{\l} 220-client_addr=192.168.10.1 220 f=sergiusz@@localhost (mtasim) @end smallexample Now, let's try a real-life example. Suppose you wish to test the greylisting functionality of the filter script described in @ref{Filter Script Example}. To do this, you start @command{mtasim}: @smallexample $ @kbd{mtasim -Xauto -- -I. -I../mflib test.rc} 220-mtasim (mailfromd @value{VERSION}) ready 220 Connected to milter unix:/tmp/mtasim-ak3DEc/socket (mtasim) @end smallexample The script in @file{test.rc} needs to know @code{client_addr} macro, so you supply it to @command{mtasim}: @smallexample (mtasim) @kbd{\Dclient_addr=10.10.1.13} @end smallexample Now, you try an @acronym{SMTP} session: @smallexample (mtasim) @kbd{ehlo yahoo.com} 250-pleased to meet you 250 HELP (mtasim) @kbd{mail from: } 250 Sender OK (mtasim) @kbd{rcpt to: } 450 4.7.0 You are greylisted for 300 seconds @end smallexample OK, this shows that the greylisting works. Now quit the session: @smallexample (mtasim) @kbd{quit} 221 Done @end smallexample @node expect commands @section @command{mtasim} expect commands @cindex expect mode, @command{mtasim} @cindex @command{mtasim} expect mode @cindex E, @code{\E}, a @command{mtasim} command Until now we were using @command{mtasim} interactively. However, it is often useful in shell scripts, for example the @command{mailfromd} test suite is written in shell and @command{mtasim}. To avoid the necessity to use auxiliary programs like @command{expect} or @command{DejaGNU}, @command{mtasim} contains a built-in expect feature. The administrative command @code{\E} introduces the @acronym{SMTP} code that the next command is expected to yield. For example, @smallexample @group \E250 rcpt to: @end group @end smallexample @noindent tells @command{mtasim} that the response to @code{RCPT TO} command must begin with @samp{250} code. If it does, @command{mtasim} continues execution. Otherwise, it prints an error message and terminates with exit code 1. The error message it prints looks like: @smallexample Expected 250 but got 470 @end smallexample The expect code given with the @code{\E} command may have less than 3 digits. In this case it specifies the first digits of expected reply. For example, the command @samp{\E2} matches replies @samp{200}, @samp{220}, etc. This feature can be used to automate your tests. For example, the following script tests the greylisting functionality (see the previous section): @smallexample # @r{Test the greylisting functionality} # \E220 \Dclient_addr=10.10.1.13 \E250 ehlo yahoo.com \E250 mail from: \E450 rcpt to: \E221 quit @end smallexample @cindex @command{mtasim}, using in shell scripts This example also illustrates the fact that you can use @samp{#}-style comments in the @command{mtasim} input. Such a script can be used in shell programs, for example: @smallexample @group mtasim -Xauto --statedir -- -I. -I../mflib test.rc < @file{scriptfile} if $? -ne 0; then echo "Greylisting test failed" fi @end group @end smallexample @node traces @section Trace Files @cindex trace file, @command{mtasim} It is possible to log an entire @acronym{SMTP} session to a file. This is called @dfn{session tracing}. Two options are provided for this purpose: @table @option @mtasimopt{trace-file, described} @item --trace-file=@var{file} Sets the name of the trace file, i.e. a file to which the session transcript will be written. Both the input commands, and the @command{mtasim} responses are logged. If the file @var{file} exists, it will be truncated before logging. This, however, can be changed using the following option: @mtasimopt{append, described} @item -a @itemx --append If the trace file exists, append new trace data to it. @end table @node daemon mode @section Daemon Mode @mtasimopt{daemon, described} @cindex @command{mtasim} daemon mode To start @command{mtasim} in @dfn{daemon} mode, use the @option{--daemon} (or @option{-bd}) command line option. This mode is not quite the same as Sendmail @option{-bd} mode. When started in @dfn{daemon} mode, @command{mtasim} selects the first available @acronym{TCP} port to use from the range @samp{1024 -- 65535}. It prints the selected port number on the standard output and starts listening on it. When a connection comes, it serves a @emph{single} @acronym{SMTP} session and exits immediately when it is ended. This mode is designed for use in shell scripts and automated test cases. @node option summary @section @command{mtasim} command line options This section summarizes all available @command{mtasim} command line options. @table @option @mtasimopt{append, summary} @mtindex a, -a, @command{mtasim} option, summary @item --append @itemx -a Append to the trace file. @xref{traces}. @mtasimopt{body-chunk, summary} @item --body-chunk=@var{number} Set the body chunk length (bytes) for @code{xxfi_body} calls. @mtasimopt{daemon, summary} @mtindex bs, -bd, @command{mtasim} option, summary @item --daemon @itemx -bd Run as daemon. @xref{daemon mode}. @mtasimopt{define, summary} @mtindex D, -D, @command{mtasim} option, summary @item --define=@var{macro}=@var{value} @itemx -D @var{macro}=@var{value} Define Sendmail macro @var{macro} to the given @var{value}. It is similar to the @code{\D} administrative command (@pxref{D command}) @item --help @itemx -? Display a short help summary @mtasimopt{milter-version, summary} @item --milter-version=@var{number} Force using the given Milter protocol version number. This option is intended for development and testing of the Gacopyz library (@pxref{Gacopyz}). @mtasimopt{no-interactive, summary} @item --no-interactive Not-interactive mode (disable readline). @xref{Command Line Editing, , Command Line Editing, readline, GNU Readline Library}. @mtasimopt{port, summary} @mtindex X, -X, @command{mtasim} option, summary @item --port=@var{port} @itemx -X @var{port} Communicate with given Milter @var{port}. @xref{mtasim milter port}. @mtasimopt{prompt, summary} @item --prompt=@var{string} Set readline prompt. The default prompt string is @samp{(mtasim) }. @mtasimopt{statedir, summary} @item --statedir When using @option{-Xauto}, use the temporary directory name as @command{mailfromd} state directory (@pxref{statedir mtasim option}). @mtasimopt{stdio, summary} @mtindex bs, -bs, @command{mtasim} option, summary @item --stdio @itemx -bs Use the @acronym{SMTP} protocol on standard input and output. This is the default mode for @command{mtasim}. @xref{interactive mode}. @mtasimopt{trace-file, summary} @item --trace-file=@var{file} Set name of the trace file. @xref{traces}. @item --usage Display option summary @mtasimopt{verbose, summary} @mtindex v, -v, @command{mtasim} option, summary @item --verbose @itemx -v Increase verbosity level. @item --version @itemx -V Print program version @end table