Make radix tree generic (length independent), use it for mod_access ipv4/ipv6
parent
3bc425fef1
commit
931e8fea0c
|
@ -3,68 +3,16 @@
|
|||
|
||||
#include <lighttpd/settings.h>
|
||||
|
||||
/*
|
||||
* Radix tree with 32bit key, lookup, insert and remove in O(32).
|
||||
* This is where the bit magic happens.
|
||||
*/
|
||||
typedef struct liRadixTree liRadixTree;
|
||||
|
||||
typedef struct liRadixNode32 liRadixNode32;
|
||||
struct liRadixNode32 {
|
||||
guint32 key;
|
||||
guint32 mask;
|
||||
gpointer data;
|
||||
liRadixNode32 *parent;
|
||||
liRadixNode32 *right;
|
||||
liRadixNode32 *left;
|
||||
};
|
||||
LI_API liRadixTree* li_radixtree_new();
|
||||
LI_API void li_radixtree_free(liRadixTree *tree, GFunc free_func, gpointer free_userdata);
|
||||
|
||||
typedef struct liRadixTree32 liRadixTree32;
|
||||
struct liRadixTree32 {
|
||||
liRadixNode32 **root;
|
||||
guint32 size;
|
||||
guint32 root_width;
|
||||
guint32 root_mask;
|
||||
};
|
||||
LI_API gpointer li_radixtree_insert(liRadixTree *tree, const void *key, guint32 bits, gpointer data); /* returns old data after overwrite */
|
||||
LI_API gpointer li_radixtree_remove(liRadixTree *tree, const void *key, guint32 bits); /* returns data from removed node */
|
||||
LI_API gpointer li_radixtree_lookup(liRadixTree *tree, const void *key, guint32 bits); /* longest matching prefix */
|
||||
LI_API gpointer li_radixtree_lookup_exact(liRadixTree *tree, const void *key, guint32 bits);
|
||||
|
||||
LI_API void li_radixtree_foreach(liRadixTree *tree, GFunc func, gpointer userdata);
|
||||
|
||||
LI_API liRadixTree32 *li_radixtree32_new(guint32 root_width);
|
||||
LI_API guint32 li_radixtree32_free(liRadixTree32 *tree);
|
||||
|
||||
LI_API void li_radixtree32_insert(liRadixTree32 *tree, guint32 key, guint32 mask, gpointer data);
|
||||
LI_API gboolean li_radixtree32_remove(liRadixTree32 *tree, guint32 key, guint32 mask);
|
||||
|
||||
/* lookup tree node (best match) */
|
||||
LI_API liRadixNode32 *li_radixtree32_lookup_node(liRadixTree32 *tree, guint32 key);
|
||||
|
||||
/* lookup data pointer (best match) */
|
||||
LI_API gpointer li_radixtree32_lookup(liRadixTree32 *tree, guint32 key);
|
||||
/* lookup data pointer (exact match) */
|
||||
LI_API gpointer li_radixtree32_lookup_exact(liRadixTree32 *tree, guint32 key);
|
||||
|
||||
/*
|
||||
typedef struct liRadixNode128 liRadixNode128;
|
||||
struct liRadixNode128 {
|
||||
guint32 key[4];
|
||||
guint32 mask[3];
|
||||
gpointer data;
|
||||
liRadixNode128 *parent;
|
||||
liRadixNode128 *right;
|
||||
liRadixNode128 *left;
|
||||
};
|
||||
|
||||
|
||||
struct liRadixTree128 {
|
||||
liRadixNode128 **root;
|
||||
guint64 size;
|
||||
guint32 root_width;
|
||||
guint32 root_mask;
|
||||
}
|
||||
|
||||
LI_api liRadixTree128 *radixtree128_new(guint32 root_width);
|
||||
LI_API guint li_radixtree128_free(liRadixTree128 *tree);
|
||||
|
||||
LI_API void li_radixtree128_insert(liRadixTree128 *tree, guint32 *key, guint32 *mask, gpointer data);
|
||||
LI_API gboolean li_radixtree128_remove(liRadixTree128 *tree, guint32 *key, guint32 *mask);
|
||||
LI_API gpointer li_radixtree128_lookup(liRadixTree128 *tree, guint32 *key);
|
||||
*/
|
||||
#endif
|
||||
|
|
|
@ -1,254 +1,318 @@
|
|||
|
||||
#include <lighttpd/radix.h>
|
||||
|
||||
liRadixTree32 *li_radixtree32_new(guint32 root_width) {
|
||||
guint32 i;
|
||||
liRadixTree32 *tree = g_slice_new(liRadixTree32);
|
||||
/* internal data is saved in "host"-order; search from high to low bit */
|
||||
typedef guint32 rdxBase;
|
||||
#define HTON_RDX(x) ((rdxBase) (htonl(x)))
|
||||
|
||||
if (root_width == 0)
|
||||
root_width = 1;
|
||||
else if (root_width > 8)
|
||||
root_width = 8;
|
||||
#define RDXBITS (sizeof(rdxBase)*8)
|
||||
|
||||
tree->root = g_new0(liRadixNode32*, 1 << root_width);
|
||||
tree->size = 0;
|
||||
tree->root_width = root_width;
|
||||
tree->root_mask = 0;
|
||||
/* 1^(width) 0^(RDXBITS-width): "1..10..0" */
|
||||
#define RDX_MASK(width) ( bits ? ~( (((rdxBase)1) << (RDXBITS - width)) - 1 ) : 0 )
|
||||
|
||||
for (i = 0; i < root_width; i++)
|
||||
tree->root_mask = ~(~tree->root_mask >> 1);
|
||||
#define INPUT_SIZE(bits) ( bits ? (bits+RDXBITS-1) / RDXBITS : 1 )
|
||||
#define INPUT_CHARS(bits) ( (bits+7) / 8 )
|
||||
|
||||
typedef struct liRadixNode liRadixNode;
|
||||
struct liRadixNode{
|
||||
rdxBase key;
|
||||
guint32 width;
|
||||
gpointer data;
|
||||
liRadixNode *right;
|
||||
liRadixNode *left;
|
||||
};
|
||||
|
||||
struct liRadixTree {
|
||||
liRadixNode *zero;
|
||||
};
|
||||
|
||||
liRadixTree* li_radixtree_new() {
|
||||
liRadixTree *tree;
|
||||
|
||||
tree = g_slice_new0(liRadixTree);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
guint li_radixtree32_free(liRadixTree32 *tree) {
|
||||
guint32 i;
|
||||
liRadixNode32 *node, *parent;
|
||||
guint32 n = 0;
|
||||
/* node != NULL */
|
||||
static void li_radixtree_free_node(liRadixNode *node, GFunc free_func, gpointer free_userdata) {
|
||||
if (node->right) {
|
||||
li_radixtree_free_node(node->right, free_func, free_userdata);
|
||||
}
|
||||
if (node->left) {
|
||||
li_radixtree_free_node(node->left, free_func, free_userdata);
|
||||
}
|
||||
|
||||
/* walk the tree and free every node */
|
||||
for (i = 0; i < ((guint32)1 << tree->root_width); i++) {
|
||||
node = tree->root[i];
|
||||
if (free_func && node->data) free_func(node->data, free_userdata);
|
||||
|
||||
while (node) {
|
||||
if (node->left)
|
||||
node = node->left;
|
||||
else if (node->right)
|
||||
node = node->right;
|
||||
else {
|
||||
parent = node->parent;
|
||||
g_slice_free(liRadixNode, node);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (parent->left == node)
|
||||
parent->left = NULL;
|
||||
else
|
||||
parent->right = NULL;
|
||||
}
|
||||
void li_radixtree_free(liRadixTree *tree, GFunc free_func, gpointer free_userdata) {
|
||||
if (tree->zero) li_radixtree_free_node(tree->zero, free_func, free_userdata);
|
||||
|
||||
g_slice_free(liRadixNode32, node);
|
||||
node = parent;
|
||||
n++;
|
||||
g_slice_free(liRadixTree, tree);
|
||||
}
|
||||
|
||||
static void rdx_get_input(rdxBase *dest, const void *key, guint32 bits) {
|
||||
guint32 entries = INPUT_SIZE(bits), chars = INPUT_CHARS(bits), padding = entries*sizeof(rdxBase) - chars, i;
|
||||
|
||||
memcpy(dest, key, chars);
|
||||
memset(((char*)dest) + chars, 0, padding);
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
dest[i] = HTON_RDX(dest[i]);
|
||||
}
|
||||
}
|
||||
|
||||
gpointer li_radixtree_insert(liRadixTree *tree, const void *key, guint32 bits, gpointer data) {
|
||||
liRadixNode *node, **nodeloc;
|
||||
rdxBase input[INPUT_SIZE(bits)], current;
|
||||
guint32 pos = 0;
|
||||
rdx_get_input(input, key, bits);
|
||||
|
||||
if (!data) return NULL;
|
||||
|
||||
pos = 0;
|
||||
current = input[0];
|
||||
|
||||
nodeloc = &tree->zero;
|
||||
|
||||
while (NULL != (node = *nodeloc)) {
|
||||
rdxBase mask = RDX_MASK(node->width);
|
||||
|
||||
if (node->width > bits || (current & mask) != node->key) { /* prefix longer than key or key doesn't match */
|
||||
/* split node */
|
||||
liRadixNode *newnode;
|
||||
guint32 width = (node->width > bits) ? bits : node->width;
|
||||
mask = RDX_MASK(width);
|
||||
while ((current & mask) != (node->key & mask)) {
|
||||
width--;
|
||||
mask <<= 1;
|
||||
}
|
||||
newnode = g_slice_new0(liRadixNode);
|
||||
newnode->width = width;
|
||||
newnode->key = current & mask;
|
||||
if (node->key & (1 << (RDXBITS-width-1))) { /* current may not have a "next" bit */
|
||||
newnode->right = node;
|
||||
*nodeloc = newnode;
|
||||
nodeloc = &newnode->left;
|
||||
} else {
|
||||
newnode->left = node;
|
||||
*nodeloc = newnode;
|
||||
nodeloc = &newnode->right;
|
||||
}
|
||||
if (width == bits) {
|
||||
newnode->data = data;
|
||||
return NULL;
|
||||
} else {
|
||||
/* NULL == *nodeloc */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->width == bits) { /* exact match */
|
||||
gpointer olddata = node->data;
|
||||
node->data = data;
|
||||
return olddata;
|
||||
}
|
||||
|
||||
if (mask & 0x1) {
|
||||
/* next "layer" */
|
||||
current = input[++pos];
|
||||
bits -= RDXBITS;
|
||||
nodeloc = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
||||
} else {
|
||||
nodeloc = (current & (1 << (RDXBITS-node->width-1))) ? &node->right : &node->left;
|
||||
}
|
||||
}
|
||||
|
||||
g_free(tree->root);
|
||||
g_slice_free(liRadixTree32, tree);
|
||||
while (bits > RDXBITS) {
|
||||
node = g_slice_new0(liRadixNode);
|
||||
node->width = RDXBITS;
|
||||
node->key = current;
|
||||
*nodeloc = node;
|
||||
|
||||
return n;
|
||||
/* next "layer" */
|
||||
current = input[++pos];
|
||||
bits -= RDXBITS;
|
||||
nodeloc = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
||||
}
|
||||
|
||||
node = g_slice_new0(liRadixNode);
|
||||
node->width = bits;
|
||||
node->key = current & RDX_MASK(bits);
|
||||
node->data = data;
|
||||
*nodeloc = node;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void li_radixtree32_insert(liRadixTree32 *tree, guint32 key, guint32 mask, gpointer data) {
|
||||
liRadixNode32 *last_node, *leaf;
|
||||
liRadixNode32 *node = tree->root[(key & tree->root_mask) >> (32 - tree->root_width)];
|
||||
/* *nodeptr == node is the only pointer to node from the parent! */
|
||||
static void node_compact(liRadixNode **nodeptr, liRadixNode *node) {
|
||||
liRadixNode *child;
|
||||
|
||||
if (!node) {
|
||||
/* no root node yet */
|
||||
node = g_slice_new(liRadixNode32);
|
||||
node->key = key & mask;
|
||||
node->mask = mask;
|
||||
node->data = data;
|
||||
node->parent = NULL;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
tree->root[(key & tree->root_mask) >> (32 - tree->root_width)] = node;
|
||||
tree->size++;
|
||||
/* can't remove nodes with data: */
|
||||
if (NULL != node->data) return;
|
||||
|
||||
if (NULL == node->left && NULL == node->right) {
|
||||
/* delete node */
|
||||
*nodeptr = NULL;
|
||||
g_slice_free(liRadixNode, node);
|
||||
return;
|
||||
}
|
||||
/* else: at least one child */
|
||||
|
||||
do {
|
||||
if ((key & mask & node->mask) != node->key) {
|
||||
/* node key differs, split tree */
|
||||
liRadixNode32 *new_node;
|
||||
|
||||
/* the new internal node */
|
||||
new_node = g_slice_new(liRadixNode32);
|
||||
new_node->data = NULL;
|
||||
new_node->parent = node->parent;
|
||||
new_node->mask = node->mask;
|
||||
new_node->key = node->key;
|
||||
|
||||
node->parent = new_node;
|
||||
|
||||
/* the new leaf */
|
||||
leaf = g_slice_new(liRadixNode32);
|
||||
leaf->key = key & mask;
|
||||
leaf->mask = mask;
|
||||
leaf->data = data;
|
||||
leaf->parent = new_node;
|
||||
leaf->left = NULL;
|
||||
leaf->right = NULL;
|
||||
|
||||
do {
|
||||
new_node->mask <<= 1;
|
||||
new_node->key &= new_node->mask;
|
||||
} while ((key & mask & new_node->mask) != new_node->key);
|
||||
|
||||
if ((key & new_node->mask) > (key & (~ (~ new_node->mask >> 1)))) {
|
||||
new_node->left = node;
|
||||
new_node->right = leaf;
|
||||
} else {
|
||||
new_node->left = leaf;
|
||||
new_node->right = node;
|
||||
}
|
||||
|
||||
if (new_node->parent) {
|
||||
if (new_node->parent->left == node)
|
||||
new_node->parent->left = new_node;
|
||||
else
|
||||
new_node->parent->right = new_node;
|
||||
} else {
|
||||
tree->root[(key & tree->root_mask) >> (32 - tree->root_width)] = new_node;
|
||||
}
|
||||
|
||||
tree->size++;
|
||||
|
||||
return;
|
||||
} else if ((key & mask) == node->key) {
|
||||
node->data = data;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
last_node = node;
|
||||
|
||||
/* compare next bit */
|
||||
if ((key & node->mask) > (key & (~ (~ node->mask >> 1))))
|
||||
node = node->right;
|
||||
else
|
||||
node = node->left;
|
||||
} while (node);
|
||||
|
||||
/* new leaf at end of tree */
|
||||
leaf = g_slice_new0(liRadixNode32);
|
||||
leaf->data = data;
|
||||
leaf->key = key & mask;
|
||||
leaf->mask = mask;
|
||||
leaf->parent = last_node;
|
||||
|
||||
if ((key & last_node->mask) > (key & (~ (~ last_node->mask >> 1))))
|
||||
last_node->right = leaf;
|
||||
else
|
||||
last_node->left = leaf;
|
||||
|
||||
tree->size++;
|
||||
}
|
||||
|
||||
gboolean li_radixtree32_remove(liRadixTree32 *tree, guint32 key, guint32 mask) {
|
||||
liRadixNode32 *node = tree->root[(key & tree->root_mask) >> (32 - tree->root_width)];
|
||||
|
||||
while (node) {
|
||||
if (!node->data || (key & mask) != node->key) {
|
||||
/* compare next bit */
|
||||
if ((key & node->mask) > (key & (~ (~ node->mask >> 1))))
|
||||
node = node->right;
|
||||
else
|
||||
node = node->left;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!node->left && !node->right) {
|
||||
/* leaf */
|
||||
if (node->parent) {
|
||||
if (node->parent->data) {
|
||||
/* set current node to parent */
|
||||
node->data = node->parent->data;
|
||||
node->key = node->parent->key;
|
||||
node->mask = node->parent->mask;
|
||||
node->parent->data = NULL;
|
||||
|
||||
return TRUE;
|
||||
} else {
|
||||
/* the parent internal node has no data, we can set our sibling as the new internal node */
|
||||
liRadixNode32 *sibling = (node->parent->left == node) ? node->parent->right : node->parent->left;
|
||||
if (node->parent->parent) {
|
||||
if (node->parent->parent->left == node->parent)
|
||||
node->parent->parent->left = sibling;
|
||||
else
|
||||
node->parent->parent->right = sibling;
|
||||
} else {
|
||||
/* the parent is the tree root, set root to our sibling */
|
||||
tree->root[(key & tree->root_mask) >> (32 - tree->root_width)] = sibling;
|
||||
}
|
||||
|
||||
/* old internal node not needed anymore */
|
||||
tree->size--;
|
||||
g_slice_free(liRadixNode32, node->parent);
|
||||
}
|
||||
} else {
|
||||
/* tree root */
|
||||
tree->root[(key & tree->root_mask) >> (32 - tree->root_width)] = NULL;
|
||||
}
|
||||
/* childs in the same layer? */
|
||||
if (node->width != RDXBITS) {
|
||||
/* exactly one child? (we have at least one!) */
|
||||
if (NULL == node->left) {
|
||||
child = node->right;
|
||||
} else if (NULL == node->right) {
|
||||
child = node->left;
|
||||
} else {
|
||||
/* internal node */
|
||||
node->data = NULL;
|
||||
|
||||
return TRUE;
|
||||
return; /* two childs */
|
||||
}
|
||||
|
||||
tree->size--;
|
||||
g_slice_free(liRadixNode32, node);
|
||||
|
||||
return TRUE;
|
||||
/* replace our own node with child */
|
||||
*nodeptr = child;
|
||||
g_slice_free(liRadixNode, node);
|
||||
return;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
liRadixNode32 *li_radixtree32_lookup_node(liRadixTree32 *tree, guint32 key) {
|
||||
liRadixNode32 *node = tree->root[(key & tree->root_mask) >> (32 - tree->root_width)];
|
||||
liRadixNode32 *result = NULL;
|
||||
/* *nodeptr == node is the only pointer to the node from the parent! */
|
||||
static gpointer radixtree_remove(liRadixNode **nodeptr, rdxBase *input, guint32 bits) {
|
||||
liRadixNode **nextnode;
|
||||
gpointer data;
|
||||
rdxBase current, mask;
|
||||
liRadixNode *node = *nodeptr;
|
||||
|
||||
if (!node) return NULL;
|
||||
|
||||
current = *input;
|
||||
mask = RDX_MASK(node->width);
|
||||
|
||||
if (node->width > bits) return NULL; /* prefix longer than key */
|
||||
|
||||
if ((current & mask) != node->key) return NULL; /* doesn't match */
|
||||
|
||||
if (node->width == bits) { /* exact match */
|
||||
data = node->data;
|
||||
node->data = NULL;
|
||||
node_compact(nodeptr, node);
|
||||
return data;
|
||||
}
|
||||
|
||||
if (mask & 0x1) {
|
||||
/* next "layer" */
|
||||
input++;
|
||||
bits -= RDXBITS;
|
||||
nextnode = (current & (1 << (RDXBITS-1))) ? &node->right : &node->left;
|
||||
} else {
|
||||
nextnode = (current & (1 << (RDXBITS-node->width-1))) ? &node->right : &node->left;
|
||||
}
|
||||
|
||||
data = radixtree_remove(nextnode, input, bits);
|
||||
|
||||
if (data == NULL) return NULL; /* nothing deleted */
|
||||
|
||||
node_compact(nodeptr, node);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
gpointer li_radixtree_remove(liRadixTree *tree, const void *key, guint32 bits) {
|
||||
rdxBase input[INPUT_SIZE(bits)];
|
||||
gpointer data;
|
||||
rdx_get_input(input, key, bits);
|
||||
|
||||
data = radixtree_remove(&tree->zero, input, bits);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
gpointer li_radixtree_lookup(liRadixTree *tree, const void *key, guint32 bits) { /* longest matching prefix */
|
||||
liRadixNode *node;
|
||||
rdxBase input[INPUT_SIZE(bits)], current;
|
||||
guint32 pos = 0;
|
||||
gpointer data = NULL;
|
||||
|
||||
rdx_get_input(input, key, bits);
|
||||
|
||||
pos = 0;
|
||||
current = input[0];
|
||||
|
||||
node = tree->zero;
|
||||
|
||||
while (node) {
|
||||
if ((key & node->mask) != node->key)
|
||||
return result;
|
||||
rdxBase mask = RDX_MASK(node->width);
|
||||
|
||||
if (node->data)
|
||||
result = node;
|
||||
if (node->width > bits) break; /* prefix longer than key */
|
||||
|
||||
/* compare next bit */
|
||||
if ((key & node->mask) > (key & (~ (~ node->mask >> 1))))
|
||||
node = node->right;
|
||||
else
|
||||
node = node->left;
|
||||
if ((current & mask) != node->key) break; /* doesn't match */
|
||||
|
||||
if (node->data) data = node->data; /* longest matching prefix */
|
||||
|
||||
if (node->width == bits) break; /* "end of key" */
|
||||
|
||||
if (mask & 0x1) {
|
||||
/* next "layer" */
|
||||
current = input[++pos];
|
||||
bits -= RDXBITS;
|
||||
node = (current & (1 << (RDXBITS-1))) ? node->right : node->left;
|
||||
} else {
|
||||
node = (current & (1 << (RDXBITS-node->width-1))) ? node->right : node->left;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return data;
|
||||
}
|
||||
|
||||
gpointer li_radixtree32_lookup(liRadixTree32 *tree, guint32 key) {
|
||||
liRadixNode32 *node = li_radixtree32_lookup_node(tree, key);
|
||||
|
||||
return node ? node->data : NULL;
|
||||
gpointer li_radixtree_lookup_exact(liRadixTree *tree, const void *key, guint32 bits) {
|
||||
liRadixNode *node;
|
||||
rdxBase input[INPUT_SIZE(bits)], current;
|
||||
guint32 pos = 0;
|
||||
|
||||
rdx_get_input(input, key, bits);
|
||||
|
||||
pos = 0;
|
||||
current = input[0];
|
||||
|
||||
node = tree->zero;
|
||||
|
||||
while (node) {
|
||||
rdxBase mask = RDX_MASK(node->width);
|
||||
|
||||
if (node->width > bits) break; /* prefix longer than key */
|
||||
|
||||
if ((current & mask) != node->key) break; /* doesn't match */
|
||||
|
||||
if (node->width == bits) return node->data; /* exact match */
|
||||
|
||||
if (mask & 0x1) {
|
||||
/* next "layer" */
|
||||
current = input[++pos];
|
||||
bits -= RDXBITS;
|
||||
node = (current & (1 << (RDXBITS-1))) ? node->right : node->left;
|
||||
} else {
|
||||
node = (current & (1 << (RDXBITS-node->width-1))) ? node->right : node->left;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpointer li_radixtree32_lookup_exact(liRadixTree32 *tree, guint32 key) {
|
||||
liRadixNode32 *node = li_radixtree32_lookup_node(tree, key);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
return (node->key == key) ? node->data : NULL;
|
||||
/* node != NULL */
|
||||
static void radixtree_foreach(liRadixNode *node, GFunc func, gpointer userdata) {
|
||||
if (node->data) {
|
||||
func(node->data, userdata);
|
||||
}
|
||||
if (node->right) radixtree_foreach(node->right, func, userdata);
|
||||
if (node->left) radixtree_foreach(node->left, func, userdata);
|
||||
}
|
||||
|
||||
void li_radixtree_foreach(liRadixTree *tree, GFunc func, gpointer userdata) {
|
||||
if (tree->zero) radixtree_foreach(tree->zero, func, userdata);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
* Example config:
|
||||
* access.redirect_url = "http://www.example.tld/denied.html";
|
||||
* access.check (
|
||||
* "allow" => ("127.0.0.0/24", "192.168.0.0/16"),
|
||||
* "deny" => "all"
|
||||
* "allow" => ("127.0.0.0/24", "192.168.0.0/16", "::1"),
|
||||
* "deny" => ( "all" )
|
||||
* );
|
||||
* if req.path =$ ".inc" { access.deny; }
|
||||
*
|
||||
|
@ -36,7 +36,6 @@
|
|||
*
|
||||
* Todo:
|
||||
* - access.redirect_url
|
||||
* - ipv6 support
|
||||
*
|
||||
* Author:
|
||||
* Copyright (c) 2009 Thomas Porzelt
|
||||
|
@ -52,10 +51,12 @@ LI_API gboolean mod_access_free(liModules *mods, liModule *mod);
|
|||
|
||||
struct access_check_data {
|
||||
liPlugin *p;
|
||||
liRadixTree32 *ipv4;
|
||||
liRadixTree *ipv4, *ipv6;
|
||||
};
|
||||
typedef struct access_check_data access_check_data;
|
||||
|
||||
enum { ACCESS_DENY = 1, ACCESS_ALLOW = 2 } access_values;
|
||||
|
||||
|
||||
static liHandlerResult access_check(liVRequest *vr, gpointer param, gpointer *context) {
|
||||
access_check_data *acd = param;
|
||||
|
@ -67,7 +68,7 @@ static liHandlerResult access_check(liVRequest *vr, gpointer param, gpointer *co
|
|||
UNUSED(redirect_url);
|
||||
|
||||
if (addr->plain.sa_family == AF_INET) {
|
||||
if (li_radixtree32_lookup(acd->ipv4, htonl(addr->ipv4.sin_addr.s_addr))) {
|
||||
if (GINT_TO_POINTER(ACCESS_DENY) == li_radixtree_lookup(acd->ipv4, &addr->ipv4.sin_addr.s_addr, 32)) {
|
||||
if (!li_vrequest_handle_direct(vr))
|
||||
return LI_HANDLER_GO_ON;
|
||||
|
||||
|
@ -76,9 +77,18 @@ static liHandlerResult access_check(liVRequest *vr, gpointer param, gpointer *co
|
|||
if (log_blocked)
|
||||
VR_INFO(vr, "access.check: blocked %s", vr->con->remote_addr_str->str);
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
} else if (addr->plain.sa_family == AF_INET6) {
|
||||
VR_ERROR(vr, "%s", "access.check doesn't support ipv6 clients yet");
|
||||
return LI_HANDLER_ERROR;
|
||||
if (GINT_TO_POINTER(ACCESS_DENY) == li_radixtree_lookup(acd->ipv6, &addr->ipv6.sin6_addr.s6_addr, 128)) {
|
||||
if (!li_vrequest_handle_direct(vr))
|
||||
return LI_HANDLER_GO_ON;
|
||||
|
||||
vr->response.http_status = 403;
|
||||
|
||||
if (log_blocked)
|
||||
VR_INFO(vr, "access.check: blocked %s", vr->con->remote_addr_str->str);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
VR_ERROR(vr, "%s", "access.check only supports ipv4 or ipv6 clients");
|
||||
return LI_HANDLER_ERROR;
|
||||
|
@ -92,7 +102,8 @@ static void access_check_free(liServer *srv, gpointer param) {
|
|||
|
||||
UNUSED(srv);
|
||||
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
li_radixtree_free(acd->ipv4, NULL, NULL);
|
||||
li_radixtree_free(acd->ipv6, NULL, NULL);
|
||||
g_slice_free(access_check_data, acd);
|
||||
}
|
||||
|
||||
|
@ -102,7 +113,6 @@ static liAction* access_check_create(liServer *srv, liPlugin* p, liValue *val) {
|
|||
guint i, j;
|
||||
guint32 ipv4, netmaskv4;
|
||||
gboolean deny = FALSE;
|
||||
gboolean got_deny = FALSE;
|
||||
access_check_data *acd = NULL;
|
||||
|
||||
UNUSED(srv);
|
||||
|
@ -116,77 +126,77 @@ static liAction* access_check_create(liServer *srv, liPlugin* p, liValue *val) {
|
|||
|
||||
acd = g_slice_new0(access_check_data);
|
||||
acd->p = p;
|
||||
acd->ipv4 = li_radixtree32_new(2);
|
||||
acd->ipv4 = li_radixtree_new(2);
|
||||
acd->ipv6 = li_radixtree_new(2);
|
||||
li_radixtree_insert(acd->ipv4, NULL, 0, GINT_TO_POINTER(ACCESS_DENY));
|
||||
li_radixtree_insert(acd->ipv6, NULL, 0, GINT_TO_POINTER(ACCESS_DENY));
|
||||
|
||||
for (i = 0; i < arr->len; i++) {
|
||||
v = g_array_index(arr, liValue*, i);
|
||||
|
||||
if (v->type != LI_VALUE_LIST || v->data.list->len != 2) {
|
||||
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
|
||||
v = g_array_index(v->data.list, liValue*, 0);
|
||||
|
||||
if (v->type != LI_VALUE_STRING) {
|
||||
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
|
||||
if (g_str_equal(v->data.string->str, "allow")) {
|
||||
deny = FALSE;
|
||||
} else if (g_str_equal(v->data.string->str, "deny")) {
|
||||
deny = TRUE;
|
||||
got_deny = TRUE;
|
||||
} else {
|
||||
ERROR(srv, "access_check: invalid option \"%s\"", v->data.string->str);
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
|
||||
v = g_array_index(g_array_index(arr, liValue*, i)->data.list, liValue*, 1);
|
||||
|
||||
if (v->type != LI_VALUE_LIST) {
|
||||
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
|
||||
for (j = 0; j < v->data.list->len; j++) {
|
||||
guint8 ipv6_addr[16];
|
||||
guint ipv6_network;
|
||||
ip = g_array_index(v->data.list, liValue*, j);
|
||||
|
||||
if (ip->type != LI_VALUE_STRING) {
|
||||
ERROR(srv, "%s", "access_check expects a list of one or two string,list tuples as parameter");
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
|
||||
if (g_str_equal(ip->data.string->str, "all")) {
|
||||
li_radixtree32_insert(acd->ipv4, 0, 0x00000000, GINT_TO_POINTER(deny));
|
||||
li_radixtree_insert(acd->ipv4, NULL, 0, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
||||
li_radixtree_insert(acd->ipv6, NULL, 0, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
||||
} else if (li_parse_ipv4(ip->data.string->str, &ipv4, &netmaskv4, NULL)) {
|
||||
li_radixtree32_insert(acd->ipv4, htonl(ipv4), htonl(netmaskv4), GINT_TO_POINTER(deny));
|
||||
/*} else if (li_parse_ipv6(v->data.string->str, ..., NULL) {
|
||||
li_radixtree128_insert(acd->ipv6, ipv6, netmaskv6, (gpointer)allow;*/
|
||||
gint prefixlen;
|
||||
netmaskv4 = ntohl(netmaskv4);
|
||||
prefixlen = 32 - g_bit_nth_lsf(netmaskv4, -1);
|
||||
if (prefixlen < 0 || prefixlen > 32) prefixlen = 0;
|
||||
li_radixtree_insert(acd->ipv4, &ipv4, prefixlen, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
||||
} else if (li_parse_ipv6(ip->data.string->str, ipv6_addr, &ipv6_network, NULL)) {
|
||||
li_radixtree_insert(acd->ipv6, ipv6_addr, ipv6_network, GINT_TO_POINTER(deny ? ACCESS_DENY : ACCESS_ALLOW));
|
||||
} else {
|
||||
ERROR(srv, "access_check: error parsing ip: %s", ip->data.string->str);
|
||||
li_radixtree32_free(acd->ipv4);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
goto failed_free_acd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_deny)
|
||||
li_radixtree32_insert(acd->ipv4, 0, 0x00000000, GINT_TO_POINTER(TRUE));
|
||||
|
||||
return li_action_new_function(access_check, NULL, access_check_free, acd);
|
||||
|
||||
failed_free_acd:
|
||||
li_radixtree_free(acd->ipv4, NULL, NULL);
|
||||
li_radixtree_free(acd->ipv6, NULL, NULL);
|
||||
g_slice_free(access_check_data, acd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue