SAGE - Sage feature

applying sendmail anti-spam checks at the user level

szeto_bailey

by Bailey Szeto
<[email protected]>

Bailey Szeto is a systems administrator for Cisco Systems Inc. When not dealing with mail issues, he enjoys . . . oh wait, there's never a time when he's not dealing with mail issues. . . .



For most corporations, email is a mission-critical application. It often is the number one communications medium for developers, sales, and customers. However, unsolicited commercial email (UCE or spam) has reached levels at which it is starting to interfere with the effectiveness of email as a communication tool. Separating junk from real email wastes not only network/computing resources but also employee time. More important, many people consider spam to be an invasion of their private mailboxes; arguably the worst aspect of spam is that it demoralizes employees and can even jeopardize their emotional well-being.

Most corporate postmasters have been given the responsibility of dealing with spam. A quick search on the Internet reveals various technical solutions that have been created to help stop spam. One big implementation problem with these anti-spam measures is that they are usually applied on a site-wide basis. For most corporations, some email addresses — such as sales, technical support, and bug reporting — must not be blocked. Some of the spammers are our customers; we want their purchase orders to get through but not their spam. We never want to block bug reports from coming in, even if they are from a known spammer.

This article discusses configuration changes that can be made to sendmail rulesets in order to implement an anti-spam filtering policy on a per-user basis. Users can decide if they want to activate anti-spam features and what level of filtering they want.

The Anti-Spam Features of sendmail

Beginning with sendmail 8.8, the check_* group of rulesets were added as features. This group of rulesets provides hooks into the SMTP dialog. For the sake of clarity, I'll show the SMTP dialog here:

1. The sending machine issues a HELO (or EHLO) in which it identifies itself.

2. The sending machine issues a MAIL FROM in which it identifies the sender of the message.

3. The sending machine issues a RCPT TO in which it identifies the recipient of the message.

4. The sending machine issues a DATA to tell the receiving machine it is about to transfer the message.

5. The message is transferred, and the sending machine ends the message with a "." on a line by itself.

6. The receiving machine acknowledges that it got the message, usually by issuing a unique number.

Sendmail 8.8 included the following four check rulesets:

  •  check_relay - this ruleset is called after step 1 in the SMTP dialog above. It is used to prevent unauthorized IPs from connecting to your machine.

  •  check_mail - this ruleset is called after step 2 in the SMTP dialog. It is used to stop mail from known senders.

  •  check_rcpt - this ruleset is called after step 3 in the SMTP dialog. It is primarily used to stop relaying (not to be confused with check_relay above.) Relaying occurs when an external user sends mail to your server meant for a different external user. They are using your server as a relay for their email. Spammers often do this in order to hide their identity or to take advantage of your resources. Since we know both the sender and recipient at this point, we can decide whether or not the email is relayed.

  •  check_compat - this ruleset is called after step 5 in the SMTP dialog. It can be used to stop delivery of a message after it has been accepted.

    Although these check_* hooks were provided, it was left to the system administrator to actually develop rules using these hooks. Claus Assmann[1] and Robert Harker[2] maintain a set of effective rules based on these hooks.

    When Sendmail 8.9 was released, Eric Allman included some basic anti-spam features that could be configured into sendmail to take advantage of these hooks. By default, Sendmail 8.9 had relaying turned off (implemented in the check_rcpt ruleset). Furthermore, you could enable rejection of email based on either a DNS lookup or the results of a database lookup (implemented in the check_mail ruleset).

    The Problem

    The main problem with the anti-spam features included with sendmail is that the checks are made too early in the SMTP dialog. As configured by sendmail, both the DNS and database check are made in check_mail (SMTP step 2), after the sender has been identified. If the sender fails the checks, the mail is rejected.

    The rejection comes too early because we do not know whom the mail is meant for yet. Also, this means that mail will be bounced regardless of who the recipient was. This is a problem for corporations because there may be some addresses that must receive all email. Also, some users may actually want to get spam (true case)!

    The Solution

    I thought about ways we could block spam for our users while at the same time allowing full access for other addresses. After a little experimenting, I came up with a ruleset that I call "Extended_check_rcpt." Basically, I hold off on the spam checks until the recipient is identified. Then we can check to see if the recipient wants filtering and apply the spam checks as appropriate.

    At first I thought about implementing this delayed check by taking advantage of check_compat. According to the sendmail book, "Not all situations can be resolved by simply checking the recipient or sender address. Sometimes you will need to make judgments based on pairs of addresses. To handle this situation, V8.8 introduced the check_compat rule set."[3] Unfortunately, the problem with check_compat is that it is called after the message has been accepted. That means that the sender has already transmitted the message and has closed the connection. If you decide to bounce the message because it fits the spam criteria, your server is then tasked with delivering a bounce message back to the sender. If the sender's address is fake, the bounce messages may back up and clog your mail queue.

    Ideally you want to be able to reject a message before it is accepted and the sender has closed the connection. This will shift the burden of delivering a bounce message back to the sending machine. Therefore the best place to apply our spam checks is in check_rcpt, after both the sender and recipient are identified but before the message is sent. Fortunately, sendmail stores the sender's address in a macro, and we can use sendmail's delayed macro expansion capabilities to access this value during check_rcpt.

    Since sendmail supports the use of a database to keep track of spamming addresses, we can create another database to keep track of user preferences. After our modifications are done, the SMTP dialog would look something like this:

    1. The sending machine issues a HELO (or EHLO) in which it identifies itself.

    2. The sending machine issues a MAIL FROM in which it identifies the sender of the message.

    3. The sending machine issues a RCPT TO in which it identifies the recipient of the
    message.

       a. Look into the user database to see if the recipient wants spam filtering.

       b. Apply DNS check if appropriate.

       c. Apply spam database check if appropriate.

       d. Reject the message if step b or c fails, otherwise continue with step 4.

    4. The sending machine issues a DATA to tell the receiving machine it is about to transfer the message.

    5. The message is transferred, and the sending machine ends the message with a "." on a line by itself.

    6. The receiving machine acknowledges that it got the message, usually by issuing a unique number.

    The new ruleset is called Extended_check_rcpt because it is called after the sendmail's Basic_check_rcpt, which in turn is called by check_rcpt.

    sendmail.cf Changes to Implement Extended_check_rcpt

    I'll assume that you already know how to create a sendmail.cf file from an mc file. You can have other features in your mc file, but the two you should have in order to implement Extended_check_rcpt are:

    FEATURE(access_db, dbm -o /etc/mail_access)dnl
    FEATURE(accept_unresolvable_domains)dnl

    Even though the access database checks too early during the SMTP process, it is still a useful feature to enable because the database serves other purposes. First, it is used to enable selective relaying. By listing domains in the access database, you can allow other domains to relay through your site. Second, there may be instances in which you really want to block email from a particular domain, regardless of users' settings. You can use the access database to globally block a particular sender or domain. However, we will not use the access database for "spam stomping," as the config file puts it. We will put our spammers into a different database.

    The second feature, "accept unresolvable domains," is necessary because by default sendmail will block email coming from domains that do not exist. As with the access database, this check comes too early. We need to disable this feature so that the DNS check doesn't get included in the config file at check_mail. Instead, we will use our own custom code in check_rcpt to do the DNS check.

    Once the cf file has been generated, you will need to hand-edit it to make a few changes.

    1. Define the databases. A good place to add the database definitions is after the access database line. My additions are in bold:

    # Access list database (for spam stomping)
    Kaccess dbm -o /etc/mail_access

    # Spam database (database of known spammers)
    Kspammer dbm -o /etc/spammer

    # User opt-in database
    Kspamuser dbm -o /etc/spam_user

    # Resolve map
    Kresolve host -a<OK> -T<TEMP>

    Explanation: The K configuration options tell sendmail that we will be using two databases (referenced by the names spammers and spamuser). The databases are set up as dbm files, but you can substitute whatever database format you are comfortable with here (db, hash, etc.). The Kresolve map is needed for the DNS check.

    2. Modify the current check_rcpt. The check_rcpt ruleset as created by sendmail will look like this (note that the numbers on the left are for reference only and will not appear in the cf file itself):

    1 SLocal_check_rcpt
    2 Scheck_rcpt
    3 R$*                        $: $1 $| $>"Local_check_rcpt" $1
    4 R$* $| $#$*        $#$2
    5 R$* $| $*            $@ $>"Basic_check_rcpt" $1

    Change the check_rcpt ruleset so that it reads:

    1 Slocal_check_rcpt
    2 Scheck_rcpt
    3 R$*             $: $1 $| $>"Local_check_rcpt" $1
    4 R$* $| $#$*     $#$2
    5 R$* $| $*    $: $1 $| $>"Basic_check_rcpt" $1
    6 R$* $| OK    $: $1 $| $>"Extended_check_rcpt" $1
    7 R$* $| $*    $: $2

    Explanation: The first thing needed is to change line 5. You'll see that I replaced the $@ with a $:. The reason is that $@ tells sendmail to do the rewrite and exit the rule set. Instead, we want sendmail to do the rewrite and continue to the next line in which we call our customized checks. Also, note that I prepended a $1 $| before the command to execute Basic_check_rcpt. This is needed because Basic_check_rcpt is called with the recipient name but returns with a status condition (OK, err, etc.). We still need to keep track of the recipient so that we can call our customized rulesets with it.

    As an example, let's say that the recipient is [email protected]. In the original unmodified ruleset, line 5 would be called with the argument [email protected]. However, the entire workspace is replaced with the results from Basic_check_rcpt. Assuming that Basic_check_rcpt finds the address acceptable, the workspace would read just "OK." We have lost the original address and cannot use it to call our customized ruleset.

    However, with our modified ruleset, line 5 would rewrite our workspace to be:

    [email protected] $| (whatever Basic_check_rcpt returns).

    Assuming Basic_check_rcpt finds the address to be acceptable, our workspace would now read:

    [email protected] $| OK

    We can now check for the OK as the second token and call our customized ruleset Extended_check_rcpt with the original address that was saved as the first token. This is what happens with line 6.

    3. Insert into your cf file the code for Extended_check_rcpt. This code should be inserted after the last line in Basic_check_rcpt but before the first line of mailer definitions. The source for Extended_check_mail is in the Appendix. You can find an online version of Extended_check_rcpt at <http://www.employees.org/beetle/sendmail.html>.

    R$*$: [ $1 ]put brackets around it...
    R$=w $@ OK                        ... and see if it is local

    # anything else is bogus
    R$*     $#error $@ 5.7.1 $: "550 Relaying denied"
    [Insert code for Extended_check_rcpt here]

    Mailer Definitions
    The code for Extended_check_mail is basically the same DNS check code and access database code as is shipped with sendmail. I have broken out the checks into separate rulesets. I have also added some logic to check the user choice database, which then calls the separate rulesets as necessary. I'll comment on the interesting portions of Extended_check_mail:

      SExtended_check_rcpt
      R$*$: $1 $| $>3 $&f
      R$* $| $*$: $2 $| $>3 $1
      R$* <@ $* > $| $*  $: <$1 @ $2> $| $3
      R$* $| $+ < @ $* > $*  $: $1 $| $2 < @ $3 > $4 $| $2

    Again, we are making use of the $| token to separate fields in our workspace. The code above basically rearranges our workspace so that it contains:

      sender  $| recipient           $|     username

    The username is just the recipient with the @domain chopped off. We will use the username to look into our database to see if this user wants spam filtering.

      R$* $| $* $| $* $: $1 $| $2 $| $(spamuser $3 $:<?NOKEY> $)

      # No such user in database. Don't do any checks
      R$* $| $* $| <?NOKEY> $@ $2

    After we get the username as the third field, we pass that into the database lookup. The $:<?NOKEY> tells sendmail to return <?NOKEY> as a default value if no entry is found. If <?NOKEY> is returned, the very next line tells sendmail to return the second field (the recipient) and exit the ruleset.

    If the database lookup did return a value, then we will check that value and call My_check_domain (which does the DNS check), or My_check_db (which checks the spammer database), or both. Depending on the results of My_check_db or My_check_domain, we will return either the original recipient or an error and exit.

    Creating the Databases
    Once you are done making the sendmail configuration file changes, you'll need to create the databases that contain the information. The sendmail distribution comes with a handy tool to do this, called makemap. My sample entries are shown in bold. We'll use these sample entries for testing later.

    1. The spam_user database is where your users' preferences are kept. It will be used to tell sendmail if the recipient wants DNS checking, spam database checking, or both. To create this database, first create a text file called spam_user in this format:

    Username <choice>

    where <choice> can be <?DOMAIN> for DNS checking, <?DB> for database checking, or <?BOTH> for both checks. After the text file is created, use makemap to create the database:

    tim   <?BOTH>
    brad  <?DOMAIN>
    john  <?DB>

    % makemap dbm /etc/spam_user < /etc/spam_user

    Note that the location of the file (in my case /etc/spam_user) should correspond with the location you defined with the Kspamuser configuration command in step 1. Also, be sure that the database type corresponds.

    2. Create the spammer database. This is the database where we will keep all of our known spammers. The procedure is essentially the same as creating the spam_user database. Make a text file called spammers that contains:

    Address Message

    where Address can be a fully qualified address ([email protected]), an address alone (free.stealth.mailer@), or just a domain (somewhere.com). The message can be either REJECT or a customized message (550 - We don't want spammers here).

    free.stealth.mailer@        550 - We don't want spammers here
    [email protected]              REJECT
    spamrus.com                 REJECT

    % makemap dbm /etc/spammers < /etc/spammers

    3. Create the access database. If you recall, we will not be using the access database for spam stomping but have configured it to take advantage of its other functions. This is the database where we will keep addresses that are allowed to relay and also addresses that will be globally blocked, regardless of how the users have their spam preferences set. Read the sendmail documentation on how to use the access database if you need to enable relaying. For the purposes of this article, I will create an empty access database:

    % touch /etc/access
    % makemap dbm /etc/mail_access < /etc/mail_access

    Testing the New Configuration
    It's very important to test the new configuration before using it in production. You can test the changes with sendmail's address-test mode (sendmail -bt).

    Before testing, you will need to define the f macro value that holds the sender's address:

      .Df<sender address>

    Test Case #1 — Tim has enabled both DNS and database checking.

    A. Testing mail from someone in the spammer database to Tim. Since Tim has both checks turned on, the system should reject the email.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]
    rewrite: ruleset   3 returns:   hot999 < @ aol . com . >
    rewrite: ruleset 199 input:     hot999 < @ aol . com . >
    rewrite: ruleset 199 returns:   hot999 < @ aol . com . >
    rewrite: ruleset 183 returns: $# error $@ 5 . 7 . 1 $: "550 Access denied"

    B.  Testing mail from a bogus domain to Tim. Since Tim has both checks turned on, the system should reject the email.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]

    rewrite: ruleset   3 returns:   spam < @ garbagedomain . com >
    rewrite: ruleset 199  input:    spam < @ garbagedomain . com >
    rewrite: ruleset 199 returns:   spam < @ garbagedomain . com >
    rewrite: ruleset 180 returns:   < PERM>
    rewrite: ruleset 181 returns:   < PERM>
    rewrite: ruleset 184 returns:   $# error $@ 5 . 1 . 8 $: "501 Sender domain must exist"

    C.  Testing mail from a valid address not in spammer database to Tim. The sender address comes from a valid domain and is not in the spammer database, so the system should allow the email to pass.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]

    rewrite: ruleset   3 returns: tim < @ cisco . com . >
    rewrite: ruleset 199  input:  tim< @ cisco . com . >
    rewrite: ruleset 199 returns: tim < @ cisco . com . >
    rewrite: ruleset 179  input:  < cisco . com > < ? > < >
    rewrite: ruleset 196  input:  < com > < ? > < >
    rewrite: ruleset 196 returns: < ? > < >
    rewrite: ruleset 179 returns: < ? > < >
    rewrite: ruleset 183 returns: < OK >

    Test Case #2 — Brad has only enabled the DNS check.

    A.  Testing mail from someone in the spammer database to Brad. Since Brad has only enabled the DNS check, the mail should be accepted.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]

    rewrite: ruleset   3 returns:   hot999 < @ aol . com . >
    rewrite: ruleset 199  input:    hot999 < @ aol . com . >
    rewrite: ruleset 199 returns:   hot999 < @ aol . com . >
    rewrite: ruleset 180 returns:   < OK >

    B.  Testing mail from a bogus domain to Brad. Since Brad has enabled DNS checking, the system should reject the email.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]

    rewrite: ruleset   3 returns:   spam < @ garbagedomain . com >
    rewrite: ruleset 199  input:    spam < @ garbagedomain . com >
    rewrite: ruleset 199 returns:   spam < @ garbagedomain . com >
    rewrite: ruleset 180 returns:   < PERM >
    rewrite: ruleset 181 returns:   < PERM >
    rewrite: ruleset 184 returns:   $# error $@ 5 . 1 . 8 $: "501 Sender domain must exist"

    C. Testing mail from a valid address not in spammer database to Brad. The sender address comes from a valid domain and is not in the spammer database, so the system should allow the email to pass.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    [lots of output deleted]

    rewrite: ruleset   3 returns:   brad < @ cisco . com . >
    rewrite: ruleset 199  input:    brad < @ cisco . com . >
    rewrite: ruleset 199 returns:   brad < @ cisco . com . >
    rewrite: ruleset 180 returns:   < OK >

    Test Case #3 — the undefined user.

    I'll leave it as an exercise for the reader to try John's settings. One more test we should try is for the person who has not set up his or her spam settings. In this case, the system should allow all email to pass through.

    A.  Mail from an address in the spammer database to Sally. Since Sally is not defined in the spam_user database, the email should be allowed through.

    % /usr/lib/sendmail -bt
    ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
    Enter <ruleset> <address>
    > [email protected]
    > check_rcpt [email protected]

    rewrite: ruleset   3 returns:   sally < @ cisco . com . >
    rewrite: ruleset 184 returns:   sally < @ cisco . com . >
    rewrite: ruleset 186 returns:   sally < @ cisco . com . >

    B.  Mail from a bogus domain should be ok too:

    > [email protected]
    > check_rcpt [email protected]

    rewrite: ruleset   3 returns:   sally < @ cisco . com . >
    rewrite: ruleset 184 returns:   sally < @ cisco . com . >
    rewrite: ruleset 186 returns:   sally < @ cisco . com . >

    C.  Of course, mail from a valid domain that is not in the spammer database should be allowed through:

    > [email protected]
    > check_rcpt [email protected]

    rewrite: ruleset   3 returns:   sally < @ cisco . com . >
    rewrite: ruleset 184 returns:   sally < @ cisco . com . >
    rewrite: ruleset 186 returns:   sally < @ cisco . com . >

    Implementation Details
    Now that you have tested the sendmail configuration changes, you will have to create some infrastructure to support the new features of Extended_check_rcpt. First, the user must have a way to change his or her settings in the spam_user database. We have created a Web page so that the user can log in, read about how we fight spam, and change the settings. The CGI then goes and updates the spam_user database. If you are a small site, you could probably get away with rdisting a text file and running makemap once in a while.

    I think it is important to explain to the user the ramifications of turning on the spam checks. In the Web page that we use, we explain that the DNS check may be dangerous and that you should not turn it on if it is critical that you get all email. We even have a JavaScript popup that makes the user acknowledge that they have chosen a spam configuration that may cause them to miss email from misconfigured domains.

    If the DNS check is too aggressive, then the user can just enable the spam database check. This option is pretty safe to use, since we will only enter addresses from known spammers.

    We have set up a mailing list called spam-fighters for our employees to forward spam to. A volunteer team then looks at the email and adds the address to the spammer database if appropriate. Instead of telling users to "just delete the spam," we now tell them to forward it to the spam-fighters alias so that the address can be blocked. Sometimes we can get a report in fast enough that we can block the address before the bulk of the spam comes through. Furthermore, we have created Web pages that show the daily spam statistics, who is in our spammer database, and the number of times we have blocked them. A user can submit a spam report, see that the address gets added to the database, and see the results of that report — that the spammer was blocked from further attempts at connecting to our servers. This is a big PR win for the sysadmin team and a psychological win for the user.

    Finally, we try to be proactive about adding spammers into our spam database. We monitor <news.admin.net-abuse.sightings> for spam reports from other sites. We also have set up some fake addresses used by the spam-fighters team. We have publicized these fake addresses by posting to USENET news and using other methods. Since these addresses are not used by anyone inside of the company, any mail going to them is unsolicited and therefore classified as spam.

    Overall we have found the checks to be somewhat effective in blocking spam and tremendously effective in fostering the notion that we are actively doing something to curb the spam problem.

    Notes

    [1] Claus Assmann's rulesets can be found at <http://www.sendmail.org//%7Eca/ email/check.html>.

    [2] Robert Harker's rulesets can be found at <http://www.harker.com>.

    [3] Costales, Bryan and Allman, Eric. Sendmail. O'Reilly & Associates, 1997, p. 512.

    Appendix: The Extended_check_rcpt Ruleset

    #############################################################################
    ### Extended_check_rcpt -- called after check_rcpt. Apply DNS and spam db
    ###    checks on a per-user basis. #############################################################################
    SExtended_check_rcpt

    # First expand $&f to get the sender's address.
    R$*                              $: $1 $| $>3 $&f
    # Apply various rewrites to get workspace into the format:
    #  sender $| recipient $| username
    R$* $| $*                        $: $2 $| $>3 $1
    R$* <@ $* > $| $*                $: <$1 @ $2> $| $3
    R$* $| $+ < @ $* > $*            $: $1 $| $2 < @ $3 > $4 $| $2
    # Now look into our username to see what kind of checks the user wants
    R$* $| $* $| $*                  $: $1 $| $2 $| $(spamuser $3 $:<?NOKEY> $)

    # No such user in database. Don't do any check
    R$* $| $* $| <?NOKEY>            $@ $2

    # See if user wants domain checking or both and apply check.
    R$* $| $* $| <?BOTH>             $: $1 $| $2 $| <?BOTH> $| $>My_check_domain $1
    R$* $| $* $| <?DOMAIN>           $: $1 $| $2 $| <?DOMAIN> $| $>My_check_domain $1

    # Check results of domain check.
    R$* $| $* $| <?BOTH> $| $#error $@ 5.1.8 $: "501 Sender domain must exist"
    R$* $| $* $| <?DOMAIN> $$#error $@ 5.1.8 $: "501 Sender domain must exist"
    R$* $| $* $| <?BOTH> $| $#error $@ 4.1.8 $: "451 Sender domain must resolve"
    R$* $| $* $| <?DOMAIN> $$#error $@ 4.1.8 $: "451 Sender domain must resolve"

    # Clear workspace of DNS check results.
    R$* $| $* $| $* $| $*            $: $1 $| $2 $| $3

    # See if user wants database checking or both and apply check.
    R$* $| $* $| <?BOTH>             $: $1 $| $2 $| <?BOTH> $| $>My_check_db $1
    R$* $| $* $| <?DB>               $: $1 $| $2 $| <?DB> $| $>My_check_db $1

    # Check results. If OK then return original recipient addr and exit.
    R$* $| $* $| <?BOTH>  $| <OK>    $@$2
    R$* $| $* $| <?DB>    $| <OK>    $@$2

    # User didn't want database check, so return original recipient and exit.
    R$* $| $* $| $*        $@ $2

    # Otherwise, recipient found in spam database, return error from database.
    R$* $| $* $| $* $| $*  $@ $4

    #########################################################################
    ### Supporting Rulesets for Extended_check_rcpt. Mostly stolen from
    ###  stock sendmail package and broken up into separate rulesets. #########################################################################
    SMy_check_db
    # check for deferred delivery mode
    R$*              $: < ${deliveryMode} > $1
    R< d > $*        $@ deferred
    R< $* > $*       $: $2

    R<>$@            <OK>
    R$*              $: <?> $>Parse0 $>3 $  make domain canonical
    R<?> $* < @ $+ . > $* <?> $1 < @ $2 > $3     strip trailing dots
    # lookup localpart (user@)
    R<$+> $* < @ $+ > $*     $: <USER $(spammer $2@ $: ? $) > <$1> $2 < @ $3 > $4
    # no match, try full address (user@domain rest)
    R<USER ?> <$+> $* < @ $* $: <USER $(spammer $2@$3$4 $: ? $) > <$1> $2 < @ $3 > $4
    # no match, try address (user@domain)
    R<USER ?> <$+> $+ < @ $+ $: <USER $(spammer $2@$3 $: ? $) > <$1> $2 < @ $3 > $4
    # no match, try (sub)domain (domain)

    R<USER ?> <$+> $* < @ $+ $: $>SpammerLookUpDomain <$3> <$1> <>
    # check unqualified user in access database
    R<?> $*                  $: <USER $(spammer $1@ $: ? $) > <?> $1

    # retransform for further use
    R<USER $+> <$+> $*       $: <$1> $3

    # check results
    R<?> $*                  $@ <OK>
    R<OK> $*                 $@ <OK>
    R<DISCARD> $*            $#discard $: discard
    R<REJECT> $*             $#error $@ 5.7.1 $: "550 Access denied"
    R<$+> $*                 $#error $@ 5.7.1 $: $1 error from access db

    SMy_Local_check_domain
    SMy_check_domain
    R$*                 $: $1 $| $>"My_Local_check_domain" $1
    R$* $| $#$*         $#$2
    R$* $| $*           $@ $>"My_Basic_check_domain" $1

    SMy_Basic_check_domain
    # check for deferred delivery mode
    R$*                 $: < ${deliveryMode} > $1
    R< d > $*           $@ deferred
    R< $* > $*          $: $2
    R<>                 $@ <OK>
    R$*                 $: <?> $>Parse0 $>3 $1 make domain canonical
    R<?> $* < @ $+ . >  $* <?> $1 < @ $2 > $3 strip trailing dots

    # handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
    R<?> $* < $* $=P > $*   $: <OK> $1 < @ $2 $3 > $4
    R<?> $* < @ $+ > $*     $: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 > $3
    R<? $* <$->> $* < @ $+ >$: <$2> $3 < @ $4 > $5

    # handle case of @localhost on address
    R<$+> $* < @localhost >  $: < ? $&{client_name} > <$1> $2 < @localhost >
    R<$+> $* < @localhost.$m $: < ? $&{client_name} > <$1> $2 < @localhost.$m >
    R<$+> $* < @localhost.UU $: < ? $&{client_name} > <$1> $2 < @localhost.UUCP >
    R<? $=w> <$+> $*         <?> <$2> $3
    R<? $+> <$+> $*          $#error $@ 5.5.4 $: "553 Real domain name required"
    R<?> <$+> $*             $: <$1> $2

    # retransform for further use
    R<USER $+> <$+> $*       $: <$1> $3

    # handle case of no @domain on address
    R<?> $*                  $: < ? $&{client_name} > $1
    R<?> $*                  $@ <OK>                              ...local unqualed ok
    >R<? $+> $*              $#error $@ 5.5.4 $: "553 Domain      ...remote is not
    # check results
    R<?> $*                  $@ <OK>
    R<OK> $*                 $@ <OK>
    R<TEMP> $*               $@ <TEMP>
    R<PERM> $*               $@ <PERM>
    R<RELAY> $*              $@ <RELAY>
    R<DISCARD> $*            $#discard $: discard

    SSpammerLookUpDomain
    R<$+> <$+> <$*>          $: < $(spammer $1 $: ? $) > <$1> <$2> <$3>
    R<?> <$+.$+> <$+> <$*>   $@ $>LookUpDomain <$2> <$3> <$4>
    R<?> <$+> <$+> <$*>      $@ <$2> <$3>
    R<$*> <$+> <$+> <$*>     $@ <$1> <$4>

    ###
    ### END of Extended_check_rcpt package
    ###


  • ?Need help? Use our Contacts page.
    Last changed: 16 Nov. 1999 mc
    Issue index
    ;login: index
    SAGE home