#include "policyd.h"
/*
*
*
* Policy Daemon
*
* policy daemon is used in conjuction with postfix to combat spam.
*
* Copyright (C) 2004 Cami Sardinha (cami@mweb.co.za)
*
*
* 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 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
*
*
*
*/
/*
* function: w_mysql_query
* purpose: wrapper function for mysql_query()
* return: 0=success, -1=failure
*/
int
w_mysql_query(unsigned int volatile fd, const char *function)
{
/* cut down latency, do not do any queries for 30 seconds if 3 failures occur */
if(mysql_failure_count >= 3)
{
/* has 30 seconds passed? */
if(timenow >= (last_mysql_failure+30))
{ /* timer has expired */
mysql_failure_count=0;
mysql_close(mysql);
mysql = db_connect(MYSQLDBASE); /* reconnect to database */
} else { /* timer has not expired */
return (-1); /* so we fail the query */
}
}
/* catch query timeouts */
if (sigsetjmp (sjmp, 1))
{
if(DEBUG > 2)
logmessage("%s()/mysql_query(): mysql_query timeout number: %d\n",
function, mysql_failure_count);
mysql_failure_count++;
last_mysql_failure=timenow;
/* reset the timer */
alarm (0);
/* unset the signal handler */
signal (SIGALRM, SIG_DFL);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/* set the SIGALRM handler */
signal (SIGALRM, (void *) sigalrm_handler);
/* mysql timeout */
alarm(mysql_timeout);
/* fire off query */
if (mysql_query(mysql, mysqlquery_array[fd]) != 0)
{
/* reset the timer */
alarm (0);
/* unset the signal handler */
signal (SIGALRM, SIG_DFL);
return (-1);
}
/* reset the timer */
alarm (0);
/* unset the signal handler */
signal (SIGALRM, SIG_DFL);
/* ensure that we only go into bypass mode after 3 consecutive failures */
mysql_failure_count=0;
return (0); /* success */
}
/*
* function: db_doquery
* purpose: do mysql queries
* return: 0=success, -1=failure
*/
int
db_doquery(unsigned int volatile fd)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d db_doquery(): %s\n", fd, mysqlquery_array[fd]);
/* fire off query */
if (w_mysql_query(fd, "db_doquery") != 0)
goto err;
/* select() result */
if (mysql_field_count(mysql) > 0)
{
int num_fields, i=0;
MYSQL_RES *res;
MYSQL_ROW row, end_row;
if (!(res = mysql_store_result(mysql)))
goto err;
num_fields = mysql_num_fields(res);
while ((row = mysql_fetch_row(res)))
{
for (end_row=row+num_fields;row<end_row;++row,i++)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %d (recieved)\n", fd, i, atol((char *)*row));
mysql_array[fd][i]=atol(row ? (char*)*row : "0"); /* return seconds from epoch */
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %d (extracted)\n", fd, i, mysql_array[fd][i]);
}
}
mysql_free_result(res);
} else {
/* update() result */
mysql_array[fd][0]=mysql_affected_rows(mysql);
}
return (0); /* success */
err:
if(DEBUG > 1)
logmessage("db_doquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/*
* function: db_charquery
* purpose: do mysql query
* return: 0=success, -1=failure
* return: multi-dimensional char array
*/
int
db_charquery(unsigned int volatile fd)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d db_charquery(): %s\n", fd, mysqlquery_array[fd]);
/* fire off query */
if (w_mysql_query(fd, "db_charquery") != 0)
goto err;
/* get rid of stale data */
for(t=0 ; t<20 ; t++)
memset(mysqlchar_array[fd][t], 0x00, 64);
/* select() result */
if (mysql_field_count(mysql) > 0)
{
int num_fields, i=0;
MYSQL_RES *res;
MYSQL_ROW row, end_row;
if (!(res = mysql_store_result(mysql)))
goto err;
num_fields = mysql_num_fields(res);
while ((row = mysql_fetch_row(res)))
{
for (end_row=row+num_fields;row<end_row;++row)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %s (recieved)\n", fd, i, (char *)*row);
strncpy(mysqlchar_array[fd][i], (char*)*row, 64);
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %s (extracted)\n", fd, i, mysqlchar_array[fd][i]);
i++;
}
}
mysql_free_result(res);
}
return (0); /* success */
err:
if(DEBUG > 1)
logmessage("db_charquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/*
* function: db_optquery
* purpose: do mysql opt in/out queries
* return: 0=success, -1=failure
*/
int
db_optquery(unsigned int volatile fd)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d, db_optquery(): %s\n", fd, mysqlquery_array[fd]);
/* fire off query */
if (w_mysql_query(fd, "db_optquery") != 0)
goto err;
/* select() result */
if (mysql_field_count(mysql) > 0)
{
int num_fields, i=0;
MYSQL_RES *res;
MYSQL_ROW row, end_row;
if (!(res = mysql_store_result(mysql)))
goto err;
num_fields = mysql_num_fields(res);
while ((row = mysql_fetch_row(res)))
{
for (end_row=row+num_fields;row<end_row;++row,i++)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %d (recieved)\n", fd, i, atoi((char *)*row));
mysql_optarray[fd][0]=atol(row ? (char*)*row : "0");
if(DEBUG > 1)
logmessage("DEBUG: fd: %d row: %d data: %d (extracted)\n", fd, i, mysql_optarray[fd][0]);
}
}
mysql_free_result(res);
}
return (0); /* success */
err:
if(DEBUG > 1)
logmessage("db_optquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/*
* function: db_deletequery
* purpose: expire triplet in database
* return: 0=success, -1=failure
*/
int
db_deletequery(unsigned int volatile fd)
{
count=0;
start:
if(DEBUG > 1)
logmessage("DEBUG: fd: %d, db_deletequery(): %s\n", fd, mysqlquery_array[fd]);
/* fire off query */
if (w_mysql_query(fd, "db_deletequery") != 0)
goto err;
/* select() result */
if (mysql_field_count(mysql) > 0)
{
int num_fields, i=0;
MYSQL_RES *res;
MYSQL_ROW row, end_row;
if (!(res = mysql_store_result(mysql)))
goto err;
num_fields = mysql_num_fields(res);
while ((row = mysql_fetch_row(res)))
{
for (end_row=row+num_fields;row<end_row;++row,i++)
{
logmessage("%ld\n", atol(row ? (char*)*row : "0"));
}
}
mysql_free_result(res);
} else {
/* MySQL does not handle extremely large deletes very well */
if((int)mysql_affected_rows(mysql) == 100000)
{
count=count+(int)mysql_affected_rows(mysql);
goto start;
}
/* didnt reach 100000 deletes */
count=count+(int)mysql_affected_rows(mysql);
}
logmessage("expired: %d records\n", count);
return (0); /* success */
err:
if(count != 0)
logmessage("expired: %d records\n", count);
if(DEBUG > 1)
logmessage("db_deletequery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/*
* function: db_deletequery
* purpose: expire triplet in database
* return: 0=success, -1=failure
*/
int
db_printquery(unsigned int volatile fd)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d, db_printquery(): %s\n", fd, mysqlquery_array[fd]);
/* fire off query */
if (w_mysql_query(fd, "db_printquery") != 0)
goto err;
/* select() result */
if (mysql_field_count(mysql) > 0)
{
int num_fields;
MYSQL_RES *res;
MYSQL_ROW row;
if (!(res = mysql_store_result(mysql)))
goto err;
num_fields = mysql_num_fields(res);
while ((row = mysql_fetch_row(res)))
{
unsigned long *lengths;
lengths = mysql_fetch_lengths(res);
logmessage("%s\t-> %s", row[0], row[1]);
}
mysql_free_result(res);
}
return (0); /* success */
err:
if(DEBUG > 1)
logmessage("db_printquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
mysql_array[fd][0] = -1;
return (-1); /* failure */
}
/*
* function: db_connect
* purpose: open connection to mysql database
* return: MySQL structure
*/
MYSQL *db_connect(const char *dbname)
{
/* initializes mysql structure */
/*
* volatile:
*
* It's a potential problem, because Sun doesn't guarrantee that automatic
* variables are preserved when the saved function's context is restored.
*
* Leandro Santi
*/
MYSQL * volatile db=mysql_init(NULL);
if(!db)
{
logmessage("mysql_init(): no memory\n");
exit(-1);
}
logmessage("connecting to mysql database: %s\n", MYSQLHOST);
/* catch connection timeouts */
if (sigsetjmp (sjmp, 1))
{
if(DEBUG > 1)
logmessage("db_connect()/mysql_real_connect(): connection timeout\n");
/* if the connection fails, that counts as a hard failure */
mysql_failure_count++;
last_mysql_failure=timenow;
logmessage("NOT connected..\n");
return (db); /* failure */
}
/* set the SIGALRM handler */
signal (SIGALRM, (void *) sigalrm_handler);
/* mysql timeout */
alarm(mysql_timeout);
#if defined(MYSQL_VERSION_ID)
# if MYSQL_VERSION_ID >= 50003 && MYSQL_VERSION_ID < 50013
/* hack to allow 5.0.3 => 5.0.13 to reconnect */
db->reconnect = 1;
# elif MYSQL_VERSION_ID >= 50013
mysql_options(db, MYSQL_OPT_RECONNECT, "1");
# endif
#endif
/* fire off query */
/* connect to mysql server */
if(!mysql_real_connect(db, MYSQLHOST, MYSQLUSER, MYSQLPASS, dbname, MYSQLPORT, NULL, 0))
{
logmessage("mysql_real_connect(): %s\n", mysql_error(db));
mysql_failure_count++;
last_mysql_failure=timenow;
} else {
logmessage("connected..\n");
}
/* reset the timer & unset the signal handler */
alarm (0); signal (SIGALRM, SIG_DFL);
return (db);
}
/*
* function: database_probe
* purpose: check to see if connection to the database is open
* return: 0=success, -20=failure
*/
int
database_probe(unsigned int fd)
{
if(DEBUG > 1)
logmessage("DEBUG: fd: %d database_probe(): mysql_ping\n", fd);
/* catch query timeouts */
if (sigsetjmp (sjmp, 1))
{
if(DEBUG > 2)
logmessage("db_doquery()/mysql_query(): mysql_query timeout -> %s\n", mysqlquery_array[fd]);
/* if the probe fails, that counts as a hard failure */
mysql_failure_count++;
last_mysql_failure=timenow;
return (-20); /* failure */
}
/* set the SIGALRM handler */
signal (SIGALRM, (void *) sigalrm_handler);
/* mysql timeout */
alarm (mysql_timeout);
/* reconnect to the database, only after 120 seconds has passed */
if(DEBUG > 1)
logmessage("DEBUG: fd: %d database_probe(): reconnecting..\n", fd);
mysql_close(mysql);
mysql = db_connect(MYSQLDBASE);
/* reset the timer & unset the signal handler */
alarm (0);
signal (SIGALRM, SIG_DFL);
return (0);
}
/* EOF */
syntax highlighted by Code2HTML, v. 0.9.1