Delivery to Commands

As mentioned earlier in the chapter, you can't use local aliases, .forward files, and mailing-list programs with virtual domains delivered by the virtual delivery agent. You've seen that you can easily set up aliases through the virtual_alias_maps parameter, but you cannot deliver messages to a command. In this last section, we'll look at working around that issue by demonstrating how to deliver virtual addresses to external programs. The first example sets up delivery to an autoreply program, and the second to a mailing-list manager.

Auto-responders are scripts or programs that process incoming messages and return a reply to the sender of the message without any human intervention. The autoreply program used in this example, inforeply.pl, is listed in Example 8-1. This program is meant to handle mail for a dedicated information email address. Users or customers can send a message to the address to request special information. Note that this simple example is inadequate as a general autoreply program, such as the Unix vacation command. It does not cache addresses it has already replied to, and it does not do full checking for addresses that should not receive automatic replies (see the sidebar). You might also like to enhance the program to return different types of information, based on the subject or a keyword in the body of the request messages.

Example 8-1. Simple automatic reply program

#!/usr/bin/perl -w
#
# inforeply.pl - Automatic email reply.
#
# All messages are logged to your mail log. Check the
# log after executing the script to see the results.
#
# Set $UID to the uid of the process that runs the script.
# Check the entry in master.cf that calls this script. Use
# the uid of the account you assign to the user= attribute.
# If you want to test the script from the command line, 
# set $UID to your own uid.
#
# Set $ENV_FROM to the envelope FROM address you want on
# outgoing replies. By default it's blank, which will
# use the NULL sender address <>. You can set it to an
# address to receive bounces, but make sure you don't set
# it to the same address that invokes the program, or
# you'll create a mail loop.
#
# Point $INFOFILE to a text file that contains the text of
# the outgoing reply. Include any headers you want in the
# message such as Subject: and From:. The To: header is 
# set automatically based on the sender's address. Make
# sure you have an empty line between your headers and the
# body of the message.
#
# If necessary, change the path to sendmail in $MAILBIN.
#
# @MAILOPTS contains options to sendmail. Make changes if
# necessary. The default options should work in most 
# situations.
#
# The calls to syslog require that your Perl installation
# converted the necessary header files. See h2ph in your
# Perl distribution.
# 

require 5.004; # for setlogsock in Sys::Syslog module

use strict;
use Sys::Syslog qw(:DEFAULT setlogsock);

#
# Config options. Set these according to your needs.
#
my $UID = 500;
my $ENV_FROM = "";
my $INFOFILE = "/home/autoresp/inforeply.txt";
my $MAILBIN = "/usr/sbin/sendmail";
my @MAILOPTS = ("-oi", "-tr", "$ENV_FROM");
my $SELF = "inforeply.pl";
#
# end of config options

my $EX_TEMPFAIL = 75;
my $EX_UNAVAILABLE = 69;
my $EX_OK = 0;
my $sender;
my $euid = $>;

$SIG{PIPE} = &PipeHandler;
$ENV{PATH} = "/bin:/usr/bin:/sbin:/usr/sbin";

setlogsock('unix');
openlog($SELF, 'ndelay,pid', 'user');

#
# Check our environment.
#
if ( $euid != $UID ) {
 syslog('mail|err',"error: invalid uid: $> (expecting: $UID)");
 exit($EX_TEMPFAIL);
}
if ( @ARGV != 1 ) {
 syslog('mail|err',"error: invalid invocation (expecting 1 argument)");
 exit($EX_TEMPFAIL);
} else {
 $sender = $ARGV[0];
 if ( $sender =~ /([w-.%]+@[w.-]+)/ ) { # scrub address
 $sender = $1;
 } else {
 syslog('mail|err',"error: Illegal sender address");
 exit($EX_UNAVAILABLE);
 }
}
if (! -x $MAILBIN ) {
 syslog('mail|err', "error: $MAILBIN not found or not executable");
 exit($EX_TEMPFAIL);
}
if (! -f $INFOFILE ) {
 syslog('mail|err', "error: $INFOFILE not found");
 exit($EX_TEMPFAIL);
}

#
# Check sender exceptions.
#
if ($sender eq ""
 || $sender =~ /^owner-|-(request|owner)@|^(mailer-daemon|postmaster)@/i) {
 exit($EX_OK);
}

#
# Check message contents for Precedence header.
#
while (  ) {
 last if (/^$/);
 exit($EX_OK) if (/^precedence:s+(bulk|list|junk)/i);
}

#
# Open info file.
#
if (! open(INFO, "<$INFOFILE") ) {
 syslog('mail|err',"error: can't open $INFOFILE: %m");
 exit($EX_TEMPFAIL);
}

#
# Open pipe to mailer.
#
my $pid = open(MAIL, "|-") || exec("$MAILBIN", @MAILOPTS);

#
# Send reply.
#
print MAIL "To: $sender
";
print MAIL while ();

if (! close(MAIL) ) {
 syslog('mail|err',"error: failure invoking $MAILBIN: %m");
 exit($EX_UNAVAILABLE);
}

close(INFO);
syslog('mail|info',"sent reply to $sender");
exit($EX_OK);


sub PipeHandler {
 syslog('mail|err',"error: broken pipe to mailer");
}

8.5.1 Configuring a Virtual Auto-Responder

To configure an auto-responder to work with virtual domains, you must create a special transport type in master.cf for delivery to the specific command. In order to have messages delivered to your new component, you have to map an address to the transport you created using transport maps.

Many auto-responders can handle only a single message at a time with only one recipient. You can limit the number of recipients to any transport type by setting a parameter of the form transport_destination_recipient_limit, where the string transport is the name of the transport type. If a transport called inforeply should be limited to only one recipient at a time, set the following parameter:

inforeply_destination_recipient_limit = 1

Writing an Auto Responder

If you are writing your own auto-responder, there are several considerations you should take into account. The first, and possibly most important, is that your program is receiving data from the network, which is an untrusted source. Don't make any assumptions about the supplied input you are processing, other than to assume that it's designed to compromise your system in some way. Under no circumstances should you invoke a shell where the untrusted input might be able to gain access to your system.

Other issues to think about have more to do with being polite than anything else. For example, you don't want your auto-responder to blast out a reply to a mailing list of hundreds or thousands of recipients. Never send replies to addresses that have the form owner-list or list-request. There are several other addresses you probably don't want to reply to, such as postmaster, daemon, and majordomo. Your program should set its own envelope address to the null string to prevent mailer loops.

Many mailing lists make use of a header field called Precedence:. They generally set the value to something like bulk to indicate its purpose. Your program should check the Precedence: field, and if it is set to bulk, list, or junk, do not send a reply.

Finally, make sure that your program has a way to log what happens to each message received. Once Postfix delivers a message to your program, the program has the responsibility of checking for errors and providing a way to communicate them to an administrator.

The following steps walk through setting up the email address info@ora.com to use inforeply.pl. The domain ora.com is configured as a virtual domain. The local domain on the host is example.com:

  1. Create a local account under whose privileges the inforeply.pl program should execute. In this example, an account called autoresp is used. You should create a new pseudo-account for this purpose. Use the normal administrative tools on your system to make the account.
  2. Create a transport type called inforeply by adding an entry to your master.cf file. The entry should look something like the following:

    inforeply unix - n n - - pipe
     flags= user=autoresp argv=/usr/local/bin/inforeply.pl ${sender}

    The pipe daemon is used to deliver messages to external commands. You can specify flags if your program requires any special options. (See the pipe(8) man page for information on the available options.) The user attribute is required for any pipe components in master.cf. The argv attribute must be specified last, and should start with the path to the autoreply command. There are several values that you can pass to your command when Postfix executes it. The values are supplied through special macros. In this example, the envelope sender address (${sender}) is passed. For the simple inforeply.pl responder, you need only the sender address, but you will often want the recipient (${recipient}) address, too, for auto-responders that can handle multiple recipient addresses. See the pipe(8) manpage for the list of available macros.

  3. If you haven't already set up any transports on your system, set the transport_maps parameter in main.cf to point to the transport table:

    transport_maps = hash:/etc/postfix/transport
  4. Add an entry in your transport table that contains the address to direct messages to the inforeply transport. In this case, we'll use the address autoresp@ora.com:

    autoresp@ora.com inforeply

    Now, all messages sent to autoresp@ora.com are delivered to the auto-responder.

  5. Execute postmap against the transport lookup table:

    # postmap /etc/postfix/transport
  6. Point virtual_alias_maps to your virtual alias lookup table:

    virtual_alias_maps = hash:/etc/postfix/virtual_alias
  7. Add an entry to the virtual_alias lookup table to map info@ora.com to both the new autoreply address and the actual recipient address that can receive the messages:

    info@ora.com autoresp@ora.com service@oreilly.com
  8. Execute postmap against the virtual alias lookup table:

    # postmap /etc/postfix/virtual_alias

    Now messages sent to info@ora.com will be delivered to autoresp@ora.com and service@oreilly.com.

  9. Reload Postfix so that it recognizes the changes to its main.cf and master.cf files:

    # postfix reload

    When a message is sent to info@ora.com, Postfix first finds the destination address in the virtual_alias lookup table. The address points both to autoresp@ora.com and service@oreilly.com. Postfix finds autoresp@ora.com in the transport lookup table, which points to the inforeply transport in the master.cf file. The entry in master.cf pipes the message to the inforeply.pl program, which sends the reply to the original sender. Finally, the message is also resubmitted for delivery to service@oreilly.com.

8.5.2 Configuring a Virtual Mailing List Manager

In the next example, you'll set up a mailing list for a virtual domain. Mailing-list managers are discussed in Chapter 10. You may want to review that chapter before setting up your virtual mailing lists. This example creates a mailing list for Majordomo. You should first install and configure Majordomo according to the directions in Chapter 10.

Virtual mailing lists work by creating a parallel version of the list under a local domain. The local version is only used internally on your system. External users can use the virtual addresses and never know that the local version exists. When naming the local version, you may want to include the virtual domain name in some way to distinguish the list from lists for other virtual domains hosted on your system. The following procedure creates a mailing list at the virtual address astronomy@ora.com that is handled by the local version astronomy-ora@example.com:

  1. Set up the local version of the mailing list just as you would a normal mailing list, as described in Chapter 10, by adding the following entries to /usr/local/majordomo/aliases:

    # astronomy@ora.com list
    astronomy-ora: :include:/usr/local/majordomo/lists/astronomy
    owner-astronomy-ora: kdent@ora.com
    astronomy-ora-request: "|/usr/local/majordomo/wrapper request-answer 
     astronomy-ora"
    astronomy-ora-approval: kdent@ora.com
  2. Rebuild the Majordomo aliases table:

    # postaliases /usr/local/majordomo/aliases
  3. Create the file to hold the email addresses for list subscribers, and set its ownership to the majordom account:

    # touch /usr/local/majordomo/lists/astronomy
    # chown majordom /usr/local/majordomo/lists/astronomy
  4. If desired, create an info file for the list at /usr/local/majordomo/lists/astronomy-ora.info.
  5. Create the necessary addresses for the list at the virtual domain. Add the following entries to the virtual alias map file virtual_alias:

    # astronomy@ora.com list
    astronomy@ora.com astronomy-ora@localhost
    owner-astronomy@ora.com owner-astronomy-ora@localhost
    astronomy-request@ora.com astronomy-ora-request@localhost
    astronomy-approval@ora.com astronomy-ora-approval@localhost
  6. Rebuild the virtual alias map file:

    # postmap virtual_alias
  7. Add addresses to the /usr/local/majordomo/lists/astronomy list file.

You should now be able to send messages to astronomy@ora.com for distribution to all of the addresses in your list file.

Introduction

Prerequisites

Postfix Architecture

General Configuration and Administration

Queue Management

Email and DNS

Local Delivery and POP/IMAP

Hosting Multiple Domains

Mail Relaying

Mailing Lists

Blocking Unsolicited Bulk Email

SASL Authentication

Transport Layer Security

Content Filtering

External Databases

Appendix A. Configuration Parameters

Appendix B. Postfix Commands

Appendix C. Compiling and Installing Postfix

Appendix D. Frequently Asked Questions



Postfix(c) The Definitive Guide
Postfix: The Definitive Guide
ISBN: 0596002122
EAN: 2147483647
Year: 2006
Pages: 130
Authors: Kyle Dent D.

Similar book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net