Browse Source

* use linked-list in f2b_matches_t instead preallocated fixed-size array

master
Alex 'AdUser' Z 4 years ago
parent
commit
4800f71eef
  1. 10
      src/ipaddr.c
  2. 4
      src/ipaddr.h
  3. 26
      src/jail.c
  4. 77
      src/matches.c
  5. 40
      src/matches.h
  6. 4
      src/statefile.c
  7. 2
      src/statefile.h
  8. 19
      t/t_ipaddr.c
  9. 43
      t/t_matches.c
  10. 7
      t/t_statefile.c

10
src/ipaddr.c

@ -8,11 +8,10 @@
#include "ipaddr.h" #include "ipaddr.h"
f2b_ipaddr_t * f2b_ipaddr_t *
f2b_ipaddr_create(const char *addr, size_t matches) { f2b_ipaddr_create(const char *addr) {
f2b_ipaddr_t *a = NULL; f2b_ipaddr_t *a = NULL;
assert(addr != NULL); assert(addr != NULL);
assert(matches != 0);
if ((a = calloc(1, sizeof(f2b_ipaddr_t))) != NULL) { if ((a = calloc(1, sizeof(f2b_ipaddr_t))) != NULL) {
strlcpy(a->text, addr, sizeof(a->text)); 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) if (inet_pton(a->type, addr, &a->binary.v6) < 1)
goto cleanup; goto cleanup;
} }
if (f2b_matches_create(&a->matches, matches) == false)
goto cleanup;
} }
return a; return a;
@ -40,7 +36,7 @@ void
f2b_ipaddr_destroy(f2b_ipaddr_t *ipaddr) { f2b_ipaddr_destroy(f2b_ipaddr_t *ipaddr) {
assert(ipaddr != NULL); assert(ipaddr != NULL);
f2b_matches_destroy(&ipaddr->matches); f2b_matches_flush(&ipaddr->matches);
free(ipaddr); free(ipaddr);
} }
@ -57,7 +53,7 @@ f2b_ipaddr_status(f2b_ipaddr_t *addr, char *res, size_t ressize) {
"banned_at: %u\n" "banned_at: %u\n"
"release_at: %u\n"; "release_at: %u\n";
snprintf(res, ressize, fmt, addr->text, addr->banned ? "yes" : "no", 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 * f2b_ipaddr_t *

4
src/ipaddr.h

@ -44,10 +44,10 @@ typedef struct f2b_ipaddr_t {
/** /**
* @brief Create address record and fill related metadata * @brief Create address record and fill related metadata
* @param addr Textual address * @param addr Textual address
* @param max_matches Maximum matches count
* @returns Pointer to address or NULL no error * @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 * @brief Free address metadata
* @param ipaddr Pointer to f2b_ipaddr_t struct * @param ipaddr Pointer to f2b_ipaddr_t struct

26
src/jail.c

@ -145,8 +145,7 @@ f2b_jail_ban(f2b_jail_t *jail, f2b_ipaddr_t *addr) {
assert(jail != NULL); assert(jail != NULL);
assert(addr != NULL); assert(addr != NULL);
addr->matches.hits = 0; f2b_matches_flush(&addr->matches);
addr->matches.used = 0;
addr->banned = true; addr->banned = true;
addr->banned_at = addr->lastseen; addr->banned_at = addr->lastseen;
@ -224,6 +223,7 @@ f2b_jail_find(f2b_jail_t *list, const char *name) {
size_t size_t
f2b_jail_process(f2b_jail_t *jail) { f2b_jail_process(f2b_jail_t *jail) {
f2b_match_t *match = NULL;
f2b_ipaddr_t *prev = NULL; f2b_ipaddr_t *prev = NULL;
f2b_ipaddr_t *addr = NULL; f2b_ipaddr_t *addr = NULL;
size_t processed = 0; size_t processed = 0;
@ -249,7 +249,7 @@ f2b_jail_process(f2b_jail_t *jail) {
jail->matchcount++; jail->matchcount++;
addr = f2b_addrlist_lookup(jail->ipaddrs, matchbuf); addr = f2b_addrlist_lookup(jail->ipaddrs, matchbuf);
if (!addr) { if (!addr) {
addr = f2b_ipaddr_create(matchbuf, jail->maxretry); addr = f2b_ipaddr_create(matchbuf);
jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr); jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr);
f2b_log_msg(log_debug, "jail '%s': found new ip %s", jail->name, matchbuf); 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); f2b_log_msg(log_warn, "jail '%s': ip %s was already banned", jail->name, matchbuf);
continue; 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 = now - jail->findtime;
findtime -= (int) ((addr->matches.hits - jail->maxretry) * findtime -= (int) ((addr->matches.count - jail->maxretry) *
(jail->findtime * jail->incr_findtime)); (jail->findtime * jail->incr_findtime));
} else { } else {
findtime = now - jail->findtime; findtime = now - jail->findtime;
} }
f2b_matches_expire(&addr->matches, findtime); f2b_matches_expire(&addr->matches, findtime);
f2b_matches_append(&addr->matches, now); f2b_matches_append(&addr->matches, match);
if (addr->matches.used < jail->maxretry) { if (addr->matches.count < jail->maxretry) {
f2b_log_msg(log_info, "jail '%s': new match for ip %s (%zu/%zu)", 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; continue;
} }
/* limit reached, ban ip */ /* limit reached, ban ip */
@ -412,7 +413,7 @@ f2b_jail_start(f2b_jail_t *jail) {
/* error occured, must be already logged, just drop flag */ /* error occured, must be already logged, just drop flag */
jail->flags &= ~JAIL_HAS_STATE; jail->flags &= ~JAIL_HAS_STATE;
} else { } 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 void
f2b_jail_cmd_ip_xxx(char *res, size_t ressize, f2b_jail_t *jail, int op, const char *ip) { 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; f2b_ipaddr_t *addr = NULL;
assert(res != 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) { if (op > 0) {
/* ban */ /* ban */
time_t now = time(NULL); time_t now = time(NULL);
addr = f2b_ipaddr_create(ip, jail->maxretry); addr = f2b_ipaddr_create(ip);
if (!addr) { if (!addr) {
snprintf(res, ressize, "can't parse ip address: %s", ip); snprintf(res, ressize, "can't parse ip address: %s", ip);
return; return;
} }
addr->lastseen = now; 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); jail->ipaddrs = f2b_addrlist_append(jail->ipaddrs, addr);
if (jail->flags & JAIL_HAS_STATE) if (jail->flags & JAIL_HAS_STATE)
jail->sfile->need_save = true; jail->sfile->need_save = true;

77
src/matches.c

@ -7,54 +7,65 @@
#include "common.h" #include "common.h"
#include "matches.h" #include "matches.h"
bool f2b_match_t *
f2b_matches_create(f2b_matches_t *m, size_t max) { f2b_match_create(time_t t) {
assert(m != NULL); f2b_match_t *m;
assert(max != 0);
if ((m->times = calloc(max, sizeof(time_t))) == NULL) if ((m = calloc(1, sizeof(f2b_match_t))) == NULL)
return false; return false;
m->time = t;
m->used = 0; return m;
m->max = max;
return true;
} }
void void
f2b_matches_destroy(f2b_matches_t *m) { f2b_matches_flush(f2b_matches_t *ms) {
assert(m != NULL); f2b_match_t *head, *tmp;
assert(ms != NULL);
free(m->times); head = ms->list;
m->times = NULL; while (head != NULL) {
m->used = 0; tmp = head;
m->max = 0; head = head->next;
free(tmp);
}
memset(ms, 0x0, sizeof(f2b_matches_t));
} }
bool void
f2b_matches_append(f2b_matches_t *m, time_t t) { f2b_matches_append(f2b_matches_t *ms, f2b_match_t *m) {
assert(ms != NULL);
assert(m != NULL); assert(m != NULL);
m->hits++; m->next = ms->list;
ms->list = m;
if (m->used >= m->max) ms->count++;
return false; ms->last = m->time > ms->last ? m->time : ms->last;
m->times[m->used] = t;
m->used++;
return true;
} }
void void
f2b_matches_expire(f2b_matches_t *m, time_t t) { f2b_matches_expire(f2b_matches_t *ms, time_t before) {
assert(m != NULL); f2b_match_t *prev, *node, *next;
assert(ms != NULL);
for (size_t i = 0; i < m->used; ) { if (ms->list == NULL)
if (m->times[i] > t) { return;
i++;
node = ms->list;
prev = NULL;
while (node != NULL) {
if (node->time > before) {
prev = node; node = node->next;
continue; continue;
} }
m->used--; next = node->next;
m->times[i] = m->times[m->used]; free(node);
m->times[m->used] = 0; if (prev) {
prev->next = next;
} else {
ms->list = next;
}
node = next;
ms->count--;
} }
ms->last = ms->list ? ms->list->time : 0;
} }

40
src/matches.h

@ -12,38 +12,46 @@
* This file contains definition of ipaddr matches struct and related routines * 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 */ /** matches container */
typedef struct { typedef struct {
size_t hits; /**< how many times this ip matched by filter */ size_t count; /**< Count of entries in linked list */
size_t max; /**< max matches that this in struct can contain */ time_t last; /**< latest match time */
size_t used; /**< currently used matches count */ f2b_match_t *list; /**< linked list */
time_t *times; /**< dynamic unix timestamps array */
} f2b_matches_t; } f2b_matches_t;
/** /**
* @brief Init matches struct, allocate memory for @a hits * @brief Allocate memory for new match struct & initialize it
* @param m Pointer to struct * @param t Match time
* @param max Max expected matches count * @returns pointer to allocated struct
* @returns true on success, false otherwise
*/ */
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 * @brief Clean list of matches and free memory
* @param m Pointer to struct * @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 * @brief Push new match time to struct
* @param m Pointer 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 * @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 * @brief Remove matches before given time
* @param m Pointer to struct * @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_ */ #endif /* F2B_MATCHES_H_ */

4
src/statefile.c

@ -51,7 +51,7 @@ f2b_statefile_destroy(f2b_statefile_t *sf) {
} }
f2b_ipaddr_t * 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 int fields = 3;
const char *format = "%48s %u %u"; /* 48 == IPADDR_MAX == sizeof(addr) */ const char *format = "%48s %u %u"; /* 48 == IPADDR_MAX == sizeof(addr) */
f2b_ipaddr_t *addrlist = NULL, *ipaddr = NULL; 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); f2b_log_msg(log_warn, "can't parse, ignoring line: %s", buf);
continue; 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); f2b_log_msg(log_warn, "can't parse addr: %s", addr);
continue; continue;
} }

2
src/statefile.h

@ -19,7 +19,7 @@ void
f2b_statefile_destroy(f2b_statefile_t *sf); f2b_statefile_destroy(f2b_statefile_t *sf);
f2b_ipaddr_t * f2b_ipaddr_t *
f2b_statefile_load(f2b_statefile_t *sf, size_t matches); f2b_statefile_load(f2b_statefile_t *sf);
bool bool
f2b_statefile_save(f2b_statefile_t *sf, f2b_ipaddr_t *addrlist); f2b_statefile_save(f2b_statefile_t *sf, f2b_ipaddr_t *addrlist);

19
t/t_ipaddr.c

@ -13,17 +13,16 @@ int main() {
addr = f2b_addrlist_lookup(list, "127.0.0.1"); addr = f2b_addrlist_lookup(list, "127.0.0.1");
assert(addr == NULL); assert(addr == NULL);
addr = f2b_ipaddr_create("400.400.400.400", 15); addr = f2b_ipaddr_create("400.400.400.400");
assert(addr == NULL); 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 != NULL);
assert(addr->type == AF_INET); assert(addr->type == AF_INET);
assert(addr->next == NULL); assert(addr->next == NULL);
assert(strncmp(addr->text, "127.0.0.1", sizeof(addr->text)) == 0); assert(strncmp(addr->text, "127.0.0.1", sizeof(addr->text)) == 0);
assert(addr->matches.times != NULL); assert(addr->matches.list != NULL);
assert(addr->matches.max == 15); assert(addr->matches.count == 0);
assert(addr->matches.used == 0);
list = f2b_addrlist_append(list, addr); list = f2b_addrlist_append(list, addr);
assert(list != NULL); assert(list != NULL);
@ -35,15 +34,15 @@ int main() {
list = f2b_addrlist_remove(list, "127.0.0.1"); list = f2b_addrlist_remove(list, "127.0.0.1");
assert(list == NULL); 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((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((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((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((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_append(list, addr)) != NULL);
assert((list = f2b_addrlist_remove(list, "127.0.0.2")) != NULL); assert((list = f2b_addrlist_remove(list, "127.0.0.2")) != NULL);

43
t/t_matches.c

@ -2,6 +2,7 @@
#include "../src/matches.h" #include "../src/matches.h"
int main() { int main() {
f2b_match_t *match = NULL;
f2b_matches_t matches; f2b_matches_t matches;
bool result = false; bool result = false;
size_t size = 15; size_t size = 15;
@ -9,28 +10,36 @@ int main() {
UNUSED(result); UNUSED(result);
result = f2b_matches_create(&matches, size); memset(&matches, 0x0, sizeof(matches));
assert(result == true);
assert(matches.used == 0);
assert(matches.max == 15);
assert(matches.times != NULL);
for (size_t i = 0; i < size; i++) { match = f2b_match_create(now);
result = f2b_matches_append(&matches, now - 60 * i); assert(match != NULL);
assert(result == true); 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); f2b_matches_expire(&matches, now + 1);
assert(result == false); 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); f2b_matches_expire(&matches, now - 60 * 4);
assert(matches.used == 4); assert(matches.count == 3);
assert(matches.max == 15);
f2b_matches_destroy(&matches); f2b_matches_flush(&matches);
assert(matches.used == 0); assert(matches.count == 0);
assert(matches.max == 0); assert(matches.last == 0);
assert(matches.times == NULL); assert(matches.list == NULL);
return 0; return 0;
} }

7
t/t_statefile.c

@ -3,7 +3,6 @@
#include "../src/statefile.h" #include "../src/statefile.h"
int main() { int main() {
const char matches = 4;
bool res = false; bool res = false;
f2b_ipaddr_t *list = NULL; f2b_ipaddr_t *list = NULL;
f2b_statefile_t *sf = NULL; f2b_statefile_t *sf = NULL;
@ -19,13 +18,13 @@ int main() {
sf = f2b_statefile_create("/tmp", jailname); sf = f2b_statefile_create("/tmp", jailname);
assert(sf != NULL); assert(sf != NULL);
list = f2b_statefile_load(sf, matches); list = f2b_statefile_load(sf);
assert(list == NULL); assert(list == NULL);
banned_at = time(NULL) - 60; banned_at = time(NULL) - 60;
release_at = banned_at + 60 * 2; 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 = true;
list->banned_at = banned_at; list->banned_at = banned_at;
list->release_at = release_at; list->release_at = release_at;
@ -43,7 +42,7 @@ int main() {
f2b_ipaddr_destroy(list); f2b_ipaddr_destroy(list);
list = NULL; list = NULL;
list = f2b_statefile_load(sf, matches); list = f2b_statefile_load(sf);
assert(list != NULL); assert(list != NULL);
assert(list->banned == true); assert(list->banned == true);
assert(list->banned_at == banned_at); assert(list->banned_at == banned_at);

Loading…
Cancel
Save