Compare commits
2 commits
048bf96e51
...
6248d19cf9
| Author | SHA1 | Date | |
|---|---|---|---|
| 6248d19cf9 | |||
| 54c8012836 |
8 changed files with 607 additions and 4 deletions
|
|
@ -3,7 +3,19 @@ cmake_minimum_required(VERSION 3.10)
|
|||
|
||||
project(AutoFW)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
|
||||
add_executable(autofw
|
||||
autofw.c
|
||||
matcher.c
|
||||
pbuf.c
|
||||
vbuf.c
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND pcre2-config --libs8
|
||||
OUTPUT_VARIABLE LIBPCRE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
target_link_libraries(autofw ${LIBPCRE})
|
||||
|
||||
|
|
|
|||
|
|
@ -2,3 +2,11 @@
|
|||
|
||||
Inspired by fail2ban.
|
||||
|
||||
## Building
|
||||
|
||||
On Ubuntu:
|
||||
apt-get install -y cmake libpcre2-dev
|
||||
|
||||
cmake .
|
||||
make
|
||||
|
||||
|
|
|
|||
114
matcher.c
Normal file
114
matcher.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
|
||||
#include "matcher.h"
|
||||
#include "vbuf.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
matcher_free_match_data_ptr(pcre2_match_data** p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
pcre2_match_data_free(*p);
|
||||
*p = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
matcher_pcre2_code_free_ptr(pcre2_code** p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
pcre2_code_free(*p);
|
||||
*p = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
matcher_init_regexp(
|
||||
regexp_matcher_t* matcher,
|
||||
const char* regexp)
|
||||
{
|
||||
if (!matcher || !regexp)
|
||||
return -1;
|
||||
|
||||
int errorcode;
|
||||
PCRE2_SIZE errorpos;
|
||||
|
||||
matcher->regexp = pcre2_compile(
|
||||
(PCRE2_SPTR8) regexp,
|
||||
PCRE2_ZERO_TERMINATED,
|
||||
0, /* options */
|
||||
&errorcode,
|
||||
&errorpos,
|
||||
NULL /* context */);
|
||||
if (!matcher->regexp) {
|
||||
PCRE2_UCHAR8 errortext[1024];
|
||||
pcre2_get_error_message(errorcode, errortext, sizeof(errortext));
|
||||
fprintf(stderr, "%s: regexp at %p errorpos %ld: `%s'\n",
|
||||
__FUNCTION__, matcher->regexp, errorpos, errortext);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pbuf_init(&matcher->results, 10, 0, (pbuf_func_free_t) vbuf_destroy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
matcher_destroy(regexp_matcher_t* matcher)
|
||||
{
|
||||
if (!matcher || !matcher->regexp)
|
||||
return;
|
||||
pcre2_code_free(matcher->regexp);
|
||||
pbuf_destroy(&matcher->results);
|
||||
}
|
||||
|
||||
int
|
||||
matcher_match(regexp_matcher_t* matcher, const char* subject)
|
||||
{
|
||||
if (!matcher || !subject)
|
||||
return -1;
|
||||
|
||||
pbuf_clear(&matcher->results);
|
||||
pcre2_match_data* __attribute__((cleanup(matcher_free_match_data_ptr))) match_data =
|
||||
pcre2_match_data_create(30, NULL);
|
||||
int const n = pcre2_match(matcher->regexp,
|
||||
(PCRE2_SPTR8) subject, strlen(subject),
|
||||
0, // startoffset
|
||||
PCRE2_ANCHORED, // options
|
||||
match_data, // match data
|
||||
NULL // match context
|
||||
);
|
||||
fprintf(stderr, "%s: pcre2_match returned %d for `%s'\n",
|
||||
__FUNCTION__, n, subject);
|
||||
if (n < 0)
|
||||
return n;
|
||||
#define START_IX(i) ((i) * 2 + 0)
|
||||
#define END_IX(i) ((i) * 2 + 1)
|
||||
|
||||
PCRE2_SIZE* const ovector = pcre2_get_ovector_pointer(match_data);
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ovector[START_IX(i)] == ovector[END_IX(i)])
|
||||
continue;
|
||||
#if 1
|
||||
fprintf(stderr, "%s: match %d: %ld .. %ld: `%.*s'\n",
|
||||
__FUNCTION__, i,
|
||||
ovector[START_IX(i)], ovector[END_IX(i)] - 1,
|
||||
(int) (ovector[END_IX(i)] - ovector[START_IX(i)]),
|
||||
subject + ovector[START_IX(i)]);
|
||||
#endif
|
||||
pbuf_putat(&matcher->results, i,
|
||||
vbuf_createbuf2(subject + ovector[START_IX(i)],
|
||||
ovector[END_IX(i)] - ovector[START_IX(i)]));
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
const char*
|
||||
matcher_result(regexp_matcher_t* matcher, int index)
|
||||
{
|
||||
if (!matcher)
|
||||
return NULL;
|
||||
return vbuf_notnull(pbuf_getat(&matcher->results, index));
|
||||
}
|
||||
|
||||
18
matcher.h
18
matcher.h
|
|
@ -5,7 +5,21 @@
|
|||
#include "pbuf.h"
|
||||
|
||||
typedef struct {
|
||||
pcre2_code* regexp;
|
||||
pbuf_t results;
|
||||
pcre2_code* regexp;
|
||||
pbuf_t results;
|
||||
} regexp_matcher_t;
|
||||
|
||||
int
|
||||
matcher_init_regexp(
|
||||
regexp_matcher_t* matcher,
|
||||
const char* regexp);
|
||||
|
||||
void
|
||||
matcher_destroy(regexp_matcher_t* matcher);
|
||||
|
||||
int
|
||||
matcher_match(regexp_matcher_t* matcher, const char* needle);
|
||||
|
||||
const char*
|
||||
matcher_result(regexp_matcher_t* matcher, int index);
|
||||
|
||||
|
|
|
|||
168
pbuf.c
Normal file
168
pbuf.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
|
||||
#include "pbuf.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
pbuf_init(pbuf_t* pbuf, int size, int chunksize, pbuf_func_free_t func_free)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
pbuf->used = 0;
|
||||
if (size <= 0)
|
||||
size = 1;
|
||||
pbuf->allocated = size;
|
||||
if (chunksize <= 0)
|
||||
chunksize = size / 2;
|
||||
if (chunksize <= 8)
|
||||
chunksize = 8;
|
||||
pbuf->chunksize = chunksize;
|
||||
pbuf->ptr = calloc(pbuf->allocated, sizeof(pbuf->ptr[0]));
|
||||
pbuf->func_free = func_free;
|
||||
}
|
||||
|
||||
static void
|
||||
pbuf_clear_range(pbuf_t* pbuf, int first, int firstNot)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
for (int i = first; i < firstNot; ++i) {
|
||||
void* obj = pbuf->ptr[i];
|
||||
if (!obj)
|
||||
continue;
|
||||
if (pbuf->func_free != PBUF_NONE) {
|
||||
if (pbuf->func_free)
|
||||
pbuf->func_free(obj);
|
||||
else
|
||||
free(obj);
|
||||
}
|
||||
pbuf->ptr[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pbuf_clear(pbuf_t* pbuf)
|
||||
{
|
||||
if (!pbuf || (pbuf->used == 0))
|
||||
return;
|
||||
pbuf_clear_range(pbuf, 0, pbuf->used);
|
||||
pbuf->used = 0;
|
||||
}
|
||||
|
||||
void
|
||||
pbuf_destroy(pbuf_t *pbuf)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
pbuf_clear(pbuf);
|
||||
free(pbuf->ptr);
|
||||
}
|
||||
|
||||
int
|
||||
pbuf_size(const pbuf_t* pbuf)
|
||||
{
|
||||
if (!pbuf)
|
||||
return 0;
|
||||
return pbuf->used;
|
||||
}
|
||||
|
||||
static void
|
||||
pbuf_ensure(pbuf_t *pbuf, int amount)
|
||||
{
|
||||
int const needed = pbuf->used + amount;
|
||||
if (needed < pbuf->allocated)
|
||||
return;
|
||||
int const blocks = needed / pbuf->chunksize + 1;
|
||||
int const current = pbuf->allocated;
|
||||
pbuf->allocated = blocks * pbuf->chunksize;
|
||||
pbuf->ptr = realloc(pbuf->ptr, pbuf->allocated * sizeof(pbuf->ptr[0]));
|
||||
memset(pbuf->ptr + current, 0,
|
||||
(pbuf->allocated - current) * sizeof(pbuf->ptr[0]));
|
||||
}
|
||||
|
||||
void
|
||||
pbuf_append(pbuf_t* pbuf, void* ptr)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
pbuf_ensure(pbuf, 1);
|
||||
pbuf->ptr[pbuf->used++] = ptr;
|
||||
}
|
||||
|
||||
void
|
||||
pbuf_sort(pbuf_t* pbuf, pbuf_func_sort_t func)
|
||||
{
|
||||
if (!pbuf || (pbuf->used < 2))
|
||||
return;
|
||||
qsort(pbuf->ptr, pbuf->used, sizeof(pbuf->ptr[0]), func);
|
||||
}
|
||||
|
||||
void*
|
||||
pbuf_find(const pbuf_t* pbuf, pbuf_func_find_t func_find, const void* arg)
|
||||
{
|
||||
if (!pbuf || !func_find)
|
||||
return NULL;
|
||||
for (int i = 0; i < pbuf->used; ++i) {
|
||||
void* const obj = pbuf->ptr[i];
|
||||
if (!obj)
|
||||
continue;
|
||||
if (func_find(obj, arg))
|
||||
return obj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pbuf_trim_end(pbuf_t* pbuf)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
while (pbuf->used > 0) {
|
||||
if (pbuf->ptr[pbuf->used - 1])
|
||||
break;
|
||||
pbuf->used--;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
pbuf_clearat(pbuf_t* pbuf, int ix)
|
||||
{
|
||||
if (!pbuf || (pbuf->used <= ix))
|
||||
return false;
|
||||
void* const obj = pbuf->ptr[ix];
|
||||
if (!obj)
|
||||
return false;
|
||||
pbuf->ptr[ix] = NULL;
|
||||
if (pbuf->func_free != PBUF_NONE) {
|
||||
if (pbuf->func_free)
|
||||
pbuf->func_free(obj);
|
||||
else
|
||||
free(obj);
|
||||
}
|
||||
pbuf_trim_end(pbuf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
pbuf_putat(pbuf_t* pbuf, int ix, void* obj)
|
||||
{
|
||||
if (!pbuf)
|
||||
return;
|
||||
pbuf_ensure(pbuf, ix + 1 - pbuf->used);
|
||||
pbuf_clearat(pbuf, ix);
|
||||
pbuf->ptr[ix] = obj;
|
||||
if (ix + 1 > pbuf->used)
|
||||
pbuf->used = ix + 1;
|
||||
pbuf_trim_end(pbuf);
|
||||
}
|
||||
|
||||
void*
|
||||
pbuf_getat(const pbuf_t* pbuf, int ix)
|
||||
{
|
||||
if (!pbuf || (pbuf->used <= ix) || (ix < 0))
|
||||
return NULL;
|
||||
return pbuf->ptr[ix];
|
||||
}
|
||||
|
||||
49
pbuf.h
49
pbuf.h
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
typedef void (*pbuf_func_free_t)(void*);
|
||||
|
||||
#define PBUF_NONE ((void*)-1)
|
||||
|
||||
typedef struct {
|
||||
int used;
|
||||
int allocated;
|
||||
|
|
@ -9,6 +11,49 @@ typedef struct {
|
|||
pbuf_func_free_t func_free;
|
||||
} pbuf_t;
|
||||
|
||||
// Add a new item at the end of the pbuf_t.
|
||||
extern void pbuf_append(pbuf_t* pbuf, void* ptr);
|
||||
void pbuf_init(
|
||||
pbuf_t* pbuf,
|
||||
int size,
|
||||
int chunksize,
|
||||
pbuf_func_free_t func_free);
|
||||
|
||||
void pbuf_destroy(pbuf_t* pbuf);
|
||||
|
||||
void
|
||||
pbuf_clear(pbuf_t* pbuf);
|
||||
|
||||
void
|
||||
pbuf_append(pbuf_t* pbuf, void* ptr);
|
||||
|
||||
void
|
||||
pbuf_putat(pbuf_t* pbuf, int ix, void* obj);
|
||||
|
||||
void*
|
||||
pbuf_getat(const pbuf_t* pbuf, int ix);
|
||||
|
||||
int
|
||||
pbuf_size(const pbuf_t* pbuf);
|
||||
|
||||
typedef int (*pbuf_func_find_t)(void* obj, const void* arg);
|
||||
|
||||
void*
|
||||
pbuf_find(
|
||||
const pbuf_t* pbuf,
|
||||
pbuf_func_find_t func,
|
||||
const void* arg);
|
||||
|
||||
typedef int (*pbuf_func_sort_t)(const void* obj1, const void* obj2);
|
||||
|
||||
void
|
||||
pbuf_sort(
|
||||
pbuf_t* pbuf,
|
||||
pbuf_func_sort_t func);
|
||||
|
||||
#define PBUF_FOREACH(element, list) { \
|
||||
int pbuf_i; \
|
||||
if ((void*)list != NULL) for (pbuf_i = 0; pbuf_i < (list)->used; ++pbuf_i) { \
|
||||
element = (list)->ptr[pbuf_i]; \
|
||||
if (!element) continue;
|
||||
|
||||
#define PBUF_FOREACH_END } }
|
||||
|
||||
|
|
|
|||
201
vbuf.c
Normal file
201
vbuf.c
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
|
||||
#include "vbuf.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
vbuf_init3(vbuf_t* vbuf, int size, int chunksize)
|
||||
{
|
||||
if (!vbuf)
|
||||
return;
|
||||
// There is no idea to use a smaller buffer than the "abuf",
|
||||
// so we might as well use it all.
|
||||
// Besides, size may be 0 here, and we have to watch for that.
|
||||
//
|
||||
unsigned int sz = size;
|
||||
if (size < (int)sizeof(vbuf->abuf))
|
||||
sz = sizeof(vbuf->abuf);
|
||||
if (chunksize <= 0)
|
||||
chunksize = sz / 2;
|
||||
if (chunksize < 32)
|
||||
chunksize = 32;
|
||||
*vbuf = (vbuf_t) { .chunksize = chunksize };
|
||||
if (sz <= sizeof(vbuf->abuf)) {
|
||||
vbuf->ptr = vbuf->abuf;
|
||||
vbuf->size = sz;
|
||||
} else {
|
||||
vbuf_ensure(vbuf, sz);
|
||||
vbuf_init(vbuf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vbuf_init0(vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf)
|
||||
return;
|
||||
vbuf_init3(vbuf, 0, 0);
|
||||
}
|
||||
|
||||
vbuf_t*
|
||||
vbuf_createbuf2(const char* buf, int len)
|
||||
{
|
||||
vbuf_t* const vbuf = malloc(sizeof(vbuf_t));
|
||||
vbuf_init3(vbuf, len + 1, 0);
|
||||
vbuf->len = len;
|
||||
if (buf)
|
||||
memcpy(vbuf->ptr, buf, vbuf->len);
|
||||
vbuf->ptr[vbuf->len] = '\0';
|
||||
return vbuf;
|
||||
}
|
||||
|
||||
void
|
||||
vbuf_init(vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf)
|
||||
return;
|
||||
vbuf->len = 0;
|
||||
if (vbuf->ptr)
|
||||
vbuf->ptr[0] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
vbuf_freeptr(vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf)
|
||||
return;
|
||||
if (vbuf->ptr != vbuf->abuf)
|
||||
free(vbuf->ptr);
|
||||
}
|
||||
|
||||
void
|
||||
vbuf_destroy(vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf)
|
||||
return;
|
||||
vbuf_freeptr(vbuf);
|
||||
free(vbuf);
|
||||
}
|
||||
|
||||
static char nullbyte = 0;
|
||||
|
||||
const char*
|
||||
vbuf_notnull(const vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf || !vbuf->ptr)
|
||||
return &nullbyte;
|
||||
return vbuf->ptr;
|
||||
}
|
||||
|
||||
char*
|
||||
vbuf_tostring(const vbuf_t* vbuf)
|
||||
{
|
||||
if (!vbuf)
|
||||
return NULL;
|
||||
if (!vbuf->ptr)
|
||||
return &nullbyte;
|
||||
return vbuf->ptr;
|
||||
}
|
||||
|
||||
void
|
||||
vbuf_ensure(vbuf_t* vbuf, unsigned int len)
|
||||
{
|
||||
unsigned int const needed = vbuf->len + len + 1;
|
||||
if (needed < vbuf->size)
|
||||
return;
|
||||
unsigned int const blocks = needed / vbuf->chunksize + 1;
|
||||
unsigned int const oldsize = vbuf->size;
|
||||
vbuf->size = blocks * vbuf->chunksize;
|
||||
if ((vbuf->size > sizeof(vbuf->abuf)) || (vbuf->ptr != vbuf->abuf)) {
|
||||
const char* const oldptr = vbuf->ptr;
|
||||
if (oldptr == vbuf->abuf) {
|
||||
vbuf->ptr = malloc(vbuf->size);
|
||||
memcpy(vbuf->ptr, oldptr, oldsize);
|
||||
} else {
|
||||
vbuf->ptr = realloc(vbuf->ptr, vbuf->size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vbuf_append2(vbuf_t* vbuf, const char* const ptr, const int len)
|
||||
{
|
||||
if (!vbuf)
|
||||
return 0;
|
||||
if (!ptr || (len == 0))
|
||||
return vbuf->len;
|
||||
|
||||
int datalen = len >= 0 ? len : (int) strlen(ptr);
|
||||
vbuf_ensure(vbuf, datalen);
|
||||
switch (datalen) {
|
||||
case 8:
|
||||
vbuf->ptr[vbuf->len + 7] = ptr[7];
|
||||
// fall-through
|
||||
case 7:
|
||||
vbuf->ptr[vbuf->len + 6] = ptr[6];
|
||||
// fall-through
|
||||
case 6:
|
||||
vbuf->ptr[vbuf->len + 5] = ptr[5];
|
||||
// fall-through
|
||||
case 5:
|
||||
vbuf->ptr[vbuf->len + 4] = ptr[4];
|
||||
// fall-through
|
||||
case 4:
|
||||
vbuf->ptr[vbuf->len + 3] = ptr[3];
|
||||
// fall-through
|
||||
case 3:
|
||||
vbuf->ptr[vbuf->len + 2] = ptr[2];
|
||||
// fall-through
|
||||
case 2:
|
||||
vbuf->ptr[vbuf->len + 1] = ptr[1];
|
||||
// fall-through
|
||||
case 1:
|
||||
vbuf->ptr[vbuf->len + 0] = ptr[0];
|
||||
break;
|
||||
default:
|
||||
if (vbuf && vbuf->ptr)
|
||||
memcpy(&vbuf->ptr[vbuf->len], ptr, datalen);
|
||||
}
|
||||
vbuf->len += datalen;
|
||||
if (vbuf && vbuf->ptr)
|
||||
vbuf->ptr[vbuf->len] = '\0';
|
||||
return vbuf->len;
|
||||
}
|
||||
|
||||
static int
|
||||
vbuf_gets2(vbuf_t* vbuf, FILE* fp, bool keepnewline)
|
||||
{
|
||||
if (!fp)
|
||||
return -1;
|
||||
if (!vbuf)
|
||||
return 0;
|
||||
vbuf_init(vbuf);
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
char buf[1024];
|
||||
buf[0] = 0;
|
||||
const char* const p = fgets(buf, sizeof(buf), fp);
|
||||
if (!p) {
|
||||
if (vbuf->len == 0)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
int len = strlen(buf);
|
||||
if ((len > 0) && (buf[len - 1] == '\n')) {
|
||||
done = true;
|
||||
if (!keepnewline)
|
||||
--len;
|
||||
}
|
||||
vbuf_append2(vbuf, buf, len);
|
||||
}
|
||||
return vbuf->len;
|
||||
}
|
||||
|
||||
int
|
||||
vbuf_gets(vbuf_t* vbuf, FILE* fp)
|
||||
{
|
||||
return vbuf_gets2(vbuf, fp, false);
|
||||
}
|
||||
|
||||
41
vbuf.h
41
vbuf.h
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#define VBUF_ABUF_SIZE 16
|
||||
|
||||
typedef struct {
|
||||
unsigned int flags;
|
||||
unsigned int len;
|
||||
unsigned int size;
|
||||
unsigned int chunksize;
|
||||
char* ptr;
|
||||
char abuf[VBUF_ABUF_SIZE];
|
||||
} vbuf_t;
|
||||
|
||||
void
|
||||
vbuf_init0(vbuf_t* vbuf);
|
||||
|
||||
void
|
||||
vbuf_destroy(vbuf_t* vbuf);
|
||||
|
||||
vbuf_t*
|
||||
vbuf_createbuf2(const char* buf, int len);
|
||||
|
||||
void
|
||||
vbuf_init(vbuf_t* vbuf);
|
||||
|
||||
void
|
||||
vbuf_freeptr(vbuf_t* vbuf);
|
||||
|
||||
int
|
||||
vbuf_gets(vbuf_t* vbuf, FILE* fp);
|
||||
|
||||
void
|
||||
vbuf_ensure(vbuf_t* vbuf, unsigned int delta);
|
||||
|
||||
char*
|
||||
vbuf_tostring(const vbuf_t* vbuf);
|
||||
|
||||
const char*
|
||||
vbuf_notnull(const vbuf_t* vbuf);
|
||||
|
||||
Loading…
Add table
Reference in a new issue