[EXIM] Using embedded Perl and LDAP for address resolution

Top Page
Delete this message
Reply to this message
Author: Paul Makepeace
Date:  
To: exim-users
Subject: [EXIM] Using embedded Perl and LDAP for address resolution
Hello all

I am currently in the process of attempting emulate a sendmail/PH
hack that uses a program called phquery.c[1] to do fuzzy matching
against a PH server to resolve email addresses.

I am recoding this using exim and its embedded Perl and LDAP.

The point of this exercise is to enable alias@??? to resolve
either where (in LDAP-speak) the unique alias=$local_part, failing
that try the last name sn=$local_part, failing that, try full name
with punctuation mapped to single spaces. Various useful (for us)
messages are reported in the case of no or multiple messages.

(To answer the "you actually don't want to do this because nasty things
will happen" objection up-front: well, various mechanisms and policies
are in place that'll prevent completely or at least heavily mitigate
the sorts of problems people've mentioned in the past.)

In any of those cases, the results will be 0, 1 or >2 results. For
0, simply try the next option, for 1 assume it's correct and deliver
and for >2 stop and report a failure. For all '0' report a failure
too.

I would really appreciate comments on my as yet partial solution.
I've never fiddled with exim.conf before or any of these facilities so
have more then likely done something sub-optimal or not in the 'Zen'
of exim... Please be nice ;-)

Once I have a working solution (hopefully not much more than is below)
I'll repost it in its entirety.

The bit I'm stuck at is how to deliver the message when it does
actually get a single match. You'll see why below.


1. in exim.conf set up some macros:

LDAP_URL = ldap://localhost:489/ou=employee,o=slb,c=an?mail,cn,ou?one?

ALIAS_MATCH = &(mail=*)(alias=$local_part)
SN_MATCH    = &(mail=*)(cn=* $local_part)
CN_MATCH    = &(mail=*)(cn=${perl{punc_to_spc}{$local_part}})


The perl sub is the trivial:

sub punc_to_spc ($) {
        my $local_part = shift;
        $local_part =~ s/\W+/ /;
        $local_part;
}


2. Ensure Perl's ready to go. It's always used. Currently just sub's
are defined (see later).

perl_at_start
perl_startup = do '/etc/exim/exim.pl'

3. Create a failure transport to cope with 0 or >2 results.

# Added this from the "Automatic mail processing" chapter in the Exim Spec.

unknownuser_pipe:
driver = pipe
command = /etc/exim/baduser.pl $local_part $domain ${perl{possible_addresses}
}
ignore_status
return_output
user = nobody

Here the even more trivial perl sub is:

sub possible_addresses {
        return $addresses;
}


4. Create a director to perform the LDAP lookups. Dump results into perl sub
that basically just sets variables. (I notice that \'ed line breaks
cause exim PANICs; why is this?)

localuser:
driver = smartuser
transport = ${perl{ldap_who} {${lookup ldapm {LDAP_URL(ALIAS_MATCH)}{$value}
{${lookup ldapm {LDAP_URL(SN_MATCH)}{$value} {${lookup ldapm {LDAP_URL(CN_MAT
CH)}{$value}{DUNNO}}}}}}}}

Here ldap_who performs the transport decision and records the results
for later: There is some legacy code in here, please ignore.

# remote_smtp is wrong! How do I do this to deliver to an address
# returned in $address (modulo some parsing)?

sub ldap_who {
        $addresses = shift;
        @addresses = split /\n/, $addresses, 10;


        return @addresses == 1 && $addresses ne 'DUNNO' ?
               'remote_smtp' :
               'unknownuser_pipe';
}


5. The only remaining piece of perl is actually the failure program
baduser.pl which does little more than:

my ($local_part, $domain, $addresses) = @ARGV;

$addresses eq 'DUNNO' ? none() : multiple();

and a little parsing and user-friendly message generation.



PROBLEM: Currently I'm "slipping in" a call to perl to do the
resolution in the guise of a string expansion for transport. What I
would really rather do is give it to new_address in the smartuser or
some failure-with-message. I would also rather not spawn a new process
using the unknownuser_pipe. I don't see how to perform this 'redirect
to new_address or return to user with an error message'. ${if ${perl{}}
{new_address=...}{transport=...}} but this isn't possible. Can I use
this basic approach I have above? Or do I need to look at another way?
System filters?

WISH LIST: Modifying local_part inside Perl: having Exim::* accessible
as variable (read/write) rather than via a function. Having exim or
something I write return failed emails as attachments not included inline.
Philosophically or ideologically or elsewise some may not like this idea
(why?) but I'd like to do it anyway.


Many thanks,

Paul

[1] http://www-wsg.cso.uiuc.edu/sendmail/sendmail-phmap/
and follow the link in the Introduction to
http://www-wsg.cso.uiuc.edu/sendmail/UIUCnet/phquery.html.

--
*** Exim information can be found at http://www.exim.org/ ***