]> git.ipfire.org Git - people/ms/libloc.git/blobdiff - src/network.c
network: Add function to return a reverse pointer for networks
[people/ms/libloc.git] / src / network.c
index a7ee4a1694f1542a7d60477eab3fbb423cbb1c8f..69306a91f4b18ed0be8f4280373b34bbb7496c81 100644 (file)
@@ -51,6 +51,8 @@ struct loc_network {
 
 LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
                struct in6_addr* address, unsigned int prefix) {
+       struct loc_network* n = NULL;
+
        // Validate the prefix
        if (!loc_address_valid_prefix(address, prefix)) {
                ERROR(ctx, "Invalid prefix in %s: %u\n", loc_address_str(address), prefix);
@@ -58,7 +60,8 @@ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network
                return 1;
        }
 
-       struct loc_network* n = calloc(1, sizeof(*n));
+       // Allocate a new network
+       n = calloc(1, sizeof(*n));
        if (!n)
                return 1;
 
@@ -72,7 +75,7 @@ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network
                n->prefix = prefix;
 
        // Convert the prefix into a bitmask
-       struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
+       const struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
 
        // Store the first and last address in the network
        n->first_address = loc_address_and(address, &bitmask);
@@ -162,6 +165,10 @@ LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
        return 0;
 }
 
+unsigned int loc_network_raw_prefix(struct loc_network* network) {
+       return network->prefix;
+}
+
 LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
        return &network->first_address;
 }
@@ -264,6 +271,29 @@ LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* oth
        return 0;
 }
 
+int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
+       int r;
+
+       // Check country code
+       r = loc_country_code_cmp(self->country_code, other->country_code);
+       if (r)
+               return r;
+
+       // Check ASN
+       if (self->asn > other->asn)
+               return 1;
+       else if (self->asn < other->asn)
+               return -1;
+
+       // Check flags
+       if (self->flags > other->flags)
+               return 1;
+       else if (self->flags < other->flags)
+               return -1;
+
+       return 0;
+}
+
 LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
        // Either of the start addresses must be in the other subnet
        if (loc_network_matches_address(self, &other->first_address))
@@ -552,6 +582,70 @@ LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
        return subnets;
 }
 
+int loc_network_merge(struct loc_network** n,
+               struct loc_network* n1, struct loc_network* n2) {
+       struct loc_network* network = NULL;
+       struct in6_addr address;
+       int r;
+
+       // Reset pointer
+       *n = NULL;
+
+       DEBUG(n1->ctx, "Attempting to merge %s and %s\n", loc_network_str(n1), loc_network_str(n2));
+
+       // Family must match
+       if (n1->family != n2->family)
+               return 0;
+
+       // The prefix must match, too
+       if (n1->prefix != n2->prefix)
+               return 0;
+
+       // Cannot merge ::/0 or 0.0.0.0/0
+       if (!n1->prefix || !n2->prefix)
+               return 0;
+
+       const unsigned int prefix = loc_network_prefix(n1);
+
+       // How many bits do we need to represent this address?
+       const size_t bitlength = loc_address_bit_length(&n1->first_address);
+
+       // We cannot shorten this any more
+       if (bitlength >= prefix) {
+               DEBUG(n1->ctx, "Cannot shorten this any further because we need at least %jd bits,"
+                       " but only have %d\n", bitlength, prefix);
+
+               return 0;
+       }
+
+       // Increment the last address of the first network
+       address = n1->last_address;
+       loc_address_increment(&address);
+
+       // If they don't match they are not neighbours
+       if (loc_address_cmp(&address, &n2->first_address) != 0)
+               return 0;
+
+       // All properties must match, too
+       if (loc_network_properties_cmp(n1, n2) != 0)
+               return 0;
+
+       // Create a new network object
+       r = loc_network_new(n1->ctx, &network, &n1->first_address, prefix - 1);
+       if (r)
+               return r;
+
+       // Copy everything else
+       loc_country_code_copy(network->country_code, n1->country_code);
+       network->asn = n1->asn;
+       network->flags = n1->flags;
+
+       // Return pointer
+       *n = network;
+
+       return 0;
+}
+
 int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
        // Add country code
        loc_country_code_copy(dbobj->country_code, network->country_code);
@@ -607,256 +701,120 @@ int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** n
        return 0;
 }
 
-struct loc_network_tree {
-       struct loc_ctx* ctx;
-       int refcount;
-
-       struct loc_network_tree_node* root;
-};
-
-struct loc_network_tree_node {
-       struct loc_ctx* ctx;
-       int refcount;
-
-       struct loc_network_tree_node* zero;
-       struct loc_network_tree_node* one;
-
-       struct loc_network* network;
-};
-
-int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
-       struct loc_network_tree* t = calloc(1, sizeof(*t));
-       if (!t)
-               return 1;
-
-       t->ctx = loc_ref(ctx);
-       t->refcount = 1;
-
-       // Create the root node
-       int r = loc_network_tree_node_new(ctx, &t->root);
-       if (r) {
-               loc_network_tree_unref(t);
-               return r;
-       }
-
-       DEBUG(t->ctx, "Network tree allocated at %p\n", t);
-       *tree = t;
-       return 0;
-}
-
-struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
-       return loc_network_tree_node_ref(tree->root);
-}
-
-static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
-       struct loc_network_tree_node** n;
-
-       if (path == 0)
-               n = &node->zero;
-       else
-               n = &node->one;
-
-       // If the desired node doesn't exist, yet, we will create it
-       if (*n == NULL) {
-               int r = loc_network_tree_node_new(node->ctx, n);
-               if (r)
-                       return NULL;
-       }
-
-       return *n;
-}
+static char* loc_network_reverse_pointer6(struct loc_network* network, const char* suffix) {
+       char* buffer = NULL;
+       int r;
 
-static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
-       struct loc_network_tree_node* node = tree->root;
+       unsigned int prefix = loc_network_prefix(network);
 
-       for (unsigned int i = 0; i < prefix; i++) {
-               // Check if the ith bit is one or zero
-               node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
+       // Must border on a nibble
+       if (prefix % 4) {
+               errno = ENOTSUP;
+               return NULL;
        }
 
-       return node;
-}
-
-static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
-               int(*filter_callback)(struct loc_network* network, void* data),
-               int(*callback)(struct loc_network* network, void* data), void* data) {
-       int r;
-
-       // Finding a network ends the walk here
-       if (node->network) {
-               if (filter_callback) {
-                       int f = filter_callback(node->network, data);
-                       if (f < 0)
-                               return f;
-
-                       // Skip network if filter function returns value greater than zero
-                       if (f > 0)
-                               return 0;
-               }
+       if (!suffix)
+               suffix = "ip6.arpa.";
 
-               r = callback(node->network, data);
-               if (r)
-                       return r;
-       }
+       // Initialize the buffer
+       r = asprintf(&buffer, "%s", suffix);
+       if (r < 0)
+               goto ERROR;
 
-       // Walk down on the left side of the tree first
-       if (node->zero) {
-               r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
-               if (r)
-                       return r;
+       for (unsigned int i = 0; i < (prefix / 4); i++) {
+               r = asprintf(&buffer, "%x.%s", loc_address_get_nibble(&network->first_address, i), buffer);
+               if (r < 0)
+                       goto ERROR;
        }
 
-       // Then walk on the other side
-       if (node->one) {
-               r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
-               if (r)
-                       return r;
+       // Add the asterisk
+       if (prefix < 128) {
+               r = asprintf(&buffer, "*.%s", buffer);
+               if (r < 0)
+                       goto ERROR;
        }
 
-       return 0;
-}
+       return buffer;
 
-int loc_network_tree_walk(struct loc_network_tree* tree,
-               int(*filter_callback)(struct loc_network* network, void* data),
-               int(*callback)(struct loc_network* network, void* data), void* data) {
-       return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
-}
-
-static void loc_network_tree_free(struct loc_network_tree* tree) {
-       DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
-
-       loc_network_tree_node_unref(tree->root);
-
-       loc_unref(tree->ctx);
-       free(tree);
-}
-
-struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
-       if (--tree->refcount > 0)
-               return tree;
+ERROR:
+       if (buffer)
+               free(buffer);
 
-       loc_network_tree_free(tree);
        return NULL;
 }
 
-static int __loc_network_tree_dump(struct loc_network* network, void* data) {
-       DEBUG(network->ctx, "Dumping network at %p\n", network);
-
-       const char* s = loc_network_str(network);
-       if (!s)
-               return 1;
-
-       INFO(network->ctx, "%s\n", s);
-
-       return 0;
-}
-
-int loc_network_tree_dump(struct loc_network_tree* tree) {
-       DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
-
-       return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
-}
+static char* loc_network_reverse_pointer4(struct loc_network* network, const char* suffix) {
+       char* buffer = NULL;
+       int r;
 
-int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
-       DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
+       unsigned int prefix = loc_network_prefix(network);
 
-       struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
-                       &network->first_address, network->prefix);
-       if (!node) {
-               ERROR(tree->ctx, "Could not find a node\n");
-               return -ENOMEM;
+       // Must border on an octet
+       if (prefix % 8) {
+               errno = ENOTSUP;
+               return NULL;
        }
 
-       // Check if node has not been set before
-       if (node->network) {
-               DEBUG(tree->ctx, "There is already a network at this path\n");
-               return -EBUSY;
+       if (!suffix)
+               suffix = "in-addr.arpa.";
+
+       switch (prefix) {
+               case 32:
+                       r = asprintf(&buffer, "%d.%d.%d.%d.%s",
+                               loc_address_get_octet(&network->first_address, 3),
+                               loc_address_get_octet(&network->first_address, 2),
+                               loc_address_get_octet(&network->first_address, 1),
+                               loc_address_get_octet(&network->first_address, 0),
+                               suffix);
+                       break;
+
+               case 24:
+                       r = asprintf(&buffer, "*.%d.%d.%d.%s",
+                               loc_address_get_octet(&network->first_address, 2),
+                               loc_address_get_octet(&network->first_address, 1),
+                               loc_address_get_octet(&network->first_address, 0),
+                               suffix);
+                       break;
+
+               case 16:
+                       r = asprintf(&buffer, "*.%d.%d.%s",
+                               loc_address_get_octet(&network->first_address, 1),
+                               loc_address_get_octet(&network->first_address, 0),
+                               suffix);
+                       break;
+
+               case 8:
+                       r = asprintf(&buffer, "*.%d.%s",
+                               loc_address_get_octet(&network->first_address, 0),
+                               suffix);
+                       break;
+
+               case 0:
+                       r = asprintf(&buffer, "*.%s", suffix);
+                       break;
+
+               // To make the compiler happy
+               default:
+                       return NULL;
        }
 
-       // Point node to the network
-       node->network = loc_network_ref(network);
-
-       return 0;
-}
-
-static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
-       size_t counter = 1;
-
-       if (node->zero)
-               counter += __loc_network_tree_count_nodes(node->zero);
-
-       if (node->one)
-               counter += __loc_network_tree_count_nodes(node->one);
-
-       return counter;
-}
-
-size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
-       return __loc_network_tree_count_nodes(tree->root);
-}
-
-int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
-       struct loc_network_tree_node* n = calloc(1, sizeof(*n));
-       if (!n)
-               return -ENOMEM;
-
-       n->ctx = loc_ref(ctx);
-       n->refcount = 1;
-
-       n->zero = n->one = NULL;
-
-       DEBUG(n->ctx, "Network node allocated at %p\n", n);
-       *node = n;
-       return 0;
-}
-
-struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
-       if (node)
-               node->refcount++;
+       if (r < 0)
+               return NULL;
 
-       return node;
+       return buffer;
 }
 
-static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
-       DEBUG(node->ctx, "Releasing network node at %p\n", node);
-
-       if (node->network)
-               loc_network_unref(node->network);
-
-       if (node->zero)
-               loc_network_tree_node_unref(node->zero);
-
-       if (node->one)
-               loc_network_tree_node_unref(node->one);
+LOC_EXPORT char* loc_network_reverse_pointer(struct loc_network* network, const char* suffix) {
+       switch (network->family) {
+               case AF_INET6:
+                       return loc_network_reverse_pointer6(network, suffix);
 
-       loc_unref(node->ctx);
-       free(node);
-}
+               case AF_INET:
+                       return loc_network_reverse_pointer4(network, suffix);
 
-struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
-       if (--node->refcount > 0)
-               return node;
+               default:
+                       break;
+       }
 
-       loc_network_tree_node_free(node);
        return NULL;
 }
-
-struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
-       if (index == 0)
-               node = node->zero;
-       else
-               node = node->one;
-
-       if (!node)
-               return NULL;
-
-       return loc_network_tree_node_ref(node);
-}
-
-int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
-       return (!!node->network);
-}
-
-struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
-       return loc_network_ref(node->network);
-}