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;
- }
-
-