1 #include "policyd.h"
2
3
4 /*
5 *
6 *
7 * Policy Daemon
8 *
9 * policy daemon is used in conjuction with postfix to combat spam.
10 *
11 * Copyright (C) 2004 Cami Sardinha (cami@mweb.co.za)
12 *
13 *
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation; either version 2 of the License, or (at your
17 * option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 *
29 *
30 */
31
32 /*
33 * function: w_mysql_query
34 * purpose: wrapper function for mysql_query()
35 * return: 0=success, -1=failure
36 */
37 int
w_mysql_query(unsigned int volatile fd,const char * function)38 w_mysql_query(unsigned int volatile fd, const char *function)
39 {
40
41 /* cut down latency, do not do any queries for 30 seconds if 3 failures occur */
42 if(mysql_failure_count >= 3)
43 {
44
45 /* has 30 seconds passed? */
46 if(timenow >= (last_mysql_failure+30))
47 { /* timer has expired */
48 mysql_failure_count=0;
49 mysql_close(mysql);
50 mysql = db_connect(MYSQLDBASE); /* reconnect to database */
51 } else { /* timer has not expired */
52
53 return (-1); /* so we fail the query */
54 }
55 }
56
57 /* catch query timeouts */
58 if (sigsetjmp (sjmp, 1))
59 {
60 if(DEBUG > 2)
61 logmessage("%s()/mysql_query(): mysql_query timeout number: %d\n",
62 function, mysql_failure_count);
63
64 mysql_failure_count++;
65 last_mysql_failure=timenow;
66
67 /* reset the timer */
68 alarm (0);
69
70 /* unset the signal handler */
71 signal (SIGALRM, SIG_DFL);
72
73 mysql_array[fd][0] = -1;
74 return (-1); /* failure */
75 }
76
77 /* set the SIGALRM handler */
78 signal (SIGALRM, (void *) sigalrm_handler);
79
80 /* mysql timeout */
81 alarm(mysql_timeout);
82
83 /* fire off query */
84 if (mysql_query(mysql, mysqlquery_array[fd]) != 0)
85 {
86 /* reset the timer */
87 alarm (0);
88
89 /* unset the signal handler */
90 signal (SIGALRM, SIG_DFL);
91
92 return (-1);
93 }
94
95 /* reset the timer */
96 alarm (0);
97
98 /* unset the signal handler */
99 signal (SIGALRM, SIG_DFL);
100
101 /* ensure that we only go into bypass mode after 3 consecutive failures */
102 mysql_failure_count=0;
103
104 return (0); /* success */
105 }
106
107
108
109
110 /*
111 * function: db_doquery
112 * purpose: do mysql queries
113 * return: 0=success, -1=failure
114 */
115 int
db_doquery(unsigned int volatile fd)116 db_doquery(unsigned int volatile fd)
117 {
118
119 if(DEBUG > 1)
120 logmessage("DEBUG: fd: %d db_doquery(): %s\n", fd, mysqlquery_array[fd]);
121
122 /* fire off query */
123 if (w_mysql_query(fd, "db_doquery") != 0)
124 goto err;
125
126 /* select() result */
127 if (mysql_field_count(mysql) > 0)
128 {
129 int num_fields, i=0;
130 MYSQL_RES *res;
131 MYSQL_ROW row, end_row;
132
133 if (!(res = mysql_store_result(mysql)))
134 goto err;
135
136 num_fields = mysql_num_fields(res);
137 while ((row = mysql_fetch_row(res)))
138 {
139 for (end_row=row+num_fields;row<end_row;++row,i++)
140 {
141 if(DEBUG > 1)
142 logmessage("DEBUG: fd: %d row: %d data: %d (recieved)\n", fd, i, atol((char *)*row));
143
144 mysql_array[fd][i]=atol(row ? (char*)*row : "0"); /* return seconds from epoch */
145
146 if(DEBUG > 1)
147 logmessage("DEBUG: fd: %d row: %d data: %d (extracted)\n", fd, i, mysql_array[fd][i]);
148 }
149 }
150 mysql_free_result(res);
151 } else {
152
153 /* update() result */
154 mysql_array[fd][0]=mysql_affected_rows(mysql);
155 }
156
157
158 return (0); /* success */
159
160 err:
161
162 if(DEBUG > 1)
163 logmessage("db_doquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
164
165 mysql_array[fd][0] = -1;
166 return (-1); /* failure */
167 }
168
169
170
171
172 /*
173 * function: db_charquery
174 * purpose: do mysql query
175 * return: 0=success, -1=failure
176 * return: multi-dimensional char array
177 */
178 int
db_charquery(unsigned int volatile fd)179 db_charquery(unsigned int volatile fd)
180 {
181
182 if(DEBUG > 1)
183 logmessage("DEBUG: fd: %d db_charquery(): %s\n", fd, mysqlquery_array[fd]);
184
185 /* fire off query */
186 if (w_mysql_query(fd, "db_charquery") != 0)
187 goto err;
188
189 /* get rid of stale data */
190 for(t=0 ; t<20 ; t++)
191 memset(mysqlchar_array[fd][t], 0x00, 64);
192
193 /* select() result */
194 if (mysql_field_count(mysql) > 0)
195 {
196 int num_fields, i=0;
197 MYSQL_RES *res;
198 MYSQL_ROW row, end_row;
199
200 if (!(res = mysql_store_result(mysql)))
201 goto err;
202
203 num_fields = mysql_num_fields(res);
204 while ((row = mysql_fetch_row(res)))
205 {
206 for (end_row=row+num_fields;row<end_row;++row)
207 {
208 if(DEBUG > 1)
209 logmessage("DEBUG: fd: %d row: %d data: %s (recieved)\n", fd, i, (char *)*row);
210
211 strncpy(mysqlchar_array[fd][i], (char*)*row, 64);
212
213 if(DEBUG > 1)
214 logmessage("DEBUG: fd: %d row: %d data: %s (extracted)\n", fd, i, mysqlchar_array[fd][i]);
215 i++;
216 }
217 }
218 mysql_free_result(res);
219 }
220
221 return (0); /* success */
222
223 err:
224
225 if(DEBUG > 1)
226 logmessage("db_charquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
227
228 mysql_array[fd][0] = -1;
229 return (-1); /* failure */
230 }
231
232
233
234
235 /*
236 * function: db_optquery
237 * purpose: do mysql opt in/out queries
238 * return: 0=success, -1=failure
239 */
240 int
db_optquery(unsigned int volatile fd)241 db_optquery(unsigned int volatile fd)
242 {
243
244 if(DEBUG > 1)
245 logmessage("DEBUG: fd: %d, db_optquery(): %s\n", fd, mysqlquery_array[fd]);
246
247 /* fire off query */
248 if (w_mysql_query(fd, "db_optquery") != 0)
249 goto err;
250
251 /* select() result */
252 if (mysql_field_count(mysql) > 0)
253 {
254 int num_fields, i=0;
255 MYSQL_RES *res;
256 MYSQL_ROW row, end_row;
257
258 if (!(res = mysql_store_result(mysql)))
259 goto err;
260
261 num_fields = mysql_num_fields(res);
262 while ((row = mysql_fetch_row(res)))
263 {
264 for (end_row=row+num_fields;row<end_row;++row,i++)
265 {
266 if(DEBUG > 1)
267 logmessage("DEBUG: fd: %d row: %d data: %d (recieved)\n", fd, i, atoi((char *)*row));
268
269 mysql_optarray[fd][0]=atol(row ? (char*)*row : "0");
270
271 if(DEBUG > 1)
272 logmessage("DEBUG: fd: %d row: %d data: %d (extracted)\n", fd, i, mysql_optarray[fd][0]);
273 }
274 }
275 mysql_free_result(res);
276 }
277
278 return (0); /* success */
279
280 err:
281
282 if(DEBUG > 1)
283 logmessage("db_optquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
284
285 mysql_array[fd][0] = -1;
286 return (-1); /* failure */
287 }
288
289
290
291
292 /*
293 * function: db_deletequery
294 * purpose: expire triplet in database
295 * return: 0=success, -1=failure
296 */
297 int
db_deletequery(unsigned int volatile fd)298 db_deletequery(unsigned int volatile fd)
299 {
300 count=0;
301
302 start:
303
304 if(DEBUG > 1)
305 logmessage("DEBUG: fd: %d, db_deletequery(): %s\n", fd, mysqlquery_array[fd]);
306
307 /* fire off query */
308 if (w_mysql_query(fd, "db_deletequery") != 0)
309 goto err;
310
311 /* select() result */
312 if (mysql_field_count(mysql) > 0)
313 {
314 int num_fields, i=0;
315 MYSQL_RES *res;
316 MYSQL_ROW row, end_row;
317
318 if (!(res = mysql_store_result(mysql)))
319 goto err;
320
321 num_fields = mysql_num_fields(res);
322 while ((row = mysql_fetch_row(res)))
323 {
324 for (end_row=row+num_fields;row<end_row;++row,i++)
325 {
326 logmessage("%ld\n", atol(row ? (char*)*row : "0"));
327 }
328 }
329 mysql_free_result(res);
330 } else {
331 /* MySQL does not handle extremely large deletes very well */
332 if((int)mysql_affected_rows(mysql) == 100000)
333 {
334 count=count+(int)mysql_affected_rows(mysql);
335 goto start;
336 }
337
338 /* didnt reach 100000 deletes */
339 count=count+(int)mysql_affected_rows(mysql);
340
341 }
342
343 logmessage("expired: %d records\n", count);
344
345 return (0); /* success */
346
347 err:
348
349 if(count != 0)
350 logmessage("expired: %d records\n", count);
351
352 if(DEBUG > 1)
353 logmessage("db_deletequery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
354
355 mysql_array[fd][0] = -1;
356 return (-1); /* failure */
357 }
358
359
360
361 /*
362 * function: db_deletequery
363 * purpose: expire triplet in database
364 * return: 0=success, -1=failure
365 */
366 int
db_printquery(unsigned int volatile fd)367 db_printquery(unsigned int volatile fd)
368 {
369 if(DEBUG > 1)
370 logmessage("DEBUG: fd: %d, db_printquery(): %s\n", fd, mysqlquery_array[fd]);
371
372 /* fire off query */
373 if (w_mysql_query(fd, "db_printquery") != 0)
374 goto err;
375
376 /* select() result */
377 if (mysql_field_count(mysql) > 0)
378 {
379 int num_fields;
380 MYSQL_RES *res;
381 MYSQL_ROW row;
382
383 if (!(res = mysql_store_result(mysql)))
384 goto err;
385
386 num_fields = mysql_num_fields(res);
387 while ((row = mysql_fetch_row(res)))
388 {
389 unsigned long *lengths;
390 lengths = mysql_fetch_lengths(res);
391 logmessage("%s\t-> %s", row[0], row[1]);
392 }
393 mysql_free_result(res);
394 }
395
396
397 return (0); /* success */
398
399 err:
400
401 if(DEBUG > 1)
402 logmessage("db_printquery()/mysql_query(): %s -> %s\n", mysql_error(mysql), mysqlquery_array[fd]);
403
404 mysql_array[fd][0] = -1;
405 return (-1); /* failure */
406 }
407
408
409
410 /*
411 * function: db_connect
412 * purpose: open connection to mysql database
413 * return: MySQL structure
414 */
db_connect(const char * dbname)415 MYSQL *db_connect(const char *dbname)
416 {
417 /* initializes mysql structure */
418
419 /*
420 * volatile:
421 *
422 * It's a potential problem, because Sun doesn't guarrantee that automatic
423 * variables are preserved when the saved function's context is restored.
424 *
425 * Leandro Santi
426 */
427 MYSQL * volatile db=mysql_init(NULL);
428 if(!db)
429 {
430 logmessage("mysql_init(): no memory\n");
431 exit(-1);
432 }
433 logmessage("connecting to mysql database: %s\n", MYSQLHOST);
434
435 /* catch connection timeouts */
436 if (sigsetjmp (sjmp, 1))
437 {
438 if(DEBUG > 1)
439 logmessage("db_connect()/mysql_real_connect(): connection timeout\n");
440
441 /* if the connection fails, that counts as a hard failure */
442 mysql_failure_count++;
443 last_mysql_failure=timenow;
444
445 logmessage("NOT connected..\n");
446 return (db); /* failure */
447 }
448
449 /* set the SIGALRM handler */
450 signal (SIGALRM, (void *) sigalrm_handler);
451
452 /* mysql timeout */
453 alarm(mysql_timeout);
454
455 #if defined(MYSQL_VERSION_ID)
456 # if MYSQL_VERSION_ID >= 50003 && MYSQL_VERSION_ID < 50013
457 /* hack to allow 5.0.3 => 5.0.13 to reconnect */
458 db->reconnect = 1;
459 # elif MYSQL_VERSION_ID >= 50013
460 mysql_options(db, MYSQL_OPT_RECONNECT, "1");
461 # endif
462 #endif
463
464 /* fire off query */
465 /* connect to mysql server */
466 if(!mysql_real_connect(db, MYSQLHOST, MYSQLUSER, MYSQLPASS, dbname, MYSQLPORT, NULL, 0))
467 {
468 logmessage("mysql_real_connect(): %s\n", mysql_error(db));
469 mysql_failure_count++;
470 last_mysql_failure=timenow;
471 } else {
472 logmessage("connected..\n");
473 }
474
475 /* reset the timer & unset the signal handler */
476 alarm (0); signal (SIGALRM, SIG_DFL);
477
478 return (db);
479 }
480
481
482
483
484 /*
485 * function: database_probe
486 * purpose: check to see if connection to the database is open
487 * return: 0=success, -20=failure
488 */
489 int
database_probe(unsigned int fd)490 database_probe(unsigned int fd)
491 {
492 if(DEBUG > 1)
493 logmessage("DEBUG: fd: %d database_probe(): mysql_ping\n", fd);
494
495 /* catch query timeouts */
496 if (sigsetjmp (sjmp, 1))
497 {
498 if(DEBUG > 2)
499 logmessage("db_doquery()/mysql_query(): mysql_query timeout -> %s\n", mysqlquery_array[fd]);
500
501 /* if the probe fails, that counts as a hard failure */
502 mysql_failure_count++;
503 last_mysql_failure=timenow;
504
505 return (-20); /* failure */
506 }
507
508 /* set the SIGALRM handler */
509 signal (SIGALRM, (void *) sigalrm_handler);
510
511 /* mysql timeout */
512 alarm (mysql_timeout);
513
514 /* reconnect to the database, only after 120 seconds has passed */
515 if(DEBUG > 1)
516 logmessage("DEBUG: fd: %d database_probe(): reconnecting..\n", fd);
517
518 mysql_close(mysql);
519 mysql = db_connect(MYSQLDBASE);
520
521 /* reset the timer & unset the signal handler */
522 alarm (0);
523 signal (SIGALRM, SIG_DFL);
524
525 return (0);
526 }
527
528 /* EOF */
529