diff --git a/src/ipaddr.c b/src/ipaddr.c index 9badd45..7b202a5 100644 --- a/src/ipaddr.c +++ b/src/ipaddr.c @@ -8,11 +8,10 @@ #include "ipaddr.h" f2b_ipaddr_t * -f2b_ipaddr_create(const char *addr, size_t matches) { +f2b_ipaddr_create(const char *addr) { f2b_ipaddr_t *a = NULL; assert(addr != NULL); - assert(matches != 0); if ((a = calloc(1, sizeof(f2b_ipaddr_t))) != NULL) { strlcpy(a->text, addr, sizeof(a->text)); @@ -25,9 +24,6 @@ f2b_ipaddr_create(const char *addr, size_t matches) { if (inet_pton(a->type, addr, &a->binary.v6) < 1) goto cleanup; } - - if (f2b_matches_create(&a->matches, matches) == false) - goto cleanup; } return a; @@ -40,7 +36,7 @@ void f2b_ipaddr_destroy(f2b_ipaddr_t *ipaddr) { assert(ipaddr != NULL); - f2b_matches_destroy(&ipaddr->matches); + f2b_matches_flush(&ipaddr->matches); free(ipaddr); } @@ -57,7 +53,7 @@ f2b_ipaddr_status(f2b_ipaddr_t *addr, char *res, size_t ressize) { "banned_at: %u\n" "release_at: %u\n"; snprintf(res, ressize, fmt, addr->text, addr->banned ? "yes" : "no", - addr->bancount, addr->matches.used, addr->lastseen, addr->banned_at, addr->release_at); + addr->bancount, addr->matches.count, addr->lastseen, addr->banned_at, addr->release_at); } f2b_ipaddr_t * diff --git a/src/ipaddr.h b/src/ipaddr.h index b3af2d8..8420e2a 100644 --- a/src/ipaddr.h +++ b/src/ipaddr.h @@ -44,10 +44,10 @@ typedef struct f2b_ipaddr_t { /** * @brief Create address record and fill related metadata * @param addr Textual address - * @param max_matches Maximum matches count * @returns Pointer to address or NULL no error */ -f2b_ipaddr_t * f2b_ipaddr_create (const char *addr, size_t max_matches); +f2b_ipaddr_t * f2b_ipaddr_create (const char *addr); + /** * @brief Free address metadata * @param ipaddr Pointer to f2b_ipaddr_t struct diff --git a/src/jail.c b/src/jail.c index 5afcb97..22b5e4b 100644 --- a/src/jail.c +++ b/src/jail.c @@ -145,8 +145,7 @@ f2b_jail_ban(f2b_jail_t *jail, f2b_ipaddr_t *addr) { assert(jail != NULL); assert(addr != NULL); - addr->matches.hits = 0; - addr->matches.used = 0; + f2b_matches_flush(&addr->matches); addr->banned = true; addr->banned_at = addr->lastseen; @@ -224,6 +223,7 @@ f2b_jail_find(f2b_jail_t *list, const char *name) { size_t f2b_jail_process(f2b_jail_t *jail) { + f2b_match_t *match = NULL; f2b_ipaddr_t *prev = NULL; f2b_ipaddr_t *addr = NULL; size_t processed = 0; @@ -249,7 +249,7 @@ f2b_jail_process(f2b_jail_t *jail) { jail->matchcount++; addr = f2b_addrlist_lookup(jail->ipaddrs, matchbuf); if (!addr) { - addr = f2b_ipaddr_create(matchbuf, jail->maxretry); + addr = f2b_ipaddr_create(matchbuf); jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr); f2b_log_msg(log_debug, "jail '%s': found new ip %s", jail->name, matchbuf); } @@ -259,18 +259,19 @@ f2b_jail_process(f2b_jail_t *jail) { f2b_log_msg(log_warn, "jail '%s': ip %s was already banned", jail->name, matchbuf); continue; } - if (jail->incr_findtime > 0 && addr->matches.hits > jail->maxretry) { + match = f2b_match_create(now); + if (jail->incr_findtime > 0 && addr->matches.count > jail->maxretry) { findtime = now - jail->findtime; - findtime -= (int) ((addr->matches.hits - jail->maxretry) * + findtime -= (int) ((addr->matches.count - jail->maxretry) * (jail->findtime * jail->incr_findtime)); } else { findtime = now - jail->findtime; } f2b_matches_expire(&addr->matches, findtime); - f2b_matches_append(&addr->matches, now); - if (addr->matches.used < jail->maxretry) { + f2b_matches_append(&addr->matches, match); + if (addr->matches.count < jail->maxretry) { f2b_log_msg(log_info, "jail '%s': new match for ip %s (%zu/%zu)", - jail->name, matchbuf, addr->matches.used, addr->matches.max); + jail->name, matchbuf, addr->matches.count, jail->maxretry); continue; } /* limit reached, ban ip */ @@ -412,7 +413,7 @@ f2b_jail_start(f2b_jail_t *jail) { /* error occured, must be already logged, just drop flag */ jail->flags &= ~JAIL_HAS_STATE; } else { - jail->ipaddrs = f2b_statefile_load(jail->sfile, jail->maxretry); + jail->ipaddrs = f2b_statefile_load(jail->sfile); } } @@ -525,6 +526,7 @@ f2b_jail_cmd_set(char *res, size_t ressize, f2b_jail_t *jail, const char *param, */ void f2b_jail_cmd_ip_xxx(char *res, size_t ressize, f2b_jail_t *jail, int op, const char *ip) { + f2b_match_t *match = NULL; f2b_ipaddr_t *addr = NULL; assert(res != NULL); @@ -536,13 +538,15 @@ f2b_jail_cmd_ip_xxx(char *res, size_t ressize, f2b_jail_t *jail, int op, const c if (op > 0) { /* ban */ time_t now = time(NULL); - addr = f2b_ipaddr_create(ip, jail->maxretry); + addr = f2b_ipaddr_create(ip); if (!addr) { snprintf(res, ressize, "can't parse ip address: %s", ip); return; } addr->lastseen = now; - f2b_matches_append(&addr->matches, now); + match = f2b_match_create(now); + f2b_matches_append(&addr->matches, match); + f2b_matches_flush(&addr->matches); jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr); if (jail->flags & JAIL_HAS_STATE) jail->sfile->need_save = true; diff --git a/src/matches.c b/src/matches.c index 3e74cbe..251c94b 100644 --- a/src/matches.c +++ b/src/matches.c @@ -7,54 +7,65 @@ #include "common.h" #include "matches.h" -bool -f2b_matches_create(f2b_matches_t *m, size_t max) { - assert(m != NULL); - assert(max != 0); +f2b_match_t * +f2b_match_create(time_t t) { + f2b_match_t *m; - if ((m->times = calloc(max, sizeof(time_t))) == NULL) + if ((m = calloc(1, sizeof(f2b_match_t))) == NULL) return false; - - m->used = 0; - m->max = max; - return true; + m->time = t; + return m; } void -f2b_matches_destroy(f2b_matches_t *m) { - assert(m != NULL); +f2b_matches_flush(f2b_matches_t *ms) { + f2b_match_t *head, *tmp; + assert(ms != NULL); - free(m->times); - m->times = NULL; - m->used = 0; - m->max = 0; + head = ms->list; + while (head != NULL) { + tmp = head; + head = head->next; + free(tmp); + } + memset(ms, 0x0, sizeof(f2b_matches_t)); } -bool -f2b_matches_append(f2b_matches_t *m, time_t t) { +void +f2b_matches_append(f2b_matches_t *ms, f2b_match_t *m) { + assert(ms != NULL); assert(m != NULL); - m->hits++; - - if (m->used >= m->max) - return false; - - m->times[m->used] = t; - m->used++; - return true; + m->next = ms->list; + ms->list = m; + ms->count++; + ms->last = m->time > ms->last ? m->time : ms->last; } void -f2b_matches_expire(f2b_matches_t *m, time_t t) { - assert(m != NULL); +f2b_matches_expire(f2b_matches_t *ms, time_t before) { + f2b_match_t *prev, *node, *next; + assert(ms != NULL); - for (size_t i = 0; i < m->used; ) { - if (m->times[i] > t) { - i++; + if (ms->list == NULL) + return; + + node = ms->list; + prev = NULL; + while (node != NULL) { + if (node->time > before) { + prev = node; node = node->next; continue; } - m->used--; - m->times[i] = m->times[m->used]; - m->times[m->used] = 0; + next = node->next; + free(node); + if (prev) { + prev->next = next; + } else { + ms->list = next; + } + node = next; + ms->count--; } + ms->last = ms->list ? ms->list->time : 0; } diff --git a/src/matches.h b/src/matches.h index d04a793..ac08368 100644 --- a/src/matches.h +++ b/src/matches.h @@ -12,38 +12,46 @@ * This file contains definition of ipaddr matches struct and related routines */ +typedef struct f2b_match_t { + struct f2b_match_t *next; + time_t time; + /* more fields? */ +} f2b_match_t; + /** matches container */ typedef struct { - size_t hits; /**< how many times this ip matched by filter */ - size_t max; /**< max matches that this in struct can contain */ - size_t used; /**< currently used matches count */ - time_t *times; /**< dynamic unix timestamps array */ + size_t count; /**< Count of entries in linked list */ + time_t last; /**< latest match time */ + f2b_match_t *list; /**< linked list */ } f2b_matches_t; /** - * @brief Init matches struct, allocate memory for @a hits - * @param m Pointer to struct - * @param max Max expected matches count - * @returns true on success, false otherwise + * @brief Allocate memory for new match struct & initialize it + * @param t Match time + * @returns pointer to allocated struct */ -bool f2b_matches_create (f2b_matches_t *m, size_t max); +f2b_match_t * f2b_match_create(time_t t); + /** - * @brief Destroy matches struct and free memory - * @param m Pointer to struct + * @brief Clean list of matches and free memory + * @param ms Pointer to struct */ -void f2b_matches_destroy(f2b_matches_t *m); +void f2b_matches_flush(f2b_matches_t *ms); + /** * @brief Push new match time to struct * @param m Pointer to struct - * @param t Match time + * @param time Match time + * @param score Match score * @returns true on success, false if capacity exceeded */ -bool f2b_matches_append (f2b_matches_t *m, time_t t); +void f2b_matches_append (f2b_matches_t *ms, f2b_match_t *m); + /** * @brief Remove matches before given time * @param m Pointer to struct - * @param t Start time + * @param before Start time */ -void f2b_matches_expire (f2b_matches_t *m, time_t t); +void f2b_matches_expire (f2b_matches_t *m, time_t before); #endif /* F2B_MATCHES_H_ */ diff --git a/src/statefile.c b/src/statefile.c index 90e5075..5eef53b 100644 --- a/src/statefile.c +++ b/src/statefile.c @@ -51,7 +51,7 @@ f2b_statefile_destroy(f2b_statefile_t *sf) { } f2b_ipaddr_t * -f2b_statefile_load(f2b_statefile_t *sf, size_t matches) { +f2b_statefile_load(f2b_statefile_t *sf) { const int fields = 3; const char *format = "%48s %u %u"; /* 48 == IPADDR_MAX == sizeof(addr) */ f2b_ipaddr_t *addrlist = NULL, *ipaddr = NULL; @@ -76,7 +76,7 @@ f2b_statefile_load(f2b_statefile_t *sf, size_t matches) { f2b_log_msg(log_warn, "can't parse, ignoring line: %s", buf); continue; } - if ((ipaddr = f2b_ipaddr_create(addr, matches)) == NULL) { + if ((ipaddr = f2b_ipaddr_create(addr)) == NULL) { f2b_log_msg(log_warn, "can't parse addr: %s", addr); continue; } diff --git a/src/statefile.h b/src/statefile.h index 595f164..62e3bdc 100644 --- a/src/statefile.h +++ b/src/statefile.h @@ -19,7 +19,7 @@ void f2b_statefile_destroy(f2b_statefile_t *sf); f2b_ipaddr_t * -f2b_statefile_load(f2b_statefile_t *sf, size_t matches); +f2b_statefile_load(f2b_statefile_t *sf); bool f2b_statefile_save(f2b_statefile_t *sf, f2b_ipaddr_t *addrlist); diff --git a/t/t_ipaddr.c b/t/t_ipaddr.c index 4ea76b7..76ce094 100644 --- a/t/t_ipaddr.c +++ b/t/t_ipaddr.c @@ -13,17 +13,16 @@ int main() { addr = f2b_addrlist_lookup(list, "127.0.0.1"); assert(addr == NULL); - addr = f2b_ipaddr_create("400.400.400.400", 15); + addr = f2b_ipaddr_create("400.400.400.400"); assert(addr == NULL); - addr = f2b_ipaddr_create("127.0.0.1", 15); + addr = f2b_ipaddr_create("127.0.0.1"); assert(addr != NULL); assert(addr->type == AF_INET); assert(addr->next == NULL); assert(strncmp(addr->text, "127.0.0.1", sizeof(addr->text)) == 0); - assert(addr->matches.times != NULL); - assert(addr->matches.max == 15); - assert(addr->matches.used == 0); + assert(addr->matches.list != NULL); + assert(addr->matches.count == 0); list = f2b_addrlist_append(list, addr); assert(list != NULL); @@ -35,15 +34,15 @@ int main() { list = f2b_addrlist_remove(list, "127.0.0.1"); assert(list == NULL); - assert((addr = f2b_ipaddr_create("127.0.0.1", 15)) != NULL); + assert((addr = f2b_ipaddr_create("127.0.0.1")) != NULL); assert((list = f2b_addrlist_append(list, addr)) != NULL); - assert((addr = f2b_ipaddr_create("127.0.0.2", 15)) != NULL); + assert((addr = f2b_ipaddr_create("127.0.0.2")) != NULL); assert((list = f2b_addrlist_append(list, addr)) != NULL); - assert((addr = f2b_ipaddr_create("127.0.0.3", 15)) != NULL); + assert((addr = f2b_ipaddr_create("127.0.0.3")) != NULL); assert((list = f2b_addrlist_append(list, addr)) != NULL); - assert((addr = f2b_ipaddr_create("127.0.0.4", 15)) != NULL); + assert((addr = f2b_ipaddr_create("127.0.0.4")) != NULL); assert((list = f2b_addrlist_append(list, addr)) != NULL); - assert((addr = f2b_ipaddr_create("127.0.0.5", 15)) != NULL); + assert((addr = f2b_ipaddr_create("127.0.0.5")) != NULL); assert((list = f2b_addrlist_append(list, addr)) != NULL); assert((list = f2b_addrlist_remove(list, "127.0.0.2")) != NULL); diff --git a/t/t_matches.c b/t/t_matches.c index 174a45f..1a049ad 100644 --- a/t/t_matches.c +++ b/t/t_matches.c @@ -2,6 +2,7 @@ #include "../src/matches.h" int main() { + f2b_match_t *match = NULL; f2b_matches_t matches; bool result = false; size_t size = 15; @@ -9,28 +10,36 @@ int main() { UNUSED(result); - result = f2b_matches_create(&matches, size); - assert(result == true); - assert(matches.used == 0); - assert(matches.max == 15); - assert(matches.times != NULL); + memset(&matches, 0x0, sizeof(matches)); - for (size_t i = 0; i < size; i++) { - result = f2b_matches_append(&matches, now - 60 * i); - assert(result == true); - } + match = f2b_match_create(now); + assert(match != NULL); + assert(match->next == NULL); + + f2b_matches_append(&matches, match); + assert(matches.count == 1); + assert(matches.last == now); + assert(matches.list == match); - result = f2b_matches_append(&matches, 0); - assert(result == false); + f2b_matches_expire(&matches, now + 1); + assert(matches.count == 0); + assert(matches.last == 0); + assert(matches.list == NULL); + + for (size_t i = 1; i < size; i++) { + match = f2b_match_create(now - 60 * (size - i)); + f2b_matches_append(&matches, match); + assert(matches.count == i); + assert(matches.last == now - (time_t) (60 * (size - i))); + } f2b_matches_expire(&matches, now - 60 * 4); - assert(matches.used == 4); - assert(matches.max == 15); + assert(matches.count == 3); - f2b_matches_destroy(&matches); - assert(matches.used == 0); - assert(matches.max == 0); - assert(matches.times == NULL); + f2b_matches_flush(&matches); + assert(matches.count == 0); + assert(matches.last == 0); + assert(matches.list == NULL); return 0; } diff --git a/t/t_statefile.c b/t/t_statefile.c index 2f2ff90..f3501b3 100644 --- a/t/t_statefile.c +++ b/t/t_statefile.c @@ -3,7 +3,6 @@ #include "../src/statefile.h" int main() { - const char matches = 4; bool res = false; f2b_ipaddr_t *list = NULL; f2b_statefile_t *sf = NULL; @@ -19,13 +18,13 @@ int main() { sf = f2b_statefile_create("/tmp", jailname); assert(sf != NULL); - list = f2b_statefile_load(sf, matches); + list = f2b_statefile_load(sf); assert(list == NULL); banned_at = time(NULL) - 60; release_at = banned_at + 60 * 2; - list = f2b_ipaddr_create("1.2.3.4", matches); + list = f2b_ipaddr_create("1.2.3.4"); list->banned = true; list->banned_at = banned_at; list->release_at = release_at; @@ -43,7 +42,7 @@ int main() { f2b_ipaddr_destroy(list); list = NULL; - list = f2b_statefile_load(sf, matches); + list = f2b_statefile_load(sf); assert(list != NULL); assert(list->banned == true); assert(list->banned_at == banned_at);