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

FreeBSD Manual Pages


home | help
avenger(1)		      Mail Avenger 0.8.5		    avenger(1)

       avenger - Mail Avenger

       Mail Avenger is a highly-configurable MTA-independent SMTP (Simple Mail
       Transport Protocol) server designed to let you filter and fight SPAM
       before accepting	incoming mail from a client machine.  avenger is the
       script run on behalf of each user to decide whether to accept incoming

       When a client attempts to send mail to a	user on	the system, the
       avenger SMTP daemon, asmtpd, runs avenger to process the	file
       .avenger/rcpt in	the user's home	directory.  That file, a shell script
       with access to special functions, determines how	the SMTP server	should
       proceed.	 The possible outcomes are:

       o   Provisionally accept	the mail, falling back to system-default rules

       o   Accept the mail immediately with no further checks

       o   Reject the mail immediately

       o   Defer the mail, telling the client to re-send it later

       o   Redirect the	processing to another local name.  The name can	be
	   another email address belonging to the current user,	or an email
	   address belonging to	the special AvengerUser	user.  In the later
	   case, avenger will be re-run	with a different user ID, and hence
	   can,	for example, employ utilities that maintain state across
	   multiple users (assuming they all redirect processing the same

       o   Run a "bodytest" rule.  With	this outcome, the the SMTP transaction
	   continues on	to receive the entire contents of the mail message,
	   after which a program is run	on the contents	of the mail message.
	   That	program	can decide, based on the contents, whether to accept,
	   reject, defer, or silently discard the message.

       Mail Avenger should typically be	configured to have a Separator
       character, allowing each	user to	maintain multiple email	addresses.
       With sendmail, Separator	is typically "+", with qmail it	is typically
       "-".  If	the separator is "+", then any email sent to
       user+ext@your-host will be processed by files in	user's .avenger

       Avenger first checks for	a file named rcpt+ext in a user's .avenger
       directory, then for rcpt+default.  If ext itself	contains the separator
       character, for example user+ext1+ext2@your-host,	avenger	will check
       first for rcpt+ext1+ext2, then for rcpt+ext1+default, then for
       rcpt+default.  The same algorithm is extended for arbitrarily many
       separator characters.  (If separator is "-", simply replace "+" with
       "-" throughout the above	description, including in the names of files
       such as rcpt-default.)

       If mail is rejected by the recipient checks but the sender address of a
       message is local	and UserMail is	1 in asmtpd.conf (which	is not the
       default), then before rejecting mail, avenger will be run on behalf of
       the sending user.  In this case,	the address will be parsed as above,
       but avenger will	look for rules in files	beginning mail instead of
       rcpt.  This mechanism can be used by local users	who want to relay mail
       through the server from an untrusted IP address.

       Using the mail configuration files, each	user can, for instance,
       configure a mail+... file to accept mail	from an	IP address he or she
       trusts, even if that address is not trusted by all users.
       (Alternatively, using tools such	as macutil, a user might set up
       relaying	of mail	in which the envelope sender contains a	cryptographic
       code, checked by	the mail+... script.)

       Error output of an avenger script rcpt+ext or mail+ext is redirected to
       a file called log+ext in	the same directory, for	use in debugging.

       Avenger configuration files are simply shell scripts, using the syntax
       described in sh(1).  Each line of the file contains a variable
       assignment, command, or function	to run.	 Scripts can additionally make
       use of a	number of avenger-specific functions and variables.  This
       section describes avenger functions.  The next two sections describe

	   Certain error conditions result in Mail Avenger rejecting mail by
	   default, unless the message is explicitly accepted through an
	   accept or successful	bodytest check.	 These conditions are
	   indicated by	the MAIL_ERROR environment variable described below.
	   If your script either rejects mail or falls through to the default
	   behavior, there is often no reason to run tests on a	message	that
	   will	end up being rejected either way.  errcheck exits immediately
	   with	the default error if the default would be to reject or defer
	   the mail.

       accept [message]
	   Immediately accepts the message (without falling back to any
	   default rules).  If message is supplied, it will be returned	to the
	   SMTP	client.	 The default message is	"ok".

       reject [message]
	   Reject the mail, with message.  (The	default	message	is "command
	   rejected for	policy reasons").

       defer [message]
	   Reject the mail with	a temporary error code,	so that	a legitimate
	   mail	client will attempt to re-send it later.  The default for
	   message is "temporary error in processing".

       bodytest	command	[arg ...]
	   Accept the current SMTP "RCPT" command.  However, once the whole
	   mail	message	has been received with the SMTP	"DATA" command,	run
	   command with	the message as its standard input.  Depending on the
	   exit	status of command return to the	client's "DATA"	command	either
	   success, temporary, or permanent failure.  Exit code	0 means	accept
	   the mail, 100 means reject, 111 means reject	with a temporary error
	   code	(i.e., defer the mail).	 See the description of	bodytest in
	   the asmtpd/avenger interface	description for	more information on
	   bodytest (since this	function directly invokes bodytest in asmtpd).

	   Error output	from command will be redirected	to the same log	file
	   as output from the rcpt+... avenger script invoking the bodytest
	   function.  Standard output of command will be included as a
	   diagnostic the bounce message if the	exit code defers or rejects
	   the mail.

	   Note	that command and the arguments passed to bodytest will be run
	   by the shell.  Thus,	it is important	not to pass any	arguments that
	   might contain shell metacharacters such as ">" and "$".

       redirect	local
	   Finish processing, and re-run avenger as if mail were being sent to
	   a different username	local (possibly	belonging to the special
	   AvengerUser user).  See the description of redirect in the
	   asmtpd/avenger interface description	for more information on
	   redirect (since this	function directly invokes redirect in asmtpd).

       greylist	[sender-key]
	   This	command	defers mail the	first time mail	is received from a
	   particular sender at	a particular IP	address.  However, after a
	   certain interval, greylist_delay, if	the client re-sends the	mail,
	   it will be accepted.	 Furthermore, from that	point on, all mail
	   will	be immediately accepted	from that sender and IP	address,
	   unless the sender stops sending mail	for a period of	greylist_ttl2
	   or more.  If, however, after	sending	the initial, defered piece of
	   mail, the client does not try again within a	period of
	   greylist_ttl1, then any record of the client	will be	erased,	and
	   the next time it tries to send mail it will be defered again.

	   The parameters can be tuned by setting variables in the script.
	   The default values are:

	       greylist_delay=30m  # Time to wait before allowing message
	       greylist_ttl1=5h	   # How long to remember first-time senders
	       greylist_ttl2=36D   # How long to remember ok senders

	   m means minutes, h hours, and D days.  For a	complete list of
	   allowed suffixes, see the documentation for dbutil(1) (in
	   particular for the --expire option).

	   sender-key, if supplied, is used to identify	the sender.  The
	   default value is "$CLIENT_IP	$RECIPIENT $SENDER".  If, for example,
	   you wanted to record	only the first 24-bits of IP address and
	   didn't care about the recipient, you	could use the command:

	       greylist	"${CLIENT_IP%.*} $SENDER"

	   All functions that set a variable by	means of an external query to
	   asmtpd are performed	asynchronously.	 setvars actually waits	for
	   results and sets the	values of those	variables.  In this way, a
	   number of potentially slow requests (such as	DNS lookups) can be
	   initiated concurrently, and their latencies overlapped.  However,
	   one must remember to	call setvars, or else variables	that should
	   contain the results of operations will remain unset.

       dns var type domain-name
	   Performs a DNS lookup of domain-name	for records of type type, and
	   assigns the result to variable var when you call setvars.  type
	   must	be one of a, mx, ptr, or txt (lower-case only).

       rbl [-ipf] var domain
	   Looks up the	current	mail sender in a real-time blackhole list
	   (RBL).  domain is the domain	name of	the RBL	(e.g.,
	   "").  If the sender is	listed,	set var	to the result
	   of the DNS lookup when you next call	setvars.  -i looks up the
	   sender's IP address (the default if no options are specified).  -p
	   looks up the	sender's domain	name (verified DNS PTR record).	 -f
	   looks up the	envelope sender	domain name in the RBL.

       spf0 var	[spf-mechanism ...]
       spf var [spf-mechanism ...]
	   Tests the sender against an arbitrary query formulated in the SPF
	   language.  This is a	powerful way to	whitelist or blacklist
	   particular senders.	For example, suppose you want to accept	any
	   mail	from machines in the list maintained by,
	   accept mail from any	machine	name ending ""	reject any
	   mail	from users in the spamcop RBL, and for other users fall	back
	   to the default system-wide rules.  You might	use the	following rcpt

	       spf MYSPF \ -exists:%{ir} ?all
	       case "$MYSPF" in
		       accept "I like you"
		       reject "I don't like you"
		       # Note, could instead fall through to default here
		       defer "Temporary	DNS error"

	   Note	that commands spf0 and spf are synonymous, but spf is
	   deprecated, because in a later release of Mail Avenger spf will
	   become synonymous with spf1.

       spf1 var	[spf-mechanism ...]
	   Performs the	same tests as the spf directive, but returns the
	   result strings None,	Neutral, Pass, Fail, SoftFail, TempError, and
	   PermError instead of	none, neutral, pass, fail, softfail, error,
	   and unknown.

       These variables are set by the avenger script.  In addition, asmtpd
       sets a number of	environment variables before running avenger.  These
       are documented in the next section, ENVIRONMENT.

	   The extension on the	file currently being processed.	 For example,
	   if file rcpt+ext is being processed,	will be	set to "+ext".	Empty
	   when	processing just	rcpt (or mail).	 May also contain default when
	   a default rule file for some	suffix is being	run.

	   Assuming the	separator is "+", when processing a file
	   rcpt+base+default or	mail+base+default, PREFIX is set to base,
	   while SUFFIX	is set to the portion of the name for which default
	   was substituted.  When the file does	not end	with default, SUFFIX
	   is empty.  When the file is just rcpt with no extension, both
	   PREFIX and SUFFIX are empty.	 When SUFFIX itself contains a "+"
	   character, SUFFIX1 contains to the part of SUFFIX after the first
	   "+" character, SUFFIX2 contains the part after the second "+", and
	   so on for each "+" character	in suffix.

	   If Mail Avenger was compiled	with SASL support (which is not	the
	   default, unless you supplied	the --enable-sasl argument to
	   "configure"), and if	the client successfully	authenticates to the
	   server using	SASL, then AUTH_USER will be set to the	name of	the
	   authenticated user.

	   Set to "rcpt" when testing whether a	recipient should receive mail.
	   Set to "mail" (possibly after an "rcpt" check fails)	when checking
	   whether to relay mail (possibly on behalf of	a local	user).

	   The effective local username	for which avenger is being run.
	   Ordinarily, this will be the	same as:


	   However, for	special	avenger	files like unknown and default,	it can
	   contain useful information, because unlike the RECIPIENT_LOCAL
	   environment variable, AVUSER	reflects substitutions from the	Mail
	   Avenger domains and aliases files.

	   This	variable contains the name of the client machine, as typically
	   reported in "Received:" headers.  Its value has the form:


	   user	is the user name for the connection reported by	the client, if
	   the client supports the RFC 1413 identification protocol, otherwise
	   it is omitted.  host	is a verified DNS hostname for the IP, if
	   asmtpd could	find one.  Otherwise, it is simply the numeric IP

	   Set to 1 if the client included a space between the colon in	the
	   command "MAIL FROM:"	or "RCPT TO:" and the subsequent "<" that
	   begins an email address.

	   If AllowDNSFail is set to 1 in the asmtpd.conf file and resolving
	   the client's	IP to a	hostname returns a temporary error, then this
	   variable will be set	to a description of the	error.

	   Set to the argument the client supplied to the SMTP "HELO" or
	   "EHLO" command.

	   Set to the IP address of the	client.

	   Set to the verified DNS name	of the client, if asmtpd can find one.

	   Set to the number of	network	hops between the server	and the
	   client, if asmtpd can get the client	or its firewall	to return an
	   ICMP	destination unreachable	(type 3	packet)	in response to a UDP
	   probe.  Whether or not this is set will depend on firewall

	   Set to as many intermediary network hops as asmtpd can determine
	   between the server and the client.  How close to the	client asmtpd
	   can probe will depend on firewalls.

	   Set to 1 if the client wrote	data after the SMTP HELO or EHLO
	   command, before receiving its response.  A correct SMTP client
	   should not "pipeline" commands until	after receiving	the result of
	   the HELO command and	verifying that the server accepts pipelined

	   The TCP port	number of the client.

	   Set to 1 if the client sent a "POST"	command	at some	point during
	   the SMTP session.  "POST" is	not a valid SMTP command; it is	an
	   HTTP	command.  However, one technique for sending spam involves
	   exploiting an open web proxy	to "post" an SMTP session to a mail
	   server.  The	initial	HTTP headers (including	the HTTP post command)
	   simply cause	SMTP syntax errors, while the body of the POST command
	   contains SMTP commands.  By checking	the CLIENT_POST	environment
	   variable, you to reject mail	sent in	this way.

	   The value of	CLIENT_IP with the order of the	bytes reversed.
	   Suitable for	prepending to "" or an RBL	domain to
	   perform a DNS lookup	based on IP address.

	   Contains a fingerprint, abstracting the contents of the initial TCP
	   SYN packet the client sent to establish the TCP connection.	The
	   exact contents of SYN packets depends on the	operating system and
	   version of the client, and can therefore reveal interesting
	   information about the type of client	connecting to your mail
	   server.  The	format of the fingerprint is:


	   Where the fields are	as follows:

	       the initial TCP window size

	   ttt the IP ttl of the received packet

	   D   the IP "don't fragment" bit

	   ss  total size of the SYN packet (including IP header)

	   OOO a comma-separated list of TCP options, as follows:

	       N   NOP option

		   window scaling option with value nnn

		   maximum segment size	value nnn

	       S   Selective ACK OK

	       T   timestamp option

	       T0  timestamp option with value zero

	   If asmtpd can guess the client's operating system based on
	   CLIENT_SYNFP, it will set CLIENT_SYNOS to the value of that guess.
	   For example,	to greylist mail from Windows machines,	you can	run:

	      match -q "*Windows*" "$CLIENT_SYNOS" && greylist

	   This	variable is not	really an avenger variable, as it is only
	   available in	bodytest commands.  It specifies the number of bytes
	   of message transfered in the	SMTP DATA command, but after
	   converting CR NL sequences to NL.  Roughly speaking this is how
	   many	bytes are in the message including all headers after the
	   X-Avenger:, SPF-Received, or	Received: header.

	   The value of	EtcDir from the	asmtpd configuration file (or
	   /etc/avenger	by default).

       EXT When	avenger	runs on	behalf of a user EXT is	set to the part	of the
	   address that	determines the suffix of the rcpt or mail file.	 For
	   example, suppose Separator is "-" and the recipient is
	   list-subscribe@host,	where host is not a virtual domain.  If	the
	   AliasFile contains:

	       list: user-mylist

	   Then	avenger	will be	run on behalf of "user"	(because alias
	   expansion yields user-mylist-subscribe).  EXT will be set to

	   Note	that EXT is empty when there is	no suffix, and that it is
	   equal to the	name of	the system file	being processed	when avenger
	   is run on a system file.  Like RECIPIENT, this variable is not set
	   for bodytest	commands.

	   Set to the name of the local	host, as specified by the HostName
	   directive in	avenger.conf.

	   This	variable is set	when the SPF disposition of the	sender is
	   fail, or when asmtpd	is unable to send a bounce message to the
	   sender address.  In either case, Mail Avenger will reject the mail
	   if the script falls through to the default.

	   A randomly generated	string for this	message, which can be useful
	   to correlate	calls to rcpt scripts with bodytest scripts.  Note
	   this	is unrelated to	the Message-ID header in the message, but does
	   show	up in the Received header that Mail Avenger inserts.

	   IP address of local end of SMTP TCP connection.

	   TCP port number of local end	of SMTP	TCP connection.	 Ordinarily
	   this	will be	25.

	   The envelope	recipient of the message.  Note	that this environment
	   variable is not present for bodytest	programs, since	such programs
	   may be run on behalf	of multiple users.

	   The domain part of RECIPIENT, folded	to lower-case--i.e., host when
	   RECIPIENT is	local@host.  Not present for bodytest programs,	as
	   noted in the	description of RECIPIENT.

	   The local part of RECIPIENT,	folded to lower-case--i.e., local when
	   RECIPIENT is	local@host.  Not present for bodytest programs,	as
	   noted in the	description of RECIPIENT.

	   The envolope	sender of this mail message (i.e., the argument
	   supplied by the client to the "MAIL FROM:" SMTP command.)

	   The hostname	part of	SENDER,	converted to lower-case	(i.e., host in

	   The local part of SENDER, converted to lower-case (i.e., user in

	   A list of DNS MX records for	SENDER_HOST, if	that hostname has any
	   MX records.

	   For non-empty envelope senders, asmtpd attempts to see if it	is
	   possible to deliver bounce messages for the sender.	If not,
	   SENDER_BOUNCERES is set to a	three-digit SMTP error code.  If the
	   first digit is 4, the error was temporary.  If the first digit is
	   5, the error	was permanent.	Note that failure to accept bounce
	   messages is considered a MAIL_ERROR as described above, and will
	   cause mail to be rejected by	default.

	   The value of	Separator from the asmtpd configuration	file.  There
	   is no default (SEPARATOR will not be	set if no Separator is
	   specified in	the configuration file).  However, it should be
	   configured for "+" with sendmail and	"-" with qmail.

       SPF The result of performing an SPF check on the	message.  Will be one
	   of: none, neutral, pass, fail, softfail, error, or unknown.	Note
	   that	SPF0 and SPF are synonymous, but SPF is	deprecated as a	future
	   release of Mail Avenger will	make SPF synonymous with SPF1.

	   Also	the result of performing an SPF	check on the message, but
	   returns different names for the results, to be compatible with
	   newer revisions of the SPF protocol specification.  The new names
	   are None, Neutral, Pass, Fail, SoftFail, TempError, and PermError.

	   The explanation string that goes along with a bad SPF status.

	   If the Mail Avenger has been	compiled with support for the STARTTLS
	   command (using the --enable-ssl option to "configure"), and the
	   client is communicating over	SSL/TLS, this variable will contain a
	   textual description of the algorithm.

	   SSL_CIPHER_BITS contains the	number of secret key bits used by the
	   SSL/TLS ciphers.  SSL_ALG_BITS is the number	of bits	used by	the
	   algorithm.  For example, if you are using 128-bit RC4 with 88 bits
	   sent	in cleartext, SSL_CIPHER_BITS will only	be 40, since that is
	   the effective security, while SSL_ALG_BITS will be 128.

	   If the client has successfully authenticated	itself using an	SSL
	   certificate,	SSL_ISSUER will	be set to the certificate signer's
	   common name,	while SSL_ISSUER_DN will be set	to a compact
	   representation of the signer's full distinguished name.  The	full
	   distinguished name is in the	form output by the command:

		   openssl x509	-noout -issuer -in cert.pem

	   Note	that this variable is mostly useful if the SSLCAcert file you
	   have	given to Mail Avenger contains more than one certificate
	   authority, or signs other CA	certificates.  Mail Avenger will not
	   accept client certificates if it does not recognize the signer of
	   the certificate.

	   If the client has successfully authenticated	itself using an	SSL
	   certificate,	SSL_SUBJECT will be set	to the client's	common name in
	   the certificate, while SSL_SUBJECT_DN will be set to	a compact
	   representation of the client's full distinguished name.  The	full
	   distinguished name is in the	form output by the command:

		   openssl x509	-noout -subject	-in cert.pem

	   The version of the SSL/TLS protocol in use.

	   An mbox "From " line	suitable for prepending	to the message before
	   passing the message to a delivery program.  (This is	mostly useful
	   for bodytest	rules.)

	   The name of the user	under which avenger is running.

       avenger is just a simple	shell script.  You can inspect the file	to see
       what it is doing.  Most of the interesting operations happen in either
       asmtpd, or in external programs spawned from avenger.  This section
       documents the interface between asmtpd and avenger.

       avenger inherits	a unix-domain socket connected to asmtpd on its
       standard	input and output.  It sends commands to	asmtpd over this
       socket, and similarly reads replies from	it.  In	order to avoid mixing
       messages	to and from asmtpd with	the output of other programs you run,
       however,	the avenger shell script reorganizes its file descriptors so
       that all	communication to and from asmtpd happens over file descriptor
       number 3.

       Each command consists of	a single line, followed	by a newline (except
       the return command, which can optionally	take multiple lines).  There
       may or may not be a reply, possibly depending on	the outcome of the
       command.	 Most replies consist of zero or more lines of the form


       VARIABLE	is typically a variable	name that was supplied as part of the
       command.	 The avenger shell script records results by setting the
       environment variable VARIABLE to	value, so that it can be accessed by
       subsequent lines	of the script.

       Replies are sent	in the order in	which the corresponding	commands were
       received.  However, asmtpd executes requests asynchronously.  Thus, one
       can perform several concurrent operations (such as DNS requests or SPF
       tests) by simply	writing	multiple commands to asmtpd before receiving
       any of the responses.

       The "." command is a no-op, but asmtpd echoes the "." back to avenger
       as the reply.  This allows one to synchronize the avenger process's
       state after issuing one or more commands.  For example, one might issue
       several DNS lookups to check various RBLs (real-time blackhole lists),
       then issue a . command, then wait for replies.  When the	. comes	back,
       all previous commands will also have completed.	The avenger setvars
       command simply sends a ".", then	loops until it reads back the ".",
       setting variables from any previous commands whose replies it reads in
       the process.

       The following commands are available:

       .   The . command is simply echoed back by asmtpd.

       bodytest	command
	   Ends	the current avenger script.  Specifies that asmtpd should
	   receive the entire body of the message, then	run command (under the
	   same	user ID	as the current avenger script) with the	entire mail
	   message as its standard input.  asmtpd then replies to the SMTP
	   "DATA" command based	on the exit status of command as follows:

	   0   If command exits	with status 0, asmtpd will reply to the	"DATA"
	       command with success (SMTP code 250), and will pass the message
	       to sendmail (or whatever	you have configured as Sendmail	in
	       asmtpd.conf) for	delivery.

	   99  If command exits	with status 99,	asmtpd will still reply	to the
	       "DATA" command with a successful	250 reply code,	but will not
	       spool the data.	Either command must have done something	with
	       the data, or the	message	will be	lost.

	   100 (also 64, 65, 70, 76, 77, 78, 112)
	       If command exits	with status 100	(or any	of the above exit
	       statuses), avenger will reject the mail with a hard SMTP	error
	       (code 554).  If command wrote output to its standard output,
	       this output will	be passed back to the mail client.  Otherwise,
	       asmtpd will supply the text "message contents rejected."

	   111 (or any other exit status)
	       If command exits	with status 111, the result is the same	as
	       exit status 100,	except that asmtpd will	use a temporary	error
	       code (451) instead of 554.

	       If command exits	abnormally because of a	signal,	asmtpd will
	       also use	451, but in this case will not pass the	program's
	       output back to the client.  It will instead pass	back a
	       description of the problem.

	   Note	that asmtpd can	only run one bodytest command per message.  If
	   there are multiple recipients of a message, all must	run the	same
	   bodytest under the same user	ID.  If	two users wish to run
	   different bodytest commands,	or even	run the	same command under
	   different user IDs, asmtpd will defer the second SMTP "RCPT"
	   command with	the message:

	       452 send	a separate copy	of the message to this user

	   This	will cause the mail client to re-send the message later	to the
	   second user.	 To avoid forcing clients to send multiple copies of
	   messages, you can place bodytest commands in	system wide files
	   (such as the	default	rule file), or use a redirect command to
	   redirect to the AvengerUser,	so that	commands for multiple users
	   can be run under the	AvengerUser user ID.

	   Note	that file descriptor 0 inherited by command is opened for both
	   reading and writing.	 Thus, it is possible to modify	the message
	   before it is	spooled	by the local MTA.  The command edinplace(1) is
	   useful for running messages through spam filters that annotate
	   messages before spooling them.

       dns-a VARIABLE domain-name
	   Requests that asmtpd	perform	a DNS lookup for A (IPv4 address)
	   records on domain-name.  If such an A record	exists,	the reply is a
	   list	of one or more IP addresses:

	       VARIABLE=IP-address ...

	   If no such A	record exists, the reply is simply:


	   With	the standard avenger script, this sets VARIABLE	to the empty
	   string.  If there is	a temporary error in DNS name resolution,
	   there is no reply, and hence	with the default avenger script
	   VARIABLE will remain	unset.

	   When	checking such things as	RBLs, it is advisable not to reject
	   mail	because	of a temporary DNS error.  You can use the shell
	   construct ${VARIABLE-default}$ to return $VARIABLE when VARIABLE is
	   set,	and default when VARIABLE is not set.  Similarly
	   ${VARIABLE+set} returns set if VARIABLE is set, and the empty
	   string otherwise.

	   For example,	if contained an	RBL of undesirable
	   sender hosts:

	       echo dns-a BADSENDER "$SENDER_HOST" >&3
	       test -n "$BADSENDER" && reject "$SENDER_HOST is a bad sender"
	       test -z "${BADSENDER+set}" \
		   && defer "$ DNS error"

	   Note	that when using	the avenger script, there is already a
	   function rbl	to check RBLs.

       dns-mx VARIABLE domain-name
	   Similar to dns-a, but looks up MX records.  A successful reply is
	   of the form:

	       VARIABLE=priority-1:host-1 [priority-2:host-2 ...]

	   Where priority-1 is the MX priority of host-1.  As before, an empty
	   string indicates no MX records exist, and no	reply indicates	an

       dns-ptr VARIABLE	IP-address
	   Returns a list of verified DNS hostnames for	IP-address.  As
	   before, an empty string for VARIABLE	indicates no PTR records
	   exist, and no reply indicates an error.

       dns-txt VARIABLE	domain-name
	   Similar to the other	dns commands, but looks	up a record of type
	   TXT.	 If multiple TXT records exist,	returns	only one.  Places some
	   restrictions	on the TXT records, for	example	will not return	one
	   that	contains a newline character.

       netpath VARIABLE	IP-address
	   Maps	out the	network	hops to	IP-address (this is similar to the
	   traceroute system utility, but more efficient).  The	reply is of
	   the form:

	       VARIABLE=#hops hop1 hop2	...

	   #hops is the	total number of	network	hops to	IP-address if asmtpd
	   can figure this out.	 (It won't always be able to if	IP-address is
	   behind a firewall.)	If asmtpd cannot figure	this out, the value is
	   -1.	hop1 and the remaining arguments are the addresses of routers
	   along the way to IP-address.

       redirect	local
	   Terminates the current avenger process, and instead processes the
	   mail	as though it is	being sent to local.  This command is only
	   available in	"rcpt" mode, as	opposed	to "mail" mode (in which
	   asmtpd runs avenger to see if it should relay mail for a local user
	   on a	non-local client machine).

	   local can be	a local	user name, or a	local user name	followed by
	   the separator character and an extension.  The name is mapped using
	   the aliases (specified by AliasFile in asmtpd.conf).

	   Note	that while the AvengerUser user	can redirect to	other users,
	   ordinary users can only redirect to themselves or the AvengerUser.

       return code explanation
       return code-explanation
       code explanation
	   Specifies the SMTP reponse desired.	Also avoids further processing
	   of the message with system-wide default rulesets (as	typically
	   happens when	avenger	simply exits with status 0).  code must	be a
	   three digit number beginning	2, 4, or 5.  (usually 250 for success,
	   451 to defer	mail, and 554 to reject	mail).

	   The first form of this command (with	a space	between	code and
	   explanation)	gives a	single line explanation	along with the result
	   code.  In the second	form, avenger specifies	a multi-line response.
	   In this case	all but	the last line must contain a - between the
	   code	and explanation, while the last	line must contain a space.
	   (Note that the return keyword only appears on the first line; after
	   starting to issue a return command, no further commands can be

       spf VARIABLE SPF-mechanism ...
       spf0 VARIABLE SPF-mechanism ...
       spf1 VARIABLE SPF-mechanism ...
	   Evaluates the mail client based on SPF mechanisms.  It will return:


	   where, for spf0, disposition	is one of:  none, neutral, pass, fail,
	   softfail, error, or unknown (though the disposition none is
	   actually impossible).  For spf1, the	equivalent disposition names
	   are None, Neutral, Pass, Fail, SoftFail, TempError, PermError.
	   (Currently spf is a synonym for spf0, but it	is recommended that
	   you avoid using spf as in a future release it may become an alias
	   for spf1.)

	   As an example, suppose that your username is	"joe", Separator is
	   "+",	and you	have subscribed	to a number of yahoo mailing lists
	   using email address "joe+yahoo".  If	spammers started sending mail
	   to "joe+yahoo", you would want to reject all	mail to	that address
	   except that originating from	yahoo's	computers.  Yahoo's computers
	   might correspond to anything	ending "" or sharing a
	   24-bit IP-address prefix with any of's MX records.	 This
	   can be accomplished with the	following script in

	       echo spf	YAHOO -all >&3
	       case "$YAHOO" in
		   reject "Sorry, this private alias for Yahoo lists only"
		   defer "Sorry, temporary DNS error"

       If you never use	your email address as an envelope sender, you can
       reject all bounces to that address with these commands in your rcpt

	   test	-z "$SENDER" \
	       && reject "<$RECIPIENT> not a valid sender;" \
	       " should	not receive bounces"

       The following script runs spamassassin (a popular spam filter,
       available from <>) on the body of a
       message,	unless the sender of the message has an	SPF disposition	of
       pass or is already going	to be rejected by default.

	   # The next line immediately falls through to	the default reject
	   # disposition when mail has an SPF disposition of fail or the
	   # sender does not accept bounce messages.

	   test	"$SPF" = pass \
	       || bodytest edinplace -x	111 spamassassin -e 100

       The following script immediately	accepts	any mail from any machine at
       MIT or NYU (provided MAIL_ERROR is not set), "greylists"	machines not
       in one of those domains,	and if the greylist passes, falls through to
       the the default,	system-wide rules:


	   spf TRUSTED ?all
	   test	pass = "$TRUSTED" && accept Trusted sender OK


       The following script rejects mail from clients that have	issued an SMTP
       "POST" command (which doesn't exist) or used aggressive,	premature
       pipelining of commands.	If the client put a space after	the colon in
       the MAIL	FROM: or RCPT TO: SMTP commands, it greylists the message
       using a key that	includes the SYN fingerprint and first 24-bits of the
       IP address.  If the SPF disposition of the message is error, it defers
       the message.  If	the SPF	disposition of the message is softfail or
       none, it	runs the body of the message through spamassassin.


	   test	-n "$CLIENT_POST" -o -n	"$CLIENT_PIPELINING" \
	       && reject "no spam please"

	   test	-n "$CLIENT_COLONSPACE"	\
	       && greylist "${CLIENT_IP%.*} $CLIENT_SYNFP $SENDER"

	   case	"$SPF" in
		   defer "Temporary error in SPF record	processing"
		   bodytest edinplace -x 111 spamassassin -e 100

       If you set your MACUTIL_SENDER environment variable to be
       "user+bounce+*" and send mail with	macutil	--sendmail,
       you can create the following rcpt+bounce+default	to accept mail only to
       valid bounce addresses.

	   macutil --check "$SUFFIX" > /dev/null \
	       || reject "<$RECIPIENT>.. user unknown"

       In conjunction with this	script,	you may	want to	reject bounce messages
       to your regular email addresss with your	rcpt script, as	described in
       the first example.

       This example is slightly	more complicated, and shows how	to use a
       bodytest	to reject mail based on	message	contents.  The goal of this
       set-up is to check each message with the	ClamAV anti-virus software
       (from <>) and the spamassassin mail filter.  If
       the message contains a virus or is flagged as spam, it should be
       rejected	with an	explanation of the problem.  We	construct a shell
       script, $HOME/.avenger/body, to run these tests on message bodies.  The
       script can be invoked with the line

	   bodytest $HOME/.avenger/body

       in your $HOME/.avenger/rcpt file.  Or, alternatively the	script could
       be configured to	run in the system-wide /etc/avenger/default file (in
       which case you want to make sure	that the AvengerUser can write its own
       home directory, so as to	store spamassassin files).  The	script is as

	   out="`clamscan -i --no-summary --mbox -  2>&1`"
	   if test "$?"	= 1; then
	       echo This message appears to be infected	with a virus
	       printf "%s\n" "$out" \
		   | sed -e '/Warning:/d' -e 's/^[^:]*:	//' | sort -u
	       exit 100

	   out="`edinplace -x 111 spamassassin -e 100`"
	   case	"$?" in
		   exit	0
		   echo	Sorry, spamassassin has	flagged	your message as	spam
		   while read a	b c; do
		       test "$a	$b" = "Content analysis" && break
		   read	a
		   read	a
		   read	a
		   while read a	b c; do
		       case "$a" in
			   printf "  %s\n" "$c"
			   printf "    %s\n" "$a $b $c"
		   exit	100
		   if test -n "$out"; then
		       echo spamassassin failure:
		       printf "%s\n" "$out"
		       echo system error in spamassassin
		   exit	111

       The first half of this script runs the clamscan virus checker, storing
       the output in variable out.  clamscan exits with	code 1 when a virus is
       found, exits 0 on success, and uses other error codes to	indicate
       various system errors.  We only want to reject mail if clamscan exits
       with code 1.  When this happens,	we take	the output of clamscan,	format
       it in a more pleasing way (stripping out	warnings), and send it to
       standard	output.	 An example of an SMTP transaction using this bodytest
       and detecting a virus will look like this (tested with the special
       EICAR test string that flags a positive with most virus checkers):

	   354 enter mail, end with "."	on a line by itself
	   Subject: eicar test

	   554-This message appears to be infected with	a virus
	   554 Eicar-Test-Signature FOUND

       If the virus check fails, the script runs the message through
       spamassassin to check for spam.	Note that spamassassin modifies	the
       mail message, so	that we	must run it with edinplace.  Note also that
       clamscan	will read to the end of	the input file,	but this is okay since
       edinplace rewinds its standard input.  We use the -e flag to tell
       spamassassin to exit 100	on spam.  Then,	if spamassassin	exits 0, we
       accept the mail.	 If it exits with anything but 100, something went
       wrong and we temporarily	defer the mail.	 Note that it might also be
       possible	to accept the mail at this point, but since spamassassin edits
       the file	in place, the message may be truncated if spamassassin exits

       If spamassassin exits 100, we reject the	mail.  We also report on why
       spamassassin has	rejected the mail.  Here again we take advantage of
       the fact	that edinplace rewinds its standard input both before and
       after processing	a message.  Because the	file descriptor	has been
       rewound,	we can start processing	the message one	line at	a time with
       the shell script.  Spamassassin by default (if you have not configred
       it with "report_safe 0")	contains a spam	report like this:

	Content	analysis details:   (11.7 points, 5.0 required)

	 pts rule name	      description
	---- --------------- --------------------------------------------------
	 1.0 RATWARE_RCVD_AT Bulk email	fingerprint (Received @) found
	 4.2 X_MESSAGE_INFO  Bulk email	fingerprint (X-Message-Info) found
	 0.0 MONEY_BACK	     BODY: Money back guarantee
	 0.5 BIZ_TLD	     URI: Contains a URL in the	BIZ top-level domain
	 0.6 URIBL_SBL	     Contains a	URL listed in the SBL blocklist
	 0.5 URIBL_WS_SURBL  Contains a	URL listed in the WS SURBL blocklist

       We skip over the	headers, and for each result, print it to the SMTP
       session.	 Negative/whitelist results (those starting -),	we do not
       report, and comment lines (not starting with a number) we print
       indented.  A typical SMTP session looks like this (using	the special
       GTUBE test line that triggers spam filters):

	   354 enter mail, end with "."	on a line by itself
	   Subject: gtube test

	   554-Sorry, spamassassin has flagged your message as spam
	   554-	 Missing Date: header
	   554	 BODY: Generic Test for	Unsolicited Bulk Email

       Here's an example of how	to use SSL client certificates for
       authentication.	If you have a private CA with common name "My CA" that
       signs the certificates of all your authorized mail clients, you can
       place the following in /etc/avenger/relay to permit those clients to

	   test	"My CA"	= "$SSL_ISSUER"	\
	       && accept "Relaying permitted for client	$SSL_SUBJECT"
	   reject "relaying denied"

       /usr/local/libexec/avenger, /etc/avenger/default, $HOME/.avenger/rcpt,
       $HOME/.avenger/rcpt* $HOME/.avenger/mail, $HOME/.avenger/mail*

       dbutil(1), deliver(1), edinplace(1), escape(1), macutil(1), match(1),
       synos(1), asmtpd.conf(5), asmtpd(8), avenger.local(8)

       The Mail	Avenger	home page: <>.

       avenger (and the	configuration files it reads) are shell	scripts.  In a
       shell script, it	is sometimes tempting to use "echo ..."	where one
       should instead use the command "printf '%s\n' ...".  (The later just
       prints its argument to standard output, while the former	interprets
       various "\" escape codes.)

       In shell	scripts, one must be careful about variables containing	shell
       metacharacters.	For example, it	is not safe to run something like:

	       bodytest	"echo $VAR > $PWD/log"

       if variable "VAR" has untrusted contents	that might contain characters
       like ">"	or ";".	 The reason is that $VAR will be expanded and sent
       back to the SMTP	server,	which will then	pass the expansion to the
       shell to	execute	the bodytest.  ($VAR effectively gets expanded twice.)
       The escape utility can be used to avoid these problems.	For example:

	       bodytest	echo `escape "$VAR"` ">" $PWD/log

       It is easy to forget to call setvars after a dns, rbl, or spf command.

       David Mazieres

Mail Avenger 0.8.5		  2018-10-09			    avenger(1)


Want to link to this manual page? Use this URL:

home | help