2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
17 #include <arpa/inet.h>
20 #include <netinet/in.h>
27 #include <sys/types.h>
35 #include <openssl/err.h>
36 #include <openssl/evp.h>
37 #include <openssl/pem.h>
39 #include <loc/libloc.h>
41 #include <loc/compat.h>
42 #include <loc/country.h>
43 #include <loc/database.h>
44 #include <loc/format.h>
45 #include <loc/network.h>
46 #include <loc/private.h>
47 #include <loc/stringpool.h>
55 enum loc_database_version version
;
62 size_t signature_length
;
64 // ASes in the database
65 struct loc_database_as_v1
* as_v1
;
69 struct loc_database_network_node_v1
* network_nodes_v1
;
70 size_t network_nodes_count
;
73 struct loc_database_network_v1
* networks_v1
;
74 size_t networks_count
;
77 struct loc_database_country_v1
* countries_v1
;
78 size_t countries_count
;
80 struct loc_stringpool
* pool
;
83 #define MAX_STACK_DEPTH 256
85 struct loc_node_stack
{
87 int i
; // Is this node 0 or 1?
91 struct loc_database_enumerator
{
93 struct loc_database
* db
;
94 enum loc_database_enumerator_mode mode
;
101 enum loc_network_flags flags
;
104 // Index of the AS we are looking at
105 unsigned int as_index
;
108 struct in6_addr network_address
;
109 struct loc_node_stack network_stack
[MAX_STACK_DEPTH
];
110 int network_stack_depth
;
111 unsigned int* networks_visited
;
114 static int loc_database_read_magic(struct loc_database
* db
) {
115 struct loc_database_magic magic
;
118 size_t bytes_read
= fread(&magic
, 1, sizeof(magic
), db
->f
);
120 // Check if we have been able to read enough data
121 if (bytes_read
< sizeof(magic
)) {
122 ERROR(db
->ctx
, "Could not read enough data to validate magic bytes\n");
123 DEBUG(db
->ctx
, "Read %zu bytes, but needed %zu\n", bytes_read
, sizeof(magic
));
127 // Compare magic bytes
128 if (memcmp(LOC_DATABASE_MAGIC
, magic
.magic
, strlen(LOC_DATABASE_MAGIC
)) == 0) {
129 DEBUG(db
->ctx
, "Magic value matches\n");
132 db
->version
= magic
.version
;
137 ERROR(db
->ctx
, "Unrecognized file type\n");
143 static int loc_database_read_as_section_v1(struct loc_database
* db
,
144 const struct loc_database_header_v1
* header
) {
145 off_t as_offset
= be32toh(header
->as_offset
);
146 size_t as_length
= be32toh(header
->as_length
);
148 DEBUG(db
->ctx
, "Reading AS section from %jd (%zu bytes)\n", (intmax_t)as_offset
, as_length
);
151 db
->as_v1
= mmap(NULL
, as_length
, PROT_READ
,
152 MAP_SHARED
, fileno(db
->f
), as_offset
);
154 if (db
->as_v1
== MAP_FAILED
)
158 db
->as_count
= as_length
/ sizeof(*db
->as_v1
);
160 INFO(db
->ctx
, "Read %zu ASes from the database\n", db
->as_count
);
165 static int loc_database_read_network_nodes_section_v1(struct loc_database
* db
,
166 const struct loc_database_header_v1
* header
) {
167 off_t network_nodes_offset
= be32toh(header
->network_tree_offset
);
168 size_t network_nodes_length
= be32toh(header
->network_tree_length
);
170 DEBUG(db
->ctx
, "Reading network nodes section from %jd (%zu bytes)\n",
171 (intmax_t)network_nodes_offset
, network_nodes_length
);
173 if (network_nodes_length
> 0) {
174 db
->network_nodes_v1
= mmap(NULL
, network_nodes_length
, PROT_READ
,
175 MAP_SHARED
, fileno(db
->f
), network_nodes_offset
);
177 if (db
->network_nodes_v1
== MAP_FAILED
)
181 db
->network_nodes_count
= network_nodes_length
/ sizeof(*db
->network_nodes_v1
);
183 INFO(db
->ctx
, "Read %zu network nodes from the database\n", db
->network_nodes_count
);
188 static int loc_database_read_networks_section_v1(struct loc_database
* db
,
189 const struct loc_database_header_v1
* header
) {
190 off_t networks_offset
= be32toh(header
->network_data_offset
);
191 size_t networks_length
= be32toh(header
->network_data_length
);
193 DEBUG(db
->ctx
, "Reading networks section from %jd (%zu bytes)\n",
194 (intmax_t)networks_offset
, networks_length
);
196 if (networks_length
> 0) {
197 db
->networks_v1
= mmap(NULL
, networks_length
, PROT_READ
,
198 MAP_SHARED
, fileno(db
->f
), networks_offset
);
200 if (db
->networks_v1
== MAP_FAILED
)
204 db
->networks_count
= networks_length
/ sizeof(*db
->networks_v1
);
206 INFO(db
->ctx
, "Read %zu networks from the database\n", db
->networks_count
);
211 static int loc_database_read_countries_section_v1(struct loc_database
* db
,
212 const struct loc_database_header_v1
* header
) {
213 off_t countries_offset
= be32toh(header
->countries_offset
);
214 size_t countries_length
= be32toh(header
->countries_length
);
216 DEBUG(db
->ctx
, "Reading countries section from %jd (%zu bytes)\n",
217 (intmax_t)countries_offset
, countries_length
);
219 if (countries_length
> 0) {
220 db
->countries_v1
= mmap(NULL
, countries_length
, PROT_READ
,
221 MAP_SHARED
, fileno(db
->f
), countries_offset
);
223 if (db
->countries_v1
== MAP_FAILED
)
227 db
->countries_count
= countries_length
/ sizeof(*db
->countries_v1
);
229 INFO(db
->ctx
, "Read %zu countries from the database\n",
230 db
->countries_count
);
235 static int loc_database_read_header_v1(struct loc_database
* db
) {
236 struct loc_database_header_v1 header
;
239 size_t size
= fread(&header
, 1, sizeof(header
), db
->f
);
241 if (size
< sizeof(header
)) {
242 ERROR(db
->ctx
, "Could not read enough data for header\n");
247 db
->created_at
= be64toh(header
.created_at
);
248 db
->vendor
= be32toh(header
.vendor
);
249 db
->description
= be32toh(header
.description
);
250 db
->license
= be32toh(header
.license
);
253 db
->signature_length
= be32toh(header
.signature_length
);
254 if (db
->signature_length
) {
255 // Check for a plausible signature length
256 if (db
->signature_length
> LOC_SIGNATURE_MAX_LENGTH
) {
257 ERROR(db
->ctx
, "Signature too long: %ld\n", db
->signature_length
);
261 DEBUG(db
->ctx
, "Reading signature of %ld bytes\n",
262 db
->signature_length
);
264 db
->signature
= malloc(db
->signature_length
);
265 for (unsigned int i
= 0; i
< db
->signature_length
; i
++)
266 db
->signature
[i
] = header
.signature
[i
];
270 off_t pool_offset
= be32toh(header
.pool_offset
);
271 size_t pool_length
= be32toh(header
.pool_length
);
273 int r
= loc_stringpool_open(db
->ctx
, &db
->pool
,
274 db
->f
, pool_length
, pool_offset
);
279 r
= loc_database_read_as_section_v1(db
, &header
);
284 r
= loc_database_read_network_nodes_section_v1(db
, &header
);
289 r
= loc_database_read_networks_section_v1(db
, &header
);
294 r
= loc_database_read_countries_section_v1(db
, &header
);
301 static int loc_database_read_header(struct loc_database
* db
) {
302 DEBUG(db
->ctx
, "Database version is %u\n", db
->version
);
304 switch (db
->version
) {
305 case LOC_DATABASE_VERSION_1
:
306 return loc_database_read_header_v1(db
);
309 ERROR(db
->ctx
, "Incompatible database version: %u\n", db
->version
);
314 static int loc_database_read(struct loc_database
* db
, FILE* f
) {
315 clock_t start
= clock();
319 // Clone file descriptor
322 ERROR(db
->ctx
, "Could not duplicate file descriptor\n");
326 // Reopen the file so that we can keep our own file handle
327 db
->f
= fdopen(fd
, "r");
329 ERROR(db
->ctx
, "Could not re-open database file\n");
333 // Rewind to the start of the file
337 int r
= loc_database_read_magic(db
);
342 r
= loc_database_read_header(db
);
346 clock_t end
= clock();
348 INFO(db
->ctx
, "Opened database in %.4fms\n",
349 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000);
354 LOC_EXPORT
int loc_database_new(struct loc_ctx
* ctx
, struct loc_database
** database
, FILE* f
) {
355 // Fail on invalid file handle
359 struct loc_database
* db
= calloc(1, sizeof(*db
));
364 db
->ctx
= loc_ref(ctx
);
367 DEBUG(db
->ctx
, "Database object allocated at %p\n", db
);
369 int r
= loc_database_read(db
, f
);
371 loc_database_unref(db
);
380 LOC_EXPORT
struct loc_database
* loc_database_ref(struct loc_database
* db
) {
386 static void loc_database_free(struct loc_database
* db
) {
389 DEBUG(db
->ctx
, "Releasing database %p\n", db
);
393 r
= munmap(db
->as_v1
, db
->as_count
* sizeof(*db
->as_v1
));
395 ERROR(db
->ctx
, "Could not unmap AS section: %s\n", strerror(errno
));
398 // Remove mapped network sections
399 if (db
->networks_v1
) {
400 r
= munmap(db
->networks_v1
, db
->networks_count
* sizeof(*db
->networks_v1
));
402 ERROR(db
->ctx
, "Could not unmap networks section: %s\n", strerror(errno
));
405 // Remove mapped network nodes section
406 if (db
->network_nodes_v1
) {
407 r
= munmap(db
->network_nodes_v1
, db
->network_nodes_count
* sizeof(*db
->network_nodes_v1
));
409 ERROR(db
->ctx
, "Could not unmap network nodes section: %s\n", strerror(errno
));
413 loc_stringpool_unref(db
->pool
);
419 // Close database file
427 LOC_EXPORT
struct loc_database
* loc_database_unref(struct loc_database
* db
) {
428 if (--db
->refcount
> 0)
431 loc_database_free(db
);
435 LOC_EXPORT
int loc_database_verify(struct loc_database
* db
, FILE* f
) {
436 // Cannot do this when no signature is available
437 if (!db
->signature
) {
438 DEBUG(db
->ctx
, "No signature available to verify\n");
442 // Start the stopwatch
443 clock_t start
= clock();
446 EVP_PKEY
* pkey
= PEM_read_PUBKEY(f
, NULL
, NULL
, NULL
);
448 char* error
= ERR_error_string(ERR_get_error(), NULL
);
449 ERROR(db
->ctx
, "Could not parse public key: %s\n", error
);
456 EVP_MD_CTX
* mdctx
= EVP_MD_CTX_new();
458 // Initialise hash function
459 r
= EVP_DigestVerifyInit(mdctx
, NULL
, NULL
, NULL
, pkey
);
461 ERROR(db
->ctx
, "Error initializing signature validation: %s\n",
462 ERR_error_string(ERR_get_error(), NULL
));
468 // Reset file to start
472 struct loc_database_magic magic
;
473 fread(&magic
, 1, sizeof(magic
), db
->f
);
475 hexdump(db
->ctx
, &magic
, sizeof(magic
));
477 // Feed magic into the hash
478 r
= EVP_DigestVerifyUpdate(mdctx
, &magic
, sizeof(magic
));
480 ERROR(db
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
487 struct loc_database_header_v1 header_v1
;
490 switch (db
->version
) {
491 case LOC_DATABASE_VERSION_1
:
492 bytes_read
= fread(&header_v1
, 1, sizeof(header_v1
), db
->f
);
493 if (bytes_read
< sizeof(header_v1
)) {
494 ERROR(db
->ctx
, "Could not read header\n");
501 for (unsigned int i
= 0; i
< sizeof(header_v1
.signature
); i
++) {
502 header_v1
.signature
[i
] = '\0';
504 header_v1
.signature_length
= 0;
506 hexdump(db
->ctx
, &header_v1
, sizeof(header_v1
));
508 // Feed header into the hash
509 r
= EVP_DigestVerifyUpdate(mdctx
, &header_v1
, sizeof(header_v1
));
511 ERROR(db
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
519 ERROR(db
->ctx
, "Cannot compute hash for database with format %d\n",
525 // Walk through the file in chunks of 64kB
526 char buffer
[64 * 1024];
528 while (!feof(db
->f
)) {
529 bytes_read
= fread(buffer
, 1, sizeof(buffer
), db
->f
);
531 hexdump(db
->ctx
, buffer
, bytes_read
);
533 r
= EVP_DigestVerifyUpdate(mdctx
, buffer
, bytes_read
);
535 ERROR(db
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
543 r
= EVP_DigestVerifyFinal(mdctx
,
544 (unsigned char*)db
->signature
, db
->signature_length
);
547 DEBUG(db
->ctx
, "The signature is invalid\n");
550 DEBUG(db
->ctx
, "The signature is valid\n");
553 ERROR(db
->ctx
, "Error verifying the signature: %s\n",
554 ERR_error_string(ERR_get_error(), NULL
));
559 hexdump(db
->ctx
, db
->signature
, db
->signature_length
);
561 clock_t end
= clock();
562 DEBUG(db
->ctx
, "Signature checked in %.4fms\n",
563 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000);
567 EVP_MD_CTX_free(mdctx
);
573 LOC_EXPORT
time_t loc_database_created_at(struct loc_database
* db
) {
574 return db
->created_at
;
577 LOC_EXPORT
const char* loc_database_get_vendor(struct loc_database
* db
) {
578 return loc_stringpool_get(db
->pool
, db
->vendor
);
581 LOC_EXPORT
const char* loc_database_get_description(struct loc_database
* db
) {
582 return loc_stringpool_get(db
->pool
, db
->description
);
585 LOC_EXPORT
const char* loc_database_get_license(struct loc_database
* db
) {
586 return loc_stringpool_get(db
->pool
, db
->license
);
589 LOC_EXPORT
size_t loc_database_count_as(struct loc_database
* db
) {
593 // Returns the AS at position pos
594 static int loc_database_fetch_as(struct loc_database
* db
, struct loc_as
** as
, off_t pos
) {
595 if ((size_t)pos
>= db
->as_count
)
598 DEBUG(db
->ctx
, "Fetching AS at position %jd\n", (intmax_t)pos
);
601 switch (db
->version
) {
602 case LOC_DATABASE_VERSION_1
:
603 r
= loc_as_new_from_database_v1(db
->ctx
, db
->pool
, as
, db
->as_v1
+ pos
);
611 DEBUG(db
->ctx
, "Got AS%u\n", loc_as_get_number(*as
));
617 // Performs a binary search to find the AS in the list
618 LOC_EXPORT
int loc_database_get_as(struct loc_database
* db
, struct loc_as
** as
, uint32_t number
) {
620 off_t hi
= db
->as_count
- 1;
623 clock_t start
= clock();
626 off_t i
= (lo
+ hi
) / 2;
628 // Fetch AS in the middle between lo and hi
629 int r
= loc_database_fetch_as(db
, as
, i
);
633 // Check if this is a match
634 uint32_t as_number
= loc_as_get_number(*as
);
635 if (as_number
== number
) {
636 clock_t end
= clock();
638 // Log how fast this has been
639 DEBUG(db
->ctx
, "Found AS%u in %.4fms\n", as_number
,
640 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000);
645 // If it wasn't, we release the AS and
646 // adjust our search pointers
649 if (as_number
< number
) {
661 // Returns the network at position pos
662 static int loc_database_fetch_network(struct loc_database
* db
, struct loc_network
** network
,
663 struct in6_addr
* address
, unsigned int prefix
, off_t pos
) {
664 if ((size_t)pos
>= db
->networks_count
) {
665 DEBUG(db
->ctx
, "Network ID out of range: %jd/%jd\n",
666 (intmax_t)pos
, (intmax_t)db
->networks_count
);
671 DEBUG(db
->ctx
, "Fetching network at position %jd\n", (intmax_t)pos
);
674 switch (db
->version
) {
675 case LOC_DATABASE_VERSION_1
:
676 r
= loc_network_new_from_database_v1(db
->ctx
, network
,
677 address
, prefix
, db
->networks_v1
+ pos
);
685 char* string
= loc_network_str(*network
);
686 DEBUG(db
->ctx
, "Got network %s\n", string
);
693 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1
* node
) {
694 return (node
->network
!= htobe32(0xffffffff));
697 static int __loc_database_lookup_handle_leaf(struct loc_database
* db
, const struct in6_addr
* address
,
698 struct loc_network
** network
, struct in6_addr
* network_address
, unsigned int prefix
,
699 const struct loc_database_network_node_v1
* node
) {
700 off_t network_index
= be32toh(node
->network
);
702 DEBUG(db
->ctx
, "Handling leaf node at %jd (%jd)\n", (intmax_t)(node
- db
->network_nodes_v1
), (intmax_t)network_index
);
705 int r
= loc_database_fetch_network(db
, network
,
706 network_address
, prefix
, network_index
);
708 ERROR(db
->ctx
, "Could not fetch network %jd from database\n", (intmax_t)network_index
);
712 // Check if the given IP address is inside the network
713 r
= loc_network_match_address(*network
, address
);
715 DEBUG(db
->ctx
, "Searched address is not part of the network\n");
717 loc_network_unref(*network
);
722 // A network was found and the IP address matches
726 // Searches for an exact match along the path
727 static int __loc_database_lookup(struct loc_database
* db
, const struct in6_addr
* address
,
728 struct loc_network
** network
, struct in6_addr
* network_address
,
729 const struct loc_database_network_node_v1
* node
, unsigned int level
) {
734 int bit
= in6_addr_get_bit(address
, level
);
735 in6_addr_set_bit(network_address
, level
, bit
);
738 node_index
= be32toh(node
->zero
);
740 node_index
= be32toh(node
->one
);
742 // If the node index is zero, the tree ends here
743 // and we cannot descend any further
744 if (node_index
> 0) {
746 if ((size_t)node_index
>= db
->network_nodes_count
)
749 // Move on to the next node
750 r
= __loc_database_lookup(db
, address
, network
, network_address
,
751 db
->network_nodes_v1
+ node_index
, level
+ 1);
753 // End here if a result was found
761 DEBUG(db
->ctx
, "No match found below level %u\n", level
);
763 DEBUG(db
->ctx
, "Tree ended at level %u\n", level
);
766 // If this node has a leaf, we will check if it matches
767 if (__loc_database_node_is_leaf(node
)) {
768 r
= __loc_database_lookup_handle_leaf(db
, address
, network
, network_address
, level
, node
);
776 LOC_EXPORT
int loc_database_lookup(struct loc_database
* db
,
777 struct in6_addr
* address
, struct loc_network
** network
) {
778 struct in6_addr network_address
;
779 memset(&network_address
, 0, sizeof(network_address
));
784 clock_t start
= clock();
786 int r
= __loc_database_lookup(db
, address
, network
, &network_address
,
787 db
->network_nodes_v1
, 0);
789 clock_t end
= clock();
791 // Log how fast this has been
792 DEBUG(db
->ctx
, "Executed network search in %.4fms\n",
793 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000);
798 LOC_EXPORT
int loc_database_lookup_from_string(struct loc_database
* db
,
799 const char* string
, struct loc_network
** network
) {
800 struct in6_addr address
;
802 int r
= loc_parse_address(db
->ctx
, string
, &address
);
806 return loc_database_lookup(db
, &address
, network
);
809 // Returns the country at position pos
810 static int loc_database_fetch_country(struct loc_database
* db
,
811 struct loc_country
** country
, off_t pos
) {
812 if ((size_t)pos
>= db
->countries_count
)
815 DEBUG(db
->ctx
, "Fetching country at position %jd\n", (intmax_t)pos
);
818 switch (db
->version
) {
819 case LOC_DATABASE_VERSION_1
:
820 r
= loc_country_new_from_database_v1(db
->ctx
, db
->pool
, country
, db
->countries_v1
+ pos
);
828 DEBUG(db
->ctx
, "Got country %s\n", loc_country_get_code(*country
));
834 // Performs a binary search to find the country in the list
835 LOC_EXPORT
int loc_database_get_country(struct loc_database
* db
,
836 struct loc_country
** country
, const char* code
) {
838 off_t hi
= db
->countries_count
- 1;
841 clock_t start
= clock();
844 off_t i
= (lo
+ hi
) / 2;
846 // Fetch country in the middle between lo and hi
847 int r
= loc_database_fetch_country(db
, country
, i
);
851 // Check if this is a match
852 const char* cc
= loc_country_get_code(*country
);
853 int result
= strcmp(code
, cc
);
856 clock_t end
= clock();
858 // Log how fast this has been
859 DEBUG(db
->ctx
, "Found country %s in %.4fms\n", cc
,
860 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000);
865 // If it wasn't, we release the country and
866 // adjust our search pointers
867 loc_country_unref(*country
);
883 LOC_EXPORT
int loc_database_enumerator_new(struct loc_database_enumerator
** enumerator
,
884 struct loc_database
* db
, enum loc_database_enumerator_mode mode
) {
885 struct loc_database_enumerator
* e
= calloc(1, sizeof(*e
));
890 e
->ctx
= loc_ref(db
->ctx
);
891 e
->db
= loc_database_ref(db
);
895 // Initialise graph search
896 //e->network_stack[++e->network_stack_depth] = 0;
897 e
->network_stack_depth
= 1;
898 e
->networks_visited
= calloc(db
->network_nodes_count
, sizeof(*e
->networks_visited
));
900 DEBUG(e
->ctx
, "Database enumerator object allocated at %p\n", e
);
906 LOC_EXPORT
struct loc_database_enumerator
* loc_database_enumerator_ref(struct loc_database_enumerator
* enumerator
) {
907 enumerator
->refcount
++;
912 static void loc_database_enumerator_free(struct loc_database_enumerator
* enumerator
) {
913 DEBUG(enumerator
->ctx
, "Releasing database enumerator %p\n", enumerator
);
915 // Release all references
916 loc_database_unref(enumerator
->db
);
917 loc_unref(enumerator
->ctx
);
919 if (enumerator
->string
)
920 free(enumerator
->string
);
922 // Free network search
923 free(enumerator
->networks_visited
);
928 LOC_EXPORT
struct loc_database_enumerator
* loc_database_enumerator_unref(struct loc_database_enumerator
* enumerator
) {
932 if (--enumerator
->refcount
> 0)
935 loc_database_enumerator_free(enumerator
);
939 LOC_EXPORT
int loc_database_enumerator_set_string(struct loc_database_enumerator
* enumerator
, const char* string
) {
940 enumerator
->string
= strdup(string
);
942 // Make the string lowercase
943 for (char *p
= enumerator
->string
; *p
; p
++)
949 LOC_EXPORT
int loc_database_enumerator_set_country_code(struct loc_database_enumerator
* enumerator
, const char* country_code
) {
950 // Set empty country code
951 if (!country_code
|| !*country_code
) {
952 *enumerator
->country_code
= '\0';
956 // Treat A1, A2, A3 as special country codes,
957 // but perform search for flags instead
958 if (strcmp(country_code
, "A1") == 0) {
959 return loc_database_enumerator_set_flag(enumerator
,
960 LOC_NETWORK_FLAG_ANONYMOUS_PROXY
);
961 } else if (strcmp(country_code
, "A2") == 0) {
962 return loc_database_enumerator_set_flag(enumerator
,
963 LOC_NETWORK_FLAG_SATELLITE_PROVIDER
);
964 } else if (strcmp(country_code
, "A3") == 0) {
965 return loc_database_enumerator_set_flag(enumerator
,
966 LOC_NETWORK_FLAG_ANYCAST
);
969 // Country codes must be two characters
970 if (!loc_country_code_is_valid(country_code
))
973 for (unsigned int i
= 0; i
< 3; i
++) {
974 enumerator
->country_code
[i
] = country_code
[i
];
980 LOC_EXPORT
int loc_database_enumerator_set_asn(
981 struct loc_database_enumerator
* enumerator
, unsigned int asn
) {
982 enumerator
->asn
= asn
;
987 LOC_EXPORT
int loc_database_enumerator_set_flag(
988 struct loc_database_enumerator
* enumerator
, enum loc_network_flags flag
) {
989 enumerator
->flags
|= flag
;
994 LOC_EXPORT
int loc_database_enumerator_set_family(
995 struct loc_database_enumerator
* enumerator
, int family
) {
996 enumerator
->family
= family
;
1001 LOC_EXPORT
int loc_database_enumerator_next_as(
1002 struct loc_database_enumerator
* enumerator
, struct loc_as
** as
) {
1005 // Do not do anything if not in AS mode
1006 if (enumerator
->mode
!= LOC_DB_ENUMERATE_ASES
)
1009 struct loc_database
* db
= enumerator
->db
;
1011 while (enumerator
->as_index
< db
->as_count
) {
1012 // Fetch the next AS
1013 int r
= loc_database_fetch_as(db
, as
, enumerator
->as_index
++);
1017 r
= loc_as_match_string(*as
, enumerator
->string
);
1019 DEBUG(enumerator
->ctx
, "AS%d (%s) matches %s\n",
1020 loc_as_get_number(*as
), loc_as_get_name(*as
), enumerator
->string
);
1031 enumerator
->as_index
= 0;
1033 // We have searched through all of them
1037 static int loc_database_enumerator_stack_push_node(
1038 struct loc_database_enumerator
* e
, off_t offset
, int i
, int depth
) {
1039 // Do not add empty nodes
1043 // Check if there is any space left on the stack
1044 if (e
->network_stack_depth
>= MAX_STACK_DEPTH
) {
1045 ERROR(e
->ctx
, "Maximum stack size reached: %d\n", e
->network_stack_depth
);
1049 // Increase stack size
1050 int s
= ++e
->network_stack_depth
;
1052 DEBUG(e
->ctx
, "Added node %jd to stack (%d)\n", (intmax_t)offset
, depth
);
1054 e
->network_stack
[s
].offset
= offset
;
1055 e
->network_stack
[s
].i
= i
;
1056 e
->network_stack
[s
].depth
= depth
;
1061 LOC_EXPORT
int loc_database_enumerator_next_network(
1062 struct loc_database_enumerator
* enumerator
, struct loc_network
** network
) {
1066 // Do not do anything if not in network mode
1067 if (enumerator
->mode
!= LOC_DB_ENUMERATE_NETWORKS
)
1072 DEBUG(enumerator
->ctx
, "Called with a stack of %u nodes\n",
1073 enumerator
->network_stack_depth
);
1076 while (enumerator
->network_stack_depth
> 0) {
1077 DEBUG(enumerator
->ctx
, "Stack depth: %u\n", enumerator
->network_stack_depth
);
1079 // Get object from top of the stack
1080 struct loc_node_stack
* node
= &enumerator
->network_stack
[enumerator
->network_stack_depth
];
1082 // Remove the node from the stack if we have already visited it
1083 if (enumerator
->networks_visited
[node
->offset
]) {
1084 enumerator
->network_stack_depth
--;
1088 // Mark the bits on the path correctly
1089 in6_addr_set_bit(&enumerator
->network_address
,
1090 (node
->depth
> 0) ? node
->depth
- 1 : 0, node
->i
);
1092 DEBUG(enumerator
->ctx
, "Looking at node %jd\n", (intmax_t)node
->offset
);
1093 enumerator
->networks_visited
[node
->offset
]++;
1095 // Pop node from top of the stack
1096 struct loc_database_network_node_v1
* n
=
1097 enumerator
->db
->network_nodes_v1
+ node
->offset
;
1099 // Add edges to stack
1100 r
= loc_database_enumerator_stack_push_node(enumerator
,
1101 be32toh(n
->one
), 1, node
->depth
+ 1);
1106 r
= loc_database_enumerator_stack_push_node(enumerator
,
1107 be32toh(n
->zero
), 0, node
->depth
+ 1);
1112 // Check if this node is a leaf and has a network object
1113 if (__loc_database_node_is_leaf(n
)) {
1114 off_t network_index
= be32toh(n
->network
);
1116 DEBUG(enumerator
->ctx
, "Node has a network at %jd\n", (intmax_t)network_index
);
1118 // Fetch the network object
1119 r
= loc_database_fetch_network(enumerator
->db
, network
,
1120 &enumerator
->network_address
, node
->depth
, network_index
);
1122 // Break on any errors
1126 // Check if we are interested in this network
1128 // Skip if the family does not match
1129 if (enumerator
->family
&& loc_network_address_family(*network
) != enumerator
->family
) {
1130 loc_network_unref(*network
);
1136 // Skip if the country code does not match
1137 if (*enumerator
->country_code
&&
1138 !loc_network_match_country_code(*network
, enumerator
->country_code
)) {
1139 loc_network_unref(*network
);
1145 // Skip if the ASN does not match
1146 if (enumerator
->asn
&&
1147 !loc_network_match_asn(*network
, enumerator
->asn
)) {
1148 loc_network_unref(*network
);
1154 // Skip if flags do not match
1155 if (enumerator
->flags
&&
1156 !loc_network_match_flag(*network
, enumerator
->flags
)) {
1157 loc_network_unref(*network
);
1165 // Reached the end of the search
1167 // Mark all nodes as non-visited
1168 for (unsigned int i
= 0; i
< enumerator
->db
->network_nodes_count
; i
++)
1169 enumerator
->networks_visited
[i
] = 0;