Download | Plain Text | No Line Numbers
    - /*
 -  * Copyright (C) 2006 Manuel Mausz (manuel@mausz.at)
 -  * Origin code copyright (c) mjd@digitaleveryware.com 2003
 -  *  (http://www.digitaleveryware.com/projects/greylisting/)
 -  *
 -  * 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-1307, USA.
 -  */
 -  
 - #include <stdio.h>
 - #include <stdlib.h>
 - #include <string.h>
 - #include <stdarg.h>
 - #include <errno.h>
 - #include <mysql.h>
 -  
 - #define SQLCMDSIZE     2048
 - #define RET_NOTFOUND   0
 - #define RET_ACCEPT     1
 - #define RET_REJECT     2
 - #define RET_TEMPREJECT 3
 - #define CMD_TEMPREJECT "E451 temporary failure (#4.3.0)\n"
 - #define CMD_REJECT     "E553 sorry, your envelope sender has been denied (#5.7.1)\n"
 - #define LOGLEVEL_FATAL 1
 - #define LOGLEVEL_ERROR 2
 - #define LOGLEVEL_WARN  3
 - #define LOGLEVEL_INFO  4
 - #define LOGLEVEL_DEBUG 5
 - #define MAXCONFIGLINESIZE 1024 // maybe change this to dynamic allocation sometime
 - #if MYSQL_VERSION_ID >= 50003
 - # define QUERYSIZE 500
 - #else
 - # define QUERYSIZE 700
 - #endif
 -  
 - static char *configfile = "control/greylisting";
 - static char *mysql_host = NULL;
 - static char *mysql_user = NULL;
 - static char *mysql_pass = NULL;
 - static char *mysql_db   = NULL;
 - unsigned int mysql_port = 3306;
 - unsigned int block_expire  = 55;
 - unsigned int record_expire = 500;
 - unsigned int record_expire_good = 36;
 - static char *relay_ip;
 - static char *mail_from;
 - static char *rcpt_to;
 - static int loglevel = LOGLEVEL_WARN;
 -  
 - void gllog(unsigned int level,  char* format, ...)
 - {
 -   va_list args;
 -   if (level > loglevel)
 -     return;
 - }
 -  
 - int load_config(void)
 - {
 -   char *tmp, *delim, *atpos;
 -   FILE *config;
 -   char buf[MAXCONFIGLINESIZE];
 -   int i;
 -   unsigned int userlen;
 -  
 -   /* first check for logging var */
 -   if (tmp)
 -  
 -   /* check if greylisting is enabled */
 -   {
 -     gllog(LOGLEVEL_DEBUG, "greylisting: greylisting is not enabled\n");
 -     return 0;
 -   }
 -  
 -   /* basic environment variables needed */
 -   if (!relay_ip || !mail_from || !rcpt_to)
 -   {
 -     gllog(LOGLEVEL_FATAL, "greylisting: one of the following envvars is undefined: TCPREMOTEIP, SMTPMAILFROM, SMTPRCPTTO\n");
 -     return 0;
 -   }
 -  
 -   /* check for BATV ("prvs=X=u@d.t" minimum) */
 -       && mail_from[0] == 'p' && mail_from[1] == 'r' && mail_from[2] == 'v'
 -       && mail_from[3] == 's' && mail_from[4] == '=')
 -   {
 -     /* BATV: prvs=HASH=user@domain.tld */
 -       mail_from = delim + 1;
 -     /* BATV: prvs=user/HASH@domain.tld */
 -     {
 -       userlen = delim - mail_from - 5;
 -       mail_from = atpos - userlen;
 -     }
 -   }
 -  
 -   /* avoid buffer overflows (max. query is ~410 chars long) */
 -   {
 -     gllog(LOGLEVEL_FATAL, "greylisting: buffer overflow protection occurs\n");
 -     return 0;
 -   }
 -  
 -   /* fetch config file path */
 -   if (tmp)
 -     configfile = tmp;
 -  
 -   /* fetch config file content */
 -   gllog(LOGLEVEL_DEBUG, "greylisting: configfile=%s\n", configfile);
 -   if (!config)
 -   else
 -   {
 -     {
 -       if (buf[0] == '#' || buf[0] == ';')
 -         continue;
 -       buf[i] = 0;
 -       {
 -       }
 -       {
 -       }
 -       {
 -       }
 -       {
 -       }
 -     }
 -   }
 -  
 -   /* environment variables */
 -   if (tmp)
 -   {
 -     mysql_host = strdup(tmp);
 -   }
 -  
 -   if (tmp)
 -  
 -   if (tmp)
 -   {
 -     mysql_user = strdup(tmp);
 -   }
 -  
 -   if (tmp)
 -   {
 -     mysql_pass = strdup(tmp);
 -   }
 -  
 -   if (tmp)
 -   {
 -     mysql_db = strdup(tmp);
 -   }
 -  
 -   if (tmp)
 -  
 -   if (tmp)
 -  
 -   if (tmp)
 -  
 -   /* logging */
 -   gllog(LOGLEVEL_DEBUG, "greylisting: mysql: host=%s, port=%d, user=%s, pass=******\n", mysql_host, mysql_port, mysql_user);
 -   gllog(LOGLEVEL_DEBUG, "greylisting: block_expire=%d, record_expire=%d, record_expire_good=%d\n", block_expire, record_expire, record_expire_good);
 -   return 1;
 - }
 -  
 - void cleanup()
 - {
 - }
 -  
 - int mysql_query_wrapper(MYSQL *mysql, char *query)
 - {
 -   int result;
 -  
 -   result = mysql_query(mysql, query);
 -   gllog(LOGLEVEL_DEBUG, "greylisting: mysql: %s - ret=%d\n", query, result);
 -   return result;
 - }
 -  
 - /* check if relay_ip or rcpt_to is white-/blacklisted */
 - int check_listed(MYSQL *mysql)
 - {
 -   MYSQL_RES *res;
 -   MYSQL_ROW row;
 -   char query[SQLCMDSIZE];
 -   int found = RET_NOTFOUND;
 -   char *rcpt_to_esc = NULL;
 -   char *domain_esc = NULL;
 -   char *domain = NULL;
 -  
 -   /* fallback to full rcpt_to if there's no domain */
 -   domain = (domain) ? domain + 1 : rcpt_to;
 -   sprintf(query, "SET @uipaddr = inet_aton('%s'), @udomain = '%s', @urcpt_to = '%s'; ", relay_ip, domain_esc, rcpt_to_esc);
 -   if (mysql_query_wrapper(mysql, query))
 -   {
 -     gllog(LOGLEVEL_ERROR, "greylisting: mysql: %s\n", mysql_error(mysql));
 -     return 0;
 -   }
 -  
 - #if MYSQL_VERSION_ID >= 50003
 -     "SELECT `id`, `block_expires` >= UTC_TIMESTAMP(), `block_expires` < UTC_TIMESTAMP() "
 -     "FROM `greylisting_lists` "
 -     "WHERE `record_expires` > UTC_TIMESTAMP() "
 -     "AND ( "
 -       "( "
 -         "`rcpt_to` IS NULL "
 -         "AND `ipaddr_start` <= @uipaddr "
 -         "AND @uipaddr <= `ipaddr_end` "
 -       ") OR ( "
 -         "`ipaddr` IS NULL "
 -         "AND ( "
 -           "`rcpt_to` = @udomain "
 -           "OR `rcpt_to` = @urcpt_to "
 -         ") "
 -       ") "
 -     ") "
 -     "ORDER BY (`ipaddr_end` - `ipaddr_start`) ASC "
 -     "LIMIT 1");
 - #else
 -     "SELECT `id`, `block_expires` >= UTC_TIMESTAMP(), `block_expires` < UTC_TIMESTAMP() "
 -     "FROM `greylisting_lists` "
 -     "WHERE `record_expires` > UTC_TIMESTAMP() "
 -     "AND ( "
 -       "( "
 -         "`rcpt_to` IS NULL "
 -         "AND (@base := IF(INSTR(`ipaddr`, '.'), 32, 128)) "
 -         "AND IF( "
 -           "INSTR(`ipaddr`, '/'), "
 -           "(@ipaddr_start := inet_aton(substring_index(`ipaddr`, '/', 1))) "
 -           "AND (@ipaddr_count := POW(2, @base - substring_index(`ipaddr`, '/', -1))) "
 -           "AND (@ipaddr_end := @ipaddr_start + @ipaddr_count - 1), "
 -           "(@ipaddr_start := inet_aton(`ipaddr`)) "
 -           "AND (@ipaddr_end := @ipaddr_start) "
 -         ") "
 -         "AND @ipaddr_start <= @uipaddr "
 -         "AND @uipaddr <= @ipaddr_end "
 -       ") OR ( "
 -         "`ipaddr` IS NULL "
 -         "AND ( "
 -           "`rcpt_to` = @udomain "
 -           "OR `rcpt_to` = @urcpt_to "
 -         ") "
 -       ") "
 -     ") "
 -     "ORDER BY (@ipaddr_end - @ipaddr_start) ASC "
 -     "LIMIT 1");
 - #endif
 -  
 -   if (mysql_query_wrapper(mysql, query) ||
 -       !(res = mysql_store_result(mysql)))
 -   {
 -     gllog(LOGLEVEL_ERROR, "greylisting: mysql: %s\n", mysql_error(mysql));
 -     return 0;
 -   }
 -  
 -   if ((row = mysql_fetch_row(res)))
 -   {
 -     {
 -       found = RET_REJECT;
 -       gllog(LOGLEVEL_INFO, "greylisting: %s/%s is blacklisted (id=%s) - rejecting\n", relay_ip, domain, row[0]);
 -     }
 -     {
 -       found = RET_ACCEPT;
 -       gllog(LOGLEVEL_INFO, "greylisting: %s/%s is whitelisted (id=%s) - accepting\n", relay_ip, domain, row[0]);
 -     }
 -   }
 -  
 -   mysql_free_result(res);
 -   return found;
 - }
 -  
 - int check_greylisted(MYSQL *mysql)
 - {
 -   MYSQL_RES *res;
 -   MYSQL_ROW row;
 -   char query[SQLCMDSIZE];
 -   char *mail_from_esc = NULL;
 -   char *rcpt_to_esc   = NULL;
 -   char *relay_ip_sub  = NULL;
 -   char *ptr;
 -   char ipdelimeter;
 -   int ret = RET_NOTFOUND;
 -  
 -  
 -   /*
 -    * 0 ... query matches anything in the same /24 subnet
 -    * 1 ... query does an exact ip match
 -    */
 -   if (0)
 -   {
 -       "SELECT `id`, `block_expires` < UTC_TIMESTAMP() "
 -       "FROM `greylisting_data` "
 -       "WHERE `record_expires` > UTC_TIMESTAMP() "
 -         "AND `relay_ip` = '%s' "
 -         "AND `mail_from` = '%s' "
 -         "AND `rcpt_to` = '%s' "
 -       "LIMIT 1",
 -       relay_ip,
 -       mail_from_esc,
 -       rcpt_to_esc);
 -   }
 -   else
 -   {
 -     /* strip off the last octet */
 -     ipdelimeter = '.';
 -       ipdelimeter = ':';
 -     relay_ip_sub = strdup(relay_ip);
 -     if (ptr)
 -       *ptr = '\0';
 -  
 -       "SELECT `id`, `block_expires` < UTC_TIMESTAMP() "
 -       "FROM `greylisting_data` "
 -       "WHERE `record_expires` > UTC_TIMESTAMP() "
 -         "AND `relay_ip` LIKE '%s%c%%' "
 -         "AND `mail_from` = '%s' "
 -         "AND `rcpt_to` = '%s' "
 -       "LIMIT 1",
 -       relay_ip_sub,
 -       ipdelimeter,
 -       mail_from_esc,
 -       rcpt_to_esc);
 -   }
 -  
 -   if (mysql_query_wrapper(mysql, query) ||
 -       !(res = mysql_store_result(mysql)))
 -   {
 -     gllog(LOGLEVEL_ERROR, "greylisting: mysql: %s\n", mysql_error(mysql));
 -     return RET_NOTFOUND;
 -   }
 -  
 -   if ((row = mysql_fetch_row(res)))
 -   {
 -     {
 -         "UPDATE `greylisting_data` "
 -         "SET `record_expires` = UTC_TIMESTAMP() + INTERVAL %u DAY, `passed_count` = `passed_count` + 1 "
 -         "WHERE `id` = '%s'",
 -         record_expire_good, row[0]);
 -       ret = RET_ACCEPT;
 -       gllog(LOGLEVEL_INFO, "greylisting: %s (%s -> %s) exists (id=%s) - accepting\n", relay_ip, mail_from, rcpt_to, row[0]);
 -     }
 -     else
 -     {
 -         "UPDATE `greylisting_data` "
 -         "SET `blocked_count` = `blocked_count` + 1 "
 -         "WHERE `id` = '%s'",
 -         row[0]);
 -       ret = RET_TEMPREJECT;
 -       gllog(LOGLEVEL_INFO, "greylisting: %s (%s -> %s) is blocked (id=%s) - temp. rejecting\n", relay_ip, mail_from, rcpt_to, row[0]);
 -     }
 -   }
 -   else
 -   {
 -       "INSERT INTO `greylisting_data` "
 -       "VALUES (0, '%s', '%s', '%s', UTC_TIMESTAMP() + INTERVAL %u MINUTE, UTC_TIMESTAMP() + INTERVAL %u MINUTE, 1, 0, 0, UTC_TIMESTAMP(), UTC_TIMESTAMP())",
 -       relay_ip, mail_from_esc, rcpt_to_esc, block_expire, record_expire);
 -     ret = RET_TEMPREJECT;
 -     gllog(LOGLEVEL_INFO, "greylisting: %s (%s -> %s) doesn't exist. - temp. rejecting\n", relay_ip, mail_from, rcpt_to);
 -   }
 -   mysql_free_result(res);
 -  
 -   if (mysql_query_wrapper(mysql, query))
 -   {
 -     gllog(LOGLEVEL_ERROR, "greylisting: mysql: %s\n", mysql_error(mysql));
 -     return RET_NOTFOUND;
 -   }
 -  
 -   return ret;
 - }
 -  
 - int main()
 - {
 -   int ret = 1;
 -   int greylisted = 0;
 -   MYSQL *mysql = NULL;
 -  
 -   /* load config */
 -   if (ret && !load_config())
 -     ret = 0;
 -  
 -   /* connect to mysql */
 -   if (ret)
 -   {
 -     mysql_library_init(-1, NULL, NULL);
 -     mysql = mysql_init(NULL);
 -     if (!mysql_real_connect(mysql, mysql_host, mysql_user, mysql_pass, mysql_db, mysql_port, NULL, 0))
 -     {
 -       gllog(LOGLEVEL_FATAL, "greylisting: mysql: %s\n", mysql_error(mysql));
 -       ret = 0;
 -     }
 -   }
 -  
 -   /* greylisting checks */
 -   if (ret && !greylisted)
 -   {
 -     greylisted = check_listed(mysql);
 -     if (greylisted == RET_NOTFOUND)
 -       greylisted = check_greylisted(mysql);
 -   }
 -  
 -   /* print smtp error code */
 -   if (ret)
 -   {
 -     switch(greylisted)
 -     {
 -       case RET_REJECT:
 -         break;
 -       case RET_TEMPREJECT:
 -         break;
 -     }
 -   }
 -  
 -   /* cleanup stuff */
 -   gllog(LOGLEVEL_DEBUG, "greylisting: exiting\n");
 -   if (mysql)
 -     mysql_close(mysql);
 -   mysql_library_end();
 -   cleanup();
 -   return !ret;
 - }
 -  
 -