1
1
mirror of https://github.com/jrbrtsn/ban2fail synced 2024-06-16 03:48:03 +00:00
ban2fail/ban2fail.c

816 lines
25 KiB
C
Raw Normal View History

2019-11-23 03:40:23 +00:00
/***************************************************************************
* Copyright (C) 2019 by John D. Robertson *
* john@rrci.com *
* *
* 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 3 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 *
* *
* 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. *
***************************************************************************/
2019-11-29 22:23:16 +00:00
#define _GNU_SOURCE
2019-11-23 03:40:23 +00:00
#include <assert.h>
#include <getopt.h>
2019-11-29 22:23:16 +00:00
#include <signal.h>
2019-11-23 03:40:23 +00:00
#include <sys/file.h>
2019-12-04 02:38:51 +00:00
#include <sys/types.h>
2019-11-29 22:23:16 +00:00
#include <unistd.h>
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
#include "addrRpt.h"
2019-11-23 03:40:23 +00:00
#include "ban2fail.h"
#include "cntry.h"
2019-11-29 22:23:16 +00:00
#include "ez_libanl.h"
2019-11-29 14:00:39 +00:00
#include "ez_libc.h"
2019-11-23 03:40:23 +00:00
#include "iptables.h"
2019-12-01 22:23:30 +00:00
#include "offEntry.h"
2019-11-23 03:40:23 +00:00
#include "logFile.h"
#include "logType.h"
#include "map.h"
#include "maxoff.h"
#include "pdns.h"
2019-11-23 03:40:23 +00:00
#include "str.h"
#include "util.h"
/*==================================================================*/
/*=================== Support structs ==============================*/
/*==================================================================*/
struct cntryStat {
char *cntry;
unsigned nAddr;
2019-11-23 03:40:23 +00:00
};
/* Need this for initialization from configuration file */
struct initInfo {
const char *symStr;
int (*init_f)(CFGMAP *map, char *symStr);
};
/*==================================================================*/
/*================= Forward declarations ===========================*/
/*==================================================================*/
2019-12-02 03:29:32 +00:00
static int addrRpt_serial_qsort(const void *p1, const void *p2);
2019-11-23 03:40:23 +00:00
static int cntryStat_count_qsort(const void *p1, const void *p2);
static int configure(CFGMAP *h_cfgmap, const char *pfix);
static int logentry_count_qsort(const void *p1, const void *p2);
2019-12-04 02:38:51 +00:00
static int logentry_latest_qsort(const void *p1, const void *p2);
2019-12-02 03:29:32 +00:00
static int map_byCountries(OFFENTRY *e, MAP *h_map);
2019-11-23 03:40:23 +00:00
static int stub_init(CFGMAP *map, char *symStr);
/*==================================================================*/
/*========================= static data ============================*/
/*==================================================================*/
static const struct bitTuple GlobalFlagBitTuples[]= {
{.name= "GLB_VERBOSE_FLG", .bit= GLB_VERBOSE_FLG},
{.name= "GLB_LIST_ADDR_FLG", .bit= GLB_LIST_ADDR_FLG},
{.name= "GLB_LIST_CNTRY_FLG", .bit= GLB_LIST_CNTRY_FLG},
{.name= "GLB_DONT_IPTABLE_FLG", .bit= GLB_DONT_IPTABLE_FLG},
{/* Terminating member */}
};
2019-11-23 03:40:23 +00:00
struct Global G= {
2019-12-04 02:38:51 +00:00
.cache= {
.dir= CACHEDIR,
.dir_mode=0770,
.file_mode=0660
},
.lock= {
.dir= LOCKDIR,
.dir_mode= 0770,
.file_mode=0660
},
2019-11-23 03:40:23 +00:00
.version= {
.major= 0,
2020-09-15 15:58:26 +00:00
.minor= 14,
2021-02-26 18:16:14 +00:00
.patch= 7
},
.bitTuples.flags= GlobalFlagBitTuples
2019-11-23 03:40:23 +00:00
};
const static struct initInfo S_initInfo_arr[] = {
{.symStr= "MAX_OFFENSES", .init_f= MAXOFF_init},
{.symStr= "LOGTYPE", .init_f= LOGTYPE_init},
{/* Terminating member */}
};
2019-11-23 03:40:23 +00:00
/*================ Local only static struct ======================*/
static struct {
enum {
PAGER_RUNNING_FLG= 1<<31
} flags;
2019-12-02 03:29:32 +00:00
/* OFFENTRY object indexed by ip address */
MAP addr2logEntry_map;
2019-11-23 03:40:23 +00:00
/* CFGMAP containing our configuration information */
2019-11-23 03:40:23 +00:00
CFGMAP cfgmap;
/* Vectors for storing ip address which are to be blocked and
* Unblocked.
*/
2019-11-23 03:40:23 +00:00
PTRVEC toBlock_vec,
toUnblock_vec;
2019-12-02 03:29:32 +00:00
/* Used to place OFFENTRY address objects into linear
2019-11-29 22:23:16 +00:00
* access container.
*/
2019-12-02 03:29:32 +00:00
OFFENTRY **lePtrArr;
2019-11-29 22:23:16 +00:00
2019-12-03 12:55:15 +00:00
/* Avoid multiple instances of filename buffers */
char fnameBuf[PATH_MAX];
int cacheLock_fd,
iptablesLock_fd;
} S= {
.cacheLock_fd= -1,
.iptablesLock_fd= -1
};
2019-11-23 03:40:23 +00:00
/*==================================================================*/
/*======================== main() ==================================*/
/*==================================================================*/
/* Enums for long options */
enum {
VERSION_OPT_ENUM=128, /* Larger than any printable character */
HELP_OPT_ENUM,
PRINT_LOGFILE_NAMES_ENUM
2019-11-23 03:40:23 +00:00
};
int
main(int argc, char **argv)
/***************************************************************
* Program execution begins here.
*/
{
2019-12-03 12:55:15 +00:00
int rtn= EXIT_FAILURE;
2019-11-23 03:40:23 +00:00
char *confFile= CONFIGFILE;
/* Prepare static data */
// global
2019-12-04 02:38:51 +00:00
struct group *gr= ez_getgrnam(GROUP_NAME);
G.gid= gr->gr_gid;
/* Default sending listing to stdout */
G.rpt.fh= stdout;
2019-11-23 03:40:23 +00:00
MAP_constructor(&G.logType_map, 10, 10);
2019-12-02 03:29:32 +00:00
MAP_constructor(&G.rpt.AddrRPT_map, 10, 10);
2019-11-23 03:40:23 +00:00
// local
2019-11-29 22:23:16 +00:00
MAP_constructor(&S.addr2logEntry_map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT);
2019-11-23 03:40:23 +00:00
2019-11-29 02:27:03 +00:00
PTRVEC_constructor(&S.toBlock_vec, N_ADDRESSES_HINT);
PTRVEC_constructor(&S.toUnblock_vec, N_ADDRESSES_HINT);
2019-11-23 03:40:23 +00:00
2019-12-01 11:55:58 +00:00
/* Parse command line arguments */
{ /*==================================================================*/
2019-11-23 03:40:23 +00:00
int c, errflg= 0;
extern char *optarg;
extern int optind, optopt;
for(;;) {
static const struct option long_options[]= {
{"help", no_argument, 0, HELP_OPT_ENUM},
{"print-lfn", no_argument, 0, PRINT_LOGFILE_NAMES_ENUM},
2019-11-23 03:40:23 +00:00
{"version", no_argument, 0, VERSION_OPT_ENUM},
{}
};
int c, option_ndx= 0;
2019-12-03 09:06:45 +00:00
c= getopt_long(argc, argv, ":a::cFst:v", long_options, &option_ndx);
2019-11-23 03:40:23 +00:00
if(-1 == c) break;
switch(c) {
/* print usage help */
case HELP_OPT_ENUM:
++errflg;
break;
case 'a':
2019-12-03 12:55:15 +00:00
G.flags |= GLB_LIST_ADDR_FLG|GLB_DONT_IPTABLE_FLG;
2019-11-29 22:23:16 +00:00
if(optarg) {
2019-12-01 18:04:41 +00:00
if(*optarg == '+') {
2019-11-29 22:23:16 +00:00
G.flags |= GLB_DNS_LOOKUP_FLG;
2019-12-01 18:04:41 +00:00
} else if(*optarg == '-') {
G.flags |= GLB_DNS_LOOKUP_FLG|GLB_DNS_FILTER_BAD_FLG;
} else
2019-11-29 22:23:16 +00:00
++errflg;
2019-11-29 14:00:39 +00:00
}
2019-11-23 03:40:23 +00:00
break;
2019-12-03 09:06:45 +00:00
case 'c':
2019-12-03 12:55:15 +00:00
G.flags |= GLB_LIST_CNTRY_FLG|GLB_DONT_IPTABLE_FLG;
2019-12-03 09:06:45 +00:00
break;
case 'F':
G.flags |= GLB_FLUSH_CACHE_FLG;
break;
case 's':
2019-12-03 12:55:15 +00:00
G.flags |= GLB_LIST_SUMMARY_FLG|GLB_DONT_IPTABLE_FLG;
break;
2019-11-23 03:40:23 +00:00
case 't':
G.flags |= GLB_DONT_IPTABLE_FLG;
2019-12-04 02:38:51 +00:00
G.cache.dir= CACHEDIR "-test";
G.lock.dir= LOCKDIR "-test";
2019-11-23 03:40:23 +00:00
confFile= optarg;
break;
case 'v':
G.flags |= GLB_VERBOSE_FLG;
break;
case PRINT_LOGFILE_NAMES_ENUM:
2019-12-03 12:55:15 +00:00
G.flags |= GLB_PRINT_LOGFILE_NAMES_FLG|GLB_DONT_IPTABLE_FLG;
break;
2019-11-23 03:40:23 +00:00
case VERSION_OPT_ENUM:
ez_fprintf(stdout, "ban2fail v%d.%d.%d\n", G.version.major, G.version.minor, G.version.patch);
2019-11-23 03:40:23 +00:00
return 0;
case '?':
ez_fprintf(stderr, "Unrecognized option: -%c\n", optopt);
++errflg;
break;
}
}
if(errflg) {
ez_fprintf(stderr,
"ban2fail v%d.%d.%d Usage:\n"
2019-12-01 22:18:43 +00:00
"%s [options] [-t confFile] [addr1 addr2 ...]\n"
" addr1 ... print offending lines from logfiles for these addresses\n"
2019-11-23 03:40:23 +00:00
" --help\tprint this usage message.\n"
2019-12-04 16:25:46 +00:00
" -a[+|-]\tList results by Address. '+' to perform DNS lookups, '-' to filter riff raff.\n"
2019-11-23 03:40:23 +00:00
" -c\t\tlist results by Country\n"
2019-12-03 09:06:45 +00:00
" -F\t\tFlush the cache\n"
2019-11-27 14:44:35 +00:00
" -s\t\tlist Summary results only\n"
2019-11-23 03:40:23 +00:00
" -t confFile\tTest confFile, do not apply iptables rules\n"
2019-11-23 15:46:02 +00:00
" -v\t\tVerbose information about unrecognized configuration info\n"
" --print-lfn\tprint the names of primary logfiles to scan\n"
2019-11-23 03:40:23 +00:00
" --version\tprint the version number and exit.\n"
2019-11-27 14:44:35 +00:00
, G.version.major, G.version.minor, G.version.patch
2019-11-23 03:40:23 +00:00
, argv[0]
);
goto abort;
}
2019-12-01 22:18:43 +00:00
/* Pick up addresses on command line */
for(; optind < argc; ++optind) {
2019-12-02 03:29:32 +00:00
AddrRPT *ar;
const char *addr= argv[optind];
/* Skip duplicates */
if(MAP_findStrItem(&G.rpt.AddrRPT_map, addr)) continue;
/* Create a new address report object */
AddrRPT_addr_create(ar, addr);
assert(ar);
/* Place it in global map */
MAP_addStrKey(&G.rpt.AddrRPT_map, addr, ar);
2019-12-03 12:55:15 +00:00
G.flags |= GLB_DONT_IPTABLE_FLG;
2019-12-01 22:18:43 +00:00
}
2019-11-23 03:40:23 +00:00
} /* Done with command line arguments */
char *pager= NULL,
*rslt= getenv("PAGER");
#if 0
/* Keep a copy of the pager environment variable */
if(rslt) pager= strdup(rslt);
2019-12-04 02:38:51 +00:00
/* So we can run iptables */
ez_setuid(0);
ez_setgid(G.gid);
/* Restore the pager environment variable */
if(pager) {
if(setenv("PAGER", pager, 1)) assert(0);
}
#endif
2019-12-04 02:38:51 +00:00
/* Get a time when the scan began */
G.begin.time_t= time(NULL);
G.begin.tm= *localtime(&G.begin.time_t);
2019-11-23 03:40:23 +00:00
2019-12-01 11:55:58 +00:00
/* Read the configuration file */
{ /*=========================================================*/
2019-11-23 03:40:23 +00:00
if(!CFGMAP_file_constructor(&S.cfgmap, confFile)) {
eprintf("ERROR: failed to read configuration from \"%s\"", confFile);
goto abort;
}
2019-12-04 02:38:51 +00:00
/* For debugging this can be useful */
// CFGMAP_print(&S.cfgmap, G.rpt.fh);
2019-11-23 03:40:23 +00:00
/* Just leave the S.cfgmap in place, so all the value strings
* don't need to be copied.
*/
}
2019-12-01 11:55:58 +00:00
/* Obtain a file lock to protect cache files */
2019-12-02 03:29:32 +00:00
/*===========================================================*/
2019-12-03 02:56:55 +00:00
{
2019-12-04 02:38:51 +00:00
if(-1 == ez_access(G.lock.dir, F_OK)) {
ez_mkdir(G.lock.dir, G.lock.dir_mode);
ez_chown(G.lock.dir, getuid(), G.gid);
}
2019-12-03 12:55:15 +00:00
2019-12-04 02:38:51 +00:00
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/cache", G.lock.dir);
2019-11-23 03:40:23 +00:00
/* Make sure the file exists by open()'ing */
2019-12-04 02:38:51 +00:00
S.cacheLock_fd= ez_open(S.fnameBuf, O_CREAT|O_RDONLY|O_CLOEXEC, G.lock.file_mode);
ez_fchown(S.cacheLock_fd, getuid(), G.gid);
2019-11-23 03:40:23 +00:00
/* Let's get a exclusive lock */
2019-12-03 12:55:15 +00:00
// TODO: set SIGALRM to knock us out of blocked wait?
int rc= ez_flock(S.cacheLock_fd, LOCK_EX);
2019-11-23 03:40:23 +00:00
}
2019-11-28 03:06:49 +00:00
#ifndef DEBUG
/* if stdout is a tty, and listing is likely
* to be long, then use $PAGER.
*/
2019-12-02 03:29:32 +00:00
if(G.flags & GLB_LONG_LISTING_MASK && isatty(fileno(G.rpt.fh))) {
S.flags |= PAGER_RUNNING_FLG;
2019-12-01 22:18:43 +00:00
G.rpt.fh= pager_open();
}
2019-11-28 03:06:49 +00:00
#endif
2019-12-01 22:18:43 +00:00
assert(G.rpt.fh);
2019-12-01 11:55:58 +00:00
/* Open our cache, instance file-specific LOGTYPE objects */
{ /*=============================================================*/
2019-12-04 02:38:51 +00:00
if(G.flags & GLB_FLUSH_CACHE_FLG &&
!ez_access(G.cache.dir, F_OK))
{
ez_rmdir_recursive(G.cache.dir);
2019-12-03 09:06:45 +00:00
}
2019-11-23 03:40:23 +00:00
/* Make the directory if needed */
2019-12-04 02:38:51 +00:00
if(ez_access(G.cache.dir, F_OK)) {
2019-11-23 03:40:23 +00:00
/* errno will be set if access() fails */
errno= 0;
2019-12-04 02:38:51 +00:00
ez_mkdir(G.cache.dir, G.cache.dir_mode);
ez_chown(G.cache.dir, getuid(), G.gid);
2019-11-23 03:40:23 +00:00
}
2019-12-02 03:29:32 +00:00
if(G.flags & GLB_LONG_LISTING_MASK) {
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "=============== ban2fail v%d.%d.%d =============\n"
2019-11-23 03:40:23 +00:00
, G.version.major
, G.version.minor
, G.version.patch
);
2019-12-01 22:18:43 +00:00
fflush(G.rpt.fh);
2019-11-23 03:40:23 +00:00
}
2019-12-01 11:55:58 +00:00
/* Implement configuration */
{ /*-----------------------------------------------------*/
2019-11-23 03:40:23 +00:00
if(configure(&S.cfgmap, NULL)) {
eprintf("ERROR: failed to realize configuration in \"%s\"", confFile);
goto abort;
}
if(G.flags & GLB_VERBOSE_FLG) { /* Warn about unused symbols */
2019-12-01 22:18:43 +00:00
CFGMAP_print_unused_symbols(&S.cfgmap, G.rpt.fh);
fflush(G.rpt.fh);
2019-11-23 03:40:23 +00:00
}
/* Just leave the S.cfgmap in place, so all the value strings
* don't need to be copied.
*/
2019-12-01 11:55:58 +00:00
} /* End implement configuration */
2019-11-23 03:40:23 +00:00
2019-12-01 11:55:58 +00:00
/*----------------- Print logfile names short circuiting --------------*/
if(G.flags & GLB_PRINT_LOGFILE_NAMES_FLG) {
/* Shortcut any further processing or reporting */
rtn= 0;
goto abort;
}
2019-12-01 11:55:58 +00:00
/* Check cache for logType directories not in our current map, and remove them */
{ /*---------------------------------------------------------------------*/
2019-12-04 02:38:51 +00:00
DIR *dir= ez_opendir(G.cache.dir);
2019-11-23 03:40:23 +00:00
struct dirent *entry;
while((entry= ez_readdir(dir))) {
/* Skip uninteresting entries */
if('.' == *entry->d_name) continue;
LOGTYPE *t= MAP_findStrItem(&G.logType_map, entry->d_name);
/* If there is a matching entry, then do not delete results */
if(t)
continue;
/* Make the path with filename */
2019-12-04 02:38:51 +00:00
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/%s", G.cache.dir, entry->d_name);
2019-11-23 03:40:23 +00:00
/* Remove unused directory & contents. */
2019-12-03 12:55:15 +00:00
ez_rmdir_recursive(S.fnameBuf);
2019-11-23 03:40:23 +00:00
}
ez_closedir(dir);
2019-12-01 11:55:58 +00:00
} /* End of cache management */
2019-11-23 03:40:23 +00:00
2019-11-23 04:51:30 +00:00
/* We're done with disk I/O, so release lock */
2019-12-01 11:55:58 +00:00
/*-----------------------------------------------------------------------*/
2019-12-03 12:55:15 +00:00
if(-1 != S.cacheLock_fd) {
ez_flock(S.cacheLock_fd, LOCK_UN);
ez_close(S.cacheLock_fd);
S.cacheLock_fd= -1;
2019-12-02 03:29:32 +00:00
}
2019-11-23 04:51:30 +00:00
2019-12-01 11:55:58 +00:00
/* Processing only for long listings */
/*-----------------------------------------------------------------------*/
2019-12-02 03:29:32 +00:00
if(G.flags & GLB_LONG_LISTING_MASK) {
MAP map;
2019-11-29 22:23:16 +00:00
MAP_constructor(&map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT);
unsigned nOffFound= 0,
nAddrFound;
MAP_visitAllEntries(&G.logType_map, (int(*)(void*,void*))LOGTYPE_offenseCount, &nOffFound);
/* Collect unique addresses into a map */
MAP_visitAllEntries(&G.logType_map, (int(*)(void*,void*))LOGTYPE_map_addr, &map);
/* Number of items in map is number of unique addresses */
nAddrFound= MAP_numItems(&map);
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh,
"===== Found %u total offenses (%u addresses) =====\n"
, nOffFound
, nAddrFound
);
2019-12-01 22:18:43 +00:00
fflush(G.rpt.fh);
/* Clean up map used for counting */
2019-12-02 03:29:32 +00:00
MAP_clearAndDestroy(&map, (void*(*)(void*))OFFENTRY_destructor);
MAP_destructor(&map);
2019-11-23 03:40:23 +00:00
}
2019-12-02 03:29:32 +00:00
} /* End of cache and logfile-specific OFFENTRY objects */
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
/* Now get a map of OFFENTRY objects that have combined counts.
2019-12-01 11:55:58 +00:00
* Perform all remaining processing and reporting.
*/
{ /*=======================================================================*/
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
/* List by address. Make a addr_map of OFFENTRY objects with composite counts */
MAP_visitAllEntries(&G.logType_map, (int(*)(void*,void*))LOGTYPE_map_addr, &S.addr2logEntry_map);
2019-11-27 22:31:15 +00:00
/* So we can run iptables */
ez_setuid(0);
ez_setgid(G.gid);
2019-11-27 22:31:15 +00:00
/* Pick up remaining blocked addresses */
IPTABLES_fill_in_missing(&S.addr2logEntry_map);
unsigned nItems= MAP_numItems(&S.addr2logEntry_map);
2019-11-23 03:40:23 +00:00
2019-11-29 22:23:16 +00:00
/* allocate this array, let it leak */
S.lePtrArr= malloc(sizeof(void*) * nItems);
assert(S.lePtrArr);
MAP_fetchAllItems(&S.addr2logEntry_map, (void**)S.lePtrArr);
2019-12-04 02:38:51 +00:00
qsort(S.lePtrArr, nItems, sizeof(OFFENTRY*), logentry_latest_qsort);
2019-11-29 22:23:16 +00:00
/* Special processing for DNS lookups */
if(G.flags & GLB_DNS_LOOKUP_FLG) {
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "Performing DNS lookups for up to %d seconds ...\n", DFLT_DNS_PAUSE_SEC);
fflush(G.rpt.fh);
2019-11-29 22:23:16 +00:00
2019-12-01 11:55:58 +00:00
int64_t begin_ms= clock_gettime_ms(CLOCK_REALTIME);
int rc= PDNS_lookup(S.lePtrArr, nItems, DFLT_DNS_PAUSE_SEC*1000);
2019-11-30 14:35:29 +00:00
assert(-1 != rc);
2019-12-01 11:55:58 +00:00
int64_t ms= clock_gettime_ms(CLOCK_REALTIME) - begin_ms;
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "\t==> Completed %d of %u lookups in %.1f seconds\n", rc, nItems, (double)ms/1000.);
}
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
/* Process each OFFENTRY item */
for(unsigned i= 0; i < nItems; ++i) {
int flags=0;
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
OFFENTRY *e= S.lePtrArr[i];
2019-11-23 03:40:23 +00:00
if(IPTABLES_is_currently_blocked(e->addr))
flags |= BLOCKED_FLG;
2019-11-23 03:40:23 +00:00
int nAllowed= MAXOFF_allowed(e->addr);
2019-11-23 03:40:23 +00:00
if(-1 == nAllowed)
flags |= WHITELIST_FLG;
2019-11-23 03:40:23 +00:00
if((flags & WHITELIST_FLG || e->count <= nAllowed) &&
(flags & BLOCKED_FLG))
{
2019-11-23 03:40:23 +00:00
flags |= UNJUST_BLOCK_FLG;
PTRVEC_addTail(&S.toUnblock_vec, e->addr);
}
2019-11-23 03:40:23 +00:00
if(!(flags & BLOCKED_FLG) &&
!(flags & WHITELIST_FLG) &&
e->count > nAllowed)
{
2019-11-23 03:40:23 +00:00
flags |= WOULD_BLOCK_FLG;
PTRVEC_addTail(&S.toBlock_vec, e->addr);
}
2019-11-23 03:40:23 +00:00
/* Print out only for list option */
2019-12-01 18:04:41 +00:00
if(G.flags & GLB_LIST_ADDR_FLG &&
!(G.flags & GLB_DNS_FILTER_BAD_FLG && e->dns.flags & PDNS_BAD_MASK))
{
2019-12-04 02:38:51 +00:00
OFFENTRY_list(e, G.rpt.fh, flags, nAllowed);
}
2019-11-23 03:40:23 +00:00
2019-12-02 03:29:32 +00:00
} /*--- End of OFFENTRY processing ---*/
2019-11-23 03:40:23 +00:00
unsigned currBlocked= MAP_numItems(&S.addr2logEntry_map);
2019-11-23 03:40:23 +00:00
/* List offenses by country if directed to do so */
if(G.flags & (GLB_LIST_CNTRY_FLG|GLB_LIST_SUMMARY_FLG)) {
2019-11-23 03:40:23 +00:00
/* Map for indexing cntryStat objects */
static MAP byCntry_map;
2019-11-29 02:27:03 +00:00
MAP_sinit(&byCntry_map, 100, 10);
2019-11-23 03:40:23 +00:00
/* Build index by trawling existing by-address map */
MAP_visitAllEntries(&S.addr2logEntry_map, (int(*)(void*,void*))map_byCountries, &byCntry_map);
2019-11-23 03:40:23 +00:00
/* Now get all cntStat handles in a vector */
unsigned vec_sz= MAP_numItems(&byCntry_map);
struct cntryStat *rtn_vec[vec_sz];
2019-11-23 03:40:23 +00:00
MAP_fetchAllItems(&byCntry_map, (void**)rtn_vec);
2019-11-23 03:40:23 +00:00
/* Sort high to low */
qsort(rtn_vec, vec_sz, sizeof(struct cntryStat*), cntryStat_count_qsort);
2019-11-23 03:40:23 +00:00
if(G.flags & GLB_LIST_CNTRY_FLG) {
2019-11-23 03:40:23 +00:00
/* Print results */
for(unsigned i= 0; i < vec_sz; ++i) {
struct cntryStat *cs= rtn_vec[i];
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "%2s %5u blocked addresses\n"
2019-11-23 03:40:23 +00:00
, cs->cntry[0] ? cs->cntry : "--"
, cs->nAddr
2019-11-23 03:40:23 +00:00
);
}
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "===============================================\n");
}
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "%6u countries affected\n" , vec_sz);
}
/* Take care of summary blocking and reporting */
unsigned n2Block= PTRVEC_numItems(&S.toBlock_vec);
unsigned n2Unblock= PTRVEC_numItems(&S.toUnblock_vec);
if(G.flags & GLB_LIST_ADDR_FLG && !(G.flags & (GLB_LIST_SUMMARY_FLG|GLB_LIST_CNTRY_FLG)))
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "===============================================\n");
if(!(G.flags & GLB_DONT_IPTABLE_FLG)) {
2019-12-03 12:55:15 +00:00
if(n2Block || n2Unblock) {
2019-12-04 02:38:51 +00:00
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/iptables", G.lock.dir);
2019-12-03 12:55:15 +00:00
/* Make sure the file exists by open()'ing */
2019-12-04 02:38:51 +00:00
S.iptablesLock_fd= ez_open(S.fnameBuf, O_CREAT|O_WRONLY|O_CLOEXEC, G.lock.file_mode);
ez_fchown(S.iptablesLock_fd, getuid(), G.gid);
2019-12-03 12:55:15 +00:00
/* Get an exclusive lock on the lockfile */
ez_flock(S.iptablesLock_fd, LOCK_EX);
}
if(n2Block) {
if(IPTABLES_block_addresses(&S.toBlock_vec)) {
eprintf("ERROR: cannot block addresses!");
goto abort;
}
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "Blocked %u new hosts\n", n2Block);
}
if(n2Unblock) {
if(IPTABLES_unblock_addresses(&S.toUnblock_vec)) {
eprintf("ERROR: cannot unblock addresses!");
goto abort;
}
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "Unblocked %u hosts\n", n2Unblock);
}
2019-12-03 12:55:15 +00:00
/* Release the lock */
if(-1 != S.iptablesLock_fd) {
ez_flock(S.iptablesLock_fd, LOCK_UN);
ez_close(S.iptablesLock_fd);
S.iptablesLock_fd= -1;
}
} else {
if(n2Block)
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "Would block %u new hosts\n", n2Block);
if(n2Unblock)
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "Would unblock %u hosts\n", n2Unblock);
2019-11-23 03:40:23 +00:00
}
if(G.flags & (GLB_LIST_ADDR_FLG|GLB_LIST_SUMMARY_FLG))
2019-12-01 22:18:43 +00:00
ez_fprintf(G.rpt.fh, "%6u addresses currently blocked\n" , currBlocked + n2Block - n2Unblock);
2019-11-23 03:40:23 +00:00
}
2019-12-02 03:29:32 +00:00
/* Print out address reports */
{ /*==========================================================*/
unsigned nItems= MAP_numItems(&G.rpt.AddrRPT_map);
AddrRPT *arArr[nItems];
MAP_fetchAllItems(&G.rpt.AddrRPT_map, (void**)arArr);
qsort(arArr, nItems, sizeof(AddrRPT*), addrRpt_serial_qsort);
for(unsigned i= 0; i < nItems; ++i) {
AddrRPT_print(arArr[i], G.rpt.fh);
}
}
2019-12-01 22:18:43 +00:00
fflush(G.rpt.fh);
2019-12-03 02:56:55 +00:00
rtn= EXIT_SUCCESS;
abort:
/* Wait for pager to finish, if it is running */
if(S.flags & PAGER_RUNNING_FLG)
2019-12-01 22:18:43 +00:00
ez_pclose(G.rpt.fh);
2019-11-23 03:40:23 +00:00
/* Make sure lock file is unlocked */
2019-12-03 12:55:15 +00:00
if(-1 != S.cacheLock_fd) {
ez_flock(S.cacheLock_fd, LOCK_UN);
ez_close(S.cacheLock_fd);
}
if(-1 != S.iptablesLock_fd) {
ez_flock(S.iptablesLock_fd, LOCK_UN);
ez_close(S.iptablesLock_fd);
2019-11-23 03:40:23 +00:00
}
return rtn;
}
/*==================================================================*/
/*============== Supporting functions ==============================*/
/*==================================================================*/
2019-12-04 02:38:51 +00:00
static int
logentry_latest_qsort(const void *p1, const void *p2)
/***************************************************************
* qsort functor puts large counts on top.
*/
{
const OFFENTRY *le1= *(const OFFENTRY *const*)p1,
*le2= *(const OFFENTRY *const*)p2;
if(le1->latest > le2->latest) return -1;
if(le1->latest < le2->latest) return 1;
return logentry_count_qsort(p1, p2);
}
2019-11-23 03:40:23 +00:00
static int
logentry_count_qsort(const void *p1, const void *p2)
/***************************************************************
* qsort functor puts large counts on top.
*/
{
2019-12-02 03:29:32 +00:00
const OFFENTRY *le1= *(const OFFENTRY *const*)p1,
*le2= *(const OFFENTRY *const*)p2;
2019-11-23 03:40:23 +00:00
if(le1->count > le2->count) return -1;
if(le1->count < le2->count) return 1;
return 0;
}
static int
cntryStat_count_qsort(const void *p1, const void *p2)
/***************************************************************
* qsort functor puts large counts on top.
*/
{
const struct cntryStat
*cs1= *(const struct cntryStat *const*)p1,
*cs2= *(const struct cntryStat *const*)p2;
if(cs1->nAddr > cs2->nAddr) return -1;
if(cs1->nAddr < cs2->nAddr) return 1;
2019-11-23 03:40:23 +00:00
return 0;
}
2019-12-02 03:29:32 +00:00
static int
addrRpt_serial_qsort(const void *p1, const void *p2)
/***************************************************************
* qsort functor sorts by serial number, low to high
*/
{
const AddrRPT *ar1= *(const AddrRPT *const*)p1,
*ar2= *(const AddrRPT *const*)p2;
if(ar1->serial < ar2->serial) return -1;
if(ar1->serial > ar2->serial) return 1;
return 0;
}
2019-11-23 03:40:23 +00:00
static int
configure(CFGMAP *h_cfgmap, const char *pfix)
/*****************************************************************
* dynamic initialization from contents of configuration
* dictionary.
*/
{
int rtn= 1;
const CFGMAP_ENTRY *pCde;
const struct initInfo *pIi;
for(pIi= S_initInfo_arr; pIi->symStr; ++pIi) {
char buf[1024];
/* Create the symbol we will look for */
snprintf(buf, sizeof(buf), "%s\\%s", pfix ? pfix : "", pIi->symStr);
if((pCde= CFGMAP_find(h_cfgmap, buf))) {
unsigned i;
for(i= 0; i < CFGMAP_ENTRY_numValues(pCde); ++i) {
/* Create the name for this object */
snprintf(buf, sizeof(buf), "%s\\%s", pfix ? pfix : "", CFGMAP_ENTRY_value(pCde, i));
/* Call the initialization function */
if((*pIi->init_f)(h_cfgmap, buf)) goto abort;
/* recurse with longer pfix */
if(configure(h_cfgmap, buf)) {
eprintf("ERROR: initialization function failed.");
goto abort;
}
}
}
}
rtn= 0;
abort:
return rtn;
}
#ifdef DEBUG
static int
stub_init(CFGMAP *map, char *symStr)
/*****************************************************************
* Stand-in xxx_init() function until a proper one is implemented.
*/
{
eprintf("HERE, symStr= \"%s\"", symStr);
return 0;
}
#endif
static int
2019-12-02 03:29:32 +00:00
map_byCountries(OFFENTRY *e, MAP *h_map)
2019-11-23 03:40:23 +00:00
/**************************************************************
* Generate a "by country" map of cntryStat objects.
*/
{
struct cntryStat *cs= MAP_findStrItem(h_map, e->cntry);
if(!cs) {
cs= calloc(1, sizeof(*cs));
cs->cntry= e->cntry;
MAP_addStrKey(h_map, cs->cntry, cs);
}
++cs->nAddr;
2019-11-23 03:40:23 +00:00
return 0;
}