Left Top
About
Code
Contact
Digitemp
Jabber
Write-Ups
Blank
w3c_v
Illx Logo
 
Code -> Perl -> qmail Recepient Check
qmail Recipient Check script:
Below are the instructions on how to install my qmail Recipient Check script.
This script exists because qmail doesn't check to make sure recipients are
valid during the SMTP conversation.

My script checks for users against the system password file /etc/passwd
and against qmail's user file /var/qmail/users/assign
for the domains your box accepts mail in the /var/qmail/control/locals
and /var/qmail/control/virtualdomains files.

Download version 0.1 here:qmrcptchk01.pl
Download the latest version 0.2 here:qmrcptchk02.pl

NOTE: Version 0.2 now checks against qmail's aliases as well as the others listed above.

This script would not be possible without Zachary Bedell's patch. His
original work can be found here Is Anbody Home?

Below in the installation instructions is a modified version of the patch
which I've tested with qmail-1.03 and netqmail-1.05.
INSTALLATION INSTRUCTIONS for applying the qmrcptchk.pl script:
To install this script you must first patch Qmail.

I'm sure everyone is using heavily patched version of either
qmail-1.03 or netqmail-1.05. In either case, this patch should work.
  1. cd to your qmail source directory (eq: cd /usr/src/mail/qmail-1.03)
  2. edit qmail-smtpd.c with your favorite editor (emacs qmail-smtpd.c)
  3. add this line:
    #include "wait.h"
    after all of the #includes at the top of the file. The result should look something like this:
    #include "timeoutwrite.h"
    #include "commands.h"
    #include "wait.h"
  4. search for 'void die_alarm()' (probably around line 45)
    add this line after the 'void die_alarm()'
    void die_rcptcheck() { out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1);}

    The result should look something like this:
    void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
    void die_rcptcheck() { out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1);}
    void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
  5. search for 'void err_wantmail()' (probably around line 60)
    add this line after the 'void err_wantmail()'
    void err_realrcpt() { out("550 sorry, no mailbox here by that name (#5.1.1 - chkusr)\r\n"); }

    The result should look something like this:
    void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
    void err_realrcpt() { out("550 sorry, no mailbox here by that name (#5.1.1 - chkusr)\r\n"); }
    void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
  6. search for 'stralloc addr = {0};' (probably around line 150)
    add this function after the 'stralloc addr = {0};'
    int realrcpt_check()
    {
      char *bincheck;
      int ret, wstat;
    
      /* For non-local domains, this function shouldn't interfere */
      if (!addrallowed()) return 1;
    
      bincheck = env_get("QMAILCHECKRCPT");
      if (!bincheck) return 1; /* No custom check script, so accept message. */
    
      switch(ret = fork())
      {
       case -1:
         return 1; /* If fork failed, just accept the mail. */
       case 0:
         execlp(bincheck, bincheck, addr.s, 0); /* changed from NULL to 0 by Nick 
                                                   gcc wouldn't compile it as NULL */
      }
      wait_pid(&wstat,ret);
      ret = wait_exitcode(wstat);
    
      switch(ret)
      {
       case 0:
             return 0; /* Check returns 0 if positive no match.  Return 0 to disallow delivery. */
           case 1:
             return 1; /* Check returns 1 if positive match.  Return 1 to allow delivery. */
           default:
             die_rcptcheck(); /* Problem running script.  Temp failure. */
             return 1; /* Never get here, but quiets compiler warning. */
      }
    }
    
    The result should look something like this:
    stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
    
    int realrcpt_check()
    {
      char *bincheck;
      int ret, wstat;
    
      /* For non-local domains, this function shouldn't interfere */
      if (!addrallowed()) return 1;
    
      bincheck = env_get("QMAILCHECKRCPT");
      if (!bincheck) return 1; /* No custom check script, so accept message. */
    
      switch(ret = fork())
      {
       case -1:
         return 1; /* If fork failed, just accept the mail. */
       case 0:
         execlp(bincheck, bincheck, addr.s, 0); /* changed from NULL to 0 by Nick 
                                                   gcc wouldn't compile it as NULL */
      }
      wait_pid(&wstat,ret);
      ret = wait_exitcode(wstat);
    
      switch(ret)
      {
       case 0:
             return 0; /* Check returns 0 if positive no match.  Return 0 to disallow delivery. */
           case 1:
             return 1; /* Check returns 1 if positive match.  Return 1 to allow delivery. */
           default:
             die_rcptcheck(); /* Problem running script.  Temp failure. */
             return 1; /* Never get here, but quiets compiler warning. */
      }
    }
    
    int addrparse(arg)
    
  7. search for 'stralloc_cats(&rcptto,"T"' (probably around line 310)
    add this line before the 'stralloc_cats(&rcptto,"T"'
    if (!realrcpt_check()) { err_realrcpt(); return; }

    The result should look something like this:
    else
      if (!addrallowed()) { err_nogateway(); return; }
    if (!realrcpt_check()) { err_realrcpt(); return; }
    if (!stralloc_cats(&rcptto,"T")) die_nomem();
  8. recompile qmail with a 'make'
  9. Download my check program from here.
  10. Make it executable
    # chmod 755 qmrcptchk.pl
  11. Copy it to /usr/local/bin
    # cp qmrcptchk.pl /usr/local/bin
  12. test qmail-smtpd by running ./qmail-smtpd in the source directory
    run a 'mail from' and 'rcpt to' through it to make sure its working.
    For example:
    bash$ export QMAILCHECKRCPT=/home/user/qmrcptchk.pl (or wherever you downloaded it to)
    bash$ ./qmail-smtpd 
    220 mail.server.com ESMTP
    mail from:test@domain.com
    250 ok
    rcpt to:to@domain.com
    550 sorry, no mailbox here by that name (#5.1.1 - chkusr)
    
  13. Stop qmail, copy the qmail-smtpd binary into the correct place.
    Make a backup of your qmail-smtpd first.
    # cp /var/qmail/bin/qmail-smtpd /var/qmail/bin/qmail-smtpd.bak

    Then copy it over:
    # cp ./qmail-smtpd /var/qmail/bin

    Then, change the permissions, so everything is back to normal:
    chown root:qmail qmail-smtpd
  14. Edit your /service/qmail-smtpd/run with your favorite editor.
    bash$ emacs /service/qmail-smtpd/run

    And after the first line
    #!/bin/sh 
    add this line
    export QMAILCHECKRCPT=/usr/local/bin/qmrcptchk.pl
  15. That should be it! You're Done!
TROUBLESHOOTING:
If you get any errors like this:
421 unable to verify recipient (#4.3.0)
  1. You may not have perl located at /usr/local/bin/perl, to fix it
    just change the first line of the qmrcptchk.pl script to where
    you have perl installed.
  2. You may have forgotten to make the qmrcptchk.pl executable.
  3. There may be problems with some of the files listed in qmrcptchk.pl
    (The qmail configuration files that it reads)
  4. If you get an error something like this from the /var/log/qmail/smtpd logs
    @400000004238b7f40a5c4e3c /usr/bin/perl: error while loading shared libraries: \
    libc.so.6: failed to map segment from shared object: Cannot allocate memory
    Than your softlimit is too low. You have to increase it to 6-8MB or higher.
    To increase your softlimit, edit your /service/qmail-smtpd/run file,
    and increase the 2000000 to 8000000 or even higher like 16000000.

If you have any other questions or comments, please feel free to email me.
web at my domain name (My domain name should be obvious as it is the
image at the top of the screen).