]> git.ipfire.org Git - people/ms/libloc.git/blobdiff - src/database.c
Fix searching for ASes
[people/ms/libloc.git] / src / database.c
index 2d930a68b322cc5b845d7cdafcf5ccba2a9ffbe7..fe5e6dc6e0ebcb86a56b5629dc982ff5bc879ff1 100644 (file)
@@ -15,6 +15,7 @@
 */
 
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <endian.h>
 #include <errno.h>
 #include <netinet/in.h>
@@ -61,6 +62,18 @@ struct loc_database {
        struct loc_stringpool* pool;
 };
 
+struct loc_database_enumerator {
+       struct loc_ctx* ctx;
+       struct loc_database* db;
+       int refcount;
+
+       // Search string
+       char* string;
+
+       // Index of the AS we are looking at
+       unsigned int as_index;
+};
+
 static int loc_database_read_magic(struct loc_database* db, FILE* f) {
        struct loc_database_magic magic;
 
@@ -396,7 +409,8 @@ LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as,
 }
 
 // Returns the network at position pos
-static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network, struct in6_addr* address, off_t pos) {
+static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
+               struct in6_addr* address, unsigned int prefix, off_t pos) {
        if ((size_t)pos >= db->networks_count)
                return -EINVAL;
 
@@ -405,7 +419,8 @@ static int loc_database_fetch_network(struct loc_database* db, struct loc_networ
        int r;
        switch (db->version) {
                case 0:
-                       r = loc_network_new_from_database_v0(db->ctx, network, address, db->networks_v0 + pos);
+                       r = loc_network_new_from_database_v0(db->ctx, network,
+                               address, prefix, db->networks_v0 + pos);
                        break;
 
                default:
@@ -422,19 +437,23 @@ static int loc_database_fetch_network(struct loc_database* db, struct loc_networ
 }
 
 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
-       return (node->zero == htobe32(0xffffffff));
+       return (node->network != htobe32(0xffffffff));
 }
 
 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
-               struct loc_network** network, struct in6_addr* network_address,
+               struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
                const struct loc_database_network_node_v0* node) {
-       DEBUG(db->ctx, "Handling leaf node at %jd\n", node - db->network_nodes_v0);
+       off_t network_index = be32toh(node->network);
+
+       DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", node - db->network_nodes_v0, network_index);
 
        // Fetch the network
        int r = loc_database_fetch_network(db, network,
-               network_address, be32toh(node->one));
-       if (r)
+               network_address, prefix, network_index);
+       if (r) {
+               ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
                return r;
+       }
 
        // Check if the given IP address is inside the network
        r = loc_network_match_address(*network, address);
@@ -450,62 +469,10 @@ static int __loc_database_lookup_handle_leaf(struct loc_database* db, const stru
        return 0;
 }
 
-// Returns the highest result available
-static int __loc_database_lookup_max(struct loc_database* db, const struct in6_addr* address,
-               struct loc_network** network, struct in6_addr* network_address,
-               const struct loc_database_network_node_v0* node, int level) {
-       // If the node is a leaf node, we end here
-       if (__loc_database_node_is_leaf(node))
-               return __loc_database_lookup_handle_leaf(db, address, network, network_address, node);
-
-       int r;
-       off_t node_index;
-
-       // Try to go down the ones path first
-       if (node->one) {
-               node_index = be32toh(node->one);
-               in6_addr_set_bit(network_address, level, 1);
-
-               // Check boundaries
-               if (node_index > 0 && (size_t)node_index <= db->network_nodes_count) {
-                       r = __loc_database_lookup_max(db, address, network, network_address,
-                               db->network_nodes_v0 + node_index, level + 1);
-
-                       // Abort when match was found or error
-                       if (r <= 0)
-                               return r;
-               }
-       }
-
-       // ... and if that fails, we try to go down one step on a zero
-       // branch and then try the ones again...
-       if (node->zero) {
-               node_index = be32toh(node->zero);
-               in6_addr_set_bit(network_address, level, 0);
-
-               // Check boundaries
-               if (node_index > 0 && (size_t)node_index <= db->network_nodes_count) {
-                       r = __loc_database_lookup_max(db, address, network, network_address,
-                               db->network_nodes_v0 + node_index, level + 1);
-
-                       // Abort when match was found or error
-                       if (r <= 0)
-                               return r;
-               }
-       }
-
-       // End of path
-       return 1;
-}
-
 // Searches for an exact match along the path
 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
                struct loc_network** network, struct in6_addr* network_address,
-               const struct loc_database_network_node_v0* node, int level) {
-       // If the node is a leaf node, we end here
-       if (__loc_database_node_is_leaf(node))
-               return __loc_database_lookup_handle_leaf(db, address, network, network_address, node);
-
+               const struct loc_database_network_node_v0* node, unsigned int level) {
        int r;
        off_t node_index;
 
@@ -518,32 +485,38 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr*
        else
                node_index = be32toh(node->one);
 
-       // If we point back to root, the path ends here
-       if (node_index == 0) {
-               DEBUG(db->ctx, "Tree ends here\n");
-               return 1;
-       }
+       // If the node index is zero, the tree ends here
+       // and we cannot descend any further
+       if (node_index > 0) {
+               // Check boundaries
+               if ((size_t)node_index >= db->network_nodes_count)
+                       return -EINVAL;
 
-       // Check boundaries
-       if ((size_t)node_index >= db->network_nodes_count)
-               return -EINVAL;
+               // Move on to the next node
+               r = __loc_database_lookup(db, address, network, network_address,
+                       db->network_nodes_v0 + node_index, level + 1);
 
-       // Move on to the next node
-       r = __loc_database_lookup(db, address, network, network_address,
-               db->network_nodes_v0 + node_index, level + 1);
+               // End here if a result was found
+               if (r == 0)
+                       return r;
 
-       // End here if a result was found
-       if (r == 0)
-               return r;
+               // Raise any errors
+               else if (r < 0)
+                       return r;
 
-       // Raise any errors
-       else if (r < 0)
-               return r;
+               DEBUG(db->ctx, "No match found below level %u\n", level);
+       } else {
+               DEBUG(db->ctx, "Tree ended at level %u\n", level);
+       }
 
-       DEBUG(db->ctx, "Could not find an exact match at %u\n", level);
+       // If this node has a leaf, we will check if it matches
+       if (__loc_database_node_is_leaf(node)) {
+               r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
+               if (r <= 0)
+                       return r;
+       }
 
-       // If nothing was found, we have to search for an inexact match
-       return __loc_database_lookup_max(db, address, network, network_address, node, level);
+       return 1;
 }
 
 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
@@ -578,3 +551,90 @@ LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
 
        return loc_database_lookup(db, &address, network);
 }
+
+// Enumerator
+
+LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator, struct loc_database* db) {
+       struct loc_database_enumerator* e = calloc(1, sizeof(*e));
+       if (!e)
+               return -ENOMEM;
+
+       // Reference context
+       e->ctx = loc_ref(db->ctx);
+       e->db = loc_database_ref(db);
+       e->refcount = 1;
+
+       DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
+
+       *enumerator = e;
+       return 0;
+}
+
+LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
+       enumerator->refcount++;
+
+       return enumerator;
+}
+
+static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
+       DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
+
+       // Release all references
+       loc_database_unref(enumerator->db);
+       loc_unref(enumerator->ctx);
+
+       if (enumerator->string)
+               free(enumerator->string);
+
+       free(enumerator);
+}
+
+LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
+       if (!enumerator)
+               return NULL;
+
+       if (--enumerator->refcount > 0)
+               return enumerator;
+
+       loc_database_enumerator_free(enumerator);
+       return NULL;
+}
+
+LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
+       enumerator->string = strdup(string);
+
+       // Make the string lowercase
+       for (char *p = enumerator->string; *p; p++)
+               *p = tolower(*p);
+
+       return 0;
+}
+
+LOC_EXPORT struct loc_as* loc_database_enumerator_next_as(struct loc_database_enumerator* enumerator) {
+       struct loc_database* db = enumerator->db;
+       struct loc_as* as;
+
+       while (enumerator->as_index < db->as_count) {
+               // Fetch the next AS
+               int r = loc_database_fetch_as(db, &as, enumerator->as_index++);
+               if (r)
+                       return NULL;
+
+               r = loc_as_match_string(as, enumerator->string);
+               if (r == 1) {
+                       DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
+                               loc_as_get_number(as), loc_as_get_name(as), enumerator->string);
+
+                       return as;
+               }
+
+               // No match
+               loc_as_unref(as);
+       }
+
+       // Reset the index
+       enumerator->as_index = 0;
+
+       // We have searched through all of them
+       return NULL;
+}