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"
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 *

4
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

26
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;

77
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;
}

40
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_ */

4
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;
}

2
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);

19
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);

43
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;
}

7
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);

Loading…
Cancel
Save