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

FreeBSD Manual Pages

  
 
  

home | help
MIMEDEFANG-FILTER(5)	      File Formats Manual	  MIMEDEFANG-FILTER(5)

NAME
       mimedefang-filter - Configuration file for MIMEDefang mail filter.

DESCRIPTION
       mimedefang-filter  is  a	 Perl fragment that controls how mimedefang.pl
       disposes	of various parts of a MIME message.  In	addition, it  contains
       some  global  variable  settings	 that  affect the operation of mimede-
       fang.pl.

CALLING	SEQUENCE
       Incoming	messages are scanned as	follows:

       1) A temporary working directory	is created.  It	is  made  the  current
       working	directory  and	the e-mail message is split into parts in this
       directory.  Each	part is	 represented  internally  as  an  instance  of
       MIME::Entity.

       2) If the file /usr/local/etc/mimedefang/mimedefang-filter.pl defines a
       Perl function called filter_begin, it is	called with a single  argument
       consisting  of  a  MIME::Entity representing the	parsed e-mail message.
       Any return value	is ignored.

       3) For each leaf	part of	the mail message, filter is called  with  four
       arguments: entity, a MIME::Entity object; fname,	the suggested filename
       taken from the MIME Content-Disposition header; ext,  the  file	exten-
       sion, and type, the MIME	Content-Type value.  For each non-leaf part of
       the mail	message, filter_multipart is called with the same  four	 argu-
       ments  as filter.  A non-leaf part of a message is a part that contains
       nested parts.  Such a part has no useful	body,  but  you	 should	 still
       perform filename	checks to check	for viruses that use malformed MIME to
       masquerade as non-leaf parts (like message/rfc822).   In	 general,  any
       action  you  perform in filter_multipart	applies	to the part itself and
       any contained parts.

       Note that both filter and filter_multipart are optional.	 If you	do not
       define them, a default function that simply accepts each	part is	used.

       4)  After  all  parts  have  been processed, the	function filter_end is
       called if it has	been defined.  It is passed a single argument consist-
       ing  of	the  (possibly	modified) MIME::Entity object representing the
       message about to	be delivered.  Within filter_end, you can  call	 func-
       tions that modify the message headers body.

       5) After	filter_end returns, the	function filter_wrapup is called if it
       has been	defined.  It is	passed a single	 argument  consisting  of  the
       (possibly  modified) MIME::Entity object	representing the message about
       to be  delivered,  including  any  modifications	 made  in  filter_end.
       Within  filter_wrapup,  you can not call	functions that modify the mes-
       sage body, but you can still add	or modify message headers.

DISPOSITION
       mimedefang.pl examines each part	of the MIME message and	chooses	a dis-
       position	 for  that part.  (A disposition is selected by	calling	one of
       the following functions from filter and	then  immediately  returning.)
       Available dispositions are:

       action_accept
	      The  part	 is passed through unchanged.  If no disposition func-
	      tion is returned,	this is	the default.

       action_accept_with_warning
	      The part is passed through unchanged, but	a warning is added  to
	      the mail message.

       action_drop
	      The part is deleted without any notification to the recipients.

       action_drop_with_warning
	      The part is deleted and a	warning	is added to the	mail message.

       action_replace_with_warning
	      The part is deleted and instead replaced with a text message.

       action_quarantine
	      The  part	is deleted and a warning is added to the mail message.
	      In addition, a copy of the part is saved on the mail  server  in
	      the  directory  /var/spool/MD-Quarantine	and  a notification is
	      sent to the MIMEDefang administrator.

       action_bounce
	      The entire e-mail	message	is rejected and	an error  returned  to
	      the  sender.   The  intended  recipients are not notified.  Note
	      that in spite of the name, MIMEDefang does not generate  and  e-
	      mail  a failure notification.  Rather, it	causes the SMTP	server
	      to return	a 5XX SMTP failure code.

       action_discard
	      The entire e-mail	message	is discarded  silently.	  Neither  the
	      sender nor the intended recipients are notified.

CONTROLLING RELAYING
       You  can	 define	 a  function called filter_relay in your filter.  This
       lets you	reject SMTP connection attempts	early on in the	 SMTP  dialog,
       rather  than  waiting until the whole message has been sent.  Note that
       for this	check to take place, you must use the -r flag with mimedefang.

       filter_relay is passed five arguments: $hostip is the IP	address	of the
       relay  host  (for  example, "127.0.0.1"), $hostname is the host name if
       known (for example, "localhost.localdomain").  If the host  name	 could
       not  be	determined,  $hostname is $hostip enclosed in square brackets.
       (That is, ("$hostname" eq "[$hostip]") will be true.)

       The remaining three arguments to	filter_relay are $port,	$myip and $my-
       port which contain the client's TCP port, the Sendmail daemon's listen-
       ing IP address and the Sendmail daemon's	listening port.

       filter_relay must return	a two-element list: ($code, $msg).  $msg spec-
       ifies  the text message to use for the SMTP reply, but because of limi-
       tations in the Milter API, this message is for  documentation  purposes
       only---you cannot set the text of the SMTP message returned to the SMTP
       client from filter_relay.

       $code is	a literal string, and can have one of the following values:

       'REJECT'
	      if the connection	should be rejected.

       'CONTINUE'
	      if the connection	should be accepted.

       'TEMPFAIL'
	      if a temporary failure code should be returned.

       'DISCARD'
	      if the message should be accepted	and silently discarded.

       'ACCEPT_AND_NO_MORE_FILTERING'
	      if the connection	should be accepted and	no  further  filtering
	      done.

       Earlier versions	of MIMEDefang used -1 for TEMPFAIL, 0 for REJECT and 1
       for CONTINUE.  These values still work, but are deprecated.

       In the case of REJECT or	TEMPFAIL, $msg specifies the text part of  the
       SMTP reply.  $msg must not contain newlines.

       For example, if you wish	to reject connection attempts from any machine
       in the spammer.com domain, you could use	this function:

       sub filter_relay	{
	    my ($ip, $name) = @_;
	    if ($name =~ /spammer\.com$/) {
		 return	('REJECT', "Sorry; spammer.com is blacklisted");
	    }
	    return ('CONTINUE',	"ok");
       }

FILTERING BY HELO
       You can define a	function called	filter_helo in your filter.  This lets
       you reject connections after the	HELO/EHLO SMTP command.	 Note that for
       this function to	be called, you must use	the -H flag with mimedefang.

       filter_helo is passed six arguments: $ip	and $name are the  IP  address
       and name	of the sending relay, as in filter_relay.  The third argument,
       $helo, is the argument supplied in the HELO/EHLO	command.

       The remaining three arguments to	filter_relay are $port,	$myip and $my-
       port which contain the client's TCP port, the Sendmail daemon's listen-
       ing IP address and the Sendmail daemon's	listening port.

       filter_helo must	return	a  two-to-five	element	 list:	($code,	 $msg,
       $smtp_code,  $smtp_dsn, $delay).	 $code is a return code, with the same
       meaning as the $code return from	filter_relay.  $msg specifies the text
       message	to  use	 for  the SMTP reply.  If $smtp_code and $smtp_dsn are
       supplied, they become the SMTP numerical	reply code  and	 the  enhanced
       status  delivery	 code  (DSN code).  If they are	not supplied, sensible
       defaults	are used.  $delay specifies a delay in seconds;	the  C	milter
       code  will sleep	for $delay seconds before returning the	reply to Send-
       mail.  $delay defaults to zero.

       (Note that the delay is implemented in the Milter C code; if you	 spec-
       ify  a  delay  of 30 seconds, that doesn't mean a Perl slave is tied up
       for the duration	of  the	 delay.	  The  delay  only  costs  one	Milter
       thread.)

FILTERING BY SENDER
       You  can	 define	 a function called filter_sender in your filter.  This
       lets you	reject messages	from certain senders, rather than waiting  un-
       til  the	whole message has been sent.  Note that	for this check to take
       place, you must use the -s flag with mimedefang.

       filter_sender is	passed four arguments:	$sender	is the envelope	e-mail
       address	of  the	sender (for example, "<dfs@roaringpenguin.com>").  The
       address may or may not be surrounded by angle brackets.	$ip and	 $name
       are  the	IP address and host name of the	SMTP relay.  Finally, $helo is
       the argument to the SMTP	"HELO" command.

       Inside filter_sender, you can  access  any  ESMTP  arguments  (such  as
       "SIZE=12345")  in  the  array @ESMTPArgs.  Each ESMTP argument occupies
       one array element.

       filter_sender must return a two-to-five element	list,  with  the  same
       meaning as the return value from	filter_helo.

       For  example,  if  you wish to reject messages from spammer@badguy.com,
       you could use this function:

       sub filter_sender {
	    my ($sender, $ip, $hostname, $helo)	= @_;
	    if ($sender	=~ /^<?spammer\@badguy\.com>?$/i) {
		 return	('REJECT', 'Sorry; spammer@badguy.com is blacklisted.');
	    }
	    return ('CONTINUE',	"ok");
       }

       As another example, some	spammers identify their	own  machine  as  your
       machine	in  the	 SMTP "HELO" command.  This function rejects a machine
       claiming	to be in the "roaringpenguin.com" domain unless	it really is a
       Roaring Penguin machine:

       sub filter_sender {
	 my($sender, $ip, $hostname, $helo) = @_;
	 if ($helo =~ /roaringpenguin.com/i) {
	   if ($ip ne "127.0.0.1" and
	       $ip ne "216.191.236.23" and
	       $ip ne "216.191.236.30")	{
		 return('REJECT', "Go away... $ip is not in roaringpenguin.com");
	   }
	 }
	 return	('CONTINUE', "ok");
       }

       As  a  third  example, you may wish to prevent spoofs by	requiring SMTP
       authentication when email is sent from some email addresses. This func-
       tion  rejects  mail from	"king@example.com", unless the connecting user
       properly	authenticated as "elvisp". Note	that this needs	access to  the
       %SendmailMacros	global,	 that  is not available	in filter_sender until
       after a call to read_commands_file.

       sub filter_sender {
	       my($sender, $ip,	$hostname, $helo) = @_;
	       read_commands_file();
	       ### notice: This	assumes	The King uses authentication without realm!
	       if ($sender =~ /^<?king\@example\.com>?$/i and
		   $SendmailMacros{auth_authen}	ne "elvisp") {
		       return('REJECT',	"Faking	mail from the king is not allowed.");
	       }
	       return ('CONTINUE', "ok");
       }

FILTERING BY RECIPIENT
       You can define a	function called	filter_recipient in your filter.  This
       lets you	reject messages	to certain recipients, rather than waiting un-
       til the whole message has been sent.  Note that for this	check to  take
       place, you must use the -t flag with mimedefang.

       filter_recipient	 is passed nine	arguments:  $recipient is the envelope
       address of the recipient	and $sender is the envelope e-mail address  of
       the  sender  (for  example, "<dfs@roaringpenguin.com>").	 The addresses
       may or may not be surrounded by angle brackets.	$ip and	$name are  the
       IP address and host name	of the SMTP relay.  $first is the envelope ad-
       dress of	the first recipient for	this message, and $helo	is  the	 argu-
       ment   to   the	 SMTP  "HELO"  command.	  The  last  three  arguments,
       $rcpt_mailer, $rcpt_host	and $rcpt_addr are the Sendmail	 mailer,  host
       and  address  triple for	the recipient address.	For example, for local
       recipients, $rcpt_mailer	is likely to be	"local", while for remote  re-
       cipients, it is likely to be "esmtp".

       Inside  filter_recipient,  you  can access any ESMTP arguments (such as
       "NOTIFY=never") in the array @ESMTPArgs.	 Each ESMTP argument  occupies
       one array element.

       filter_recipient	must return a two-to-five element list whose interpre-
       tation is the same as for filter_sender.	 Note, however,	that  if  fil-
       ter_recipient returns 'DISCARD',	then the entire	message	for all	recip-
       ients is	discarded.  (It	doesn't	really make sense, but that's how Mil-
       ter works.)

       For  example,  if  you wish to reject messages from spammer@badguy.com,
       unless they are to postmaster@mydomain.com, you could  use  this	 func-
       tion:

       sub filter_recipient {
	    my ($recipient, $sender, $ip, $hostname, $first, $helo,
		   $rcpt_mailer, $rcpt_host, $rcpt_addr) = @_;
	    if ($sender	=~ /^<?spammer\@badguy\.com>?$/i) {
		 if ($recipient	=~ /^<?postmaster\@mydomain\.com>?$/i) {
		      return ('CONTINUE', "ok");
		 }
		 return	('REJECT', 'Sorry; spammer@badguy.com is blacklisted.');
	    }
	    return ('CONTINUE',	"ok");
       }

INITIALIZATION AND CLEANUP
       Just before a slave begins processing messages, mimedefang.pl calls the
       functions filter_initialize (if it is defined) with no  arguments.   By
       the  time  filter_initialize  is	 called,  all the other	initialization
       (such as	setting	up syslog facility and priority) has been done.

       If you are not using an embedded	Perl interpreter, then	performing  an
       action  inside  filter_initialize is practically	the same as performing
       it directly in the filter file, outside any function definition.	  How-
       ever,  if you are using an embedded Perl	interpreter, then anything you
       call directly from outside a function definition	is executed once  only
       in  the parent process.	Anything in filter_initialize is executed once
       per slave.  If you use any code that opens a descriptor (for example, a
       connection  to  a  database server), you	must run that code inside fil-
       ter_initialize and not directly from the	 filter,  because  the	multi-
       plexor closes all open descriptors when it activates a new slave.

       When  a	slave  is about	to exit, mimedefang.pl calls the function fil-
       ter_cleanup (if it is defined) with no arguments.  This function	can do
       whatever	 cleanup you like, such	as closing file	descriptors and	clean-
       ing  up	long-lived  slave  resources.	The  return  value  from  fil-
       ter_cleanup becomes the slave's exit status.  (You should therefore en-
       sure that filter_cleanup	returns	an integer suitable for	a process exit
       status.)

       If  filter_cleanup  takes  longer  than 10 seconds to run, the slave is
       sent a SIGTERM signal.  If that doesn't kill it (because	you're	catch-
       ing  signals,  perhaps),	 then a	further	10 seconds later, the slave is
       sent a SIGKILL signal.

CONTROLLING PARSING
       If you define a function	called filter_create_parser  taking  no	 argu-
       ments,  then mimedefang.pl will call it to create a MIME::Parser	object
       for parsing mail	messages.

       Filter_create_parser is expected	to return a MIME::Parser object	(or an
       instance	of a class derived from	MIME::Parser).

       You  can	 use  filter_create_parser  to	change	the  behavior  of  the
       MIME::Parser used by mimedefang.pl.

       If you do not define a filter_create_parser function, then  a  built-in
       version equivalent to this is used:

	    sub	filter_create_parser ()	{
		 my $parser = MIME::Parser->new();
		 $parser->extract_nested_messages(1);
		 $parser->extract_uuencode(1);
		 $parser->output_to_core(0);
		 $parser->tmp_to_core(0);
		 return	$parser;
	    }

EXTENDING MIMEDEFANG
       The  man	page for mimedefang-protocol(7)	lists commands that are	passed
       to slaves in server mode	(see "SERVER COMMANDS".)   You	can  define  a
       function	 called	 filter_unknown_cmd to extend the set of commands your
       filter can handle.

       If you define filter_unknown_cmd, it is passed the unknown command as a
       single  argument.   It  should return a list of values as follows:  The
       first element of	the list must be either	"ok"  or  "error:"  (with  the
       colon.)	 The remaining arguments are percent-encoded.  All the result-
       ing pieces are joined together with a single space  between  them,  and
       the resulting string passed back	as the reply to	the multiplexor.

       For  example,  the  following function will make	your filter reply to a
       "PING" command with "PONG":

       sub filter_unknown_cmd ($) {
	   my($cmd) = @_;
	   if ($cmd eq "PING") {
	       return("ok", "PONG");
	   }
	   return("error:", "Unknown command");
       }

       You can test this filter	by typing the following	as root:

       md-mx-ctrl PING

       The response should be:

       ok PONG

       If you extend the set of	commands using filter_unknown_cmd, you	should
       make all	your commands start with an upper-case letter to avoid clashes
       with future built-in commands.

REJECTING UNKNOWN USERS	EARLY
       A very common mail setup	is to have a MIMEDefang	machine	act as an SMTP
       proxy,  accepting  and  scanning	 mail and then relaying	it to the real
       mail server.  Unfortunately, this means	that  the  MIMEDefang  machine
       cannot  know  if	 a local address is valid or not, and will forward all
       mail for	the appropriate	domains.  If a mail comes in  for  an  unknown
       user,  the  MIMEDefang machine will be forced to	generate a bounce mes-
       sage when it tries to relay the mail.

       It's often desirable to have the	MIMEDefang host	reply with a "User un-
       known"  SMTP  response directly.	 While this can	be done	by copying the
       list of local users to the MIMEDefang machine, MIMEDefang has a	built-
       in  function  called  md_check_against_smtp_server for querying another
       relay host:

       md_check_against_smtp_server($sender, $recip,  $helo,  $server,	$port)
       This
	      function	connects  to  the  SMTP	server $server and pretends to
	      send mail	from $sender to	$recip.	 The return value is always  a
	      two-element array.  If the RCPT TO: command succeeds, the	return
	      value is ("CONTINUE", "OK").  If the RCPT	fails with a permanent
	      failure, the return value	is ("REJECT", $msg), where $msg	is the
	      message from the SMTP server.  Any temporary  failures,  connec-
	      tion  errors,  etc.  result  in  a  return value of ("TEMPFAIL",
	      $msg).

	      The optional argument $port specifies the	TCP  port  to  connect
	      to.   If it is not supplied, then	the default SMTP port of 25 is
	      used.

       Suppose the machine filter.domain.tld is	filtering  mail	 destined  for
       the  real mail server mail.domain.tld.  You could have a	filter_recipi-
       ent function like this:

       sub filter_recipient
       {
	   my($recip, $sender, $ip, $host, $first, $helo,
	      $rcpt_mailer, $rcpt_host,	$rcpt_addr) = @_;
	   return md_check_against_smtp_server($sender,	$recip,
				"filter.domain.tld",
				"mail.domain.tld");
       }

       For each	RCPT TO: command,  MIMEDefang  opens  an  SMTP	connection  to
       mail.domain.tld and checks if the command would succeed.

       Please  note  that  you should only use md_check_against_smtp_server if
       your mail server	responds with a	failure	code for nonexistent users  at
       the  RCPT  TO: level.  Also, this function may impose too much overhead
       if you receive a	lot of e-mail, and it will generate  lots  of  useless
       log  entries  on	 the  real  mail  server  (because of all the RCPT TO:
       probes.)	 It may	also significantly increase the	load on	the real  mail
       server.

GLOBAL VARIABLES YOU CAN SET
       The following Perl global variables should be set in mimedefang-filter:

       $AdminAddress
	      The e-mail address of the	MIMEDefang administrator.

       $DaemonAddress
	      The  e-mail  address  from which MIMEDefang-originated notifica-
	      tions come.

       $AddWarningsInline
	      If this variable is set to 0, then all MIMEDefang	warnings (such
	      as created by action_quarantine or action_drop_with_warning) are
	      collected	together and added in  a  separate  MIME  part	called
	      WARNING.TXT.  If the variable is set to 1, then the warnings are
	      added directly in	the first text/plain and  text/html  parts  of
	      the  message.  If	the message does not contain any text/plain or
	      text/html	parts, then a WARNING.TXT MIME part is	added  as  be-
	      fore.

       $MaxMIMEParts
	      A	 message  containing  many MIME	parts can cause	MIME::Tools to
	      consume large amounts of memory and bring	 your  system  to  its
	      knees.  If you set $MaxMIMEParts to a positive number, then MIME
	      parsing is terminated for	messages  with	more  than  that  many
	      parts,  and  the message is bounced.  In this case, none of your
	      filter functions is called.

	      By default, $MaxMIMEParts	is set to  -1,	meaning	 there	is  no
	      limit  on	 the number of parts in	a message.  Note that in order
	      to use this variable,  you  must	install	 the  Roaring  Penguin
	      patched  version of MIME::Tools, version 5.411a-RP-Patched-02 or
	      newer.

       $Stupidity{"NoMultipleInlines"}
	      Set this to 1 if your e-mail is too stupid to  display  multiple
	      MIME parts in-line.  In this case, a nasty hack causes the first
	      part of the original message to appear as	an attachment if warn-
	      ing  are issued.	Mail clients that are not this stupid are Net-
	      scape Communicator and Pine.  On the other hand,	Microsoft  Ex-
	      change  and  Microsoft  Outlook are indeed this stupid.  Perhaps
	      users of those clients should switch.

	      The following global variables may optionally be set.   If  they
	      are not set, sensible defaults are used:

       $AddApparentlyToForSpamAssassin
	      By default, MIMEDefang tries to pass SpamAssassin	a message that
	      looks exactly like one it	 would	receive	 via  procmail.	  This
	      means  adding  a Received: header, adding	a Message-ID header if
	      necessary, and adding a Return-Path: header.  If you set $AddAp-
	      parentlyToForSpamAssassin	to 1, then MIMEDefang also adds	an Ap-
	      parently-To: header with	all  the  envelope  recipients	before
	      passing the message to SpamAssassin.  This lets SpamAssassin de-
	      tect possibly whitelisted	recipient addresses.

	      The default value	for $AddApparentlyToForSpamAssassin is 0.

       $SyslogFacility
	      This specifies the logging facility used by  mimedefang.pl.   By
	      default, it is set to "mail", but	you can	set it to other	possi-
	      bilites.	See the	openlog(3) man page for	details.   You	should
	      name  facilities	as  all-lowercase  without the leading "LOG_".
	      That is, use "local3", not "LOG_LOCAL3".

       $WarningLocation	(default 0)
	      If set to	0 (the default), non-inline warnings are placed	first.
	      If  you  want  the  warning at the end of	the e-mail, set	$Warn-
	      ingLocation to -1.

       $DaemonName (default "MIMEDefang")
	      The full name used when MIMEDefang sends out notifications.

       $AdminName (default "MIMEDefang Administrator")
	      The full name of the MIMEDefang administrator.

       $SALocalTestsOnly (default 1)
	      If set to	1, SpamAssassin	calls will use only local tests.  This
	      is the default and recommended setting.  This disables Received,
	      RBL and Razor tests in an	all or nothing fashion.	 To use	 Razor
	      this  MUST be set	to 0.  You can add 'skip_rbl_checks 1' to your
	      SpamAssassin config file if you need to.

       $NotifySenderSubject (default "MIMEDefang Notification")
	      The  subject  used  when	e-mail	is  sent  out  by   action_no-
	      tify_sender().  If you set this, you should set it each time you
	      call action_notify_sender() to ensure consistency.

       $NotifyAdministratorSubject (default "MIMEDefang	Notification")
	      The subject used when e-mail is sent out by action_notify_admin-
	      istrator().   If	you  set this, you should set it each time you
	      call action_notify_administrator() to ensure consistency.

       $QuarantineSubject (default "MIMEDefang Quarantine Report")
	      The subject used when a quarantine notice	is sent	to the	admin-
	      istrator.	 If you	set this, you should set it each time you call
	      action_quarantine() or action_quarantine_entire_message().

       $NotifyNoPreamble (default 0)
	      Normally,	notifications sent by  action_notify_sender()  have  a
	      preamble	warning	 about	message	 modifications.	 If you	do not
	      want this, set $NotifyNoPreamble to 1.

       $CSSHost	(default 127.0.0.1:7777:local)
	      Host and port for	the Symantec CarrierScan Server	virus scanner.
	      This takes the form ip_addr:port:local_or_nonlocal.  The ip_addr
	      and port are the host and	port on	which  CarrierScan  Server  is
	      listening.   If  you  want to scan local files, append :local to
	      force the	use of the AVSCANLOCAL command.	  If  the  CarrierScan
	      Server  is  on  another host, append :nonlocal to	force the file
	      contents to be sent to the scanner over the socket.

       $SophieSock (default /var/spool/MIMEDefang/sophie)
	      Socket  used  for	 Sophie	 daemon	 calls	 within	  message_con-
	      tains_virus_sophie  and  entity_contains_virus_sophie  unless  a
	      socket is	provided by the	calling	routine.

       $ClamdSock (default /var/run/clamav/clamd.sock)
	      Socket  used  for	 clamd	daemon	 calls	 within	  message_con-
	      tains_virus_clamd	  and	entity_contains_virus_clamd  unless  a
	      socket is	provided by the	calling	routine.

       $TrophieSock (default /var/spool/MIMEDefang/trophie)
	      Socket  used  for	 Trophie  daemon  calls	 within	  message_con-
	      tains_virus_trophie  and	entity_contains_virus_trophie unless a
	      socket is	provided by the	calling	routine.

FILTER
       The heart of mimedefang-filter is the filter procedure.	See the	 exam-
       ples  that came with MIMEDefang to learn	to write a filter.  The	filter
       is called with the following arguments:

       $entity
	      The MIME::Entity object.	(See the MIME::tools Perl module docu-
	      mentation.)

       $fname The suggested attachment filename, or "" if none was supplied.

       $ext   The  file	extension (all characters from the rightmost period to
	      the end of the filename.)

       $type  The MIME type (for example, "text/plain".)

       The filename is derived as follows:

       o      First, if	the Content-Disposition	header has a "filename"	field,
	      it is used.

       o      Otherwise,  if the Content-Type header has a "name" field, it is
	      used.

       o      Otherwise, the Content-Description header	value is used.

       Note that the truly paranoid will check all three fields	 for  matches.
       The  functions  re_match	 and  re_match_ext  perform regular expression
       matches on all three of the fields named	above, and  return  1  if  any
       field  matches.	 See  the sample filters for details.  The calling se-
       quence is:

	    re_match($entity, "regexp")
	    re_match_ext($entity, "regexp")

       re_match	returns	true if	any of the fields matches the  regexp  without
       regard  to  case.   re_match_ext	 returns  true if the extension	in any
       field matches.  An extension is defined as the last dot in a  name  and
       all remaining characters.

       A  third	function called	re_match_in_zip_directory will look inside zip
       files and return	true if	any of the file	names inside the  zip  archive
       match the regular expression.  Call it like this:

	    my $bh = $entity->bodyhandle();
	    my $path = (defined($bh)) ?	$bh->path() : undef;
	    if (defined($path) and re_match_in_zip_directory($path, "regexp")) {
		# Take action...
	    }

       You  should not call re_match_in_zip_directory unless you know that the
       entity is a zip file attachment.

GLOBAL VARIABLES SET BY	MIMEDEFANG.PL
       The following global variables are set by mimedefang.pl and are	avail-
       able  for use in	your filter.  All of these variables are always	avail-
       able to filter_begin, filter, filter_multipart and filter_end.  In  ad-
       dition,	some  of  them are available in	filter_relay, filter_sender or
       filter_recipient.  If this is the case, it will be noted	below.

       %Features
	      This hash	lets you determine at run-time whether	certain	 func-
	      tionality	is available.  This hash is available at all times as-
	      suming  the  detect_and_load_perl_modules()  function  has  been
	      called.  The defined features are:

	      $Features{"SpamAssassin"}	 is 1 if SpamAssassin 1.6 or better is
	      installed; 0 otherwise.

	      $Features{"HTML::Parser"}	is 1 if	HTML::Parser is	 installed;  0
	      otherwise.

	      $Features{"Virus:FPROTD"}	is currently always 0.	Set it to 1 in
	      your filter file if you have  F-Risk's  FPROTD  scanner  earlier
	      than version 6.

	      $Features{"Virus:FPROTD6"}  is  currently	always 0.  Set it to 1
	      in your filter file if you have version  6  of  F-Risk's	FPROTD
	      scanner.

	      $Features{"Virus:SymantecCSS"} is	currently always 0.  Set it to
	      1	in your	filter file  if	 you  have  the	 Symantec  CarrierScan
	      Server virus scanner.

	      $Features{"Virus:NAI"}  is  the full path	to NAI uvscan if it is
	      installed; 0 if it is not.

	      $Features{"Virus:BDC"} is	the full path to Bitdefender bdc if it
	      is installed; 0 if it is not.

	      $Features{"Virus:NVCC"} is the full path to Norman Virus Control
	      nvcc if it is installed; 0 if it is not.

	      $Features{"Virus:HBEDV"} is the full path	to H+BEDV  AntiVir  if
	      it is installed; 0 if it is not.

	      $Features{"Virus:VEXIRA"}	 is  the  full path to Central Command
	      Vexira if	it is installed; 0 if it is not.

	      $Features{"Virus:SOPHOS"}	is the full path to Sophos sweep if it
	      is installed; 0 if it is not.

	      $Features{"Virus:SAVSCAN"} is the	full path to Sophos savscan if
	      it is installed; 0 if it is not.

	      $Features{"Virus:CLAMAV"}	is the full path to Clam  AV  clamscan
	      if it is installed; 0 if it is not.

	      $Features{"Virus:AVP"} is	the full path to AVP AvpLinux if it is
	      installed; 0 if it is not.

	      $Features{"Virus:AVP5"} is the  full  path  to  Kaspersky	 "ave-
	      client" if it is installed; 0 if it is not.

	      $Features{"Virus:CSAV"}  is  the full path to Command csav if it
	      is installed; 0 if it is not.

	      $Features{"Virus:FSAV"} is the full path to F-Secure fsav	if  it
	      is installed; 0 if it is not.

	      $Features{"Virus:FPROT"} is the full path	to F-Risk f-prot if it
	      is installed; 0 if it is not.

	      $Features{"Virus:FPSCAN"}	is the full path to F-Risk  fpscan  if
	      it is installed; 0 if it is not.

	      $Features{"Virus:SOPHIE"}	 is  the  full path to Sophie if it is
	      installed; 0 if it is not.

	      $Features{"Virus:CLAMD"} is the full path	to clamd if it is  in-
	      stalled; 0 if it is not.

	      $Features{"Virus:TROPHIE"}  is the full path to Trophie if it is
	      installed; 0 if it is not.

	      $Features{"Virus:NOD32"} is the full path	to ESET	NOD32 nod32cli
	      if it is installed; 0 if it is not.

	      NOTE: Perl-module	based features such as SpamAssassin are	deter-
	      mined at runtime and may change as these are added and  removed.
	      Most  Virus features are predetermined at	the time of configura-
	      tion and do not adapt to runtime availability unless changed  by
	      the filter rules.

       $CWD   This  variable  holds the	working	directory for the current mes-
	      sage.  During filter processing, mimedefang.pl chdir's into this
	      directory	 before	 calling  any  of the filter_ functions.  Note
	      that this	variable is set	correctly in  filter_sender  and  fil-
	      ter_recipient, but not in	filter_relay.

       $SuspiciousCharsInHeaders
	      If  this variable	is true, then mimedefang has discovered	suspi-
	      cious characters in message headers.  This might be  an  exploit
	      for  bugs	 in  MIME-parsing  routines in some badly-written mail
	      user agents (e.g.	Microsoft Outlook.)  You  should  always  drop
	      such messages.

       $SuspiciousCharsInBody
	      If  this variable	is true, then mimedefang has discovered	suspi-
	      cious characters in the message body.  This might	be an  exploit
	      for  bugs	 in  MIME-parsing  routines in some badly-written mail
	      user agents (e.g.	Microsoft Outlook.)  You  should  always  drop
	      such messages.

       $RelayHostname
	      The  host	 name of the relay.  This is the name of the host that
	      is attempting to send e-mail to your host.  May  be  "undef"  if
	      the  host	name could not be determined.  This variable is	avail-
	      able in filter_relay, filter_sender and filter_recipient in  ad-
	      dition to	the body filtering functions.

       $RelayAddr
	      The  IP  address of the sending relay (as	a string consisting of
	      four dot-separated decimal numbers.)  One	potential use of  $Re-
	      layAddr  is  to  limit mailing to	certain	lists to people	within
	      your organization.  This variable	is available in	 filter_relay,
	      filter_sender  and filter_recipient in addition to the body fil-
	      tering functions.

	      $Helo The	argument given to the SMTP "HELO" command.  This vari-
	      able is available	in filter_sender and filter_recipient, but not
	      in filter_relay.

       $Subject
	      The contents of the "Subject:" header.

       $Sender
	      The sender of the	e-mail.	 This variable is set in filter_sender
	      and  filter_recipient  in	 addition  to the body filtering func-
	      tions.

       @Recipients
	      A	list of	the recipients.	 In filter_recipient, it is set	to the
	      single  recipient	currently under	consideration. Or, after call-
	      ing read_commands_file within filter_recipient, the current  re-
	      cipient  under consideration is in the final position of the ar-
	      ray, at $Recipients[-1], while any previous (and	accepted)  re-
	      cipients are at the beginning of the array, that is, in @Recipi-
	      ents[0 ..	$#Recipients-1].

       $MessageID
	      The contents of the "Message-ID:"	 header	 if  one  is  present.
	      Otherwise, contains the string "NOQUEUE".

       $QueueID
	      The Sendmail queue identifier if it could	be determined.	Other-
	      wise, contains the string	"NOQUEUE". This	variable is  set  cor-
	      rectly  in  filter_sender	 and  filter_recipient,	 but it	is not
	      available	in filter_relay.

       $MsgID Set to $QueueID if the queue ID could be determined;  otherwise,
	      set  to  $MessageID.  This identifier should be used in logging,
	      because it matches the identifier	used by	Sendmail to  log  mes-
	      sages.   Note  that  this	 variable  is  set  correctly  in fil-
	      ter_sender and filter_recipient, but it is not available in fil-
	      ter_relay.

       $VirusScannerMessages
	      Each time	a virus-scanning function is called, messages (if any)
	      from the virus scanner are accumulated in	 this  variable.   You
	      can  use	it  in	filter_end to formulate	a notification (if you
	      wish.)

       $VirusName
	      If a virus-scanning function found a virus, this	variable  will
	      hold the virus name (if it could be determined.)

       $SASpamTester
	      If  defined,  this  is  the configured Mail::SpamAssassin	object
	      used for mail tests.  It may  be	initialized  with  a  call  to
	      spam_assassin_init which also returns it.

       %SendmailMacros
	      This hash	contains the values of some Sendmail macros.  The hash
	      elements exist only for macros defined  by  Sendmail.   See  the
	      Sendmail documentation for the meanings of the macros.

	      By  default,  mimedefang	passes	the  values  of	 the following
	      macros: ${daemon_name}, ${daemon_port}, ${if_name},  ${if_addr},
	      $j,   $_,	  $i,	${tls_version},	  ${cipher},   ${cipher_bits},
	      ${cert_subject}, ${cert_issuer},	${auth_type},  ${auth_authen},
	      ${auth_ssf},  ${auth_author},  ${mail_mailer},  ${mail_host} and
	      ${mail_addr}.   In  addition,  ${client_port}  is	 set  to   the
	      client's TCP port.

	      If  any macro is not set or not passed to	milter,	it will	be un-
	      available.  To access the	value of a macro, use:

		   $SendmailMacros{"macro_name"}

	      Do not place curly brackets around the macro name.   This	 vari-
	      able  is available in filter_sender and filter_recipient after a
	      call to read_commands_file.

       @SenderESMTPArgs
	      This array contains all the ESMTP	arguments supplied in the MAIL
	      FROM: command.  For example:

	      sub print_sender_esmtp_args {
		  foreach (@SenderESMTPArgs) {
		      print STDERR "Sender ESMTP arg: $_0;
		  }
	      }

       %RecipientESMTPArgs
	      This hash	contains all the ESMTP arguments supplied in each RCPT
	      TO: command.  For	example:

	      sub print_recip_esmtp_args {
		  foreach my $recip (@Recipients) {
		      foreach(@{$RecipientESMTPArgs{$recip}}) {
			  print	STDERR "Recip ESMTP arg	for $recip: $_0;
		      }
		  }
	      }

       %RecipientMailers
	      This hash	contains the Sendmail "mailer-host-address" triple for
	      each recipient.  Here's an example of how	to use it:

	      sub print_mailer_info {
		  my($recip, $mailer, $host, $addr);
		  foreach $recip (@Recipients) {
		      $mailer =	${RecipientMailers{$recip}}[0];
		      $host = ${RecipientMailers{$recip}}[1];
		      $addr =  ${RecipientMailers{$recip}}[2];
		      print STDERR "$recip: mailer=$mailer, host=$host,	addr=$addr\n";
		  }
	      }

	      In  filter_recipient, this variable by default only contains in-
	      formation	on the recipient currently under investigation.	Infor-
	      mation  on  all  recipients is available after calling read_com-
	      mands_file.

ACTIONS
       When the	filter procedure decides how to	dispose	of a part,  it	should
       call one	or more	action_	subroutines.  The action subroutines are:

       action_accept()
	      Accept the part.

       action_rebuild()
	      Rebuild the mail body, even if mimedefang	thinks no changes were
	      made.  Normally, mimedefang does	not  alter  a  message	if  no
	      changes  were  made.   action_rebuild  may  be  used if you make
	      changes to entities directly (by	manipulating  the  MIME::Head,
	      for  example.)   Unless you call action_rebuild, mimedefang will
	      be unaware of the	changes.  Note that all	the built-in action...
	      routines that change a message implicitly	call action_rebuild.

       action_add_header($hdr, $val)
	      Add  a  header to	the message.  This can be used in filter_begin
	      or filter_end.  The $hdr component is the	 header	 name  without
	      the  colon,  and	the $val is the	header value.  For example, to
	      add the header:

		   X-MyHeader: A nice piece of text

	      use:

		   action_add_header("X-MyHeader", "A nice piece of text");

       action_change_header($hdr, $val,	$index)
	      Changes an existing header in the	message. This can be  used  in
	      filter_begin  or	filter_end.   The $hdr parameter is the	header
	      name without the colon, and $val is the header  value.   If  the
	      header  does  not	 exist,	 then a	header with the	given name and
	      value is added.

	      The $index parameter is optional;	it defaults to 1.  If you sup-
	      ply  it, then the	$index'th occurrence of	the header is changed,
	      if there is more than one	header with the	same name.   (This  is
	      common with the Received:	header,	for example.)

       action_insert_header($hdr, $val,	$index)
	      Add  a  header to	the message int	the specified position $index.
	      A	position of 0 specifies	that the header	 should	 be  prepended
	      before  existing	headers.   This	can be used in filter_begin or
	      filter_end.  The $hdr component is the header name  without  the
	      colon, and the $val is the header	value.

       action_delete_header($hdr, $index)
	      Deletes  an  existing header in the message. This	can be used in
	      filter_begin or filter_end.  The $hdr parameter  is  the	header
	      name without the colon.

	      The $index parameter is optional;	it defaults to 1.  If you sup-
	      ply it, then the $index'th occurrence of the header is  deleted,
	      if there is more than one	header with the	same name.

       action_delete_all_headers($hdr)
	      Deletes  all  headers with the specified name.  This can be used
	      in filter_begin or filter_end.  The $hdr parameter is the	header
	      name without the colon.

       action_drop()
	      Drop  the	part.  If called from filter_multipart,	drops all con-
	      tained parts also.

       action_drop_with_warning($msg)
	      Drop the part, but add the warning $msg to the  e-mail  message.
	      If called	from filter_multipart, drops all contained parts also.

       action_accept_with_warning($msg)
	      Accept the part, but add the warning $msg	to the e-mail message.

       action_replace_with_warning($msg)
	      Drop  the	 part and replace it with a text part $msg.  If	called
	      from filter_multipart, drops all contained parts also.

       action_replace_with_url($entity,	$doc_root, $base_url, $msg, [$cd_data,
       $salt])
	      Drop the part, but save it in a unique location under $doc_root.
	      The part is replaced with	the text  message  $msg.   The	string
	      "_URL_"  in  $msg	is replaced with $base_url/something, that can
	      be used to retrieve the message.

	      You should not use this function in filter_multipart.

	      This action is intended for stripping large  parts  out  of  the
	      message  and  replacing  them to a link on a Web server.	Here's
	      how you would use	it in filter():

	      $size = (stat($entity->bodyhandle->path))[7];
	      if ($size	> 1000000) {
		   return action_replace_with_url($entity,
			"/home/httpd/html/mail_parts",
			"http://mailserver.company.com/mail_parts",
			"The attachment	was larger than	1,000,000 bytes.\n" .
			"It was	removed, but may be accessed at	this URL:\n\n" .
			"\t_URL_\n");
	      }

	      This example moves attachments greater than 1,000,000 bytes into
	      /home/httpd/html/mail_parts  and replaces	them with a link.  The
	      directory	 should	 be   accessible   via	 a   Web   server   at
	      http://mailserver.company.com/mail_parts.

	      The  generated  name is created by performing a SHA1 hash	of the
	      part and adding the extension to the ASCII-HEX representation of
	      the  hash.   If  many  different	e-mails	are sent containing an
	      identical	large part, only one copy of the part is  stored,  re-
	      gardless of the number of	senders	or recipients.

	      For  privacy  reasons,  you must turn off	Web server indexing in
	      the directory in which you place mail parts, or anyone  will  be
	      able  to	read them.  If indexing	is disabled, an	attacker would
	      have to guess the	SHA1 hash of a part in order to	read it.

	      Optionally, a fifth argument can supply data to be saved into  a
	      hidden  dot filename based on the	generated name.	 This data can
	      then be read in on the fly by a CGI script  or  mod_perl	module
	      before  serving the file to a web	client,	and used to add	infor-
	      mation to	the response, such as Content-Disposition data.

	      A	sixth optional argument, $salt,	is mixed in to the SHA1	 hash.
	      This  salt  can  be  any string and should be kept confidential.
	      The salt is designed to prevent people from guessing whether  or
	      not  a particular	attachment has been received on	your server by
	      altering the SHA1	hash calculation.

       action_defang($entity, $name, $fname, $type)
	      Accept the part, but change its name  to	$name,	its  suggested
	      filename	to  $fname  and	 its  MIME type	to $type.  If $name or
	      $fname are "", then mimedefang.pl	generates generic  names.   Do
	      not use this action in filter_multipart.

	      If  you  use  action_defang, you must define a subroutine	called
	      defang_warning in	your filter.  This  routine  takes  two	 argu-
	      ments: $oldfname (the original name of an	attachment) and	$fname
	      (the defanged version.)  It should return	a message telling  the
	      user what	happened.  For example:

	      sub defang_warning {
		  my($oldfname,	$fname)	= @_;
		  return "The attachment '$oldfname' was renamed to '$fname'\n";
	      }

       action_external_filter($entity, $cmd)
	      Run  an  external	UNIX command $cmd.  This command must read the
	      part from	the file ./FILTERINPUT and leave the result in	./FIL-
	      TEROUTPUT.   If  the  command  executes successfully, returns 1,
	      otherwise	0.  You	can test the return value and call another ac-
	      tion_  if	 the  filter  failed.	Do not use this	action in fil-
	      ter_multipart.

       action_quarantine($entity, $msg)
	      Drop and quarantine the part, but	add the	warning	$msg to	the e-
	      mail message.

       action_quarantine_entire_message($msg)
	      Quarantines  the entire message in a quarantine directory	on the
	      mail server, but does not	otherwise affect  disposition  of  the
	      message.	If "$msg" is non-empty,	it is included in any adminis-
	      trator notification.

       action_sm_quarantine($reason)
	      Quarantines a message in the Sendmail mail queue using  the  new
	      QUARANTINE facility of Sendmail 8.13.  Consult the Sendmail doc-
	      umentation for details about this	 facility.   If	 you  use  ac-
	      tion_sm_quarantine  with	a  version  of Sendmail	that lacks the
	      QUARANTINE facility, mimedefang will log an  error  message  and
	      not quarantine the message.

       action_bounce($reply, $code, $dsn)
	      Reject  the entire e-mail	message	with an	SMTP failure code, and
	      the one-line error message $reply.  If the  optional  $code  and
	      $dsn arguments are supplied, they	specify	the numerical SMTP re-
	      ply code and the extended	status code (DSN code).	 If the	 codes
	      you  supply  do  not  make sense for a bounce, they are replaced
	      with "554" and "5.7.1" respectively.

	      action_bounce merely makes a note	that  the  message  is	to  be
	      bounced;	remaining parts	are still processed.  If action_bounce
	      is called	for more than one part,	the mail is bounced  with  the
	      message  in the final call to action_bounce.  You	can profitably
	      call action_quarantine followed by action_bounce if you want  to
	      keep a copy of the offending part.  Note that the	message	is not
	      bounced immediately; rather, remaining parts are	processed  and
	      the message is bounced after all parts have been processed.

	      Note  that  despite  its name, action_bounce does	not generate a
	      "bounce message".	 It merely rejects the message	with  an  SMTP
	      failure code.

	      WARNING: action_bounce() may cause the sending relay to generate
	      spurious bounce messages if the sender address is	 faked.	  This
	      is  a particular problem with viruses.  However, we believe that
	      on balance, it's better to bounce	a virus	than to	silently  dis-
	      card it.	It's almost never a good idea to hide a	problem.

       action_tempfail($msg, $code, $dsn)
	      Cause  an	 SMTP  "temporary failure" code	to be returned,	so the
	      sending mail relay requeues the message and tries	 again	later.
	      The  message  $msg  is included with the temporary failure code.
	      If the optional $code and	 $dsn  arguments  are  supplied,  they
	      specify  the  numerical  SMTP reply code and the extended	status
	      code (DSN	code).	If the codes you supply	do not make sense  for
	      a	 temporary  failure,  they are replaced	with "450" and "4.7.1"
	      respectively.

       action_discard()
	      Silently discard the message, notifying nobody.  You  can	 prof-
	      itably  call action_quarantine followed by action_discard	if you
	      want to keep a copy of the offending part.  Note that  the  mes-
	      sage  is	not discarded immediately; rather, remaining parts are
	      processed	and the	message	is discarded after all parts have been
	      processed.

       action_notify_sender($message)
	      This action sends	an e-mail back to the original sender with the
	      indicated	message.  You may call another action after this  one.
	      If  action_notify_sender	is called more than once, the messages
	      are accumulated into a single e-mail message -- at most one  no-
	      tification  message  is  sent per	incoming message.  The message
	      should be	terminated with	a newline.

	      The notification is delivered in deferred	mode; you should run a
	      client-queue runner if you are using Sendmail 8.12.

	      NOTE:  Viruses  often fake the sender address.  For that reason,
	      if a virus-scanner has detected a	virus, action_notify_sender is
	      disabled	and will simply	log an error message if	you try	to use
	      it.

       action_notify_administrator($message)
	      This action e-mails the MIMEDefang  administrator	 the  supplied
	      message.	You may	call another action after this one; action_no-
	      tify_administrator does not  affect  mail	 processing.   If  ac-
	      tion_notify_administrator	is called more than once, the messages
	      are accumulated into a single e-mail message -- at most one  no-
	      tification  message  is  sent per	incoming message.  The message
	      should be	terminated with	a newline.

	      The notification is delivered in deferred	mode; you should run a
	      client-queue runner if you are using Sendmail 8.12.

       append_text_boilerplate($entity,	$boilerplate, $all)
	      This  action  should only	be called from filter_end.  It appends
	      the text "\n$boilerplate\n" to the  first	 text/plain  part  (if
	      $all is 0) or to all text/plain parts (if	$all is	1).

       append_html_boilerplate($entity,	$boilerplate, $all)
	      This  action should only be called from filter_end.  It adds the
	      text "\n$boilerplate\n" to the first text/html part (if $all  is
	      0)  or  to  all  text/html  parts	(if $all is 1).	 This function
	      tries to be smart	 about	inserting  the	boilerplate;  it  uses
	      HTML::Parser  to detect closing tags and inserts the boilerplate
	      before the </body> tag if	there is one, or  before  the  </html>
	      tag  if  there is	no </body>.  If	there is no </body> or </html>
	      tag, it appends the boilerplate to the end of the	part.

	      Do not use append_html_boilerplate unless	you have installed the
	      HTML::Parser Perl	module.

	      Here is an example illustrating how to use the boilerplate func-
	      tions:

		   sub filter_end {
			my($entity) = @_;
			append_text_boilerplate($entity,
			     "Lame text	disclaimer", 0);
			append_html_boilerplate($entity,
			     "<em>Lame</em> HTML disclaimer", 0);
		   }

       action_add_part($entity,	$type, $encoding, $data, $fname,  $disposition
       [, $offset])
	      This  action  should only	be called from the filter_end routine.
	      It adds a	new part to the	message, converting the	original  mes-
	      sage to mutipart if necessary.  The function returns the part so
	      that additional mime attributes may be set on it.	 Here's	an ex-
	      ample:

		   sub filter_end {
			my($entity) = @_;

			action_add_part($entity, "text/plain", "-suggest",
				  "This	e-mail does not	represent" .
				  "the official	policy of FuBar, Inc.\n",
				  "disclaimer.txt", "inline");
		      }

	      The  $entity  parameter  must  be	the argument passed in to fil-
	      ter_end.	The $offset parameter is optional; if omitted, it  de-
	      faults  to  -1,  which  adds  the	 new part at the end.  See the
	      MIME::Entity man page and	the add_part member function  for  the
	      meaning of $offset.

	      Note that	action_add_part	tries to be more intelligent than sim-
	      ply calling $entity->add_part.  The decision process is as  fol-
	      lows:

       o      If  the  top-level  entity  is multipart/mixed, then the part is
	      simply added.

       o      Otherwise, a new top-level multipart/mixed container  is	gener-
	      ated,  and  the original top-level entity	is made	the first part
	      of the multipart/mixed container.	 The new part is then added to
	      the multipart/mixed container.

       action_add_entity($entity [, $offset])
	      This  is	similar	 to  action_add_part  but  takes  a  pre-built
	      MIME::Entity object rather than constructing one based on	$type,
	      $encoding, $data,	$fname and $disposition	arguments.

USEFUL ROUTINES
       mimedefang.pl  includes	some  useful  functions	you can	call from your
       filter:

       detect_and_load_perl_modules()
	      Unless you really	know what you're doing,	this function must  be
	      called first thing in your filter	file.  It causes mimedefang.pl
	      to detect	and load  Perl	modules	 such  as  Mail::SpamAssassin,
	      Net::DNS,	etc., and to populate the %Features hash.

       send_quarantine_notifications()
	      This  function  should  be called	from filter_end.  If any parts
	      were quarantined,	a  quarantine  notification  is	 sent  to  the
	      MIMEDefang  administrator.   Please note that if you do not call
	      send_quarantine_notifications, then no quarantine	 notifications
	      are sent.

       get_quarantine_dir()
	      This  function  returns the full path name of the	quarantine di-
	      rectory.	If you have not	yet quarantined	any parts of the  mes-
	      sage,  a	quarantine  directory  is created and its pathname re-
	      turned.

       change_sender($sender)
	      This function changes the	envelope sender	to  $sender.   It  can
	      only  be called from filter_begin	or any later function.	Please
	      note that	this function is only supported	 with  Sendmail/Milter
	      8.14.0  or newer.	 It has	no effect if you're running older ver-
	      sions.

       add_recipient($recip)
	      This function adds $recip	to the list of envelope	recipients.  A
	      copy of the message (after any modifications by MIMEDefang) will
	      be sent to $recip	in addition to the original recipients.	  Note
	      that  add_recipient  does	 not  modify the @Recipients array; it
	      just makes a note	to Sendmail to add the recipient.

       delete_recipient($recip)
	      This function deletes $recip from	the list of recipients.	  That
	      person  will  not	receive	a copy of the mail.  $recip should ex-
	      actly match an entry in the @Recipients array for	delete_recipi-
	      ent()  to	 work.	Note that delete_recipient does	not modify the
	      @Recipients array; it just makes a note to  Sendmail  to	delete
	      the recipient.

       resend_message($recip1, $recip2,	...)
	      or

       resend_message(@recips)
	      This  function immediately resends the original, unmodified mail
	      message to each of the named recipients.	The  sender's  address
	      is preserved.  Be	very careful when using	this function, because
	      it resends the original message, which may contain undesired at-
	      tachments.   Also,  you  should not call this function from fil-
	      ter(), because it	resends	the message each time  it  is  called.
	      This  may	 result	 in  multiple copies being sent	if you are not
	      careful.	Call from filter_begin() or filter_end() to be safe.

	      The function returns true	on success, or false if	it fails.

	      Note that	the resend_message function delivers the mail  in  de-
	      ferred  mode  (using  Sendmail's	"-odd"	flag.)	You must run a
	      client-submission	queue processor	if you use Sendmail 8.12.   We
	      recommend	executing this command as part of the Sendmail startup
	      sequence:

		   sendmail -Ac	-q5m

       remove_redundant_html_parts($entity)
	      This function should only	be called from filter_end.  It removes
	      redundant	HTML parts from	the message.  It works by deleting any
	      part of type text/html from the message if (1) it	is a  sub-part
	      of  a  multipart/alternative part, and (2) there is another part
	      of type text/plain under the multipart/alternative part.

       replace_entire_message($entity)
	      This function can	only be	called from filter_end.	  It  replaces
	      the  entire message with $entity,	a MIME::Entity object that you
	      have constructed.	 You can use any of the	MIME::Tools  functions
	      to construct the entity.

       read_commands_file()
	      This  function should only be called from	filter_sender and fil-
	      ter_recipient. This will read the	COMMANDS file (as described in
	      mimedefang-protocol(7)),	and  will fill or update the following
	      global variables:	$Sender, @Recipients, %RecipientMailers,  $Re-
	      layAddr,	 $RealRelayAddr,  $RelayHostname,  $RealRelayHostname,
	      $QueueID,	$Helo, %SendmailMacros.

	      If you do	not call read_commands_file, then the only information
	      available	in filter_sender and filter_recipient is that which is
	      passed as	an argument to the function.

       stream_by_domain()
	      Do not use this function unless you have Sendmail	8.12  and  lo-
	      cally- submitted e-mail is submitted using SMTP.

	      This  function  should  only  be called at the very beginning of
	      filter_begin(), like this:

		   sub filter_begin {
			if (stream_by_domain())	{
			     return;
			}
			# Rest of filter_begin
		   }

	      stream_by_domain() looks at all the recipients of	 the  message,
	      and  if  they  belong  to	the same domain	(e.g., joe@domain.com,
	      jane@domain.com and sue@domain.com), it returns 0	and  sets  the
	      global  variable $Domain to the domain (domain.com in this exam-
	      ple.)

	      If users are in different	 domains,  stream_by_domain()  resends
	      the  message (once to each domain) and returns 1 For example, if
	      the  original  recipients	 are  joe@abc.net,  jane@xyz.net   and
	      sue@abc.net,  the	 original message is resent twice: One copy to
	      joe@abc.net and sue@abc.net, and another copy  to	 jane@xyz.net.
	      Also,  any  subsequent  scanning	is canceled (filter() and fil-
	      ter_end()	will not be called for the original message)  and  the
	      message is silently discarded.

	      If  you  have Sendmail 8.12, then	locally-submitted messages are
	      sent via SMTP, and MIMEDefang will be  called  for  each	resent
	      message.	It is possible to set up Sendmail 8.12 so locally-sub-
	      mitted  messages	are  delivered	 directly;   in	  this	 case,
	      stream_by_domain will not	work.

	      Using stream_by_domain allows you	to customize your filter rules
	      for each domain.	If you use the function	 as  described	above,
	      you can do this in your filter routine:

		   sub filter {
			my($entity, $fname, $ext, $type) = @_;
			if ($Domain eq "abc.com") {
			     # Filter actions for abc.com
			} elsif	($Domain eq "xyz.com") {
			     # Filter actions for xyz.com
			} else {
			     # Default filter actions
			}
		   }

	      You  cannot  rely	 on  $Domain  being set	unless you have	called
	      stream_by_domain().

       stream_by_recipient()
	      Do not use this function unless you have Sendmail	8.12  and  lo-
	      cally- submitted e-mail is submitted using SMTP.

	      This  function  should  only  be called at the very beginning of
	      filter_begin(), like this:

		   sub filter_begin {
			if (stream_by_recipient()) {
			     return;
			}
			# Rest of filter_begin
		   }

	      If there is more than one	recipient,  stream_by_recipient()  re-
	      sends  the  message  once	 to each recipient.  That way, you can
	      customize	your filter rules on a per-recipient basis.  This  may
	      increase the load	on your	mail server considerably.

	      Also,  a	"recipient"  is	determined before alias	expansion.  So
	      "all@mydomain.com" is considered a  single  recipient,  even  if
	      Sendmail delivers	to a list.

	      If  you  have Sendmail 8.12, then	locally-submitted messages are
	      sent via SMTP, and MIMEDefang will be  called  for  each	resent
	      message.	It is possible to set up Sendmail 8.12 so locally-sub-
	      mitted  messages	are  delivered	 directly;   in	  this	 case,
	      stream_by_recipient() will not work.

	      stream_by_recipient()  allows you	to customize your filter rules
	      for each recipient in a manner similar to	stream_by_domain().

LOGGING
       md_graphdefang_log_enable($facility, $enum_recips)
	      Enables the md_graphdefang_log function (described  next).   The
	      function	logs  to  syslog using the specified facility.	If you
	      omit $facility, it defaults to  'mail'.	If  you	 do  not  call
	      md_graphdefang_log_enable	 in  your  filter,  then  any calls to
	      md_graphdefang_log simply	do nothing.

	      If you supply $enum_recips as 1, then a line of logging is  out-
	      put  for	each recipient of a mail message.  If it is zero, then
	      only a single line is output for	each  message.	 If  you  omit
	      $enum_recips, it defaults	to 1.

       md_graphdefang_log($event, $v1, $v2)
	      Logs  an	event  with  up	to two optional	additional parameters.
	      The log message has a specific format useful for graphing	tools;
	      the message looks	like this:

		   MDLOG,msgid,event,v1,v2,sender,recipient,subj

	      "MDLOG"  is literal text.	 "msgid" is the	Sendmail queue identi-
	      fier.  "event" is	the event name,	and "v1" and "v2" are the  ad-
	      ditional	parameters.   "sender" is the sender's e-mail address.
	      "recipient" is the recipient's e-mail address, and "subj"	is the
	      message  subject.	  If  a	 message  has more than	one recipient,
	      md_graphdefang_log may log an event message for each  recipient,
	      depending	on how you called md_graphdefang_log_enable.

	      Note that	md_graphdefang_log should not be used in filter_relay,
	      filter_sender or filter_recipient.  The global variables it  re-
	      lies on are not valid in that context.

	      If  you want to log general text strings,	do not use md_graphde-
	      fang_log.	 Instead, use md_syslog	(described next).

       md_syslog($level, $msg)
	      Logs the message $msg to syslog, using level $level.  The	 level
	      is a literal string, and should be one of	'err', 'debug',	'warn-
	      ing', 'emerg', 'crit', 'notice' or 'info'.  (See	syslog(3)  for
	      details.)

	      Note  that  md_syslog  does not perform %-subsitutions like sys-
	      log(3) does.  Depending on  your	Perl  installation,  md_syslog
	      boils  down  to  a  call	to  Unix::Syslog::syslog  or Sys::Sys-
	      log::syslog.  See	the Unix::Syslog or Sys::Syslog	man pages  for
	      more details.

       md_openlog($tag,	$facility)
	      Sets the tag used	in syslog messages to $tag, and	sends the logs
	      to the $facility facility.  If you do not	call md_openlog	before
	      you  call	 md_syslog, then it is called implicitly with $tag set
	      to mimedefang.pl and $facility set to mail.

RBL LOOKUP FUNCTIONS
       mimedefang.pl includes the following functions for looking  up  IP  ad-
       dresses	 in  DNS-based	real-time  blacklists.	 Note  that  the  "re-
       lay_is_blacklisted" functions are deprecated and	may be	removed	 in  a
       future  release.	 Instead, you should use the module Net::DNSBL::Client
       from CPAN.

       relay_is_blacklisted($relay, $domain)
	      This checks a DNS-based real-time	spam  blacklist,  and  returns
	      true  if the relay host is blacklisted, or false otherwise.  (In
	      fact, the	return value is	whatever the blacklist	returns	 as  a
	      resolved hostname, such as "127.0.0.4")

	      Note  that  relay_is_blacklisted uses the	built-in gethostbyname
	      function;	this is	usually	quite inefficient and does not	permit
	      you to set a timeout on the lookup.  Instead, we recommend using
	      one of the other DNS lookup function described in	this  section.
	      (Note,  though,  that  the  other	 functions  require  the  Perl
	      Net::DNS module, whereas relay_is_blacklisted does not.)

	      Here's an	example	of how to use relay_is_blacklisted:

		   if (relay_is_blacklisted($RelayAddr,	"rbl.spamhaus.org")) {
			action_add_header("X-Blacklist-Warning",
			       "Relay $RelayAddr is blacklisted	by Spamhaus");
		   }

       relay_is_blacklisted_multi($relay,  $timeout,  $answers_wanted,	 [$do-
       main1, $domain2,	...], $res)
	      This function is similar to relay_is_blacklisted,	except that it
	      takes a timeout argument (specified in seconds) and an array  of
	      domains  to check.  The function checks all domains in parallel,
	      and is guaranteed	to return in $timeout seconds.	(Actually,  it
	      may take up to one second	longer.)

	      The parameters are:

	      $relay --	the IP address you want	to look	up

	      $timeout -- a timeout in seconds after which the function	should
	      return

	      $answers_wanted -- the maximum number of	positive  answers  you
	      care  about.  For	example, if you're looking up an address in 10
	      different	RBLs, but are going to bounce it if it is on  four  or
	      more, you	can set	$answers_wanted	to 4, and the function returns
	      as soon  as  four	 "hits"	 are  discovered.   If	you  set  $an-
	      swers_wanted to zero, then the function does not return early.

	      [$domain1, $domain2, ...]	-- a reference to an array of strings,
	      where each string	is an RBL domain.

	      $res -- a	Net::DNS::Resolver object.  This argument is optional;
	      if  you  do  not supply it, then relay_is_blacklisted_multi con-
	      structs its own resolver.

	      The return value is a reference to a hash; the keys of the  hash
	      are  the	original domains, and the corresponding	values are ei-
	      ther SERVFAIL, NXDOMAIN, or a list of IP	addresses  in  dotted-
	      quad notation.

	      Here's an	example:

		  $ans = relay_is_blacklisted_multi($RelayAddr,	8, 0,
		      ["sbl.spamhaus.org", "relays.ordb.org"]);

		  foreach $domain (keys(%$ans))	{
		      $r = $ans->{$domain};
		      if (ref($r) eq "ARRAY") {
			  # It's an array -- it	IS listed in RBL
			  print	STDERR "Lookup in $domain yields [ ";
			  foreach $addr	(@$r) {
			      print STDERR $addr . " ";
			  }
			  print	STDERR "]\n";
		      }	else {
			  # It is NOT listed in	RBL
			  print	STDERR "Lookup in $domain yields "
				       . $ans->{$domain} . "\n";
		      }
		  }

	      You  should  compare  each  of $ans->{$domain} to	"SERVFAIL" and
	      "NXDOMAIN" to see	if the relay is	not listed.  Any other	return
	      value will be an array of	IP addresses indicating	that the relay
	      is listed.

	      Any lookup that does not succeed within $timeout seconds has the
	      corresponding return value set to	SERVFAIL.

       relay_is_blacklisted_multi_list($relay,	  $timeout,   $answers_wanted,
       [$domain1, $domain2, ...], $res)
	      This function is similar	to  relay_is_blacklisted_multi	except
	      that the return value is simply an array of RBL domains in which
	      the relay	was listed.

       relay_is_blacklisted_multi_count($relay,	  $timeout,   $answers_wanted,
       [$domain1, $domain2, ...], $res)
	      This  function  is  similar to relay_is_blacklisted_multi	except
	      that the return value is an integer specifying the number	of do-
	      mains on which the relay was blacklisted.

       md_get_bogus_mx_hosts($domain)

	      This  function looks up all the MX records for the specified do-
	      main (or A records if there are no MX  records)  and  returns  a
	      list  of "bogus" IP addresses found amongst the records.	A "bo-
	      gus"  IP	address	 is  an	 IP  address  in  a  private   network
	      (10.0.0.0/8,  172.16.0.0/12,  192.168.0.0/16), the loopback net-
	      work (127.0.0.0/8), local-link for  auto-DHCP  (169.254.0.0/16),
	      IPv4 multicast (224.0.0.0/4) or reserved (240.0.0.0/4).

       Here's how you might use	the function in	filter_sender:

       sub filter_sender {
	   my ($sender,	$ip, $hostname,	$helo) = @_;
	   if ($sender =~ /@([^>]+)/) {
	       my $domain = $1;
	       my @bogushosts =	md_get_bogus_mx_hosts($domain);
	       if (scalar(@bogushosts))	{
		   return('REJECT', "Domain $domain contains bogus MX record(s)	" .
			  join(', ', @bogushosts));
	       }
	   }
	   return ('CONTINUE', 'ok');
       }

TEST FUNCTIONS
       mimedefang.pl includes some "test" functions:

       md_version()
	      returns  the  version  of	 MIMEDefang  as	a string (for example,
	      "2.78").

       message_rejected()
	      Returns true if any of  action_tempfail,	action_bounce  or  ac-
	      tion_discard  have  been	called for this	message; returns false
	      otherwise.

       If  you	have  the  Mail::SpamAssassin  Perl  module   installed	  (see
       http://www.spamassassin.org)  you  may  call any	of the spam_assassin_*
       functions.  They	should only be called from filter_begin	or  filter_end
       because they operate on the entire message at once.  Most functions use
       an optionally provided config file.  If no  config  file	 is  provided,
       mimedefang.pl will look for one of four default SpamAssassin preference
       files.  The first of the	following found	will be	used:

       o      /usr/local/etc/mimedefang/sa-mimedefang.cf

       o      /usr/local/etc/mimedefang/spamassassin/sa-mimedefang.cf

       o      /usr/local/etc/mimedefang/spamassassin/local.cf

       o      /usr/local/etc/mimedefang/spamassassin.cf

       Important Note:	MIMEDefang does	not permit SpamAssassin	to modify mes-
       sages.	If you want to tag spam	messages with special headers or alter
       the subject line, you must use MIMEDefang functions to do it.   Setting
       SpamAssassin configuration options to alter messages will not work.

       spam_assassin_is_spam([ $config_file ])
	      Determine	 if  the  current message is SPAM/UCE as determined by
	      SpamAssassin.  Compares the score	of  the	 message  against  the
	      threshold	 score	(see  below)  and returns true if it is.  Uses
	      spam_assassin_check below.

       spam_assassin_check([ $config_file ])
	      This function returns a four-element list	of  the	 form  ($hits,
	      $required,  $tests, $report).  $hits is the "score" given	to the
	      message by SpamAssassin (higher score means more	likely	SPAM).
	      $required	 is  the  number  of hits required before SpamAssassin
	      concludes	that the message is SPAM.  $tests is a comma-separated
	      list  of	SpamAssassin test names, and $report is	text detailing
	      which tests triggered and	their point score.  This gives you in-
	      sight  into why SpamAssassin concluded that the message is SPAM.
	      Uses spam_assassin_status	below.

       spam_assassin_status([ $config_file ])
	      This function returns a Mail::SpamAssasin::PerMsgStatus  object.
	      Read  the	 SpamAssassin documentation for	details	about this ob-
	      ject.  You are responsible for calling the  finish  method  when
	      you  are	done with it.  Uses spam_assassin_init and spam_assas-
	      sin_mail below.

       spam_assassin_init([ $config_file ])
	      This function returns the	new global  Mail::SpamAssassin	object
	      with  the	 specified or default config (outlined above).	If the
	      global object is already defined,	returns	it -- does not	change
	      config  files!   The object can be used to perform other SpamAs-
	      sassin related functions.

       spam_assassin_mail()
	      This function returns a  Mail::SpamAssassin::NoMailAudit	object
	      with  the	current	email message contained	in it.	It may be used
	      to perform other SpamAssassin related functions.

       md_copy_orig_msg_to_work_dir()
	      Normally,	virus-scanners are passed only the  unpacked,  decoded
	      parts  of	a MIME message.	 If you	want to	pass the original, un-
	      decoded message in as  well,  call  md_copy_orig_msg_to_work_dir
	      prior to calling message_contains_virus.

       md_copy_orig_msg_to_work_dir_as_mbox_file()
	      Normally,	 virus-scanners	 are passed only the unpacked, decoded
	      parts of a MIME message.	If you want to pass the	original,  un-
	      decoded	message	  in   as   a  UNIX-style  "mbox"  file,  call
	      md_copy_orig_msg_to_work_dir_as_mbox_file	prior to calling  mes-
	      sage_contains_virus.   The only difference between this function
	      and md_copy_orig_msg_to_work_dir is that this function  prepends
	      a	 "From_"  line to make the message look	like a UNIX-style mbox
	      file.  This is required for some virus scanners  (such  as  Clam
	      AntiVirus) to recognize the file as an e-mail message.

       message_contains_virus()
	      This function runs every installed virus-scanner and returns the
	      scanner results.	The function should be called in list context;
	      the return value is a three-element list ($code, $category, $ac-
	      tion).

	      $code is the actual return code from the virus scanner.

	      $category	is a string categorizing the return code:

	      "ok" - no	viruses	detected.

	      "not-installed" -	indicated virus	scanner	is not installed.

	      "cannot-execute" - for some reason, the scanner could not	be ex-
	      ecuted.

	      "virus" -	a virus	was found.

	      "suspicious" - a "suspicious" file was found.

	      "interrupted" - scanning was interrupted.

	      "swerr" -	an internal scanner software error occurred.

	      $action is a string containing the recommended action:

	      "ok" - allow the message through unmolested.

	      "quarantine" - a virus was detected; quarantine it.

	      "tempfail" - something went wrong; tempfail the message.

       message_contains_virus_trend()

       message_contains_virus_nai()

       message_contains_virus_bdc()

       message_contains_virus_nvcc()

       message_contains_virus_csav()

       message_contains_virus_fsav()

       message_contains_virus_hbedv()

       message_contains_virus_vexira()

       message_contains_virus_sophos()

       message_contains_virus_clamav()

       message_contains_virus_avp()

       message_contains_virus_avp5()

       message_contains_virus_fprot()

       message_contains_virus_fpscan()

       message_contains_virus_fprotd()

       message_contains_virus_fprotd_v6()

       message_contains_virus_nod32()

	      These  functions should be called	in list	context.  They use the
	      indicated	anti-virus software to scan the	message	 for  viruses.
	      These  functions	are intended for use in	filter_begin() to make
	      an initial scan of the e-mail message.

	      The supported virus scanners are:

       nai    NAI "uvscan" - http://www.nai.com/

       Bitdefender "bdc" - http://www.bitdefender.com/

       csav   Command Anti-Virus - http://www.commandsoftware.com/

       fsav   F-Secure Anti-Virus - http://www.f-secure.com/

       hbedv  H+BEDV "AntiVir" - http://www.hbedv.com/

       vexira Vexira "Vexira" -	http://www.centralcommand.com/

       sophos Sophos AntiVirus - http://www.sophos.com/

       avp    Kaspersky	AVP and	aveclient (AVP5) - http://www.avp.ru/

       clamav Clam AntiVirus - http://www.clamav.net/

       f-prot F-RISK F-PROT - http://www.f-prot.com/

       nod32cli
	      ESET NOD32 - http://www.eset.com/

       message_contains_virus_carrier_scan([$host])
	      Connects to the specified	 host:port:local_or_nonlocal  (default
	      $CSSHost),  where	 the Symantec CarrierScan Server daemon	is ex-
	      pected to	be listening.  Return values are the same as the other
	      message_contains_virus functions.

       message_contains_virus_sophie([$sophie_sock])
	      Connects	to  the	 specified socket (default $SophieSock), where
	      the Sophie daemon	is expected to be  listening.	Return	values
	      are the same as the other	message_contains_virus functions.

       message_contains_virus_clamd([$clamd_sock])
	      Connects to the specified	socket (default	$ClamdSock), where the
	      clamd daemon is expected to be listening.	 Return	values are the
	      same as the other	message_contains_virus functions.

       message_contains_virus_trophie([$trophie_sock])
	      Connects	to  the	specified socket (default $TrophieSock), where
	      the Trophie daemon is expected to	be listening.	Return	values
	      are the same as the other	message_contains_virus functions.

       entity_contains_virus($entity)

	      This  function runs the specified	MIME::Entity through every in-
	      stalled virus-scanner and	returns	the scanner results.  The  re-
	      turn values are the same as for message_contains_virus().

       entity_contains_virus_trend($entity)

       entity_contains_virus_nai($entity)

       entity_contains_virus_bdc($entity)

       entity_contains_virus_nvcc($entity)

       entity_contains_virus_csav($entity)

       entity_contains_virus_fsav($entity)

       entity_contains_virus_hbedv($entity)

       entity_contains_virus_sophos($entity)

       entity_contains_virus_clamav($entity)

       entity_contains_virus_avp($entity)

       entity_contains_virus_avp5($entity)

       entity_contains_virus_fprot($entity)

       entity_contains_virus_fpscan($entity)

       entity_contains_virus_fprotd($entity)

       entity_contains_virus_fprotd_v6($entity)

       entity_contains_virus_nod32($entity)
	      These  functions,	 meant to be called from filter(), are similar
	      to the message_contains_virus functions except  they  scan  only
	      the  current part.  They should be called	from list context, and
	      their return  values  are	 as  described	for  the  message_con-
	      tains_virus functions.

       entity_contains_virus_carrier_scan($entity[, $host])
	      Connects	to  the	specified host:port:local_or_nonlocal (default
	      $CSSHost), where the Symantec CarrierScan	Server daemon  is  ex-
	      pected to	be listening.  Return values are the same as the other
	      entity_contains_virus functions.

       entity_contains_virus_sophie($entity[, $sophie_sock])
	      Connects to the specified	socket	(default  $SophieSock),	 where
	      the  Sophie  daemon  is expected to be listening.	 Return	values
	      are the same as the other	entity_contains_virus functions.

       entity_contains_virus_trophie($entity[, $trophie_sock])
	      Connects to the specified	socket (default	 $TrophieSock),	 where
	      the  Trophie  daemon is expected to be listening.	 Return	values
	      are the same as the other	entity_contains_virus functions.

       entity_contains_virus_clamd($entity[, $clamd_sock])
	      Connects to the specified	socket (default	$ClamdSock), where the
	      clamd daemon is expected to be listening.	 Return	values are the
	      same as the other	entity_contains_virus functions.

SMTP FLOW
       This section illustrates	the flow of messages through MIMEDefang.

       1. INITIAL CONNECTION
	      If you invoked mimedefang	with the -r option and have defined  a
	      filter_relay routine, it is called.

       2. SMTP HELO COMMAND
	      The  HELO	 string	 is stored internally, but no filter functions
	      are called.

       3. SMTP MAIL FROM: COMMAND
	      If you invoked mimedefang	with the -s option and have defined  a
	      filter_sender routine, it	is called.

       4. SMTP RCPT TO:	COMMAND
	      If  you invoked mimedefang with the -t option and	have defined a
	      filter_recipient routine,	it is called.

       5. END OF SMTP DATA
	      filter_begin is called.  For each	MIME part, filter  is  called.
	      Then filter_end is called.

PRESERVING RELAY INFORMATION
       Most organizations have more than one machine handling internet e-mail.
       If the primary machine is down, mail is routed to a secondary (or  ter-
       tiary, etc.) MX server, which stores the	mail until the primary MX host
       comes back up.  Mail is then relayed to the primary MX host.

       Relaying	from a secondary to a primary MX host has the unfortunate side
       effect  of losing the original relay's IP address information.  MIMEDe-
       fang allows you to preserve this	information.  One way around the prob-
       lem is to run MIMEDefang	on all the secondary MX	hosts and use the same
       filter.	However, you may not have control over the secondary MX	hosts.
       If you can persuade the owners of the secondary MX hosts	to run MIMEDe-
       fang with a simple filter that only  preserves  relay  information  and
       does  no	other scanning,	your primary MX	host can obtain	relay informa-
       tion and	make decisions using $RelayAddr	and $RelayHostname.

       When you	configure MIMEDefang, supply the "--with-ipheader" argument to
       the  ./configure	 script.   When	 you install MIMEDefang, a file	called
       /usr/local/etc/mimedefang/mimedefang-ip-key will	be created which  con-
       tains  a	randomly-generated header name.	 Copy this file	to all of your
       mail relays.  It	is important that all of your MX hosts have  the  same
       key.   The  key should be kept confidential, but	it's not disastrous if
       it leaks	out.

       On your secondary MX hosts, add this line to filter_end:

	    add_ip_validation_header();

       Note:  You should only add the validation header	to mail	 destined  for
       one of your other MX hosts!  Otherwise, the validation header will leak
       out.

       When the	secondary MX hosts relay to the	primary	 MX  host,  $RelayAddr
       and  $RelayHostname  will be set	based on the IP	validation header.  If
       MIMEDefang notices this header, it sets the global variable  $WasResent
       to  1.	Since  you don't want to trust the header unless it was	set by
       one of your secondary MX	hosts, you should put this code	in  filter_be-
       gin:

	    if ($WasResent) {
		 if ($RealRelayAddr ne "ip.of.secondary.mx" and
		     $RealRelayAddr ne "ip.of.tertiary.mx") {
		      $RelayAddr = $RealRelayAddr;
		      $RelayHostname = $RealRelayHostname;
		 }
	    }

       This  resets the	relay address and hostname to the actual relay address
       and hostname, unless the	message	is coming from one of  your  other  MX
       hosts.

       On the primary MX host, you should add this in filter_begin:

	    delete_ip_validation_header();

       This prevents the validation header from	leaking	out to recipients.

       Note:  The  IP  validation  header works	only in	message-oriented func-
       tions.  It (obviously) has no effect on filter_relay, filter_sender and
       filter_recipient,  because no header information	is available yet.  You
       must take this into account when	writing	your filter;  you  must	 defer
       relay-based decisions to	the message filter for mail arriving from your
       other MX	hosts.

GLOBAL VARIABLE	LIFETIME
       The following list describes the	lifetime of global  variables  (thanks
       to Tony Nugent for providing this documentation.)

       If you set a global variable:

       Outside a subroutine in your filter file
	      It is available to all functions,	all the	time.

       In filter_relay,	filter_sender or filter_recipient
	      Not  guaranteed  to be available to any other function, not even
	      from one filter_recipient	call to	the  next,  when  receiving  a
	      multi-recipient email message.

       In filter_begin
	      Available	to filter_begin, filter	and filter_end

       In filter
	      Available	to filter and filter_end

       In filter_end
	      Available	within filter_end

       The  "built-in"	globals	like $Subject, $Sender,	etc. are always	avail-
       able to filter_begin, filter and	filter_end. Some are available to fil-
       ter_relay,  filter_sender or filter_recipient, but you should check the
       documentation of	the variable above for details.

MAINTAINING STATE
       There are four basic groups of filtering	functions:

       1      filter_relay

       2      filter_sender

       3      filter_recipient

       4      filter_begin, filter, filter_multipart, filter_end

       In general, for a given mail message, these groups of functions may  be
       called  in  completely different	Perl processes.	 Thus, there is	no way
       to maintain state inside	Perl between groups of	functions.   That  is,
       you cannot set a	variable in filter_relay and expect it to be available
       in filter_sender, because the filter_sender invocation might take place
       in a completely different process.

       For a given mail	message, it is always the case that filter_begin, fil-
       ter, filter_multipart and  filter_end  are  called  in  the  same  Perl
       process.	  Therefore, you can use global	variables to carry state among
       those functions.	 You should be very careful to initialize  such	 vari-
       ables  in  filter_begin to ensure no data leaks from one	message	to an-
       other.

       Also for	a given	mail message, the $CWD global variable holds the  mes-
       sage spool directory, and the current working directory is set to $CWD.
       Therefore, you can store	state in files inside $CWD.  If	 filter_sender
       stores  data  in	a file inside $CWD, then filter_recipient can retrieve
       that data.

       Since filter_relay is called directly after a mail connection is	estab-
       lished,	there  is  no  message	context	yet, no	per-message mimedefang
       spool directory,	and the	$CWD global is not set.	Therefore, it  is  not
       possible	 to  share  information	 from filter_relay to one of the other
       filter functions. The only thing	that filter_relay has in  common  with
       the  other functions are	the values in the globals $RelayAddr, and $Re-
       layHostname. These could	be used	to access per-remote-host  information
       in some database.

       Inside $CWD, we reserve filenames beginning with	upper-case letters for
       internal	MIMEDefang use.	 If you	want to	create files to	 store	state,
       name  them beginning with a lower-case letter to	avoid clashes with fu-
       ture releases of	MIMEDefang.

SOCKET MAPS
       If you have Sendmail 8.13 or later, and have compiled it	with the SOCK-
       ETMAP  option,  then  you  can use a special map	type that communicates
       over a socket with another program (rather than looking up a key	 in  a
       Berkeley	database, for example.)

       mimedefang-multiplexor  implements  the	Sendmail SOCKETMAP protocol if
       you supply the -N option.  In that case,	 you  can  define  a  function
       called filter_map to implement map lookups.  filter_map takes two argu-
       ments:  $mapname	is the name of the Sendmail map	(as  given  in	the  K
       sendmail	configuration directive), and $key is the key to be looked up.

       filter_map  must	 return	a two-element list: ($code, $val) $code	can be
       one of:

       OK     The lookup was successful.  In this case,	$val must be  the  re-
	      sult of the lookup

       NOTFOUND
	      The  lookup  was unsuccessful -- the key was not found.  In this
	      case, $val should	be the empty string.

       TEMP   There was	a temporary failure of some kind.  $val	can be an  ex-
	      planatory	error message.

       TIMEOUT
	      There  was  a  timeout of	some kind.  $val can be	an explanatory
	      error message.

       PERM   There was	a permanent failure.  This is not the same as  an  un-
	      successful  lookup; it should be used only to indicate a serious
	      misconfiguration.	 As before, $val can be	an  explanatory	 error
	      message.

       Consider	 this small example.  Here is a	minimal	Sendmail configuration
       file:

	    V10/Berkeley
	    Kmysock socket unix:/var/spool/MIMEDefang/map.sock
	    kothersock socket unix:/var/spool/MIMEDefang/map.sock

       If  mimedefang-multiplexor   is	 invoked   with	  the	arguments   -N
       unix:/var/spool/MIMEDefang/map.sock,  and the filter defines filter_map
       as follows:

	    sub	filter_map ($$)	{
		my($mapname, $key) = @_;
		my $ans;
		if($mapname ne "mysock") {
		    return("PERM", "Unknown map	$mapname");
		}
		$ans = reverse($key);
		return ("OK", $ans);
	    }

       Then in Sendmail's testing mode,	we see the following:

	    > /map mysock testing123
	    map_lookup:	mysock (testing123) returns 321gnitset (0)
	    > /map othersock foo
	    map_lookup:	othersock (foo)	no match (69)

       (The return code	of 69 means EX_UNAVAILABLE or Service Unavailable)

       A real-world example could do map lookups in an LDAP directory  or  SQL
       database, or perform other kinds	of processing.	You can	even implement
       standard	Sendmail maps like virtusertable, mailertable, access_db, etc.
       using SOCKETMAP.

TICK REQUESTS
       If  you	supply	the -X option to mimedefang-multiplexor, then every so
       often, a	"tick" request is sent to a free slave.	 If  your  filter  de-
       fines  a	function called	filter_tick, then this function	is called with
       a single	argument: the tick type.  If you run multiple parallel	ticks,
       then  each tick has a type ranging from 0 to n-1, where n is the	number
       of parallel ticks.  If you're only running one tick request,  then  the
       argument	to filter_tick is always 0.

       You can use this	facility to run	periodic tasks from within MIMEDefang.
       Note, however, that you have no control over which slave	is  picked  to
       run  filter_tick.  Also,	at most	one filter_tick	call with a particular
       "type" argument will be active at any time, and if there	 are  no  free
       slaves when a tick would	occur, the tick	is skipped.

SUPPORTED VIRUS	SCANNERS
       The following virus scanners are	supported by MIMEDefang:

       o      Symantec	  CarrierScan	 Server	  (http://www.symantec.com/re-
	      gion/can/eng/product/scs/)

       o      Trend Micro vscan	(http://www.antivirus.com/)

       o      Sophos   Sweep   (http://www.sophos.com/products/antivirus/savu-
	      nix.html)

       o      H+BEDV AntiVir (http://www.hbedv.com/)

       o      Central Command Vexira (http://www.centralcommand.com/)

       o      NAI uvscan (http://www.nai.com)

       o      Bitdefender bdc (http://www.bitdefender.com)

       o      Norman Virus Control (NVCC) (http://www.norman.no/)

       o      Command csav (http://www.commandsoftware.com)

       o      F-Secure fsav (http://www.f-secure.com)

       o      The clamscan command-line	scanner	and the	clamd daemon from Clam
	      AntiVirus	(http://www.clamav.net/)

       o      Kaspersky	Anti-Virus (AVP) (http://www.kaspersky.com/)

       o      F-Risk F-Prot (http://www.f-prot.com/)

       o      F-Risk F-Prot v6 (http://www.f-prot.com/)

       o      F-Risk FPROTD (daemonized	version	of F-Prot)

       o      Symantec	  CarrierScan	 Server	   (http://www.symantec.ca/re-
	      gion/can/eng/product/scs/buymenu.html)

       o      Sophie (http://www.vanja.com/tools/sophie/), which uses the lib-
	      savi library from	Sophos,	is supported in	daemon-scanning	mode.

       o      Trophie (http://www.vanja.com/tools/trophie/),  which  uses  the
	      libvsapi	library	from Trend Micro, is supported in daemon-scan-
	      ning mode.

       o      ESET NOD32 (http://www.eset.com/)

AUTHORS
       mimedefang was written by Dianne	Skoll  <dfs@roaringpenguin.com>.   The
       mimedefang home page is http://www.mimedefang.org/.

SEE ALSO
       mimedefang(8), mimedefang.pl(8)

4th Berkeley Distribution	8 February 2005		  MIMEDEFANG-FILTER(5)

NAME | DESCRIPTION | CALLING SEQUENCE | DISPOSITION | CONTROLLING RELAYING | FILTERING BY HELO | FILTERING BY SENDER | FILTERING BY RECIPIENT | INITIALIZATION AND CLEANUP | CONTROLLING PARSING | EXTENDING MIMEDEFANG | REJECTING UNKNOWN USERS EARLY | GLOBAL VARIABLES YOU CAN SET | FILTER | GLOBAL VARIABLES SET BY MIMEDEFANG.PL | ACTIONS | USEFUL ROUTINES | LOGGING | RBL LOOKUP FUNCTIONS | TEST FUNCTIONS | SMTP FLOW | PRESERVING RELAY INFORMATION | GLOBAL VARIABLE LIFETIME | MAINTAINING STATE | SOCKET MAPS | TICK REQUESTS | SUPPORTED VIRUS SCANNERS | AUTHORS | SEE ALSO

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=mimedefang-filter&sektion=5&manpath=FreeBSD+12.1-RELEASE+and+Ports>

home | help