#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 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 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 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 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 */