Lots of new reporting features

This commit is contained in:
john 2019-12-03 21:38:51 -05:00
parent bd5a01f5a2
commit 5549c4d188
17 changed files with 984 additions and 130 deletions

View File

@ -32,6 +32,8 @@ src := \
pdns.c \
ptrvec.c \
str.c \
target.c \
timestamp.c \
util.c \
libs := z crypto GeoIP pthread db

View File

@ -21,6 +21,7 @@
#include <getopt.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/types.h>
#include <unistd.h>
#include "addrRpt.h"
@ -38,13 +39,6 @@
#include "str.h"
#include "util.h"
enum {
BLOCKED_FLG =1<<0,
WOULD_BLOCK_FLG =1<<1,
UNJUST_BLOCK_FLG =1<<2,
WHITELIST_FLG =1<<3
};
/*==================================================================*/
/*=================== Support structs ==============================*/
/*==================================================================*/
@ -67,6 +61,7 @@ static int addrRpt_serial_qsort(const void *p1, const void *p2);
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);
static int logentry_latest_qsort(const void *p1, const void *p2);
static int map_byCountries(OFFENTRY *e, MAP *h_map);
static int stub_init(CFGMAP *map, char *symStr);
@ -83,13 +78,21 @@ static const struct bitTuple GlobalFlagBitTuples[]= {
};
struct Global G= {
.cacheDir= CACHEDIR,
.lockDir= LOCKDIR,
.cache= {
.dir= CACHEDIR,
.dir_mode=0770,
.file_mode=0660
},
.lock= {
.dir= LOCKDIR,
.dir_mode= 0770,
.file_mode=0660
},
.version= {
.major= 0,
.minor= 13,
.patch= 3
.patch= 4
},
.bitTuples.flags= GlobalFlagBitTuples
@ -101,14 +104,6 @@ const static struct initInfo S_initInfo_arr[] = {
{/* Terminating member */}
};
static const struct bitTuple BlockBitTuples[]= {
{.name= "BLK", .bit= BLOCKED_FLG},
{.name= "+blk+", .bit= WOULD_BLOCK_FLG},
{.name= "-blk-", .bit= UNJUST_BLOCK_FLG},
{.name= "WL", .bit= WHITELIST_FLG},
{/* Terminating member */}
};
/*================ Local only static struct ======================*/
static struct {
@ -169,6 +164,11 @@ main(int argc, char **argv)
/* Prepare static data */
// global
struct group *gr= ez_getgrnam(GROUP_NAME);
G.gid= gr->gr_gid;
/* Default sending listing to stdout */
G.rpt.fh= stdout;
MAP_constructor(&G.logType_map, 10, 10);
MAP_constructor(&G.rpt.AddrRPT_map, 10, 10);
@ -232,8 +232,8 @@ main(int argc, char **argv)
case 't':
G.flags |= GLB_DONT_IPTABLE_FLG;
G.cacheDir= CACHEDIR "-test";
G.lockDir= LOCKDIR "-test";
G.cache.dir= CACHEDIR "-test";
G.lock.dir= LOCKDIR "-test";
confFile= optarg;
break;
@ -300,11 +300,13 @@ main(int argc, char **argv)
} /* Done with command line arguments */
/* Make sure we will be able to run iptables */
if(getuid()) {
eprintf("ERROR: You must be root to run iptables!");
goto abort;
}
/* So we can run iptables */
ez_setuid(0);
ez_setgid(G.gid);
/* Get a time when the scan began */
G.begin.time_t= time(NULL);
G.begin.tm= *localtime(&G.begin.time_t);
/* Read the configuration file */
{ /*=========================================================*/
@ -313,6 +315,9 @@ main(int argc, char **argv)
goto abort;
}
/* For debugging this can be useful */
// CFGMAP_print(&S.cfgmap, G.rpt.fh);
/* Just leave the S.cfgmap in place, so all the value strings
* don't need to be copied.
*/
@ -321,21 +326,21 @@ main(int argc, char **argv)
/* Obtain a file lock to protect cache files */
/*===========================================================*/
{
if(-1 == ez_access(G.lockDir, F_OK))
ez_mkdir(G.lockDir, 0750);
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);
}
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/cache", G.lockDir);
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/cache", G.lock.dir);
/* Make sure the file exists by open()'ing */
S.cacheLock_fd= ez_open(S.fnameBuf, O_CREAT|O_WRONLY|O_CLOEXEC, 0640);
assert(-1 != S.cacheLock_fd);
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);
/* Let's get a exclusive lock */
// TODO: set SIGALRM to knock us out of blocked wait?
int rc= ez_flock(S.cacheLock_fd, LOCK_EX);
}
/* Default sending listing to stdout */
G.rpt.fh= stdout;
#ifndef DEBUG
/* if stdout is a tty, and listing is likely
* to be long, then use $PAGER.
@ -349,16 +354,19 @@ main(int argc, char **argv)
/* Open our cache, instance file-specific LOGTYPE objects */
{ /*=============================================================*/
if(G.flags & GLB_FLUSH_CACHE_FLG && !access(G.cacheDir, F_OK)) {
ez_rmdir_recursive(G.cacheDir);
if(G.flags & GLB_FLUSH_CACHE_FLG &&
!ez_access(G.cache.dir, F_OK))
{
ez_rmdir_recursive(G.cache.dir);
}
/* Make the directory if needed */
if(access(G.cacheDir, F_OK)) {
if(ez_access(G.cache.dir, F_OK)) {
/* errno will be set if access() fails */
errno= 0;
ez_mkdir(G.cacheDir, 0750);
ez_mkdir(G.cache.dir, G.cache.dir_mode);
ez_chown(G.cache.dir, getuid(), G.gid);
}
if(G.flags & GLB_LONG_LISTING_MASK) {
@ -398,7 +406,7 @@ main(int argc, char **argv)
/* Check cache for logType directories not in our current map, and remove them */
{ /*---------------------------------------------------------------------*/
DIR *dir= ez_opendir(G.cacheDir);
DIR *dir= ez_opendir(G.cache.dir);
struct dirent *entry;
while((entry= ez_readdir(dir))) {
@ -412,7 +420,7 @@ main(int argc, char **argv)
continue;
/* Make the path with filename */
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/%s", G.cacheDir, entry->d_name);
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/%s", G.cache.dir, entry->d_name);
/* Remove unused directory & contents. */
ez_rmdir_recursive(S.fnameBuf);
@ -475,7 +483,7 @@ main(int argc, char **argv)
assert(S.lePtrArr);
MAP_fetchAllItems(&S.addr2logEntry_map, (void**)S.lePtrArr);
qsort(S.lePtrArr, nItems, sizeof(OFFENTRY*), logentry_count_qsort);
qsort(S.lePtrArr, nItems, sizeof(OFFENTRY*), logentry_latest_qsort);
/* Special processing for DNS lookups */
if(G.flags & GLB_DNS_LOOKUP_FLG) {
@ -525,27 +533,7 @@ main(int argc, char **argv)
if(G.flags & GLB_LIST_ADDR_FLG &&
!(G.flags & GLB_DNS_FILTER_BAD_FLG && e->dns.flags & PDNS_BAD_MASK))
{
const static struct bitTuple dns_flagsArr[]= {
{.name= "~", .bit= PDNS_FWD_FAIL_FLG},
{.name= "!!", .bit= PDNS_FWD_NONE_FLG},
{.name= "NXDOMAIN", .bit= PDNS_NXDOMAIN_FLG},
{.name= "SERVFAIL", .bit= PDNS_SERVFAIL_FLG},
{}
};
const static char *dns_fmt= "%-15s\t%5u/%-4d offenses %s [%s] %s %s\n",
*fmt= "%-15s\t%5u/%-4d offenses %s [%s]\n";
ez_fprintf(G.rpt.fh, e->dns.flags ? dns_fmt : fmt
, e->addr
, e->count
, nAllowed
, e->cntry[0] ? e->cntry : "--"
, bits2str(flags, BlockBitTuples)
, e->dns.name ? e->dns.name : ""
, bits2str(e->dns.flags, dns_flagsArr)
);
OFFENTRY_list(e, G.rpt.fh, flags, nAllowed);
}
} /*--- End of OFFENTRY processing ---*/
@ -599,10 +587,10 @@ main(int argc, char **argv)
if(!(G.flags & GLB_DONT_IPTABLE_FLG)) {
if(n2Block || n2Unblock) {
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/iptables", G.lockDir);
snprintf(S.fnameBuf, sizeof(S.fnameBuf), "%s/iptables", G.lock.dir);
/* Make sure the file exists by open()'ing */
S.iptablesLock_fd= ez_open(S.fnameBuf, O_CREAT|O_WRONLY|O_CLOEXEC, 0640);
assert(-1 != S.iptablesLock_fd);
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);
/* Get an exclusive lock on the lockfile */
ez_flock(S.iptablesLock_fd, LOCK_EX);
}
@ -684,6 +672,20 @@ abort:
/*==================================================================*/
/*============== Supporting functions ==============================*/
/*==================================================================*/
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);
}
static int
logentry_count_qsort(const void *p1, const void *p2)

View File

@ -26,6 +26,8 @@
#define _GNU_SOURCE
#include <regex.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include "cfgmap.h"
@ -59,6 +61,7 @@
#define IP6TABLES "/usr/sbin/ip6tables"
#define GEOIP_DB "/usr/share/GeoIP/GeoIP.dat"
#define GEOIP6_DB "/usr/share/GeoIP/GeoIPv6.dat"
#define GROUP_NAME "adm"
enum GlobalFlg_enum {
GLB_VERBOSE_FLG =1<<0,
@ -74,6 +77,13 @@ enum GlobalFlg_enum {
GLB_LONG_LISTING_MASK = GLB_LIST_CNTRY_FLG|GLB_LIST_ADDR_FLG
};
enum BlockedFlg_enum {
BLOCKED_FLG =1<<0,
WOULD_BLOCK_FLG =1<<1,
UNJUST_BLOCK_FLG =1<<2,
WHITELIST_FLG =1<<3
};
/* Singleton static object with global visibility */
extern struct Global {
@ -81,8 +91,21 @@ extern struct Global {
MAP logType_map;
char *cacheDir,
*lockDir;
struct {
char *dir;
mode_t dir_mode,
file_mode;
} cache;
struct {
char *dir;
mode_t dir_mode,
file_mode;
} lock;
/* This should be set to adm */
gid_t gid;
struct {
FILE *fh;
@ -99,6 +122,11 @@ extern struct Global {
const struct bitTuple *flags;
} bitTuples;
struct {
time_t time_t;
struct tm tm;
} begin;
} G;

160
ez_libc.c
View File

@ -92,8 +92,9 @@ FILE* _ez_popen (
const char *type
)
{
errno= 0;
FILE *rtn= popen (command, type);
if (!rtn) {
if (!rtn || errno) {
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "popen(\"%s\", \"%s\") failed", command, type);
abort();
}
@ -524,6 +525,7 @@ int _ez_open(
}
/***************************************************/
int _ez_access(
const char *fileName,
int lineNo,
@ -547,3 +549,159 @@ int _ez_access(
}
/***************************************************/
char *_ez_strptime(
const char *fileName,
int lineNo,
const char *funcName,
const char *s,
const char *format,
struct tm *tm
)
{
char *rtn= strptime (s, format, tm);
if(rtn) return rtn;
_eprintf(fileName, lineNo, funcName, "strptime(\"%s\",\"%s\") failed", s, format);
abort();
}
/***************************************************/
int _ez_seteuid(
const char *fileName,
int lineNo,
const char *funcName,
uid_t euid
)
{
int rtn= seteuid (euid);
if(0 == rtn) return 0;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "seteuid(%d) failed", (int)euid);
abort();
}
/***************************************************/
int _ez_setegid(
const char *fileName,
int lineNo,
const char *funcName,
gid_t egid
)
{
int rtn= setegid (egid);
if(0 == rtn) return 0;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "setegid(%d) failed", (int)egid);
abort();
}
/***************************************************/
struct group* _ez_getgrnam(
const char *fileName,
int lineNo,
const char *funcName,
const char *name
)
{
errno= 0;
struct group *rtn= getgrnam (name);
if(rtn) return rtn;
switch(errno) {
case EINTR:
case EIO:
case EMFILE:
case ENFILE:
case ENOMEM:
case ERANGE:
return NULL;
}
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "getgrnam(\"%s\") failed", name);
abort();
}
/***************************************************/
int _ez_chown(
const char *fileName,
int lineNo,
const char *funcName,
const char *pathname,
uid_t owner,
gid_t group
)
{
int rtn= chown (pathname, owner, group);
if(0 == rtn) return rtn;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "chown(\"%s\", %d, %d) failed", pathname, (int)owner, (int)group);
abort();
}
/***************************************************/
int _ez_fchown(
const char *fileName,
int lineNo,
const char *funcName,
int fd,
uid_t owner,
gid_t group
)
{
int rtn= fchown (fd, owner, group);
if(0 == rtn) return rtn;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "fchown() failed");
abort();
}
/***************************************************/
int _ez_fchmod(
const char *fileName,
int lineNo,
const char *funcName,
int fd,
mode_t mode
)
{
int rtn= fchmod (fd, mode);
if(0 == rtn) return rtn;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "fchmod() failed");
abort();
}
/***************************************************/
int _ez_setuid(
const char *fileName,
int lineNo,
const char *funcName,
uid_t uid
)
{
int rtn= setuid (uid);
if(0 == rtn) return 0;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "setuid(%d) failed", (int)uid);
abort();
}
/***************************************************/
int _ez_setgid(
const char *fileName,
int lineNo,
const char *funcName,
gid_t gid
)
{
int rtn= setgid (gid);
if(0 == rtn) return 0;
_sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "setgid(%d) failed", (int)gid);
abort();
}

View File

@ -30,17 +30,30 @@ glibc calls with boilerplate error handling.
#define _GNU_SOURCE
#include <dirent.h>
#include <grp.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ez_strptime(s, format, tm) \
_ez_strptime(__FILE__, __LINE__, __FUNCTION__, s, format, tm)
char *_ez_strptime(
const char *fileName,
int lineNo,
const char *funcName,
const char *s,
const char *format,
struct tm *tm
);
#define ez_access(pathname, mode) \
_ez_access(__FILE__, __LINE__, __FUNCTION__, pathname, mode)
int _ez_access(
@ -341,6 +354,83 @@ int _ez_flock (
int operation
);
#define ez_setuid(uid) \
_ez_setuid(__FILE__, __LINE__, __FUNCTION__, uid)
int _ez_setuid(
const char *fileName,
int lineNo,
const char *funcName,
uid_t uid
);
#define ez_setgid(gid) \
_ez_setgid(__FILE__, __LINE__, __FUNCTION__, gid)
int _ez_setgid(
const char *fileName,
int lineNo,
const char *funcName,
gid_t gid
);
#define ez_seteuid(euid) \
_ez_seteuid(__FILE__, __LINE__, __FUNCTION__, euid)
int _ez_seteuid(
const char *fileName,
int lineNo,
const char *funcName,
uid_t euid
);
#define ez_setegid(egid) \
_ez_setegid(__FILE__, __LINE__, __FUNCTION__, egid)
int _ez_setegid(
const char *fileName,
int lineNo,
const char *funcName,
gid_t egid
);
#define ez_getgrnam(name) \
_ez_getgrnam(__FILE__, __LINE__, __FUNCTION__, name)
struct group* _ez_getgrnam(
const char *fileName,
int lineNo,
const char *funcName,
const char *name
);
#define ez_chown(pathname, owner, group) \
_ez_chown(__FILE__, __LINE__, __FUNCTION__, pathname, owner, group)
int _ez_chown(
const char *fileName,
int lineNo,
const char *funcName,
const char *pathname,
uid_t owner,
gid_t group
);
#define ez_fchown(fd, owner, group) \
_ez_fchown(__FILE__, __LINE__, __FUNCTION__, fd, owner, group)
int _ez_fchown(
const char *fileName,
int lineNo,
const char *funcName,
int fd,
uid_t owner,
gid_t group
);
#define ez_fchmod(fd, mode) \
_ez_fchmod(__FILE__, __LINE__, __FUNCTION__, fd, mode)
int _ez_fchmod(
const char *fileName,
int lineNo,
const char *funcName,
int fd,
mode_t mode
);
#ifdef __cplusplus
}

View File

@ -145,7 +145,6 @@ int _ez_db_close(
u_int32_t flags
)
{
//eprintf("Closing database (%p)", db);
int rtn= db->close(db, flags);
if(!rtn) return 0;
@ -155,3 +154,22 @@ int _ez_db_close(
_sys_eprintf((const char*(*)(int))db_strerror, fileName, lineNo, funcName, "DB->close() failed");
abort();
}
/***************************************************/
int _ez_db_fd(
const char *fileName,
int lineNo,
const char *funcName,
DB *db,
int *fdp
)
{
int rtn= db->fd(db, fdp);
if(!rtn) return 0;
/* _sys_eprintf() will pass errno to db_sterror */
errno= rtn;
_sys_eprintf((const char*(*)(int))db_strerror, fileName, lineNo, funcName, "DB->fd() failed");
abort();
}

View File

@ -101,6 +101,16 @@ int _ez_db_close(
u_int32_t flags
);
#define ez_db_fd(db, fdp) \
_ez_db_fd(__FILE__, __LINE__, __FUNCTION__, db, fdp)
int _ez_db_fd(
const char *fileName,
int lineNo,
const char *funcName,
DB *db,
int *fdp
);
#ifdef __cplusplus
}
#endif

View File

@ -76,7 +76,9 @@ initialize (void)
}
fh= ez_popen(ipv->cmd, "r");
#ifdef qqDEBUG
unsigned count= 0;
#endif
while(ez_fgets(lbuf, sizeof(lbuf)-1, fh)) {
/* Filter all that looks uninteresting */
@ -93,9 +95,15 @@ initialize (void)
eprintf("WARNING: duplicate iptable entry for %s", addr);
else
MAP_addStrKey(&S.addr_map, addr, strdup(addr));
#ifdef qqDEBUG
++count;
#endif
}
ez_pclose(fh);
regfree(&re);
#ifdef qqDEBUG
eprintf("%s got %u entries", ipv->cmd, count);
#endif
}
}

View File

@ -81,7 +81,7 @@ LOGFILE_cache_constructor(
static char dbFname[PATH_MAX];
snprintf(dbFname, sizeof(dbFname), "%s.db", cacheFname);
if(0 == access(dbFname, F_OK)) {
if(0 == ez_access(dbFname, F_OK)) {
ez_db_create(&db, NULL, 0);
ez_db_open(db, NULL, dbFname, NULL, DB_BTREE, DB_RDONLY, 0664);
}
@ -157,18 +157,10 @@ LOGFILE_log_constructor(
lbuf[--line_len]= '\0';
/* Search for a match in the targets */
for(struct target *tg= h_protoType->targetArr; tg->pattern; ++tg) {
/* If there is no match, continue looking */
regmatch_t matchArr[2];
if(0 != regexec(&tg->re, lbuf, 2, matchArr, 0) || -1 == matchArr[1].rm_so)
continue;
for(Target *tg= h_protoType->targetArr; Target_is_init(tg); ++tg) {
static char addr[128];
unsigned len;
len= matchArr[1].rm_eo - matchArr[1].rm_so;
strncpy(addr, lbuf+matchArr[1].rm_so, sizeof(addr)-1);
addr[MIN(len, sizeof(addr)-1)]= '\0';
if(Target_scan(tg, addr, sizeof(addr), lbuf)) continue;
OFFENTRY *e= MAP_findStrItem(&self->addr.offEntry_map, addr);
if(!e) {
@ -179,7 +171,15 @@ LOGFILE_log_constructor(
MAP_addStrKey(&self->addr.offEntry_map, e->addr, e);
}
OFFENTRY_register(e);
/* See if we can extract the date */
time_t when= 0;
if(TS_is_prepared(&h_protoType->ts)) {
if(TS_scan(&h_protoType->ts, &when, lbuf, &G.begin.tm)) {
eprintf("WARNING: TS_scan() failed.");
}
}
OFFENTRY_register(e, Target_severity(tg), when);
{ /* Keep ObsvTpl record of offense line in log file */
ObsvTpl *ot= MAP_findStrItem(&self->addr.obsvTpl_map, e->addr);
@ -190,9 +190,8 @@ LOGFILE_log_constructor(
}
ObsvTpl_addObsv(ot, pos, line_len);
}
}
} /* End of Target loop */
} /* End of line reading loop */
/* Take car of possible address reporting */
@ -244,6 +243,8 @@ LOGFILE_writeCache(LOGFILE *self, const char *fname)
DB *dbh= NULL;
FILE *fh= ez_fopen(fname, "w");
ez_fchown(fileno(fh), getuid(), G.gid);
ez_fchmod(fileno(fh), G.cache.file_mode);
/* Writes all OFFENTRY object to fh */
rc= MAP_visitAllEntries(&self->addr.offEntry_map, (int(*)(void*,void*))OFFENTRY_cacheWrite, fh);
@ -258,7 +259,8 @@ LOGFILE_writeCache(LOGFILE *self, const char *fname)
ez_db_create(&dbh, NULL, 0);
assert(dbh);
ez_db_open(dbh, NULL, dbFname, NULL, DB_BTREE, DB_CREATE, 0664);
ez_db_open(dbh, NULL, dbFname, NULL, DB_BTREE, DB_CREATE, G.cache.file_mode);
ez_chown(dbFname, getuid(), G.gid);
/* This will write all entries in the map do the database */
MAP_visitAllEntries(&self->addr.obsvTpl_map, (int(*)(void*,void*))ObsvTpl_db_put, dbh);

View File

@ -118,9 +118,9 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto)
{ /* Compute md5sum of all patterns put together */
MD5_CTX md5ctx;
MD5_Init(&md5ctx);
const struct target *t;
for(t= proto->targetArr; t->pattern; ++t) {
MD5_Update(&md5ctx, t->pattern, strlen(t->pattern));
const Target *t;
for(t= proto->targetArr; t->rxArr; ++t) {
Target_MD5_update(t, &md5ctx);
}
static unsigned char sum[16];
MD5_Final(sum, &md5ctx);
@ -131,7 +131,7 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto)
/* At this point we can call LOGTYPE_cacheName() */
/* Keep the name of this logType's cache directory */
rc= snprintf(CacheDname, sizeof(CacheDname), "%s/%s", G.cacheDir, LOGTYPE_cacheName(self));
rc= snprintf(CacheDname, sizeof(CacheDname), "%s/%s", G.cache.dir, LOGTYPE_cacheName(self));
{ /*** Compute md5sum for each log file, then scan of read from cache ***/
@ -206,7 +206,7 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto)
LOGFILE *f;
/* Use the cache, if available */
if(!access(CacheFname, F_OK))
if(!ez_access(CacheFname, F_OK))
{
/* Construct object from cache file */
@ -215,8 +215,9 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto)
} else { /* Scan the log file, write to new cache */
if(access(CacheDname, F_OK)) {
ez_mkdir(CacheDname, 0770);
if(ez_access(CacheDname, F_OK)) {
ez_mkdir(CacheDname, G.cache.dir_mode);
ez_chown(CacheDname, getuid(), G.gid);
}
/* Construct object from log file */
@ -328,26 +329,43 @@ LOGTYPE_init(CFGMAP *h_map, char *pfix)
}
}
{ /*--- Get all regex entries ---*/
snprintf(symBuf, len, "%s\\REGEX", pfix);
{ /*--- Get the TIMESTAMP entry ---*/
snprintf(symBuf, len, "%s\\TIMESTAMP", pfix);
const char *val= CFGMAP_find_last_value(h_map, symBuf);
if(val) {
snprintf(symBuf, len, "%s\\%s", pfix, val);
if(TS_init(&proto.ts, h_map, symBuf)) {
eprintf("ERROR: TS_init() failed.");
goto abort;
}
}
}
{ /*--- Get all TARGET entries ---*/
snprintf(symBuf, len, "%s\\TARGET", pfix);
unsigned nFound= CFGMAP_find_tuples(h_map, rtn_arr, symBuf);
/* Get enough object to include a terminating entry */
struct target targetArr[nFound+1];
Target targetArr[nFound+1];
/* Clear all bits in array */
memset(targetArr, 0, sizeof(struct target)*(nFound+1));
memset(targetArr, 0, sizeof(Target)*(nFound+1));
proto.targetArr= targetArr;
for(unsigned i= 0; i < nFound; ++i) {
const struct CFGMAP_tuple *tpl= rtn_arr + i;
struct target *tg= targetArr + i;
tg->pattern= tpl->value;
if(regex_compile(&tg->re, tg->pattern, REG_EXTENDED)) {
eprintf("ERROR: regex_compile(\"%s\") failed.", tg->pattern);
Target *tg= targetArr + i;
snprintf(symBuf, len, "%s\\%s", pfix, tpl->value);
if(Target_init(tg, h_map, symBuf)) {
eprintf("ERROR: Target_init() failed.");
goto abort;
}
}
}
/* Create the LOGTYPE object, place in global map */
@ -362,13 +380,17 @@ LOGTYPE_init(CFGMAP *h_map, char *pfix)
/* Place int the global map */
MAP_addStrKey(&G.logType_map, LOGTYPE_cacheName(obj), obj);
/* Free regex pattern data */
/* Free regex stuff */
for(unsigned i= 0; i < nFound; ++i) {
struct target *tg= targetArr + i;
regfree(&tg->re);
Target *tg= targetArr + i;
Target_destructor(tg);
}
}
if(TS_is_prepared(&proto.ts)) {
TS_destructor(&proto.ts);
}
rtn= 0;
abort:
return rtn;

View File

@ -24,14 +24,8 @@
#include "ban2fail.h"
#include "cfgmap.h"
#include "map.h"
/*==================================================================*/
/*===================== target =====================================*/
/*==================================================================*/
struct target {
const char *pattern;
regex_t re;
};
#include "target.h"
#include "timestamp.h"
/*==================================================================*/
/*===================== log file prototype =========================*/
@ -39,7 +33,8 @@ struct target {
struct logProtoType {
const char *dir,
*pfix;
struct target *targetArr;
TS ts; /* For log entry timestamps */
Target *targetArr;
};
/*==================================================================*/

View File

@ -74,16 +74,29 @@ OFFENTRY_cache_constructor(OFFENTRY *self, const char *cacheFileEntry)
common_constructor(self);
int rc= sscanf(cacheFileEntry, "%u %45s %2s"
,&self->count
,self->addr
,self->cntry);
long long ll;
if(2 > rc) {
int rc= sscanf(cacheFileEntry, "%u %u %lld %45s %2s"
, &self->count
, &self->severity
, &ll
, self->addr
, self->cntry
);
if(4 > rc) {
eprintf("ERROR: failed to interpret \"%s\"", cacheFileEntry);
goto abort;
}
self->latest= ll;
#ifdef qqDEBUG
if(self->severity) {
eprintf("%s : %u", self->addr, self->severity);
}
#endif
rtn= self;
abort:
return rtn;
@ -102,13 +115,28 @@ OFFENTRY_destructor(OFFENTRY *self)
}
void
OFFENTRY_register(OFFENTRY *self)
OFFENTRY_register(OFFENTRY *self, unsigned severity, time_t when)
/********************************************************
* Register the current failure try.
*/
{
/* Keep track of count */
++self->count;
#ifdef qqDEBUG
if(severity) {
eprintf("Severity= %u", severity);
}
#endif
/* Keep track of most severe match */
if(self->severity < severity)
self->severity= severity;
/* Keep track of the most recent offense time */
if(self->latest < when)
self->latest= when;
}
@ -118,8 +146,10 @@ OFFENTRY_cacheWrite(OFFENTRY *self, FILE *fh)
* Write to the cache file in a form we can read later.
*/
{
ez_fprintf(fh, "%u %s %s\n"
ez_fprintf(fh, "%u %u %lld %s %s\n"
, self->count
, self->severity
, (long long)self->latest
, self->addr
, self->cntry
);
@ -133,11 +163,12 @@ OFFENTRY_print(OFFENTRY *self, FILE *fh)
*/
{
ez_fprintf(fh,
"\tLOGENTRY %p { addr= \"%s\", cntry= \"%2s\" count= %u }\n"
"\tLOGENTRY %p { addr= \"%s\", cntry= \"%2s\" count= %u, severity= %u }\n"
, self
, self->addr
, self->cntry
, self->count
, self->severity
);
return 0;
}
@ -158,6 +189,12 @@ OFFENTRY_map_addr(OFFENTRY *self, MAP *h_rtnMap)
}
e->count += self->count;
if(e->severity < self->severity)
e->severity= self->severity;
if(e->latest < self->latest)
e->latest= self->latest;
return 0;
}
@ -171,3 +208,44 @@ OFFENTRY_offenseCount(OFFENTRY *self, unsigned *h_sum)
//eprintf("%s numItems= %u", self->addr, PTRVEC_numItems(&self->rptObj_vec));
return 0;
}
int
OFFENTRY_list(OFFENTRY *self, FILE *fh, int flags, unsigned nAllowed)
/********************************************************
* Print in listing form
*/
{
static const struct bitTuple BlockBitTuples[]= {
{.name= "BLK", .bit= BLOCKED_FLG},
{.name= "+blk+", .bit= WOULD_BLOCK_FLG},
{.name= "-blk-", .bit= UNJUST_BLOCK_FLG},
{.name= "WL", .bit= WHITELIST_FLG},
{/* Terminating member */}
};
const static struct bitTuple dns_flagsArr[]= {
{.name= "~", .bit= PDNS_FWD_FAIL_FLG},
{.name= "!!", .bit= PDNS_FWD_NONE_FLG},
{.name= "NXDOMAIN", .bit= PDNS_NXDOMAIN_FLG},
{.name= "SERVFAIL", .bit= PDNS_SERVFAIL_FLG},
{}
};
const static char *dns_fmt= "%u %-13s %-15s\t%5u/%-4d offenses %s [%s] %s %s\n",
*fmt= "%u %-13s %-15s\t%5u/%-4d offenses %s [%s]\n";
ez_fprintf(fh, self->dns.flags ? dns_fmt : fmt
, self->severity
, self->latest ? local_strftime(&self->latest, "%b %d %H:%M") : ""
, self->addr
, self->count
, nAllowed
, self->cntry[0] ? self->cntry : "--"
, bits2str(flags, BlockBitTuples)
, self->dns.name ? self->dns.name : ""
, bits2str(self->dns.flags, dns_flagsArr)
);
return 0;
}

View File

@ -29,10 +29,14 @@
/* One of these for each offense found in a log file */
typedef struct _OFFENTRY {
unsigned logfile_ndx;
unsigned count,
severity;
time_t latest;
char addr[46],
cntry[3];
unsigned count;
/* This data populated by PDNS_lookup() */
struct {
@ -78,19 +82,11 @@ OFFENTRY_destructor(OFFENTRY *self);
*/
void
OFFENTRY_register(OFFENTRY *self);
OFFENTRY_register(OFFENTRY *self, unsigned severity, time_t when);
/********************************************************
* Register the current failure try.
*/
#if 0
int
OFFENTRY_is_blocked_country(const OFFENTRY *self);
/********************************************************
* Return 1 if the country is blocked, or 0.
*/
#endif
int
OFFENTRY_cacheWrite(OFFENTRY *self, FILE *fh);
/********************************************************
@ -103,6 +99,12 @@ OFFENTRY_print(OFFENTRY *self, FILE *fh);
* Print a human readable representation of *self.
*/
int
OFFENTRY_list(OFFENTRY *self, FILE *fh, int flags, unsigned nAllowed);
/********************************************************
* Print in listing form
*/
int
OFFENTRY_map_addr(OFFENTRY *self, MAP *h_rtnMap);
/********************************************************

140
target.c Normal file
View File

@ -0,0 +1,140 @@
/***************************************************************************
* 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. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <regex.h>
#include <stdlib.h>
#include "target.h"
#include "util.h"
int
Target_init(Target *self, CFGMAP *h_map, const char *pfix)
/********************************************************
* Prepare for use from config map
*/
{
int rtn= -1;
memset(self, 0, sizeof(*self));
self->flags |= TARGET_INIT_FLG;
unsigned arr_sz= CFGMAP_numTuples(h_map);
struct CFGMAP_tuple rtn_arr[arr_sz];
size_t len= strlen(pfix)+1024;
char symBuf[len];
{ /*--- Check for "SEVERITY" symbol ---*/
snprintf(symBuf, len, "%s\\SEVERITY", pfix);
if(CFGMAP_query_last_uint(h_map, &self->severity, 0, symBuf)) {
eprintf("ERROR: cannot interpret \"SEVERITY\" entry for REGEX %s", pfix);
goto abort;
}
#ifdef qqDEBUG
eprintf("%s = %u", symBuf, self->severity);
#endif
}
{ /*--- Get all REGEX entries ---*/
snprintf(symBuf, len, "%s\\REGEX", pfix);
self->nRx= CFGMAP_find_tuples(h_map, rtn_arr, symBuf);
self->rxArr= calloc(self->nRx, sizeof(struct TargetRx));
assert(self->rxArr);
for(unsigned i= 0; i < self->nRx; ++i) {
const struct CFGMAP_tuple *tpl= rtn_arr + i;
struct TargetRx *rx= self->rxArr + i;
/* Compile regular expression */
rx->pattern= tpl->value;
if(regex_compile(&rx->re, rx->pattern, REG_EXTENDED)) {
eprintf("ERROR: regex_compile(\"%s\") failed.", rx->pattern);
goto abort;
}
}
}
rtn= 0;
abort:
return rtn;
}
void*
Target_destructor(Target *self)
/********************************************************
* Free resources.
*/
{
if(self->nRx && self->rxArr) {
for(unsigned i= 0; i < self->nRx; ++i) {
struct TargetRx *rx= self->rxArr + i;
regfree(&rx->re);
}
free(self->rxArr);
}
return self;
}
int
Target_scan(const Target *self, char *rsltBuf, unsigned buf_sz, const char *str)
/********************************************************
* Scan a string to obtain the address.
*/
{
int rtn= EOF;
/* Exit on first match */
for(unsigned i= 0; i < self->nRx; ++i) {
const struct TargetRx *rx= self->rxArr+i;
regmatch_t matchArr[2];
if(0 != regexec(&rx->re, str, 2, matchArr, 0) || -1 == matchArr[1].rm_so)
continue;
unsigned len= matchArr[1].rm_eo - matchArr[1].rm_so;
strncpy(rsltBuf, str+matchArr[1].rm_so, buf_sz-1);
rsltBuf[MIN(len, buf_sz-1)]= '\0';
rtn= 0;
break;
}
abort:
return rtn;
}
int
Target_MD5_update(const Target *self, MD5_CTX *ctx)
/********************************************************
* For computing MD5 checksum of cumulative patterns.
*/
{
for(unsigned i= 0; i < self->nRx; ++i) {
const struct TargetRx *rx= self->rxArr+i;
MD5_Update(ctx, rx->pattern, strlen(rx->pattern));
}
return 0;
}

82
target.h Normal file
View File

@ -0,0 +1,82 @@
/***************************************************************************
* 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. *
***************************************************************************/
#ifndef TARGET_H
#define TARGET_H
#define _GNU_SOURCE
#include <openssl/md5.h>
#include "cfgmap.h"
struct TargetRx {
const char *pattern;
regex_t re;
};
typedef struct _Target {
enum {
TARGET_INIT_FLG=1<<0
} flags;
unsigned severity,
nRx;
struct TargetRx *rxArr;
} Target;
#define Target_severity(s) \
((const unsigned)(s)->severity)
#define Target_is_init(s) \
((const int)(s)->flags)
#ifdef __cplusplus
extern "C" {
#endif
int
Target_init(Target *self, CFGMAP *h_map, const char *pfix);
/********************************************************
* Prepare for use from config map
*/
void*
Target_destructor(Target *self);
/********************************************************
* Free resources.
*/
int
Target_scan(const Target *self, char *rsltBuf, unsigned buf_sz, const char *str);
/********************************************************
* Scan a string to obtain the address.
*/
int
Target_MD5_update(const Target *self, MD5_CTX *ctx);
/********************************************************
* For computing MD5 checksum of cumulative patterns.
*/
#ifdef __cplusplus
}
#endif
#endif

146
timestamp.c Normal file
View File

@ -0,0 +1,146 @@
/***************************************************************************
* 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. *
***************************************************************************/
#define _GNU_SOURCE
#include "ez_libc.h"
#include "timestamp.h"
#include "util.h"
enum Flags {
GUESS_YR_FLG= 1<<0
};
const static struct bitTuple FlagsBitTupleArr[]= {
{.name= "GUESS_YEAR", .bit= GUESS_YR_FLG},
{/* Terminating member */}
};
int
TS_init(TS *self, CFGMAP *h_map, const char *pfix)
/********************************************************
* Initialize timestamp from config map.
*/
{
int rtn= -1;
memset(self, 0, sizeof(*self));
size_t len= strlen(pfix)+1024;
char symBuf[len];
{ /*--- Check for "REGEX" symbol ---*/
snprintf(symBuf, len, "%s\\REGEX", pfix);
/* CFGMAP is left in place, so we don't need to strdup() */
self->pattern= CFGMAP_find_last_value(h_map, symBuf);
if(!self->pattern) {
eprintf("ERROR: cannot find \"REGEX\" entry for ENTRY_TIMESTAMP %s", pfix);
goto abort;
}
if(regex_compile(&self->re, self->pattern, REG_EXTENDED)) {
eprintf("ERROR: regex_compile(\"%s\") failed.", self->pattern);
goto abort;
}
}
{ /*--- Check for "STRPTIME" symbol ---*/
snprintf(symBuf, len, "%s\\STRPTIME", pfix);
/* CFGMAP is left in place, so we don't need to strdup() */
self->strptime_fmt= CFGMAP_find_last_value(h_map, symBuf);
if(!self->strptime_fmt) {
eprintf("ERROR: cannot find \"STRPTIME\" entry for ENTRY_TIMESTAMP %s", pfix);
goto abort;
}
}
{ /*--- Check for "FLAGS" symbol ---*/
const char *flagStr;
snprintf(symBuf, len, "%s\\FLAGS", pfix);
flagStr= CFGMAP_find_last_value(h_map, symBuf);
/* This is optional */
if(flagStr) {
int rc= str2bits(&self->flags, flagStr, FlagsBitTupleArr);
if(rc) {
eprintf("ERROR: cannot interpret \"FLAGS\" entry for ENTRY_TIMESTAMP %s", pfix);
goto abort;
}
}
}
rtn= 0;
abort:
return rtn;
}
void*
TS_destructor(TS *self)
/********************************************************
* Free resources.
*/
{
if(self->pattern)
regfree(&self->re);
return self;
}
int
TS_scan(const TS *self, time_t *rslt, const char *str, const struct tm *pTmRef)
/********************************************************
* Scan a string to obtain the timestamp.
*/
{
int rtn= -1;
/* If there is no match, continue looking */
regmatch_t matchArr[2];
if(0 != regexec(&self->re, str, 2, matchArr, 0) || -1 == matchArr[1].rm_so) {
eprintf("ERROR: failed to identify date in \"%s\"", str);
goto abort;
}
unsigned len= matchArr[1].rm_eo - matchArr[1].rm_so;
static char date[128];
strncpy(date, str+matchArr[1].rm_so, sizeof(date)-1);
date[MIN(len, sizeof(date)-1)]= '\0';
static struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_isdst= -1;
const char *lc= ez_strptime(date, self->strptime_fmt, &tm);
/* We may have to guess the year */
if(self->flags & GUESS_YR_FLG) {
tm.tm_year= pTmRef->tm_year;
if(tm.tm_mon > pTmRef->tm_mon)
--tm.tm_year;
}
*rslt= mktime(&tm);
//eprintf("Date string= \"%s\", lc= \"%s\", =? \"%s\"", date, lc, ctime(rslt));
rtn= 0;
abort:
return rtn;
}

71
timestamp.h Normal file
View File

@ -0,0 +1,71 @@
/***************************************************************************
* 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. *
***************************************************************************/
#ifndef TIMESTAMP_H
#define TIMESTAMP_H
#define _GNU_SOURCE
#include <regex.h>
#include <time.h>
#include "cfgmap.h"
typedef struct _TS {
const char *pattern;
regex_t re;
const char *strptime_fmt;
int64_t flags;
} TS;
#ifdef __cplusplus
extern "C" {
#endif
#define TS_is_prepared(s) \
((s)->pattern ? 1 : 0)
int
TS_init(TS *self, CFGMAP *h_map, const char *pfix);
/********************************************************
* Initialize timestamp from config map.
*/
void*
TS_destructor(TS *self);
/********************************************************
* Free resources.
*/
int
TS_scan(const TS *self, time_t *rslt, const char *str, const struct tm *pTmRef);
/********************************************************
* Scan a string to obtain the timestamp.
*/
#ifdef __cplusplus
}
#endif
#endif