X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=postlicyd%2Fiplist.c;h=0fb6934a738df3b0ff50353b402f76d1700a936c;hb=HEAD;hp=3c14fda47e2fbca60adc442f508055a41b30cf4f;hpb=f47b916bf7581b0070431eb70f43710e4c79fc98;p=apps%2Fpfixtools.git diff --git a/postlicyd/iplist.c b/postlicyd/iplist.c index 3c14fda..0fb6934 100644 --- a/postlicyd/iplist.c +++ b/postlicyd/iplist.c @@ -16,17 +16,20 @@ /* products derived from this software without specific prior written */ /* permission. */ /* */ -/* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND */ -/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */ -/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ -/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS */ -/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */ -/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */ -/* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS */ -/* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN */ -/* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) */ -/* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF */ -/* THE POSSIBILITY OF SUCH DAMAGE. */ +/* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS */ +/* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */ +/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ +/* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY */ +/* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */ +/* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS */ +/* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) */ +/* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, */ +/* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */ +/* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */ +/* POSSIBILITY OF SUCH DAMAGE. */ +/* */ +/* Copyright (c) 2006-2008 the Authors */ +/* see AUTHORS and source files for details */ /******************************************************************************/ /* @@ -43,6 +46,7 @@ #include "str.h" #include "file.h" #include "array.h" +#include "resources.h" #include "rbl.h" #define IPv4_BITS 5 @@ -62,10 +66,25 @@ enum { }; struct rbldb_t { - A(uint16_t) ips[1 << 16]; + char *filename; + A(uint16_t) *ips; }; ARRAY(rbldb_t) +typedef struct rbldb_resource_t { + time_t mtime; + off_t size; + A(uint16_t) ips[1 << 16]; +} rbldb_resource_t; + +static void rbldb_resource_wipe(rbldb_resource_t *res) +{ + for (int i = 0 ; i < 1 << 16 ; ++i) { + array_wipe(res->ips[i]); + } + p_delete(&res); +} + static int get_o(const char *s, const char **out) { int res = 0; @@ -124,22 +143,38 @@ rbldb_t *rbldb_create(const char *file, bool lock) file_map_t map; const char *p, *end; uint32_t ips = 0; + time_t now = time(0); if (!file_map_open(&map, file, false)) { return NULL; } + rbldb_resource_t *res = resource_get("iplist", file); + if (res == NULL) { + res = p_new(rbldb_resource_t, 1); + resource_set("iplist", file, res, (resource_destructor_t)rbldb_resource_wipe); + } + + db = p_new(rbldb_t, 1); + db->filename = m_strdup(file); + db->ips = res->ips; + if (map.st.st_size == res->size && map.st.st_mtime == res->mtime) { + info("%s loaded: already up-to-date", file); + file_map_close(&map); + return db; + } + res->size = map.st.st_size; + res->mtime = map.st.st_mtime; + p = map.map; end = map.end; while (end > p && end[-1] != '\n') { --end; } if (end != map.end) { - warn("file %s miss a final \\n, ignoring last line", - file); + warn("%s: final \\n missing, ignoring last line", file); } - db = p_new(rbldb_t, 1); while (p < end) { uint32_t ip; @@ -149,7 +184,7 @@ rbldb_t *rbldb_create(const char *file, bool lock) if (parse_ipv4(p, &p, &ip) < 0) { p = (char *)memchr(p, '\n', end - p) + 1; } else { - array_add(db->ips[ip >> 16], ip & 0xffff); + array_add(res->ips[ip >> 16], ip & 0xffff); ++ips; } } @@ -158,28 +193,28 @@ rbldb_t *rbldb_create(const char *file, bool lock) /* Lookup may perform serveral I/O, so avoid swap. */ for (int i = 0 ; i < 1 << 16 ; ++i) { - array_adjust(db->ips[i]); - if (lock && !array_lock(db->ips[i])) { + array_adjust(res->ips[i]); + if (lock && !array_lock(res->ips[i])) { UNIXERR("mlock"); } - if (db->ips[i].len) { + if (res->ips[i].len) { # define QSORT_TYPE uint16_t -# define QSORT_BASE db->ips[i].data -# define QSORT_NELT db->ips[i].len +# define QSORT_BASE res->ips[i].data +# define QSORT_NELT res->ips[i].len # define QSORT_LT(a,b) *a < *b # include "qsort.c" } } - info("rbl %s loaded, %d IPs", file, ips); + info("%s loaded: done in %us, %u IPs", file, (uint32_t)(time(0) - now), ips); return db; } static void rbldb_wipe(rbldb_t *db) { - for (int i = 0 ; i < 1 << 16 ; ++i) { - array_wipe(db->ips[i]); - } + resource_release("iplist", db->filename); + p_delete(&db->filename); + db->ips = NULL; } void rbldb_delete(rbldb_t **db) @@ -225,7 +260,7 @@ bool rbldb_ipv4_lookup(const rbldb_t *db, uint32_t ip) #include "filter.h" -typedef struct rbl_filter_t { +typedef struct iplist_filter_t { PA(rbldb_t) rbls; A(int) weights; A(char) hosts; @@ -234,14 +269,23 @@ typedef struct rbl_filter_t { int32_t hard_threshold; int32_t soft_threshold; -} rbl_filter_t; +} iplist_filter_t; + +typedef struct iplist_async_data_t { + A(rbl_result_t) results; + int awaited; + uint32_t sum; + bool error; +} iplist_async_data_t; -static rbl_filter_t *rbl_filter_new(void) +static filter_type_t filter_type = FTK_UNKNOWN; + +static iplist_filter_t *iplist_filter_new(void) { - return p_new(rbl_filter_t, 1); + return p_new(iplist_filter_t, 1); } -static void rbl_filter_delete(rbl_filter_t **rbl) +static void iplist_filter_delete(iplist_filter_t **rbl) { if (*rbl) { array_deep_wipe((*rbl)->rbls, rbldb_delete); @@ -254,14 +298,14 @@ static void rbl_filter_delete(rbl_filter_t **rbl) } -static bool rbl_filter_constructor(filter_t *filter) +static bool iplist_filter_constructor(filter_t *filter) { - rbl_filter_t *data = rbl_filter_new(); + iplist_filter_t *data = iplist_filter_new(); #define PARSE_CHECK(Expr, Str, ...) \ if (!(Expr)) { \ err(Str, ##__VA_ARGS__); \ - rbl_filter_delete(&data); \ + iplist_filter_delete(&data); \ return false; \ } @@ -299,7 +343,7 @@ static bool rbl_filter_constructor(filter_t *filter) lock = false; } else { PARSE_CHECK(false, "illegal locking state %.*s", - p - current, current); + (int)(p - current), current); } break; @@ -307,7 +351,7 @@ static bool rbl_filter_constructor(filter_t *filter) weight = strtol(current, &next, 10); PARSE_CHECK(next == p && weight >= 0 && weight <= 1024, "illegal weight value %.*s", - (p - current), current); + (int)(p - current), current); break; case 2: @@ -342,7 +386,7 @@ static bool rbl_filter_constructor(filter_t *filter) weight = strtol(current, &next, 10); PARSE_CHECK(next == p && weight >= 0 && weight <= 1024, "illegal weight value %.*s", - (p - current), current); + (int)(p - current), current); break; case 1: @@ -378,30 +422,81 @@ static bool rbl_filter_constructor(filter_t *filter) } }} - PARSE_CHECK(data->rbls.len, + PARSE_CHECK(data->rbls.len || data->host_offsets.len, "no file parameter in the filter %s", filter->name); filter->data = data; return true; } -static void rbl_filter_destructor(filter_t *filter) +static void iplist_filter_destructor(filter_t *filter) { - rbl_filter_t *data = filter->data; - rbl_filter_delete(&data); + iplist_filter_t *data = filter->data; + iplist_filter_delete(&data); filter->data = data; } -static filter_result_t rbl_filter(const filter_t *filter, const query_t *query) +static void iplist_filter_async(rbl_result_t *result, void *arg) +{ + filter_context_t *context = arg; + const filter_t *filter = context->current_filter; + const iplist_filter_t *data = filter->data; + iplist_async_data_t *async = context->contexts[filter_type]; + + + if (*result != RBL_ERROR) { + async->error = false; + } + --async->awaited; + + debug("got asynchronous request result for filter %s, rbl %d, still awaiting %d answers", + filter->name, (int)(result - array_ptr(async->results, 0)), async->awaited); + + if (async->awaited == 0) { + filter_result_t res = HTK_FAIL; + if (async->error) { + res = HTK_ERROR; + } else { + for (uint32_t i = 0 ; i < array_len(data->host_offsets) ; ++i) { + int weight = array_elt(data->host_weights, i); + + switch (array_elt(async->results, i)) { + case RBL_ASYNC: + crit("no more awaited answer but result is ASYNC"); + abort(); + case RBL_FOUND: + async->sum += weight; + break; + default: + break; + } + } + if (async->sum >= (uint32_t)data->hard_threshold) { + res = HTK_HARD_MATCH; + } else if (async->sum >= (uint32_t)data->soft_threshold) { + res = HTK_SOFT_MATCH; + } + } + debug("answering to filter %s", filter->name); + filter_post_async_result(context, res); + } +} + +static filter_result_t iplist_filter(const filter_t *filter, const query_t *query, + filter_context_t *context) { uint32_t ip; int32_t sum = 0; const char *end = NULL; - const rbl_filter_t *data = filter->data; + const iplist_filter_t *data = filter->data; bool error = true; - if (parse_ipv4(query->client_address, &end, &ip) != 0) { + if (parse_ipv4(query->client_address.str, &end, &ip) != 0) { + if (strchr(query->client_address.str, ':')) { + /* iplist only works on IPv4 */ + return HTK_FAIL; + } warn("invalid client address: %s, expected ipv4", - query->client_address); + query->client_address.str); return HTK_ERROR; } for (uint32_t i = 0 ; i < data->rbls.len ; ++i) { @@ -415,24 +510,22 @@ static filter_result_t rbl_filter(const filter_t *filter, const query_t *query) } error = false; } - for (uint32_t i = 0 ; i < data->host_offsets.len ; ++i) { - const char *rbl = array_ptr(data->hosts, array_elt(data->host_offsets, i)); - int weight = array_elt(data->host_weights, i); - switch (rbl_check(rbl, ip)) { - case RBL_FOUND: - error = false; - sum += weight; - if (sum >= data->hard_threshold) { - return HTK_HARD_MATCH; + if (array_len(data->host_offsets) > 0) { + iplist_async_data_t* async = context->contexts[filter_type]; + array_ensure_exact_capacity(async->results, array_len(data->host_offsets)); + async->sum = sum; + async->awaited = 0; + for (uint32_t i = 0 ; i < data->host_offsets.len ; ++i) { + const char *rbl = array_ptr(data->hosts, array_elt(data->host_offsets, i)); + if (rbl_check(rbl, ip, array_ptr(async->results, i), + iplist_filter_async, context)) { + error = false; + ++async->awaited; } - break; - case RBL_NOTFOUND: - error = false; - break; - case RBL_ERROR: - warn("rbl %s unavailable", rbl); - break; } + debug("filter %s awaiting %d asynchronous queries", filter->name, async->awaited); + async->error = error; + return HTK_ASYNC; } if (error) { err("filter %s: all the rbl returned an error", filter->name); @@ -447,25 +540,40 @@ static filter_result_t rbl_filter(const filter_t *filter, const query_t *query) } } -static int rbl_init(void) +static void *iplist_context_constructor(void) +{ + return p_new(iplist_async_data_t, 1); +} + +static void iplist_context_destructor(void *data) +{ + iplist_async_data_t *ctx = data; + array_wipe(ctx->results); + p_delete(&ctx); +} + +static int iplist_init(void) { - filter_type_t type = filter_register("iplist", rbl_filter_constructor, - rbl_filter_destructor, rbl_filter); + filter_type = filter_register("iplist", iplist_filter_constructor, + iplist_filter_destructor, iplist_filter, + iplist_context_constructor, + iplist_context_destructor); /* Hooks. */ - (void)filter_hook_register(type, "abort"); - (void)filter_hook_register(type, "error"); - (void)filter_hook_register(type, "fail"); - (void)filter_hook_register(type, "hard_match"); - (void)filter_hook_register(type, "soft_match"); + (void)filter_hook_register(filter_type, "abort"); + (void)filter_hook_register(filter_type, "error"); + (void)filter_hook_register(filter_type, "fail"); + (void)filter_hook_register(filter_type, "hard_match"); + (void)filter_hook_register(filter_type, "soft_match"); + (void)filter_hook_register(filter_type, "async"); /* Parameters. */ - (void)filter_param_register(type, "file"); - (void)filter_param_register(type, "rbldns"); - (void)filter_param_register(type, "dns"); - (void)filter_param_register(type, "hard_threshold"); - (void)filter_param_register(type, "soft_threshold"); + (void)filter_param_register(filter_type, "file"); + (void)filter_param_register(filter_type, "rbldns"); + (void)filter_param_register(filter_type, "dns"); + (void)filter_param_register(filter_type, "hard_threshold"); + (void)filter_param_register(filter_type, "soft_threshold"); return 0; } -module_init(rbl_init); +module_init(iplist_init);