qmail-1.03-libpq-greylist-1.2.4.patch

Mario Remy Almeida <[email protected]>
Newsgroups gmane.mail.qmail.ldap
Organization ISA
Message-ID <1222580968.2588.10.camel@mario>
Hi All,

I have modified the qmail-1.03-libpq-greylist-1.2.3 patch and added
1) SENDER DOMAIN check
2) enable using tcpserver ENV no more GREYLIST_STATFILE
3) logging using syslog


can someone test the patch and let me know for any bugs?

Regards,
Mario
qmail-1.03-libpq-greylist-1.2.4.patch (text/x-patch, 35.1 KB)
diff -urN qmail-1.03.orig/CHANGES.greylist qmail-1.03/CHANGES.greylist
--- qmail-1.03.orig/CHANGES.greylist    1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/CHANGES.greylist 2006-09-07 11:09:26.383018064 +0200
@@ -0,0 +1,36 @@
+2008-09-26 - v1.2.4
+- no more GREYLIST_STATFILE required
+- enable using tcpserver EVN
+- check whether the SENDER DOMAIN is white- or blacklisted
+- logging using syslog
+
+2006-09-07 - v1.2.3
+- corrected usage of postgres-account-defines from greylist.h in greylist.c
+- fixed debug define
+- major README update
+- added define for GREYLIST_STATFILE in greylist.h and changed hardcoded
+  path in qmail-smtpd.c to use defined value
+- added automatic building for greylist-cleanup
+
+2006-03-10 - v1.2.2
+- corrected messed up patch v1.2.1
+
+2006-02-17 - v1.2.1
+- added a feature which allows excluding a single user from greylisting
+
+2005-10-11 - v1.2.0
+- release of the final public version 1.2.0 for sourceforge
+- minor changes to different files (license cleanup's)
+- simple documentation added, see README.greylist and FAQ.greylist
+
+2nd quarter 2005 - v1.0.5
+- GPL license notes updated/corrected
+
+early 2005 - v1.0.4
+- added statistics table to sql structure
+- added statistics to greylist-cleanup
+
+last quarter 2004 - v1.0.3
+- got some exit codes fixed
+- allow for email black- and whitelisting in our database
+
+2nd quarter 2004 - v1.0.2
+- greylist-cleanup added
+
+early 2004 - v1.0.1
+- early initial release, posted to someone at www.qmail.org
+- some debugging code included
diff -urN qmail-1.03.orig/CREDITS.greylist qmail-1.03/CREDITS.greylist
--- qmail-1.03.orig/CREDITS.greylist    1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/CREDITS.greylist 2006-09-07 11:09:24.166355048 +0200
@@ -0,0 +1,2 @@
+2006-09-07
+- many thanks to David Elze for debugging and new ideas
diff -urN qmail-1.03.orig/FAQ.greylist qmail-1.03/FAQ.greylist
--- qmail-1.03.orig/FAQ.greylist        1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/FAQ.greylist     2006-09-07 11:09:24.167354896 +0200
@@ -0,0 +1,13 @@
+FAQ for qmail-greylist-pgsql
+============================
+1. What files does qmail-greylist-pgsql change in a vanilla qmail package?
+
+   qmail-greylist-pgsql does only change qmail-smtpd in order to launch (fork)
+   an external application called "greylist". The later will handle all
+   database related stuff.
+
+2. Does qmail-greylist-pgsql integrate with other patches?
+
+   The following sequence of patches was tested and works correctly:
+     - qmail-ldap-1.03-20060201.patch
+     - greylist patch
diff -urN qmail-1.03.orig/FILES qmail-1.03/FILES
--- qmail-1.03.orig/FILES       1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/FILES    2006-09-07 11:09:24.168354744 +0200
@@ -426,6 +426,10 @@
 tcp-environ.5
 constmap.h
 constmap.c
+greylist.h
+greylist.c
+postgres-greylist.sql
+CREDITS.greylist
 EXTTODO
 Makefile.cdb
 POPBEFORESMTP
diff -urN qmail-1.03.orig/greylist.c qmail-1.03/greylist.c
--- qmail-1.03.orig/greylist.c  1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/greylist.c       2006-09-07 11:09:24.169354592 +0200
@@ -0,0 +1,260 @@
+/*
+ * qmail-greylist-postgres - qmail add-on patch implementing greylisting
+ * Copyright (C) 2004-2005 Guenther Mair
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA
+ *  or see http://ww.gnu.org/licenses/gpl.html.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "/usr/include/libpq-fe.h"
+#include "greylist.h"
+
+#ifdef DEBUG
+#include <syslog.h>
+#endif
+
+void exit_nicely(error_code, connect, result, query, ip, fromadd, toadd, message)
+int error_code;
+PGconn *connect;
+PGresult *result;
+char *query;
+char *message;
+char *ip;
+char *fromadd;
+char *toadd;
+{
+#ifdef DEBUG
+  openlog("greylist", LOG_PID, LOG_MAIL);
+  syslog(LOG_DEBUG, "IP:%s, Sender:%s, Recipient:%s, Result:%d, %s", ip, fromadd, toadd, error_code, message);
+  syslog(LOG_DEBUG, "last query: %s\n", query);
+  syslog(LOG_DEBUG, "last pq-status: %s", PQerrorMessage(connect));
+#endif
+  PQclear(result);
+  PQfinish(connect);
+  exit(error_code);
+}
+
+int main(int argv, char *argc[])
+{
+  PGconn	*connect;
+  PGresult	*result;
+  char		query[2048];
+  char            message[2048];
+
+  int		temp, escape_length;
+  char		*remote_ip, *mailfrom, *rcptto, *rcptdomain, *fromdomain;
+  char		connect_string[256];
+  unsigned char *escaped_mailfrom, *escaped_rcptto, *escaped_rcptdomain, *escaped_fromdomain;
+
+  long int	record_id;
+
+  remote_ip = getenv("TCPREMOTEIP");
+  mailfrom = getenv("MAILFROM");
+  rcptto = getenv("RCPTTO");
+
+  /* look for environment variables */
+  if ((remote_ip == NULL) || (mailfrom == NULL) || (rcptto == NULL))
+    exit(GREYLIST_ERROR);
+  else {
+    /* create connection string */
+    sprintf(connect_string, "dbname=%s user=%s password=%s",
+      PG_DBNAME, PG_USER, PG_PASSWORD);
+    connect = PQconnectdb(connect_string);
+    /* connect to database */
+    if (PQstatus(connect) == CONNECTION_BAD) {
+#ifdef DEBUG
+      openlog("greylist", LOG_PID, LOG_MAIL);
+      syslog(LOG_DEBUG, "last pq-status: %s", PQerrorMessage(connect));
+#endif
+      exit(GREYLIST_ERROR);
+    }
+
+    /* check whether this IP is white- or blacklisted */
+    sprintf(query, "select id, tempblock_expiry > CURRENT_TIMESTAMP as black from tbl_relay where record_expiry > CURRENT_TIMESTAMP and mail_from IS NULL and rcpt_to IS NULL AND relay_ip = '%s' order by id;", remote_ip);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot Query for IP Address : %s", remote_ip);
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+    if (PQntuples(result)) { /* does an entry exist? */
+      if (strcmp(PQgetvalue(result, 0, 1),"f") ) { /* is it whitelisted? */
+	sprintf(message, "message=IP Address %s Whitelisted", remote_ip);
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* ...or blacklisted? */
+	sprintf(message, "message=IP Address %s Blacklisted", remote_ip);
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+    }
+    PQclear(result);
+
+    /*
+     * check whether the RECIPIENT DOMAIN is white- or blacklisted
+     * this should be usefull for ISPs hosting more than one domain and
+     * who do not want to or cannot activate greylisting for all of them
+     */
+    rcptdomain = strrchr(rcptto, '@') + 1;
+    if ((escape_length = strlen(rcptdomain)) > MAX_ESCAPE_LENGTH)
+      escape_length = MAX_ESCAPE_LENGTH;
+    escaped_rcptdomain = PQescapeBytea(rcptdomain, escape_length, &temp);
+
+    sprintf(query, "select id, tempblock_expiry > CURRENT_TIMESTAMP as black from tbl_relay where record_expiry > CURRENT_TIMESTAMP and mail_from IS NULL and relay_ip IS NULL AND rcpt_to = '%s' order by id;", escaped_rcptdomain);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot query for recipient domain : %s", rcptdomain);
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+    if (PQntuples(result)) { /* does an entry exist? */
+      if (strcmp(PQgetvalue(result, 0, 1),"f") ) { /* is it whitlisted? */
+	sprintf(message, "message=Recipient domain Whitlisted : %s", rcptdomain);
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* ...or blacklisted? */
+	sprintf(message, "message=Recipient domain Blacklisted : %s", rcptdomain);
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+    }
+    PQclear(result);
+
+
+    /* check whether the SENDER DOMAIN is white- or blacklisted
+    * this should be usefull for ISPs hosting more than one domain and
+    * who do not want to or cannot activate greylisting for all of them */
+                    fromdomain = strrchr(mailfrom, '@') + 1;
+    if ((escape_length = strlen(fromdomain)) > MAX_ESCAPE_LENGTH)
+      escape_length = MAX_ESCAPE_LENGTH;
+    escaped_fromdomain = PQescapeBytea(fromdomain, escape_length, &temp);
+
+    sprintf(query, "select id, tempblock_expiry > CURRENT_TIMESTAMP as black from tbl_relay where record_expiry > CURRENT_TIMESTAMP and rcpt_to IS NULL and relay_ip IS NULL AND mail_from = '%s' order by id;", escaped_fromdomain);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot query for sender domain : %s", fromdomain);
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+    if (PQntuples(result)) { /* does an entry exist? */
+      if (strcmp(PQgetvalue(result, 0, 1),"f") ) { /* is it whitlisted? */
+        sprintf(message, "message=Sender domain Whitlisted : %s", fromdomain);
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* ...or blacklisted? */
+        sprintf(message, "message=Sender domain Blacklisted : %s", fromdomain);
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+   }
+   PQclear(result);
+
+
+    /* check whether the sender email address is white- or blacklisted */
+    if ((escape_length = strlen(mailfrom)) > MAX_ESCAPE_LENGTH)
+      escape_length = MAX_ESCAPE_LENGTH;
+    escaped_mailfrom = PQescapeBytea(mailfrom, escape_length, &temp);
+
+    sprintf(query, "select id, tempblock_expiry > CURRENT_TIMESTAMP as black from tbl_relay where record_expiry > CURRENT_TIMESTAMP and rcpt_to IS NULL and relay_ip IS NULL AND mail_from = '%s' order by id;", escaped_mailfrom);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot query for sender email address : %s", mailfrom);
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+    if (PQntuples(result)) { /* does an entry exist? */
+      if (strcmp(PQgetvalue(result, 0, 1),"f") ) { /* is it whitlisted? */
+	sprintf(message, "message=Sender email address is Whitlisted : %s", mailfrom);
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* ...or blacklisted? */
+	sprintf(message, "message=Sender email address is Blacklisted :%s", mailfrom);
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+    }
+    PQclear(result);
+
+
+    /* check whether the recipient email address is white- or blacklisted */
+    if ((escape_length = strlen(rcptto)) > MAX_ESCAPE_LENGTH)
+      escape_length = MAX_ESCAPE_LENGTH;
+    escaped_rcptto = PQescapeBytea(rcptto, escape_length, &temp);
+
+    sprintf(query, "select id, tempblock_expiry > CURRENT_TIMESTAMP as black from tbl_relay where record_expiry > CURRENT_TIMESTAMP and mail_from IS NULL and relay_ip IS NULL AND rcpt_to = '%s' order by id;", escaped_rcptto);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot query for recipient email address : %s", rcptto);
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+    if (PQntuples(result)) { /* does an entry exist? */
+      if (strcmp(PQgetvalue(result, 0, 1),"f") ) { /* is it whitlisted? */
+	sprintf(message, "message=Recipient email address is Whitlisted : %s", rcptto);
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* ...or blacklisted? */
+	sprintf(message, "message=Recipient email address is Blacklisted : %s", rcptto);
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+    }
+    PQclear(result);
+
+    /*
+     * go for the greylist run, but first make sure to avoid possible
+     * buffer overflows and SQL-injections
+     * escaped_rcptto and escaped_mailfrom have already been defined
+     */
+    sprintf(query, "select id, tempblock_expiry < CURRENT_TIMESTAMP as tempblock from tbl_relay where record_expiry > CURRENT_TIMESTAMP and mail_from = '%s' and rcpt_to = '%s' and relay_ip = '%s' order by id desc;", escaped_mailfrom, escaped_rcptto, remote_ip);
+    result = PQexec(connect, query);
+    if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+      sprintf(message, "message=ERROR Cannot query if old record exist. ");
+      exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+	
+    /* if a record exists, go looking for what to do with it */
+    if (PQntuples(result)) {
+      record_id = strtol(PQgetvalue(result, 0, 0), NULL, 10);
+      sprintf(message, "message=ERROR Cannot query old record.");
+      if (errno) exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      if (strcmp(PQgetvalue(result, 0, 1),"t") ) { /* tempblock active */
+        PQclear(result);
+        sprintf(query, "update tbl_relay set blocked_count = blocked_count + 1 where id = '%d';", record_id);
+        result = PQexec(connect, query);
+	sprintf(message, "message=Delay for record is temporary active... ");
+        if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+          exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+        }
+        exit_nicely(GREYLIST_TEMPREJECT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      else { /* tempblock no longer active */
+        PQclear(result);
+        sprintf(query, "update tbl_relay set record_expiry = CURRENT_TIMESTAMP + INTERVAL '%d days', passed_count = passed_count + 1 where id = '%d';", RECORD_EXPIRY_GOOD, record_id);
+        result = PQexec(connect, query);
+	sprintf(message, "message=Record temporary not active...");
+        if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+          exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+        }
+        exit_nicely(GREYLIST_ACCEPT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+    }
+    else { /* if no record exists create one */
+      PQclear(result);
+      sprintf(query, "insert into tbl_relay values (nextval('seq_relay'), '%s', '%s', '%s', CURRENT_TIMESTAMP + INTERVAL '%d minutes', CURRENT_TIMESTAMP + INTERVAL '%d minutes', 0, 0, CURRENT_TIMESTAMP);", remote_ip, escaped_mailfrom, escaped_rcptto, TEMPBLOCK_EXPIRY, RECORD_EXPIRY);
+      result = PQexec(connect, query);
+      if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+	sprintf(message, "message=ERROR Cannot enter new record.");
+        exit_nicely(GREYLIST_ERROR, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+      }
+      sprintf(message, "message=Greylist temporary rejection - first time.");
+      exit_nicely(GREYLIST_TEMPREJECT, connect, result, &query, remote_ip, mailfrom, rcptto, &message);
+    }
+  }
+}
diff -urN qmail-1.03.orig/greylist-cleanup.c qmail-1.03/greylist-cleanup.c
--- qmail-1.03.orig/greylist-cleanup.c  1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/greylist-cleanup.c       2006-09-07 11:09:24.171354288 +0200
@@ -0,0 +1,86 @@
+/*
+ * qmail-greylist-postgres - qmail add-on patch implementing greylisting
+ * Copyright (C) 2004-2005 Guenther Mair
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA
+ *  or see http://ww.gnu.org/licenses/gpl.html.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "/usr/include/libpq-fe.h"
+#include "greylist.h"
+
+int main(int argv, char *argc[])
+{
+  PGconn	*connect;
+  PGresult	*result;
+  char		query[2048];
+  char		connect_string[256];
+
+  openlog("greylist-cleanup", LOG_PID, LOG_MAIL);
+
+  /* create connection string */
+  sprintf(connect_string, "dbname=%s user=%s password=%s",
+    PG_DBNAME, PG_USER, PG_PASSWORD);
+  connect = PQconnectdb(connect_string);
+
+  /* connect to database */
+  if (PQstatus(connect) == CONNECTION_BAD) {
+    syslog(LOG_DEBUG, "last pq-status: %s", PQerrorMessage(connect));
+    exit(GREYLIST_ERROR);
+  }
+
+  /* dump statistics before deleting */
+  sprintf(query, "insert into tbl_stats (count) select count(*) from tbl_relay where record_expiry < CURRENT_TIMESTAMP;");
+  result = PQexec(connect, query);
+  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+    PQfinish(connect);
+    syslog(LOG_DEBUG, "warning: %s", query);
+    syslog(LOG_DEBUG, "warning: %s", PQerrorMessage(connect));
+    exit(GREYLIST_ERROR);
+  }
+  syslog(LOG_DEBUG, "statistics dumped.");
+  PQclear(result);
+
+  /* delete old records */
+  sprintf(query, "delete from tbl_relay where record_expiry < CURRENT_TIMESTAMP;");
+  result = PQexec(connect, query);
+  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+    PQfinish(connect);
+    syslog(LOG_DEBUG, "warning: %s", query);
+    syslog(LOG_DEBUG, "warning: %s", PQerrorMessage(connect));
+    exit(GREYLIST_ERROR);
+  }
+  syslog(LOG_DEBUG, "cleanup finished.");
+  PQclear(result);
+
+  /* vacuum analyze table */
+  sprintf(query, "vacuum analyze tbl_relay;");
+  result = PQexec(connect, query);
+  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+    PQfinish(connect);
+    syslog(LOG_DEBUG, "warning: %s", query);
+    syslog(LOG_DEBUG, "warning: %s", PQerrorMessage(connect));
+    exit(GREYLIST_ERROR);
+  }
+  syslog(LOG_DEBUG, "vacuum analyze finished.");
+  PQclear(result);
+
+  PQfinish(connect);
+  exit(0);
+}
diff -urN qmail-1.03.orig/greylist.h qmail-1.03/greylist.h
--- qmail-1.03.orig/greylist.h  1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/greylist.h       2006-09-07 11:09:24.172354136 +0200
@@ -0,0 +1,42 @@
+/*
+ * qmail-greylist-postgres - qmail add-on patch implementing greylisting
+ * Copyright (C) 2004-2005 Guenther Mair
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA
+ *  or see http://ww.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef GREYLIST_H
+#define GREYLIST_H
+
+#define GREYLIST_ACCEPT		0
+#define GREYLIST_REJECT		100
+#define GREYLIST_TEMPREJECT	101
+#define GREYLIST_ERROR		102
+
+#define PG_DBNAME		"greylist"
+#define PG_USER			"greylist"
+#define PG_PASSWORD		"greylist"
+
+#define GREYLIST_STATFILE	"/var/qmail/control/greylist"
+
+#define TEMPBLOCK_EXPIRY	15		/* in minutes */
+#define RECORD_EXPIRY		720		/* in minutes */
+#define RECORD_EXPIRY_GOOD	60		/* in days */
+#define MAX_ESCAPE_LENGTH	256		/* in characters */
+
+#define DEBUG			1
+
+#endif
diff -urN qmail-1.03.orig/hier.c qmail-1.03/hier.c
--- qmail-1.03.orig/hier.c      1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/hier.c   2006-09-07 11:09:24.175353680 +0200
@@ -214,6 +214,8 @@
   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);
+  c(auto_qmail,"bin","greylist",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","greylist-cleanup",auto_uido,auto_gidq,0755);
   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 -urN qmail-1.03.orig/install-big.c qmail-1.03/install-big.c
--- qmail-1.03.orig/install-big.c       1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/install-big.c    2006-09-07 11:09:24.177353376 +0200
@@ -214,6 +214,8 @@
   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);
+  c(auto_qmail,"bin","greylist",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","greylist-cleanup",auto_uido,auto_gidq,0755);
   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 -urN qmail-1.03.orig/Makefile qmail-1.03/Makefile
--- qmail-1.03.orig/Makefile    1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/Makefile 2006-09-07 11:09:24.178353224 +0200
@@ -1041,7 +1041,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 greylist greylist-cleanup 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 +2071,22 @@
 	error.a fs.a auto_qmail.o dns.o str.a auto_break.o \
 	`cat dns.lib` `cat socket.lib` $(TLSLIBS) $(ZLIB)
 
+greylist: \
+load greylist.o /usr/lib/libpq.a
+	./load greylist -lpq /usr/lib/libpq.a
+
+greylist.o: \
+compile greylist.c greylist.h
+	./compile greylist.c
+
+greylist-cleanup: \
+load greylist-cleanup.o /usr/lib/libpq.a
+	./load greylist-cleanup -lpq /usr/lib/libpq.a
+
+greylist-cleanup.o: \
+compile greylist-cleanup.c greylist.h
+	./compile greylist-cleanup.c
+
 qmail-smtpd.0: \
 qmail-smtpd.8
 	nroff -man qmail-smtpd.8 > qmail-smtpd.0
@@ -2363,6 +2379,7 @@
 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 \
+greylist.c greylist-cleanup.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 -urN qmail-1.03.orig/postgres-greylist.sql qmail-1.03/postgres-greylist.sql
--- qmail-1.03.orig/postgres-greylist.sql       1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/postgres-greylist.sql    2006-09-07 11:09:24.180352920 +0200
@@ -0,0 +1,74 @@
+--
+-- qmail-greylist-postgres - qmail add-on patch implementing greylisting
+-- Copyright (C) 2004-2005 Guenther Mair
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 2 of the License, or
+--  (at your option) any later version.
+--  
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--  
+--  You should have received a copy of the GNU General Public License
+--  along with this program; if not, write to the Free Software
+--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA
+--  or see http://ww.gnu.org/licenses/gpl.html.
+--
+
+\connect - postgres
+
+create user greylist password 'greylist' createdb;
+
+\connect - greylist
+
+create database greylist;
+
+\connect greylist
+
+create sequence "seq_relay" start 1 increment 1 maxvalue 2147483647 minvalue 1 cache 1;
+
+create sequence "seq_stats" start 1 increment 1 maxvalue 2147483647 minvalue 1 cache 1;
+
+create table "tbl_relay" (
+	"id" bigint default nextval('"seq_relay"'::text) NOT NULL,
+	"relay_ip" character varying(16),
+	"mail_from" character varying(255),
+	"rcpt_to" character varying(255),
+	"tempblock_expiry" timestamp with time zone NOT NULL,
+	"record_expiry" timestamp with time zone NOT NULL,
+	"blocked_count" bigint NOT NULL,
+	"passed_count" bigint NOT NULL,
+	"created" timestamp with time zone NOT NULL	
+);
+
+create table "tbl_stats" (
+	"id" bigint default nextval('"seq_stats"'::text) NOT NULL,
+	"count" bigint NOT NULL,
+	"date" timestamp with time zone default CURRENT_TIMESTAMP NOT NULL	
+);
+
+CREATE UNIQUE INDEX idx_relayid ON tbl_relay USING btree (id);
+CREATE INDEX idx_relayip ON tbl_relay USING btree (relay_ip);
+CREATE INDEX idx_relayfrom ON tbl_relay USING btree (mail_from);
+CREATE INDEX idx_relayto ON tbl_relay USING btree (rcpt_to);
+
+insert into tbl_relay values (nextval('seq_relay'),'127.0.0.1',NULL,NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'gmail.com',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'yahoo.com',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'yahoo.co.in',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'hotmail.com',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'indiatimes.com',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+insert into tbl_relay values (nextval('seq_relay'),NULL,'rediffmail.com',NULL,CURRENT_TIMESTAMP + INTERVAL '100 years', CURRENT_TIMESTAMP + INTERVAL '100 years', 0, 0, CURRENT_TIMESTAMP);
+
+\connect - postgres
+
+grant all on table tbl_stats to greylist ;
+grant all on table seq_stats to greylist ;
+grant all on table tbl_relay to greylist ;
+grant all on table seq_relay to greylist ;
+alter table tbl_relay owner to greylist ;
+alter table tbl_stats owner to greylist ;
+alter user greylist nocreatedb;
diff -urN qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c
--- qmail-1.03.orig/qmail-smtpd.c       1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/qmail-smtpd.c    2006-09-07 11:09:24.184352312 +0200
@@ -22,6 +22,10 @@
 #include "env.h"
 #include "now.h"
 #include "exit.h"
+#include "open.h"
+#include "fork.h"
+#include "wait.h"
+#include "greylist.h"
 #include "rcpthosts.h"
 #include "rbl.h"
 #include "timeoutread.h"
@@ -48,6 +52,8 @@
 unsigned long databytes = 0;
 int timeout = 1200;
 
+void die() { _exit(111); }
+
 #ifdef TLS_SMTPD
 int flagtimedout = 0;
 void sigalrm()
@@ -165,6 +171,8 @@
 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); }
+void err_tempfail() { out("421 temporary envelope failure (#4.3.0)\r\n"); }
+void err_permfail() { out("553 sorry, permanent envelope failure or blacklist entry (#5.7.1)\r\n"); }
 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"); }
@@ -284,6 +292,7 @@
 unsigned int tarpitcount = 0;
 unsigned int tarpitdelay = 5;
 unsigned int maxrcptcount = 0;
+int greylist = 0;
 int sendercheck = 0;
 int rcptcheck = 0;
 int ldapsoftok = 0;
@@ -385,6 +394,7 @@
   if (env_get("NOBOUNCE")) nobounce = 1;
   if (env_get("SANITYCHECK")) sanitycheck = 1;
   if (env_get("RETURNMXCHECK")) returnmxcheck = 1;
+  if (env_get("GREYLIST")) greylist = 1;
   if (env_get("BLOCKRELAYPROBE")) blockrelayprobe = 1;
   if (env_get("SENDERCHECK")) {
     sendercheck = 1;
@@ -832,6 +842,41 @@
 }
 
 
+int envelope_scanner()
+{
+  int child;
+  int wstat;
+  static char *envelope_scannerarg[2] = { "bin/greylist", 0 };
+
+  switch(child = vfork()) {
+    case -1:
+      return 1;
+    case 0:
+      if (!env_put2("MAILFROM", mailfrom.s)) die();
+      if (!env_put2("RCPTTO", addr.s)) die();
+      execv(*envelope_scannerarg,envelope_scannerarg);
+      env_unset("MAILFROM");
+      env_unset("RCPTTO");
+      _exit(111);
+  }
+
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat)) {
+    return 1;
+  }
+
+  switch(wait_exitcode(wstat)) {
+    case 101:
+      err_tempfail();
+      return 0;
+    case 102:
+      err_permfail();
+      return 0;
+    default:
+      return 1;
+  }
+}
+
 void smtp_helo(char *arg)
 {
   smtp_line("250 ");
@@ -1182,6 +1227,17 @@
     }
   }
 
+  /* Greylist check */
+  if (greylist) {
+    if (!goodmailaddr()) {
+      logline(4,"Greylist check. Recipient not in goodmailaddr");
+      if (addrlocals()) {
+        logline(4,"Greylist check. Recipient is local");
+        if (!envelope_scanner()) return;
+      }
+    }
+  }
+
   if (loglevel < 4)
     logline2(2,"rcpt to: ",addr.s);
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
diff -urN qmail-1.03.orig/README.greylist qmail-1.03/README.greylist
--- qmail-1.03.orig/README.greylist     1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03/README.greylist  2006-09-07 11:09:52.234088104 +0200
@@ -0,0 +1,111 @@
+Intro
+=====
+
+This project focuses on a patch / add-on for qmail, is written in C and uses
+PostgreSQL as database backend in order to implement an AntiSpam method known
+as "greylisting".
+
+The idea behind greylisting is to block single-shot spamming attempts by
+denying SMTP deliveries at the first attempt with an temporary error (DSN code
+4.x.x) and therefor requesting further delivery-tries to common MTAs.
+
+The actual patch has been in production for over a year on different sites,
+behaves and performs nicely and like blacklisting reduces resources needed for
+handling mailtraffic. Different environments on x86 architectures have shown no
+problems, other architectures may work on the fly.
+
+
+Installation
+============
+
+This greylisting patch should work out of the box, but will require you to
+have all necessary C header files for postgres installed. This will for example
+require the package "postgresql-dev" on Debian Sarge 3.1 to be installed.
+
+Simply estract a vanilla qmail package, change into it and apply the patch:
+
+patch -p1 <../path/to/patch
+
+compile and install qmail the usual way and you are done. If you want to
+cleanup your database on a regular day (crontab), please make use of
+greylist-cleanup. This drops old and unused connection attempts, while keeping
+track of connection attempts. Usefull both for statistics and large sites.
+
+
+Activating greylisting
+======================
+
+In order to activate greylisting, define GREYLIST in tcpserver
+
+If env found greylist will be enabled, otherwise NOT!
+
+
+Debugging greylisting
+=====================
+
+You may debug qmail-greylist-pgsql in two ways. 
+"#define DEBUG 1" in "greylist.h". You will get debugging messages in your
+mail system log. change to 0 to disable debug
+
+Second, you may call the greylist binary from the shell and test it's behaviour
+prior to integrating it. The binary makes use of three environment variables in
+order to communicate with qmail-smtpd:
+
+export TCPREMOTEIP=123.123.123.123
+export [email protected]
+export [email protected]
+./greylist
+echo $?
+
+Check the return code of "echo $?" against the definitions in greylist.h and
+see the content of the database for correct operation.
+
+
+Database setup
+==============
+
+Pay attention to execute the included "postgres-greylist.sql" script as DB
+admin postgres, otherwise you may run into troubles (structures not owned by
+the correct user et. al.).
+
+If you decide to grant privileges by hand, please execute the following as
+postgres database administrator (ie. user postgres, on database template1) to
+make sure the greylisting-database has correct permissions and the greylist and
+greylist-cleanup binaries will work as expected:
+
+CREATE USER 'greylist' PASSWORD 'greylist';
+GRANT ALL PRIVILEGES ON tbl_relay, tbl_stats, seq_relay, seq_stats TO 'greylist';
+UPDATE pg_database SET datdba=(SELECT usesysid FROM pg_user WHERE usename='greylist') WHERE datname='greylist';
+
+
+Generic security hints
+======================
+
+Please avoid installing the greylist executable on multiuser environments
+without proper permissions and ownership. The executable only handles SQL
+injections and buffer overflows on email-addresses and does expect correctly
+set IP addresses.
+
+Change database credentials by hand inside of greylist.c, line 71.
+
+Also if you want greylisting to fail through nicely, please change
+GREYLIST_ERROR to 0 (zero) in greylist.h.
+
+Dates and timestamps are all handled by simple sql statements. Nothing is
+done by hand or implemented with additional libraries/support.
+
+
+Instructions for debianize-binary-tree
+======================================
+
+Assuming you are working with a vanilla qmail-1.03 tree, debianize-binary-tree
+and a directory structure bellow /tmp, you may want to change line 12 of
+'/tmp/qmail/qmail-1.03/debian/debianize-binary-tree' to include both binaries
+'greylist' and 'greylist-cleanup' replacing
+
+  elq|ping|qail|qlist2) echo usr/bin;;
+
+with
+
+  elq|ping|qail|qlist2|greylist|greylist-cleanup) echo usr/bin;;
+
diff -urN qmail-1.03.orig/TARGETS qmail-1.03/TARGETS
--- qmail-1.03.orig/TARGETS     1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03/TARGETS  2006-09-07 11:09:24.187351856 +0200
@@ -381,6 +381,8 @@
 man
 setup
 check
+greylist.o
+greylist
 auth_imap
 Makefile.cdb-p
 auth_imap.o
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.