Avoid useless strlen, faster query_format.
[apps/pfixtools.git] / postlicyd / iplist.c
index c2b471b..efc3b86 100644 (file)
@@ -43,6 +43,7 @@
 #include "str.h"
 #include "file.h"
 #include "array.h"
+#include "resources.h"
 #include "rbl.h"
 
 #define IPv4_BITS        5
@@ -62,10 +63,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 +140,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 +181,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 +190,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)
@@ -414,7 +446,7 @@ static void iplist_filter_async(rbl_result_t *result, void *arg)
     --async->awaited;
 
     debug("got asynchronous request result for filter %s, rbl %d, still awaiting %d answers",
-          filter->name, result - array_ptr(async->results, 0), async->awaited);
+          filter->name, (int)(result - array_ptr(async->results, 0)), async->awaited);
 
     if (async->awaited == 0) {
         filter_result_t res = HTK_FAIL;
@@ -455,9 +487,13 @@ static filter_result_t iplist_filter(const filter_t *filter, const query_t *quer
     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) {
@@ -509,6 +545,7 @@ static void *iplist_context_constructor(void)
 static void iplist_context_destructor(void *data)
 {
     iplist_async_data_t *ctx = data;
+    array_wipe(ctx->results);
     p_delete(&ctx);
 }