unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
tkldb.c (21482B)
1 /* 2 * Stores active *-Lines (G-Lines etc) inside a .db file for persistency 3 * (C) Copyright 2019 Gottem and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #include "unrealircd.h" 21 22 ModuleHeader MOD_HEADER = { 23 "tkldb", 24 "1.10", 25 "Stores active TKL entries (*-Lines) persistently/across IRCd restarts", 26 "UnrealIRCd Team", 27 "unrealircd-6", 28 }; 29 30 #define TKLDB_MAGIC 0x10101010 31 /* Database version */ 32 #define TKLDB_VERSION 4999 33 /* Save tkls to file every <this> seconds */ 34 #define TKLDB_SAVE_EVERY 300 35 /* The very first save after boot, apply this delta, this 36 * so we don't coincide with other (potentially) expensive 37 * I/O events like saving channeldb. 38 */ 39 #define TKLDB_SAVE_EVERY_DELTA +15 40 41 // #undef BENCHMARK 42 /* Benchmark results (2GHz Xeon Skylake, compiled with -O2, Linux): 43 * 100,000 zlines: 44 * - load db: 510 ms 45 * - save db: 72 ms 46 * Thus, saving does not take much time and can be done by a timer 47 * which executes every 5 minutes. 48 * Of course, exact figures will depend on the machine. 49 */ 50 51 #define FreeTKLRead() \ 52 do { \ 53 /* Some of these might be NULL */ \ 54 if (tkl) \ 55 free_tkl(tkl); \ 56 } while(0) 57 58 #define WARN_WRITE_ERROR(fname) \ 59 do { \ 60 unreal_log(ULOG_ERROR, "tkldb", "TKLDB_FILE_WRITE_ERROR", NULL, \ 61 "[tkldb] Error writing to temporary database file $filename: $system_error", \ 62 log_data_string("filename", fname), \ 63 log_data_string("system_error", unrealdb_get_error_string())); \ 64 } while(0) 65 66 #define R_SAFE(x) \ 67 do { \ 68 if (!(x)) { \ 69 config_warn("[tkldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \ 70 unrealdb_close(db); \ 71 FreeTKLRead(); \ 72 return 0; \ 73 } \ 74 } while(0) 75 76 #define W_SAFE(x) \ 77 do { \ 78 if (!(x)) { \ 79 WARN_WRITE_ERROR(tmpfname); \ 80 unrealdb_close(db); \ 81 return 0; \ 82 } \ 83 } while(0) 84 85 #define IsMDErr(x, y, z) \ 86 do { \ 87 if (!(x)) { \ 88 config_error("A critical error occurred when registering ModData for %s: %s", MOD_HEADER.name, ModuleGetErrorStr((z)->handle)); \ 89 return MOD_FAILED; \ 90 } \ 91 } while(0) 92 93 /* Structs */ 94 struct cfgstruct { 95 char *database; 96 char *db_secret; 97 }; 98 99 /* Forward declarations */ 100 void tkldb_moddata_free(ModData *md); 101 void setcfg(struct cfgstruct *cfg); 102 void freecfg(struct cfgstruct *cfg); 103 int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 104 int tkldb_config_posttest(int *errs); 105 int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type); 106 EVENT(write_tkldb_evt); 107 int write_tkldb(void); 108 int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl); 109 int read_tkldb(void); 110 111 /* Globals variables */ 112 const uint32_t tkldb_version = TKLDB_VERSION; 113 static struct cfgstruct cfg; 114 static struct cfgstruct test; 115 116 static long tkldb_next_event = 0; 117 118 MOD_TEST() 119 { 120 memset(&cfg, 0, sizeof(cfg)); 121 memset(&test, 0, sizeof(test)); 122 setcfg(&test); 123 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_config_test); 124 HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, tkldb_config_posttest); 125 return MOD_SUCCESS; 126 } 127 128 MOD_INIT() 129 { 130 MARK_AS_OFFICIAL_MODULE(modinfo); 131 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, -9999); 132 133 LoadPersistentLong(modinfo, tkldb_next_event); 134 135 setcfg(&cfg); 136 137 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_config_run); 138 return MOD_SUCCESS; 139 } 140 141 MOD_LOAD() 142 { 143 if (!tkldb_next_event) 144 { 145 /* If this is the first time that our module is loaded, then 146 * read the TKL DB and add all *-Lines. 147 */ 148 if (!read_tkldb()) 149 { 150 char fname[512]; 151 snprintf(fname, sizeof(fname), "%s.corrupt", cfg.database); 152 if (rename(cfg.database, fname) == 0) 153 config_warn("[tkldb] Existing database renamed to %s and starting a new one...", fname); 154 else 155 config_warn("[tkldb] Failed to rename database from %s to %s: %s", cfg.database, fname, strerror(errno)); 156 } 157 tkldb_next_event = TStime() + TKLDB_SAVE_EVERY + TKLDB_SAVE_EVERY_DELTA; 158 } 159 EventAdd(modinfo->handle, "tkldb_write_tkldb", write_tkldb_evt, NULL, 1000, 0); 160 return MOD_SUCCESS; 161 } 162 163 MOD_UNLOAD() 164 { 165 if (loop.terminating) 166 write_tkldb(); 167 freecfg(&test); 168 freecfg(&cfg); 169 SavePersistentLong(modinfo, tkldb_next_event); 170 return MOD_SUCCESS; 171 } 172 173 void tkldb_moddata_free(ModData *md) 174 { 175 if (md->i) 176 md->i = 0; 177 } 178 179 void setcfg(struct cfgstruct *cfg) 180 { 181 // Default: data/tkl.db 182 safe_strdup(cfg->database, "tkl.db"); 183 convert_to_absolute_path(&cfg->database, PERMDATADIR); 184 } 185 186 void freecfg(struct cfgstruct *cfg) 187 { 188 safe_free(cfg->database); 189 safe_free(cfg->db_secret); 190 } 191 192 int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 193 { 194 int errors = 0; 195 ConfigEntry *cep; 196 197 // We are only interested in set::tkldb::database 198 if (type != CONFIG_SET) 199 return 0; 200 201 if (!ce || strcmp(ce->name, "tkldb")) 202 return 0; 203 204 for (cep = ce->items; cep; cep = cep->next) 205 { 206 if (!cep->value) 207 { 208 config_error("%s:%i: blank set::tkldb::%s without value", cep->file->filename, cep->line_number, cep->name); 209 errors++; 210 } else 211 if (!strcmp(cep->name, "database")) 212 { 213 convert_to_absolute_path(&cep->value, PERMDATADIR); 214 safe_strdup(test.database, cep->value); 215 } else 216 if (!strcmp(cep->name, "db-secret")) 217 { 218 const char *err; 219 if ((err = unrealdb_test_secret(cep->value))) 220 { 221 config_error("%s:%i: set::tkldb::db-secret: %s", cep->file->filename, cep->line_number, err); 222 errors++; 223 continue; 224 } 225 safe_strdup(test.db_secret, cep->value); 226 } else 227 { 228 config_error("%s:%i: unknown directive set::tkldb::%s", cep->file->filename, cep->line_number, cep->name); 229 errors++; 230 } 231 } 232 233 *errs = errors; 234 return errors ? -1 : 1; 235 } 236 237 int tkldb_config_posttest(int *errs) 238 { 239 int errors = 0; 240 char *errstr; 241 242 if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret)))) 243 { 244 config_error("[tkldb] %s", errstr); 245 errors++; 246 } 247 248 *errs = errors; 249 return errors ? -1 : 1; 250 } 251 252 int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type) 253 { 254 ConfigEntry *cep; 255 256 // We are only interested in set::tkldb::database 257 if (type != CONFIG_SET) 258 return 0; 259 260 if (!ce || strcmp(ce->name, "tkldb")) 261 return 0; 262 263 for (cep = ce->items; cep; cep = cep->next) 264 { 265 if (!strcmp(cep->name, "database")) 266 safe_strdup(cfg.database, cep->value); 267 else if (!strcmp(cep->name, "db-secret")) 268 safe_strdup(cfg.db_secret, cep->value); 269 } 270 return 1; 271 } 272 273 EVENT(write_tkldb_evt) 274 { 275 if (tkldb_next_event > TStime()) 276 return; 277 tkldb_next_event = TStime() + TKLDB_SAVE_EVERY; 278 write_tkldb(); 279 } 280 281 int write_tkldb(void) 282 { 283 char tmpfname[512]; 284 UnrealDB *db; 285 uint64_t tklcount; 286 int index, index2; 287 TKL *tkl; 288 #ifdef BENCHMARK 289 struct timeval tv_alpha, tv_beta; 290 291 gettimeofday(&tv_alpha, NULL); 292 #endif 293 294 // Write to a tempfile first, then rename it if everything succeeded 295 snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32()); 296 db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret); 297 if (!db) 298 { 299 WARN_WRITE_ERROR(tmpfname); 300 return 0; 301 } 302 303 W_SAFE(unrealdb_write_int32(db, TKLDB_MAGIC)); 304 W_SAFE(unrealdb_write_int32(db, tkldb_version)); 305 306 // Count the *-Lines 307 tklcount = 0; 308 309 // First the ones in the hash table 310 for (index = 0; index < TKLIPHASHLEN1; index++) 311 { 312 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 313 { 314 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 315 { 316 if (tkl->flags & TKL_FLAG_CONFIG) 317 continue; /* config entry */ 318 tklcount++; 319 } 320 } 321 } 322 // Then the regular *-Lines 323 for (index = 0; index < TKLISTLEN; index++) 324 { 325 for (tkl = tklines[index]; tkl; tkl = tkl->next) 326 { 327 if (tkl->flags & TKL_FLAG_CONFIG) 328 continue; /* config entry */ 329 tklcount++; 330 } 331 } 332 W_SAFE(unrealdb_write_int64(db, tklcount)); 333 334 // Now write the actual *-Lines, first the ones in the hash table 335 for (index = 0; index < TKLIPHASHLEN1; index++) 336 { 337 for (index2 = 0; index2 < TKLIPHASHLEN2; index2++) 338 { 339 for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) 340 { 341 if (tkl->flags & TKL_FLAG_CONFIG) 342 continue; /* config entry */ 343 if (!write_tkline(db, tmpfname, tkl)) // write_tkline() closes the db on errors itself 344 return 0; 345 } 346 } 347 } 348 // Then the regular *-Lines 349 for (index = 0; index < TKLISTLEN; index++) 350 { 351 for (tkl = tklines[index]; tkl; tkl = tkl->next) 352 { 353 if (tkl->flags & TKL_FLAG_CONFIG) 354 continue; /* config entry */ 355 if (!write_tkline(db, tmpfname, tkl)) 356 return 0; 357 } 358 } 359 360 // Everything seems to have gone well, attempt to close and rename the tempfile 361 if (!unrealdb_close(db)) 362 { 363 WARN_WRITE_ERROR(tmpfname); 364 return 0; 365 } 366 #ifdef _WIN32 367 /* The rename operation cannot be atomic on Windows as it will cause a "file exists" error */ 368 unlink(cfg.database); 369 #endif 370 if (rename(tmpfname, cfg.database) < 0) 371 { 372 config_error("[tkldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno)); 373 return 0; 374 } 375 #ifdef BENCHMARK 376 gettimeofday(&tv_beta, NULL); 377 config_status("[tkldb] Benchmark: SAVE DB: %lld microseconds", 378 (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); 379 #endif 380 return 1; 381 } 382 383 /** Write a TKL entry */ 384 int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl) 385 { 386 char tkltype; 387 char buf[256]; 388 389 /* First, write the common attributes */ 390 tkltype = tkl_typetochar(tkl->type); 391 W_SAFE(unrealdb_write_char(db, tkltype)); // TKL char 392 393 W_SAFE(unrealdb_write_str(db, tkl->set_by)); 394 W_SAFE(unrealdb_write_int64(db, tkl->set_at)); 395 W_SAFE(unrealdb_write_int64(db, tkl->expire_at)); 396 397 if (TKLIsServerBan(tkl)) 398 { 399 char *usermask = tkl->ptr.serverban->usermask; 400 if (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) 401 { 402 snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask); 403 usermask = buf; 404 } 405 W_SAFE(unrealdb_write_str(db, usermask)); 406 W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->hostmask)); 407 W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->reason)); 408 } else 409 if (TKLIsBanException(tkl)) 410 { 411 char *usermask = tkl->ptr.banexception->usermask; 412 if (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) 413 { 414 snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.banexception->usermask); 415 usermask = buf; 416 } 417 W_SAFE(unrealdb_write_str(db, usermask)); 418 W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->hostmask)); 419 W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->bantypes)); 420 W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->reason)); 421 } else 422 if (TKLIsNameBan(tkl)) 423 { 424 char *hold = tkl->ptr.nameban->hold ? "H" : "*"; 425 W_SAFE(unrealdb_write_str(db, hold)); 426 W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->name)); 427 W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->reason)); 428 } else 429 if (TKLIsSpamfilter(tkl)) 430 { 431 char *match_type = unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type); 432 char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target); 433 char action = banact_valtochar(tkl->ptr.spamfilter->action); 434 435 W_SAFE(unrealdb_write_str(db, match_type)); 436 W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->match->str)); 437 W_SAFE(unrealdb_write_str(db, target)); 438 W_SAFE(unrealdb_write_char(db, action)); 439 W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->tkl_reason)); 440 W_SAFE(unrealdb_write_int64(db, tkl->ptr.spamfilter->tkl_duration)); 441 } 442 443 return 1; 444 } 445 446 /** Read all entries from the TKL db */ 447 int read_tkldb(void) 448 { 449 UnrealDB *db; 450 TKL *tkl = NULL; 451 uint32_t magic = 0; 452 uint32_t version; 453 uint64_t cnt; 454 uint64_t tklcount = 0; 455 uint64_t v; 456 int added_cnt = 0; 457 char c; 458 char *str; 459 460 #ifdef BENCHMARK 461 struct timeval tv_alpha, tv_beta; 462 463 gettimeofday(&tv_alpha, NULL); 464 #endif 465 466 db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret); 467 if (!db) 468 { 469 if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND) 470 { 471 /* Database does not exist. Could be first boot */ 472 config_warn("[tkldb] No database present at '%s', will start a new one", cfg.database); 473 return 1; 474 } else 475 if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED) 476 { 477 /* Re-open as unencrypted */ 478 db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL); 479 if (!db) 480 { 481 /* This should actually never happen, unless some weird I/O error */ 482 config_warn("[tkldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string()); 483 return 0; 484 } 485 } else 486 { 487 config_warn("[tkldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string()); 488 return 0; 489 } 490 } 491 492 /* The database starts with a "magic value" - unless it's some old version or corrupt */ 493 R_SAFE(unrealdb_read_int32(db, &magic)); 494 if (magic != TKLDB_MAGIC) 495 { 496 config_warn("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database); 497 config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to " 498 "delete the existing database. Just keep at least 1 server linked during the upgrade " 499 "process to preserve your global *LINES and Spamfilters."); 500 unrealdb_close(db); 501 return 0; 502 } 503 504 /* Now do a version check */ 505 R_SAFE(unrealdb_read_int32(db, &version)); 506 if (version < 4999) 507 { 508 config_warn("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version); 509 unrealdb_close(db); 510 return 0; 511 } 512 if (version > tkldb_version) 513 { 514 config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported", 515 cfg.database, (unsigned long)tkldb_version, (unsigned long)version); 516 unrealdb_close(db); 517 return 0; 518 } 519 520 R_SAFE(unrealdb_read_int64(db, &tklcount)); 521 522 for (cnt = 0; cnt < tklcount; cnt++) 523 { 524 int do_not_add = 0; 525 526 tkl = safe_alloc(sizeof(TKL)); 527 528 /* First, fetch the TKL type.. */ 529 R_SAFE(unrealdb_read_char(db, &c)); 530 tkl->type = tkl_chartotype(c); 531 if (!tkl->type) 532 { 533 /* We can't continue reading the DB if we don't know the TKL type, 534 * since we don't know how long the entry will be, we can't skip it. 535 * This is "impossible" anyway, unless we some day remove a TKL type 536 * in core UnrealIRCd. In which case we should add some skipping code 537 * here to gracefully handle that situation ;) 538 */ 539 config_warn("[tkldb] Invalid type '%c' encountered - STOPPED READING DATABASE!", tkl->type); 540 FreeTKLRead(); 541 break; /* we MUST stop reading */ 542 } 543 544 /* Read the common types (same for all TKLs) */ 545 R_SAFE(unrealdb_read_str(db, &tkl->set_by)); 546 R_SAFE(unrealdb_read_int64(db, &v)); 547 tkl->set_at = v; 548 R_SAFE(unrealdb_read_int64(db, &v)); 549 tkl->expire_at = v; 550 551 /* Save some CPU... if it's already expired then don't bother adding */ 552 if (tkl->expire_at != 0 && tkl->expire_at <= TStime()) 553 do_not_add = 1; 554 555 /* Now handle all the specific types */ 556 if (TKLIsServerBan(tkl)) 557 { 558 int softban = 0; 559 560 tkl->ptr.serverban = safe_alloc(sizeof(ServerBan)); 561 562 /* Usermask - but taking into account that the 563 * %-prefix means a soft ban. 564 */ 565 R_SAFE(unrealdb_read_str(db, &str)); 566 if (*str == '%') 567 { 568 softban = 1; 569 safe_strdup(tkl->ptr.serverban->usermask, str+1); 570 } else { 571 safe_strdup(tkl->ptr.serverban->usermask, str); 572 } 573 safe_free(str); 574 575 /* And the other 2 fields.. */ 576 R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->hostmask)); 577 R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->reason)); 578 579 if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask, 580 tkl->ptr.serverban->hostmask, softban)) 581 { 582 do_not_add = 1; 583 } 584 585 if (!do_not_add) 586 { 587 tkl_add_serverban(tkl->type, tkl->ptr.serverban->usermask, 588 tkl->ptr.serverban->hostmask, 589 tkl->ptr.serverban->reason, 590 tkl->set_by, tkl->expire_at, 591 tkl->set_at, softban, 0); 592 } 593 } else 594 if (TKLIsBanException(tkl)) 595 { 596 int softban = 0; 597 598 tkl->ptr.banexception = safe_alloc(sizeof(BanException)); 599 600 /* Usermask - but taking into account that the 601 * %-prefix means a soft ban. 602 */ 603 R_SAFE(unrealdb_read_str(db, &str)); 604 if (*str == '%') 605 { 606 softban = 1; 607 safe_strdup(tkl->ptr.banexception->usermask, str+1); 608 } else { 609 safe_strdup(tkl->ptr.banexception->usermask, str); 610 } 611 safe_free(str); 612 613 /* And the other 3 fields.. */ 614 R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->hostmask)); 615 R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->bantypes)); 616 R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->reason)); 617 618 if (find_tkl_banexception(tkl->type, tkl->ptr.banexception->usermask, 619 tkl->ptr.banexception->hostmask, softban)) 620 { 621 do_not_add = 1; 622 } 623 624 if (!do_not_add) 625 { 626 tkl_add_banexception(tkl->type, tkl->ptr.banexception->usermask, 627 tkl->ptr.banexception->hostmask, 628 NULL, 629 tkl->ptr.banexception->reason, 630 tkl->set_by, tkl->expire_at, 631 tkl->set_at, softban, 632 tkl->ptr.banexception->bantypes, 633 0); 634 } 635 } else 636 if (TKLIsNameBan(tkl)) 637 { 638 tkl->ptr.nameban = safe_alloc(sizeof(NameBan)); 639 640 R_SAFE(unrealdb_read_str(db, &str)); 641 if (*str == 'H') 642 tkl->ptr.nameban->hold = 1; 643 safe_free(str); 644 R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->name)); 645 R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->reason)); 646 647 if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name, 648 tkl->ptr.nameban->hold)) 649 { 650 do_not_add = 1; 651 } 652 653 if (!do_not_add) 654 { 655 tkl_add_nameban(tkl->type, tkl->ptr.nameban->name, 656 tkl->ptr.nameban->hold, 657 tkl->ptr.nameban->reason, 658 tkl->set_by, tkl->expire_at, 659 tkl->set_at, 0); 660 } 661 } else 662 if (TKLIsSpamfilter(tkl)) 663 { 664 int match_method; 665 char *err = NULL; 666 667 tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter)); 668 669 /* Match method */ 670 R_SAFE(unrealdb_read_str(db, &str)); 671 match_method = unreal_match_method_strtoval(str); 672 if (!match_method) 673 { 674 config_warn("[tkldb] Unhandled spamfilter match method '%s' -- spamfilter entry not added", str); 675 do_not_add = 1; 676 } 677 safe_free(str); 678 679 /* Match string (eg: regex) */ 680 R_SAFE(unrealdb_read_str(db, &str)); 681 tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err); 682 if (!tkl->ptr.spamfilter->match) 683 { 684 config_warn("[tkldb] Spamfilter '%s' does not compile: %s -- spamfilter entry not added", str, err); 685 do_not_add = 1; 686 } 687 safe_free(str); 688 689 /* Target (eg: cpn) */ 690 R_SAFE(unrealdb_read_str(db, &str)); 691 tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL); 692 if (!tkl->ptr.spamfilter->target) 693 { 694 config_warn("[tkldb] Spamfilter '%s' without any valid targets (%s) -- spamfilter entry not added", 695 tkl->ptr.spamfilter->match->str, str); 696 do_not_add = 1; 697 } 698 safe_free(str); 699 700 /* Action */ 701 R_SAFE(unrealdb_read_char(db, &c)); 702 tkl->ptr.spamfilter->action = banact_chartoval(c); 703 if (!tkl->ptr.spamfilter->action) 704 { 705 config_warn("[tkldb] Spamfilter '%s' without valid action (%c) -- spamfilter entry not added", 706 tkl->ptr.spamfilter->match->str, c); 707 do_not_add = 1; 708 } 709 710 R_SAFE(unrealdb_read_str(db, &tkl->ptr.spamfilter->tkl_reason)); 711 R_SAFE(unrealdb_read_int64(db, &v)); 712 tkl->ptr.spamfilter->tkl_duration = v; 713 714 if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str, 715 tkl->ptr.spamfilter->action, 716 tkl->ptr.spamfilter->target)) 717 { 718 do_not_add = 1; 719 } 720 721 if (!do_not_add) 722 { 723 tkl_add_spamfilter(tkl->type, tkl->ptr.spamfilter->target, 724 tkl->ptr.spamfilter->action, 725 tkl->ptr.spamfilter->match, 726 tkl->set_by, tkl->expire_at, tkl->set_at, 727 tkl->ptr.spamfilter->tkl_duration, 728 tkl->ptr.spamfilter->tkl_reason, 729 0); 730 /* tkl_add_spamfilter() does not copy the match but assign it. 731 * so set to NULL here to avoid a read-after-free later on. 732 */ 733 tkl->ptr.spamfilter->match = NULL; 734 } 735 } else 736 { 737 config_warn("[tkldb] Unhandled type!! TKLDB is missing support for type %ld -- STOPPED reading db entries!", (long)tkl->type); 738 FreeTKLRead(); 739 break; /* we MUST stop reading */ 740 } 741 742 if (!do_not_add) 743 added_cnt++; 744 745 FreeTKLRead(); 746 } 747 748 unrealdb_close(db); 749 750 if (added_cnt) 751 config_status("[tkldb] Re-added %d *-Lines", added_cnt); 752 753 #ifdef BENCHMARK 754 gettimeofday(&tv_beta, NULL); 755 unreal_log(ULOG_DEBUG, "tkldb", "TKLDB_BENCHMARK", NULL, 756 "[tkldb] Benchmark: LOAD DB: $time_msec microseconds", 757 log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); 758 #endif 759 return 1; 760 } 761