Qmail-LDAP
[Top] [All Lists]

greylist patch

To: qmail-ldap@qmail-ldap.org
Subject: greylist patch
From: Hugo Monteiro <hugo.monteiro@fct.unl.pt>
Date: Mon, 22 Jan 2007 11:10:25 +0000
Delivered-to: sp-com-lists@consult.net
Delivered-to: qmail-ldap-list@securepoint.com
Delivered-to: mailing list qmail-ldap@qmail-ldap.org
Mailing-list: contact qmail-ldap-help@qmail-ldap.org; run by ezmlm
User-agent: Icedove 1.5.0.8 (X11/20061123)
Hello all,

for some days now i've been looking into the best way to make greylisting work with qmail-ldap. I've come to the conclusion that the more common ways around where to use an rblsmtpd drop in replacement program, that would greylist by incoming ip address.

It doesn't take much to come to the conclusion that such approach is far from perfect since it takes a test condition to the three IP, MAIL FROM and RCPT TO triplets to have a solid ground for greylisting.

Looking around a bit longer i was able to find a page which could provide a starting ground for the solution. http://www.digitaleveryware.com/projects/greylisting/ had, in theory, a solution for qmail. I noticed then that there was already a patch for netqmail that would benefit from the digitaleveryware solution. Although the patch didn't apply cleanly, i was able to identify the differences and port it to qmail-ldap. I also took the chance to add support for on-the-fly enabling by using the GREYLIST env var in the tcpserver rules file.

More detailed information is available in the README.greylist file which is part of the patch.

I've been running this solution for a couple of days now and it seems very stable and fast. I had a reduction of Spam messages in the order of 80% since, and that at the level of SMTP talk, wasting almost no resources, and sparing my content filter solution which is working, after, using the ALTQUEUE enhancement.

Also i'd like to take a chance to say that it would be rather nice if this, or something similar, would go into the mainstream qmail-ldap patch.

If anyone uses this patch, please let me know how did it work for you.

Cheers!

Hugo Monteiro.

--
ci.fct.unl.pt:~# cat .signature

Hugo Monteiro
Email    : hugo.monteiro@fct.unl.pt
Telefone : +351 212948300 Ext.15307

Centro de Informática
Faculdade de Ciências e Tecnologia da
                   Universidade Nova de Lisboa
Quinta da Torre   2829-516 Caparica   Portugal
Telefone: +351 212948596   Fax: +351 212948548
www.ci.fct.unl.pt             apoio@fct.unl.pt

ci.fct.unl.pt:~# _

diff -u -N -r qmail-ldap-1.03-20060201/dbdef.sql 
qmail-ldap-1.03-20060201-greylist-0.3/dbdef.sql
--- qmail-ldap-1.03-20060201/dbdef.sql  1970-01-01 01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/dbdef.sql     2007-01-22 
10:44:24.307885032 +0000
@@ -0,0 +1,73 @@
+
+#######################################################################
+# Database definitions
+#######################################################################
+
+# Using Mysql 3.23.2 or later (required for NULLs allowed in indexed fields
+
+CREATE DATABASE relaydelay;
+grant select,insert,update,delete on relaydelay.* to milter@"localhost" 
identified by 'milter';
+
+USE relaydelay;
+
+# NOTE: We allow nulls in the 3 "triplet" fields for manual white/black 
listing.  A null indicates that that field 
+#   should not be considered when looking for a match.  Automatic entries will 
always have all three populated.
+# Note: Since we index the triplet fields, and allow them to be null, this 
requires at least Mysql 3.23.2 and MYISAM
+#   tables.
+create table relaytofrom     # Stores settings for each [relay, to, from] 
triplet
+(
+        id              bigint          NOT NULL        auto_increment, # 
unique triplet id
+        relay_ip        char(16),                                       # 
sending relay in IPV4 ascii dotted quad notation
+        mail_from       varchar(255),                                   # 
ascii address of sender
+        rcpt_to         varchar(255),                                   # the 
recipient address.
+        block_expires   datetime        NOT NULL,                       # the 
time that an initial block will/did expire
+        record_expires  datetime        NOT NULL,                       # the 
date after which this record is ignored
+        
+        blocked_count   bigint          default 0 NOT NULL,             # num 
of blocked attempts to deliver
+        passed_count    bigint          default 0 NOT NULL,             # num 
of passed attempts we have allowed
+        aborted_count   bigint          default 0 NOT NULL,             # num 
of attempts we have passed, but were later aborted
+        origin_type     enum("MANUAL","AUTO") NOT NULL,                 # 
indicates the origin of this record (auto, or manual)
+        create_time     datetime        NOT NULL,                       # 
timestamp of creation time of this record
+        last_update     timestamp       NOT NULL,                       # 
timestamp of last change to this record (automatic)
+
+        primary key(id),
+        key(relay_ip),
+        key(mail_from(20)),                                             # To 
keep the index size down, only index first 20 chars
+        key(rcpt_to(20))
+);
+
+create table dns_name        # Stores the reverse dns name lookup for records
+(
+       relay_ip      varchar(18)       NOT NULL,
+       relay_name    varchar(255)      NOT NULL,                       # dns 
name, stored in reversed character order (for index)
+       last_update   timestamp         NOT NULL,                       # 
timestamp of last change to this record (automatic)
+       primary key(relay_ip),
+       key(relay_name(20))
+);
+
+# This table is not used yet, possibly never will be
+create table mail_log        # Stores a record for every mail delivery attempt
+(
+        id              bigint          NOT NULL        auto_increment, # 
unique log entry id
+        relay_ip        varchar(16)     NOT NULL,                       # 
sending relay in IPV4 ascii dotted quad notation
+        relay_name      varchar(255),                                   # 
sending relay dns name
+        dns_mismatch    bool            NOT NULL,                       # true 
if does not match, false if matches or no dns
+        mail_from       varchar(255)    NOT NULL,                       # the 
mail from: address
+        rcpt_to         varchar(255)    NOT NULL,                       # the 
rcpt to: address
+        rcpt_host       varchar(80)     NOT NULL,                       # the 
id (hostname) of the host that generated this row
+        create_time     datetime        NOT NULL,                       # 
timestamp of inserted time, since no updates
+
+        primary key(id),
+        key(relay_ip),
+        key(mail_from(20)),
+        key(rcpt_to(20))
+);
+
+# Example wildcard whitelists for subnets
+insert into relaytofrom values (0,"127.0.0.1"   ,NULL,NULL,"0000-00-00 
00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+insert into relaytofrom values (0,"192.168"     ,NULL,NULL,"0000-00-00 
00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+# Example wildcard whitelist entry for a recieved domain or subdomain
+insert into relaytofrom values (0,NULL,NULL,"sub.domain.com","0000-00-00 
00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+
diff -u -N -r qmail-ldap-1.03-20060201/FILES 
qmail-ldap-1.03-20060201-greylist-0.3/FILES
--- qmail-ldap-1.03-20060201/FILES      2007-01-21 01:39:36.000000000 +0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/FILES 2007-01-20 20:09:23.000000000 
+0000
@@ -426,6 +426,10 @@
 tcp-environ.5
 constmap.h
 constmap.c
+local_scan.c
+qmail-envelope-scanner.c
+dbdef.sql
+local_scan.h
 EXTTODO
 Makefile.cdb
 POPBEFORESMTP
diff -u -N -r qmail-ldap-1.03-20060201/greylist-cleanup.pl 
qmail-ldap-1.03-20060201-greylist-0.3/greylist-cleanup.pl
--- qmail-ldap-1.03-20060201/greylist-cleanup.pl        1970-01-01 
01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/greylist-cleanup.pl   2007-01-21 
19:07:39.000000000 +0000
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant DBD => 'DBI:mysql:relaydelay:localhost:3306';
+use constant DBUSER => 'milter';
+use constant DBPASS => 'milter';
+
+use DBI;
+
+system ("cat /dev/null > /tmp/greylist_dbg.txt");
+
+my $dbh = DBI->connect(DBD,DBUSER,DBPASS) or die "can't connect to db ", 
$DBI::errstr, ":$!";
+
+$dbh->do("DELETE FROM relaytofrom WHERE record_expires < NOW() - INTERVAL 1 
HOUR AND origin_type = 'AUTO'");
+$dbh->do("OPTIMIZE TABLE relaytofrom");
+
+$dbh->disconnect;
+
+exit;
diff -u -N -r qmail-ldap-1.03-20060201/greylist-whitelist.pl 
qmail-ldap-1.03-20060201-greylist-0.3/greylist-whitelist.pl
--- qmail-ldap-1.03-20060201/greylist-whitelist.pl      1970-01-01 
01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/greylist-whitelist.pl 2007-01-21 
20:10:48.000000000 +0000
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant DBD => 'DBI:mysql:relaydelay:localhost:3306';
+use constant DBUSER => 'milter';
+use constant DBPASS => 'milter';
+
+use DBI;
+
+system ("cat /dev/null > /tmp/greylist_dbg.txt");
+
+my $dbh = DBI->connect(DBD,DBUSER,DBPASS) or die "can't connect to db ", 
$DBI::errstr, ":$!";
+
+$dbh->do("INSERT INTO relaytofrom values (NULL, '$ARGV[0]',NULL,NULL, 
'0000-00-00 00:00:00', '9999-12-31 23:59:59', 0, 0, 0, 'MANUAL', NOW(), 
NOW())");
+$dbh->do("OPTIMIZE TABLE relaytofrom");
+
+$dbh->disconnect;
+
+exit;
diff -u -N -r qmail-ldap-1.03-20060201/hier.c 
qmail-ldap-1.03-20060201-greylist-0.3/hier.c
--- qmail-ldap-1.03-20060201/hier.c     2007-01-21 01:39:36.000000000 +0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/hier.c        2007-01-21 
23:18:01.000000000 +0000
@@ -214,6 +214,13 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+#ifdef GREYLIST
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","greylist-whitelist.pl",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","greylist-cleanup.pl",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","README.greylist",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"doc","dbdef.sql",auto_uido,auto_gidq,0644);
+#endif
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -u -N -r qmail-ldap-1.03-20060201/install-big.c 
qmail-ldap-1.03-20060201-greylist-0.3/install-big.c
--- qmail-ldap-1.03-20060201/install-big.c      2007-01-21 01:39:36.000000000 
+0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/install-big.c 2007-01-21 
23:18:58.000000000 +0000
@@ -214,6 +214,13 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+#ifdef GREYLIST
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","greylist-whitelist.pl",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","greylist-cleanup.pl",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"doc","README.greylist",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"doc","dbdef.sql",auto_uido,auto_gidq,0644);
+#endif
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -u -N -r qmail-ldap-1.03-20060201/local_scan.c 
qmail-ldap-1.03-20060201-greylist-0.3/local_scan.c
--- qmail-ldap-1.03-20060201/local_scan.c       1970-01-01 01:00:00.000000000 
+0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/local_scan.c  2007-01-21 
18:25:35.000000000 +0000
@@ -0,0 +1,287 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include "local_scan.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mysql.h>
+
+/************************/
+#define DEFAULT_MYSQLHOST "localhost"
+#define DEFAULT_MYSQLUSER "milter"
+#define DEFAULT_MYSQLPASS "milter"
+#define DEFAULT_MYSQLDB   "relaydelay"
+#define DEFAULT_BLOCK_EXPIRE  55   /* minutes until email is accepted */
+#define DEFAULT_RECORD_EXPIRE 500  /* minutes until record expires */
+#define DEFAULT_RECORD_EXPIRE_GOOD  36 /* days until record expires after 
accepting email */
+#define DEFAULT_LOCAL_SCAN_DEBUG   0 /* set to 1 to enable debugging */
+
+/*************************/
+
+#define SQLCMDSIZE 1024
+
+char *mysqlhost, *mysqluser, *mysqlpass, *mysqldb;
+int block_expire, record_expire, record_expire_good, local_scan_debug;
+
+int mysql_query_wrapper(MYSQL *mysql, char *sqltext) 
+{
+  int result;
+
+  result = mysql_query(mysql,sqltext);
+  if(local_scan_debug == 1) fprintf(stderr,"local_scan: SQL: ret=%d  
|%s|\n",result,sqltext);
+  return result;
+}
+
+void AddOrClause(char *subquery,char *in_ipaddr)
+{
+ int loop;
+ char *ptr;
+ char ipaddr[256];
+ 
+
+ strncpy(ipaddr, in_ipaddr, sizeof(ipaddr));
+ for (loop = 0; loop < 4; loop++) {
+   if (loop)
+     strcat(subquery, " OR ");
+   strcat(subquery , "relay_ip = '" );
+   strcat(subquery ,ipaddr);
+   strcat(subquery , "'");
+   ptr = strrchr(ipaddr,'.'); // strip off the last octet
+   if(ptr)
+     *ptr = '\0';
+ }
+}
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListIP( MYSQL *mysql,  int *action) 
+{
+  MYSQL_RES *myres; 
+  MYSQL_ROW myrow;
+
+  char sql[SQLCMDSIZE], sid[32];
+  int  white, black,exists = FALSE;
+
+  /* check for whitelisted sender ip address */
+     sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() 
FROM relaytofrom WHERE record_expires > NOW()  AND mail_from IS NULL AND 
rcpt_to IS NULL AND (");
+    AddOrClause(sql, sender_host_address);
+    strcat(sql, ") ORDER BY length(relay_ip)");
+    //#ifdef local_scan_debug
+    //   fprintf(stderr,"local_scan: check whitelist: %s\n",sql);
+    //#endif
+    if(!mysql_query_wrapper(mysql,sql)) {
+      myres = mysql_store_result(mysql);
+      while ((myrow = mysql_fetch_row(myres)) != NULL) {
+       exists = TRUE;
+       strncpy(sid,myrow[0],sizeof(sid));
+       black = atoi(myrow[1]);
+       white = atoi(myrow[2]);
+      }
+      mysql_free_result(myres); // free memory used by result
+    }
+    if (exists && white) {
+      if(local_scan_debug == 1)  fprintf(stderr,"local_scan: %s -> xxx (%s) 
Whitelist IP Accept id = %s  \n",sender_address, sender_host_address,sid);
+      *action = LOCAL_SCAN_ACCEPT;
+    }
+    else if (exists && black) {
+      if(local_scan_debug == 1)  fprintf(stderr,"local_scan: %s -> xxx (%s) 
Blacklist IP Permanent Reject id = %s  \n",sender_address, 
sender_host_address,sid);
+      *action = LOCAL_SCAN_REJECT;
+    }
+
+  return exists;
+}
+
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListDomain( MYSQL *mysql,  int *action, int i) 
+{
+  MYSQL_RES *myres; 
+  MYSQL_ROW myrow;
+
+  char sql[SQLCMDSIZE], sid[32], *indomain;
+  int  white, black,exists = FALSE;
+
+  indomain = strrchr(recipients_list[i].address,'@');
+  if(indomain != NULL) {
+    indomain++; /* skip the '@' char */
+
+    /* check for whitelisted receiver domain address */
+    sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM 
relaytofrom WHERE record_expires > NOW()  AND mail_from IS NULL AND relay_ip IS 
NULL AND rcpt_to = '%s'",indomain);
+    if(!mysql_query_wrapper(mysql,sql)) {
+      myres = mysql_store_result(mysql);
+      while ((myrow = mysql_fetch_row(myres)) != NULL) {
+       exists = TRUE;
+       strncpy(sid,myrow[0],sizeof(sid));
+       black = atoi(myrow[1]);
+       white = atoi(myrow[2]);
+      }
+      mysql_free_result(myres); // free memory used by result
+    }
+    if (exists && white) {
+      if(local_scan_debug == 1)  fprintf(stderr,"local_scan: %s -> %s (%s) 
Whitelist Domain Accept id = %s  \n",sender_address, 
recipients_list[i].address, sender_host_address,sid);
+      *action = LOCAL_SCAN_ACCEPT;
+    }
+    else if (exists && black) {
+      if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> xxx (%s) 
Blacklist Domain Permanent Reject id = %s  \n",sender_address, 
sender_host_address,sid);
+      *action = LOCAL_SCAN_REJECT;
+    }
+  }
+  return exists;
+}
+
+void  buildLookupSql(char *sql, int recipIndex) 
+{
+  char hostip[32];
+  char *ptr;
+
+  if (FALSE) {
+    /* this first sprintf does an exact match on ipaddr */
+    sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE 
record_expires > NOW() AND mail_from = '%s'  AND rcpt_to   = '%s' AND relay_ip  
= '%s' order by block_expires desc",sender_address,  
recipients_list[recipIndex].address,sender_host_address);
+  }
+  else {
+    /* this second version matches anything in the same /24 subnet */
+    sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE 
record_expires > NOW() AND mail_from = '%s'  AND rcpt_to   = '%s' AND relay_ip  
like '",sender_address,  recipients_list[recipIndex].address);
+    strcpy(hostip,sender_host_address);
+    /* now remove the last octet and add a '%' */
+    ptr = strrchr(hostip,'.'); // strip off the last octet    
+    if(ptr)
+      *ptr = '\0';
+    strcat(sql,hostip);
+    strcat(sql, "%' order by block_expires desc");
+  }
+}
+
+
+int checkGreylist( MYSQL *mysql,  int *action, int i) 
+{
+ MYSQL_RES *myres; 
+ MYSQL_ROW myrow;
+
+ char sql[SQLCMDSIZE], sid[32];
+ int notexpired, exists = FALSE;
+
+ // lookup to see if record exists
+ buildLookupSql(sql,i);
+ if(!mysql_query_wrapper(mysql,sql)) {
+   myres = mysql_store_result(mysql);
+   while ((myrow = mysql_fetch_row(myres)) != NULL) {
+     exists = TRUE;
+     strncpy(sid,myrow[0],sizeof(sid));
+     notexpired = atoi(myrow[1]);
+     //fprintf(stderr,"local_scan: id = %s  expire = %d\n",sid,notexpired);
+   }
+   mysql_free_result(myres); // free memory used by result
+   // if it exists and is not record expired and the block_expired passes
+   if (exists && notexpired) {
+     // update expire time to 36 days, and pass count
+     sprintf(sql,"update relaytofrom set record_expires = NOW() + INTERVAL %d 
DAY, passed_count = passed_count + 1 where id ='%s'",record_expire_good,sid);
+     mysql_query_wrapper(mysql, sql);
+     if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) 
Exists Accept id = %s  expire = 
%d\n",sender_address,recipients_list[i].address, 
sender_host_address,sid,notexpired);
+     *action = LOCAL_SCAN_ACCEPT;
+   }
+   else if ( exists && ! notexpired) {
+     // update fail count
+     sprintf(sql,"update relaytofrom set blocked_count = blocked_count + 1 
where id = %s",sid);
+     mysql_query_wrapper(mysql, sql);
+     if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) 
Exists Block id = %s  expire = %d\n",sender_address,recipients_list[i].address, 
sender_host_address,sid,notexpired);
+     *action = LOCAL_SCAN_TEMPREJECT;
+   }
+   else /* doesn't exist */ {
+     // it doesn't exist make and entry
+     sprintf(sql,"insert into relaytofrom values (0,'%s','%s','%s',NOW() + 
INTERVAL %d MINUTE,NOW() + INTERVAL %d 
MINUTE,1,0,0,'AUTO',NOW(),NOW())",sender_host_address,sender_address,  
recipients_list[i].address,block_expire,record_expire);
+     mysql_query_wrapper(mysql,sql);
+     if(local_scan_debug == 1) fprintf(stderr,"local_scan: %s -> %s (%s) 
Doesn't Exists Block\n",sender_address,recipients_list[i].address, 
sender_host_address);
+     *action = LOCAL_SCAN_TEMPREJECT;
+   }
+ }
+}
+
+void get_config(void) {
+
+       char *t;
+
+       mysqlhost = getenv("MYSQLHOST");
+       if (!mysqlhost) {
+               mysqlhost = malloc(strlen(DEFAULT_MYSQLHOST)+1);
+               strcpy(mysqlhost, DEFAULT_MYSQLHOST);
+       }
+       mysqluser = getenv("MYSQLUSER");
+       if (!mysqluser) {
+               mysqluser = malloc(strlen(DEFAULT_MYSQLUSER)+1);
+               strcpy(mysqluser, DEFAULT_MYSQLUSER);
+       }
+       mysqlpass = getenv("MYSQLPASS");
+       if (!mysqlpass) {
+               mysqlpass = malloc(strlen(DEFAULT_MYSQLPASS)+1);
+               strcpy(mysqlpass, DEFAULT_MYSQLPASS);
+       }
+       mysqldb = getenv("MYSQLDB");
+       if (!mysqldb) {
+               mysqldb = malloc(strlen(DEFAULT_MYSQLDB)+1);
+               strcpy(mysqldb, DEFAULT_MYSQLDB);
+       }
+       t = getenv("BLOCK_EXPIRE");
+       if (t) {
+               block_expire = atoi(t);
+       } else {
+               block_expire = DEFAULT_BLOCK_EXPIRE;
+       }
+       t = getenv("RECORD_EXPIRE");
+       if (t) {
+               record_expire = atoi(t);
+       } else {
+               record_expire = DEFAULT_RECORD_EXPIRE;
+       }
+       t = getenv("RECORD_EXPIRE_GOOD");
+       if (t) {
+               record_expire_good = atoi(t);
+       } else {
+               record_expire_good = DEFAULT_RECORD_EXPIRE_GOOD;
+       }
+       t = getenv("LOCAL_SCAN_DEBUG");
+       if (t) {
+               local_scan_debug = atoi(t);
+       } else {
+               local_scan_debug = DEFAULT_LOCAL_SCAN_DEBUG;
+       }
+}
+
+int local_scan(int fd, uschar **return_text)
+{
+ MYSQL *mysql = NULL;
+ int i,  ret = LOCAL_SCAN_ACCEPT;
+
+ get_config();
+
+ if(local_scan_debug == 1)  fprintf(stderr,"local_scan: protocol = %s %s  
\n",received_protocol,sender_address);
+
+
+ fd = fd;                      /* Keep picky compilers happy */
+ return_text = return_text;                     /* Keep picky compilers happy 
*/
+ mysql = mysql_init(NULL);
+ if (mysql && strcmp(received_protocol,"local") ) {
+   if 
(mysql_real_connect(mysql,mysqlhost,mysqluser,mysqlpass,mysqldb,0,NULL,0)) {
+     if ( !checkWhiteListIP( mysql, &ret )) {      /* check for whitelisted 
sender ip address */
+       for(i= 0 ; i <  recipients_count; i++ ) {
+        if(strlen(sender_host_address) + strlen(sender_address) +
+           strlen(recipients_list[i].address) < SQLCMDSIZE - 200) { /* check 
to avoid buffer overflows */
+          if(!checkWhiteListDomain( mysql, &ret,i ))
+            checkGreylist(mysql, &ret,i);
+        }
+       }
+     }
+   }
+ }
+ if(mysql) mysql_close(mysql);
+ if(local_scan_debug == 1) fprintf(stderr, "--------\n");
+ return ret;
+}
+
+/* End of local_scan.c */
diff -u -N -r qmail-ldap-1.03-20060201/local_scan.h 
qmail-ldap-1.03-20060201-greylist-0.3/local_scan.h
--- qmail-ldap-1.03-20060201/local_scan.h       1970-01-01 01:00:00.000000000 
+0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/local_scan.h  2007-01-21 
15:52:06.000000000 +0000
@@ -0,0 +1,38 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#ifndef __GREYLIST_H__
+#define __GREYLIST_H__
+
+#define FALSE 0
+#define TRUE 1
+#define uschar char
+
+#define LOCAL_SCAN_ACCEPT  0
+#define LOCAL_SCAN_REJECT  100
+#define LOCAL_SCAN_TEMPREJECT  101
+#define SOMETHING_BAD      102
+
+struct recipients_list {
+  char *address;
+};
+
+
+int local_scan(int fd, uschar **return_text);
+
+/* globals used by exim */
+
+extern int recipients_count;
+extern char *received_protocol;
+extern char *sender_host_address;
+extern char *sender_address;
+extern struct recipients_list recipients_list[];
+
+#endif
diff -u -N -r qmail-ldap-1.03-20060201/Makefile 
qmail-ldap-1.03-20060201-greylist-0.3/Makefile
--- qmail-ldap-1.03-20060201/Makefile   2007-01-21 17:03:42.000000000 +0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/Makefile      2007-01-21 
23:22:28.000000000 +0000
@@ -19,6 +19,7 @@
 # -DQMQP_COMPRESS to use the QMQP on the fly compression (for clusters)
 # -DQUOTATRASH to include the Trash in the quota calculation (normaly it is 
not)
 # -DSMTPEXECCHECK to enable smtp DOS/Windows executable detection
+# -DGREYLIST to enable greylisting support
 #LDAPFLAGS=-DQLDAP_CLUSTER -DEXTERNAL_TODO -DDASH_EXT -DDATA_COMPRESS 
-DQMQP_COMPRESS -DSMTPEXECCHECK
 
 # Perhaps you have different ldap libraries, change them here
@@ -71,6 +72,10 @@
 #SHADOWOPTS=-DPW_SHADOW
 # To use shadow passwords under Solaris, uncomment the SHADOWOPTS line.
 
+# Greylist support
+MYSQLINCLUDES=-I/usr/include/mysql
+MYSQLLIBS=-L/usr/lib -lmysqlclient
+
 # to enable the possibility to log and debug imap and pop uncoment the
 # next line
 DEBUG=-DDEBUG
@@ -1041,7 +1046,7 @@
 predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
 qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
-qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
+qmail-smtpd qmail-envelope-scanner sendmail tcp-env qmail-newmrh config 
config-fast dnscname \
 dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
 forward preline condredirect bouncesaying except maildirmake \
 maildir2mbox maildirwatch qail elq pinq idedit install-big \
@@ -2071,6 +2076,18 @@
        error.a fs.a auto_qmail.o dns.o str.a auto_break.o \
        `cat dns.lib` `cat socket.lib` $(TLSLIBS) $(ZLIB)
 
+qmail-envelope-scanner: \
+load qmail-envelope-scanner.o local_scan.o
+       ./load qmail-envelope-scanner -lz -lm local_scan.o $(MYSQLLIBS)
+
+qmail-envelope-scanner.o: \
+compile qmail-envelope-scanner.c local_scan.h
+       ./compile qmail-envelope-scanner.c 
+
+local_scan.o: \
+compile local_scan.c local_scan.h
+       ./compile $(MYSQLINCLUDES) local_scan.c
+
 qmail-smtpd.0: \
 qmail-smtpd.8
        nroff -man qmail-smtpd.8 > qmail-smtpd.0
@@ -2362,7 +2379,7 @@
 qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \
 qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
-qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
+qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c 
qmail-envelope-scanner.c \
 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
 dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
diff -u -N -r qmail-ldap-1.03-20060201/qmail-envelope-scanner.c 
qmail-ldap-1.03-20060201-greylist-0.3/qmail-envelope-scanner.c
--- qmail-ldap-1.03-20060201/qmail-envelope-scanner.c   1970-01-01 
01:00:00.000000000 +0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/qmail-envelope-scanner.c      
2007-01-21 16:06:25.000000000 +0000
@@ -0,0 +1,34 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "local_scan.h"
+
+
+char *received_protocol;
+char *sender_host_address;
+char *sender_address;
+struct recipients_list recipients_list[1];
+int recipients_count = 1;
+
+int main(int argv, char *argc[])
+{
+  received_protocol = "notneeded4qmail";
+  sender_host_address = getenv("TCPREMOTEIP");
+  recipients_list[0].address = argc[2];
+  sender_address = argc[1];
+
+  if (argv == 3 && sender_host_address != NULL)
+    return local_scan(1,NULL);
+  else
+    return SOMETHING_BAD;
+}
+
diff -u -N -r qmail-ldap-1.03-20060201/qmail-smtpd.c 
qmail-ldap-1.03-20060201-greylist-0.3/qmail-smtpd.c
--- qmail-ldap-1.03-20060201/qmail-smtpd.c      2007-01-21 01:39:36.000000000 
+0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/qmail-smtpd.c 2007-01-21 
15:16:23.000000000 +0000
@@ -22,6 +22,10 @@
 #include "env.h"
 #include "now.h"
 #include "exit.h"
+#ifdef GREYLIST
+  #include "fork.h"
+  #include "wait.h"
+#endif
 #include "rcpthosts.h"
 #include "rbl.h"
 #include "timeoutread.h"
@@ -165,6 +169,10 @@
 void err_qqt(void) { out("451 qqt failure (#4.3.0)\r\n"); }
 void err_dns(void) { out("421 DNS temporary failure at return MX check, try 
again later (#4.3.0)\r\n"); }
 void err_soft(char *s) { out("451 "); out(s); out("\r\n"); 
logline2(1,"temporary verify error: ", s); }
+#ifdef GREYLIST
+void err_tempfail() { out("421 temporary envelope failure (#4.3.0)\r\n"); }
+void err_permfail() { out("553 sorry, permanent envelope failure 
(#5.7.1)\r\n"); }
+#endif
 void err_bmf(void) { out("553 sorry, your mail was administratively denied. 
(#5.7.1)\r\n"); }
 void err_bmfunknown(void) { out("553 sorry, your mail from a host ["); 
out(remoteip); out("] without valid reverse DNS was administratively denied 
(#5.7.1)\r\n"); }
 void err_maxrcpt(void) { out("553 sorry, too many recipients (#5.7.1)\r\n"); }
@@ -831,7 +839,39 @@
   return 0;
 }
 
+#ifdef GREYLIST
+int envelope_scanner()
+{
+  int child;
+  int wstat;
+  char *envelope_scannerarg[] = { "bin/qmail-envelope-scanner", mailfrom.s, 
addr.s, 0 };
+
+  switch(child = vfork()) {
+    case -1:
+      return 1;
+    case 0:
+      execv(*envelope_scannerarg,envelope_scannerarg);
+      _exit(111);
+  }
 
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat)) {
+    return 1;
+  }
+
+  switch(wait_exitcode(wstat)) {
+    case 101:
+      err_tempfail();
+      return 0;
+    case 100:
+      err_permfail();
+      return 0;
+    default:
+      return 1;
+  }
+}
+#endif
+                                                       
 void smtp_helo(char *arg)
 {
   smtp_line("250 ");
@@ -1134,6 +1174,10 @@
       if (errdisconnect) err_quit();
       return; 
     }
+#ifdef GREYLIST
+       if (env_get("GREYLIST"))
+               if (!envelope_scanner()) return;
+#endif
   }
   ++rcptcount;
 
diff -u -N -r qmail-ldap-1.03-20060201/README.greylist 
qmail-ldap-1.03-20060201-greylist-0.3/README.greylist
--- qmail-ldap-1.03-20060201/README.greylist    1970-01-01 01:00:00.000000000 
+0100
+++ qmail-ldap-1.03-20060201-greylist-0.3/README.greylist       2007-01-21 
23:28:05.000000000 +0000
@@ -0,0 +1,46 @@
+AKNOWLEDGEMENTS:
+
+The greylist support added is based on mjd@digitaleveryware.com greylisting
+software available at http://www.digitaleveryware.com/projects/greylisting/.
+
+Integration with qmail-ldap was almost entirely ripped off from the netqmail
+greylist patch for the same digitaleveryware software, done by Joshua Megerman
+(josh@honorablemenschen.com) and Bill Shupp (hostmaster@shupp.org).
+
+Besides the qmail-ldap integration itself, it was added the -DGREYLIST option 
to
+LDAPFLAGS so that the code can be activated/deactivated at compile time.
+Support for greylist checking depending on the existance of GREYLIST env var 
was
+also added. That way greylisting can be activated only for the desired sources,
+in the tcpserver rules way.
+
+Compilation with greylist support depends on the existence of mysql development
+libraries. Check the MYSQL* build variables on the Makefile.
+
+
+ON MYSQL FAILURE:
+
+If mysql isn't running, the relaydelay database doesn't exist or the username 
or
+password fails, greylisting will be disabled and messages will be accepted 
+according to qmail-ldap defined rules.
+
+
+WHITELISTING:
+
+Although there's the possibility to whitelist by omiting the GREYLIST env var
+on the tcpserver rules cdb, for a certain netblock, there's also the 
possibility to
+do so by inserting records into the relaydelay database. There's a small perl 
script
+(greylist-whitelist.pl) bundled to aid with such task.
+
+Ex:
+       /var/qmail/doc/greylist-whitelist.pl 1.2.3.4
+
+
+DATABASE MAINTENANCE:
+
+Database maintenance, i.e. deletion of expired records, has to be done 
separatly.
+I've bundled a perl script (greylist-cleanup.pl) formerly submitted by Gerard 
Earley
+to the QMR mailing list.
+It's advised that you preform such maintenance once a day, peharps setting up 
a cronjob.
+
+Ex: 
+       0 0 * * *       root    /usr/local/bin/greylist-cleanup.pl
diff -u -N -r qmail-ldap-1.03-20060201/TARGETS 
qmail-ldap-1.03-20060201-greylist-0.3/TARGETS
--- qmail-ldap-1.03-20060201/TARGETS    2007-01-21 01:39:36.000000000 +0000
+++ qmail-ldap-1.03-20060201-greylist-0.3/TARGETS       2007-01-20 
20:12:13.000000000 +0000
@@ -381,6 +381,9 @@
 man
 setup
 check
+local_scan.o
+qmail-envelope-scanner.o
+qmail-envelope-scanner
 auth_imap
 Makefile.cdb-p
 auth_imap.o
<Prev in Thread] Current Thread [Next in Thread>