Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
PerlMonks - Make Postfix relay recipient tables from Exchange smtp addresses

by BravoTwoZero (Scribe)
on Aug 30, 2004 at 14:48 UTC ( #386929=sourcecode: print w/replies, xml ) Need Help??
Category: E-mail programs
Author/Contact Info BravoTwoZero

We use this to run an ldap query from our external OpenBSD/Postfix MTAs to an Exchange 5.5 server so we can generate a Postfix relay recipient map of legitimate mailboxes (witty, eh?). We reject non-legit mail at the external MTA level.

YMMV... blah blah blah. We run it twice a day from cron and scp the results to the public-facing MTAs. It's only 1,000 or so addresses, so the run time and copy time are pretty short.

Also, we don't accept smtp deliveries to distribution lists. So, only directory entries of the type "organizationalperson" are grabbed. DLs would require "groupofnames" entries to be returned as well.

Updated 2004-09-02: I changed from the ldapsearch binary to Net::LDAP.

# Running this script WILL REWRITE some of your Postfix
# config files, if the paths are the same!!!
# Assumes: Postfix
# We use this to run an ldap query to an Exchange 5.5 server
# so we can generate a Postfix relay recipient map of legitimate
# mailboxes (witty, eh?). We reject non-legit mail at the 
# external MTA level.
# All ya need to change are the srvname and domain variables
# at the beginning. You might have to change the path to the
# Postfix config files, too, depending on your OS...
# ...and it calls a simple shell script to rebuild the db. Here's
# the 3-line shell script code to cut and paste:
# !/bin/sh
# /usr/local/sbin/postmap /etc/postfix/relay_recipients
# /usr/local/sbin/postfix reload
# (remove the comments and save to /usr/local/bin/
# ...and (no, it couldn't be that easy) I'm not covering how
# to use relay recipient maps in Postfix to reject unknown addys. I
# would add that the docs at or
# have 
# the answers to that. You are welcome to a copy of my file
# if you want. Email me at It may take a day
# or two to reply.
# Here's the "othermailbox" addresses that I discard:
# Scanmail:    next if ( $val =~ /^SMEX/ );
# CCMail:      next if ( $val =~ /^CCMAIL/ );
# MSMail:      next if ( $val =~ /^MS\$/ );
# You might have others you want to discard. 

use File::Copy;
use Net::LDAP;

# ldap server to query
$ldapsrv = "";

# for what domain will we search in the mail addresses?
$domain = "";

# postfix conf files live here
$postfix_conf = "/etc/postfix";

# path to relay_recipients file/db: current, new and backups
$rr_file = "$postfix_conf/relay_recipients";
$rr_db = "$postfix_conf/relay_recipients.db";
$rr_file_bak = "$postfix_conf/relay_recipients.bak";
$rr_db_bak = "$postfix_conf/relay_recipients.db.bak";
$rr_file_new = "$postfix_conf/";

# postfix db rebuild and reload command
$postfix = "/usr/local/bin/";

my @thelist;

$ldap = Net::LDAP->new($ldapsrv) or die "$@";

$mesg = $ldap->bind;

$mesg = $ldap->search(
    base   => "c=US",
    filter => "(& (mail=*$domain)(objectclass=organizationalperson))",
    attrs => ['mail','othermailbox']

$mesg->code && die $mesg->error;

foreach $entry ($mesg->entries) { 
    my $attr;
    my $line;
    my @results;
    foreach $attr ( $entry->attributes ) {
        next if ( $attr =~ /;binary$/ );
        my $val = $entry->get_value ( $attr );
        next if ( $val =~ /^SMEX/ );
        next if ( $val =~ /^CCMAIL/ );
        next if ( $val =~ /^MS\$/ );
        $_ = $val;
        $val = $_;
        $val = lc $val;
        if ($val) { push @thelist,$val; }

$mesg = $ldap->unbind;

@thelist = sort @thelist;

# make a copy of the relay_recipients file and db just in case

# open a filehandle for writing
open (RELAY, "> $rr_file_new") or die "Can't open $rr_file_new $!\n";
## yup. this is almost the same as the test case above
foreach $line (@thelist) {
    # make it all lowercase
    # here's what is different from the stdout example above:
    # print the remaining address and a newline the filehandle
    print RELAY "$line\tdummy\n";
        #print "$line\tdummy\n";
# close that filehandle
close RELAY;

## copy the new file to the current file
copy($rr_file_new,$rr_file) or die "Can't copy $rr_file_new to $rr_fil
+e: $! \n";

## rebuild db and reload postfix with an external script
  • Comment on - Make Postfix relay recipient tables from Exchange smtp addresses
  • Download Code
Replies are listed 'Best First'.
by grinder (Bishop) on Aug 30, 2004 at 16:28 UTC
    (yes, I *should* have used Net::LDAP... but I didn't)

    Heh, I was about to say as much, then I saw that comment. I initially did the same thing as you: munged the output of ldapsearch. But when I had a spare tuit I rewrote the code to use Net::LDAP. The result is much more satisfying, at two levels.

    Firstly, there's no external program to rely on. That's always a big plus in my books. And I also found that the code became simpler, because the munging disappeared.

    The main difference in my approach is that I generate the access map every twenty minutes. I then compare the it to the current version and move it over only if there are changes.

    There's one bit in the code that puzzles me, the part containing if( $some_condition ) { $_ = 0 } and then below you have if( /some_pattern/ ) {...}. I might be missing something subtle, but it seems to me that this would be better written as next if $some_condition.

    But ++ anyway for the code. Having your legitimate recipients known on the perimeter is an excellent way of cutting down on undeliverable crud.

    - another intruder with the mooring of the heat of the Perl

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://386929]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (6)
As of 2021-01-27 20:19 GMT
Find Nodes?
    Voting Booth?