]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
database: Use MAP_PRIVATE with mmap()
[people/ms/libloc.git] / src / database.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
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.
10
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.
15 */
16
17 #include <arpa/inet.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <netinet/in.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #ifdef HAVE_ENDIAN_H
32 # include <endian.h>
33 #endif
34
35 #include <openssl/err.h>
36 #include <openssl/evp.h>
37 #include <openssl/pem.h>
38
39 #include <libloc/libloc.h>
40 #include <libloc/address.h>
41 #include <libloc/as.h>
42 #include <libloc/as-list.h>
43 #include <libloc/compat.h>
44 #include <libloc/country.h>
45 #include <libloc/country-list.h>
46 #include <libloc/database.h>
47 #include <libloc/format.h>
48 #include <libloc/network.h>
49 #include <libloc/network-list.h>
50 #include <libloc/private.h>
51 #include <libloc/stringpool.h>
52
53 struct loc_database {
54 struct loc_ctx* ctx;
55 int refcount;
56
57 FILE* f;
58
59 enum loc_database_version version;
60 time_t created_at;
61 off_t vendor;
62 off_t description;
63 off_t license;
64
65 // Signatures
66 char* signature1;
67 size_t signature1_length;
68 char* signature2;
69 size_t signature2_length;
70
71 // ASes in the database
72 struct loc_database_as_v1* as_v1;
73 size_t as_count;
74
75 // Network tree
76 struct loc_database_network_node_v1* network_nodes_v1;
77 size_t network_nodes_count;
78
79 // Networks
80 struct loc_database_network_v1* networks_v1;
81 size_t networks_count;
82
83 // Countries
84 struct loc_database_country_v1* countries_v1;
85 size_t countries_count;
86
87 struct loc_stringpool* pool;
88 };
89
90 #define MAX_STACK_DEPTH 256
91
92 struct loc_node_stack {
93 off_t offset;
94 int i; // Is this node 0 or 1?
95 int depth;
96 };
97
98 struct loc_database_enumerator {
99 struct loc_ctx* ctx;
100 struct loc_database* db;
101 enum loc_database_enumerator_mode mode;
102 int refcount;
103
104 // Search string
105 char* string;
106 struct loc_country_list* countries;
107 struct loc_as_list* asns;
108 enum loc_network_flags flags;
109 int family;
110
111 // Flatten output?
112 int flatten;
113
114 // Index of the AS we are looking at
115 unsigned int as_index;
116
117 // Index of the country we are looking at
118 unsigned int country_index;
119
120 // Network state
121 struct in6_addr network_address;
122 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
123 int network_stack_depth;
124 unsigned int* networks_visited;
125
126 // For subnet search and bogons
127 struct loc_network_list* stack;
128 struct loc_network_list* subnets;
129
130 // For bogons
131 struct in6_addr gap6_start;
132 struct in6_addr gap4_start;
133 };
134
135 static int loc_database_read_magic(struct loc_database* db) {
136 struct loc_database_magic magic;
137
138 // Read from file
139 size_t bytes_read = fread(&magic, 1, sizeof(magic), db->f);
140
141 // Check if we have been able to read enough data
142 if (bytes_read < sizeof(magic)) {
143 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
144 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
145 return -ENOMSG;
146 }
147
148 // Compare magic bytes
149 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
150 DEBUG(db->ctx, "Magic value matches\n");
151
152 // Parse version
153 db->version = magic.version;
154
155 return 0;
156 }
157
158 ERROR(db->ctx, "Unrecognized file type\n");
159
160 // Return an error
161 return 1;
162 }
163
164 static int loc_database_read_as_section_v1(struct loc_database* db,
165 const struct loc_database_header_v1* header) {
166 off_t as_offset = be32toh(header->as_offset);
167 size_t as_length = be32toh(header->as_length);
168
169 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", (intmax_t)as_offset, as_length);
170
171 if (as_length > 0) {
172 db->as_v1 = mmap(NULL, as_length, PROT_READ,
173 MAP_PRIVATE, fileno(db->f), as_offset);
174
175 if (db->as_v1 == MAP_FAILED)
176 return 1;
177 }
178
179 db->as_count = as_length / sizeof(*db->as_v1);
180
181 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
182
183 return 0;
184 }
185
186 static int loc_database_read_network_nodes_section_v1(struct loc_database* db,
187 const struct loc_database_header_v1* header) {
188 off_t network_nodes_offset = be32toh(header->network_tree_offset);
189 size_t network_nodes_length = be32toh(header->network_tree_length);
190
191 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
192 (intmax_t)network_nodes_offset, network_nodes_length);
193
194 if (network_nodes_length > 0) {
195 db->network_nodes_v1 = mmap(NULL, network_nodes_length, PROT_READ,
196 MAP_PRIVATE, fileno(db->f), network_nodes_offset);
197
198 if (db->network_nodes_v1 == MAP_FAILED)
199 return 1;
200 }
201
202 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v1);
203
204 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
205
206 return 0;
207 }
208
209 static int loc_database_read_networks_section_v1(struct loc_database* db,
210 const struct loc_database_header_v1* header) {
211 off_t networks_offset = be32toh(header->network_data_offset);
212 size_t networks_length = be32toh(header->network_data_length);
213
214 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
215 (intmax_t)networks_offset, networks_length);
216
217 if (networks_length > 0) {
218 db->networks_v1 = mmap(NULL, networks_length, PROT_READ,
219 MAP_PRIVATE, fileno(db->f), networks_offset);
220
221 if (db->networks_v1 == MAP_FAILED)
222 return 1;
223 }
224
225 db->networks_count = networks_length / sizeof(*db->networks_v1);
226
227 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
228
229 return 0;
230 }
231
232 static int loc_database_read_countries_section_v1(struct loc_database* db,
233 const struct loc_database_header_v1* header) {
234 off_t countries_offset = be32toh(header->countries_offset);
235 size_t countries_length = be32toh(header->countries_length);
236
237 DEBUG(db->ctx, "Reading countries section from %jd (%zu bytes)\n",
238 (intmax_t)countries_offset, countries_length);
239
240 if (countries_length > 0) {
241 db->countries_v1 = mmap(NULL, countries_length, PROT_READ,
242 MAP_PRIVATE, fileno(db->f), countries_offset);
243
244 if (db->countries_v1 == MAP_FAILED)
245 return 1;
246 }
247
248 db->countries_count = countries_length / sizeof(*db->countries_v1);
249
250 INFO(db->ctx, "Read %zu countries from the database\n",
251 db->countries_count);
252
253 return 0;
254 }
255
256 static int loc_database_read_signature(struct loc_database* db,
257 char** dst, char* src, size_t length) {
258 // Check for a plausible signature length
259 if (length > LOC_SIGNATURE_MAX_LENGTH) {
260 ERROR(db->ctx, "Signature too long: %zu\n", length);
261 return -EINVAL;
262 }
263
264 DEBUG(db->ctx, "Reading signature of %zu bytes\n", length);
265
266 // Allocate space
267 *dst = malloc(length);
268 if (!*dst)
269 return -ENOMEM;
270
271 // Copy payload
272 memcpy(*dst, src, length);
273
274 return 0;
275 }
276
277 static int loc_database_read_header_v1(struct loc_database* db) {
278 struct loc_database_header_v1 header;
279 int r;
280
281 // Read from file
282 size_t size = fread(&header, 1, sizeof(header), db->f);
283
284 if (size < sizeof(header)) {
285 ERROR(db->ctx, "Could not read enough data for header\n");
286 return -ENOMSG;
287 }
288
289 // Copy over data
290 db->created_at = be64toh(header.created_at);
291 db->vendor = be32toh(header.vendor);
292 db->description = be32toh(header.description);
293 db->license = be32toh(header.license);
294
295 db->signature1_length = be16toh(header.signature1_length);
296 db->signature2_length = be16toh(header.signature2_length);
297
298 // Read signatures
299 if (db->signature1_length) {
300 r = loc_database_read_signature(db, &db->signature1,
301 header.signature1, db->signature1_length);
302 if (r)
303 return r;
304 }
305
306 if (db->signature2_length) {
307 r = loc_database_read_signature(db, &db->signature2,
308 header.signature2, db->signature2_length);
309 if (r)
310 return r;
311 }
312
313 // Open pool
314 off_t pool_offset = be32toh(header.pool_offset);
315 size_t pool_length = be32toh(header.pool_length);
316
317 r = loc_stringpool_open(db->ctx, &db->pool,
318 db->f, pool_length, pool_offset);
319 if (r)
320 return r;
321
322 // AS section
323 r = loc_database_read_as_section_v1(db, &header);
324 if (r)
325 return r;
326
327 // Network Nodes
328 r = loc_database_read_network_nodes_section_v1(db, &header);
329 if (r)
330 return r;
331
332 // Networks
333 r = loc_database_read_networks_section_v1(db, &header);
334 if (r)
335 return r;
336
337 // countries
338 r = loc_database_read_countries_section_v1(db, &header);
339 if (r)
340 return r;
341
342 return 0;
343 }
344
345 static int loc_database_read_header(struct loc_database* db) {
346 DEBUG(db->ctx, "Database version is %u\n", db->version);
347
348 switch (db->version) {
349 case LOC_DATABASE_VERSION_1:
350 return loc_database_read_header_v1(db);
351
352 default:
353 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
354 return 1;
355 }
356 }
357
358 static int loc_database_read(struct loc_database* db, FILE* f) {
359 clock_t start = clock();
360
361 int fd = fileno(f);
362
363 // Clone file descriptor
364 fd = dup(fd);
365 if (!fd) {
366 ERROR(db->ctx, "Could not duplicate file descriptor\n");
367 return -1;
368 }
369
370 // Reopen the file so that we can keep our own file handle
371 db->f = fdopen(fd, "r");
372 if (!db->f) {
373 ERROR(db->ctx, "Could not re-open database file\n");
374 return -1;
375 }
376
377 // Rewind to the start of the file
378 rewind(db->f);
379
380 // Read magic bytes
381 int r = loc_database_read_magic(db);
382 if (r)
383 return r;
384
385 // Read the header
386 r = loc_database_read_header(db);
387 if (r)
388 return r;
389
390 clock_t end = clock();
391
392 INFO(db->ctx, "Opened database in %.4fms\n",
393 (double)(end - start) / CLOCKS_PER_SEC * 1000);
394
395 return 0;
396 }
397
398 LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
399 // Fail on invalid file handle
400 if (!f)
401 return -EINVAL;
402
403 struct loc_database* db = calloc(1, sizeof(*db));
404 if (!db)
405 return -ENOMEM;
406
407 // Reference context
408 db->ctx = loc_ref(ctx);
409 db->refcount = 1;
410
411 DEBUG(db->ctx, "Database object allocated at %p\n", db);
412
413 int r = loc_database_read(db, f);
414 if (r) {
415 loc_database_unref(db);
416 return r;
417 }
418
419 *database = db;
420
421 return 0;
422 }
423
424 LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
425 db->refcount++;
426
427 return db;
428 }
429
430 static void loc_database_free(struct loc_database* db) {
431 int r;
432
433 DEBUG(db->ctx, "Releasing database %p\n", db);
434
435 // Removing all ASes
436 if (db->as_v1) {
437 r = munmap(db->as_v1, db->as_count * sizeof(*db->as_v1));
438 if (r)
439 ERROR(db->ctx, "Could not unmap AS section: %m\n");
440 }
441
442 // Remove mapped network sections
443 if (db->networks_v1) {
444 r = munmap(db->networks_v1, db->networks_count * sizeof(*db->networks_v1));
445 if (r)
446 ERROR(db->ctx, "Could not unmap networks section: %m\n");
447 }
448
449 // Remove mapped network nodes section
450 if (db->network_nodes_v1) {
451 r = munmap(db->network_nodes_v1, db->network_nodes_count * sizeof(*db->network_nodes_v1));
452 if (r)
453 ERROR(db->ctx, "Could not unmap network nodes section: %m\n");
454 }
455
456 // Remove mapped countries section
457 if (db->countries_v1) {
458 r = munmap(db->countries_v1, db->countries_count * sizeof(*db->countries_v1));
459 if (r)
460 ERROR(db->ctx, "Could not unmap countries section: %m\n");
461 }
462
463 if (db->pool)
464 loc_stringpool_unref(db->pool);
465
466 // Free signature
467 if (db->signature1)
468 free(db->signature1);
469 if (db->signature2)
470 free(db->signature2);
471
472 // Close database file
473 if (db->f)
474 fclose(db->f);
475
476 loc_unref(db->ctx);
477 free(db);
478 }
479
480 LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
481 if (--db->refcount > 0)
482 return NULL;
483
484 loc_database_free(db);
485 return NULL;
486 }
487
488 LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
489 size_t bytes_read = 0;
490
491 // Cannot do this when no signature is available
492 if (!db->signature1 && !db->signature2) {
493 DEBUG(db->ctx, "No signature available to verify\n");
494 return 1;
495 }
496
497 // Start the stopwatch
498 clock_t start = clock();
499
500 // Load public key
501 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
502 if (!pkey) {
503 ERROR(db->ctx, "Could not parse public key: %s\n",
504 ERR_error_string(ERR_get_error(), NULL));
505
506 return -1;
507 }
508
509 int r = 0;
510
511 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
512
513 // Initialise hash function
514 r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
515 if (r != 1) {
516 ERROR(db->ctx, "Error initializing signature validation: %s\n",
517 ERR_error_string(ERR_get_error(), NULL));
518 r = 1;
519
520 goto CLEANUP;
521 }
522
523 // Reset file to start
524 rewind(db->f);
525
526 // Read magic
527 struct loc_database_magic magic;
528 bytes_read = fread(&magic, 1, sizeof(magic), db->f);
529 if (bytes_read < sizeof(magic)) {
530 ERROR(db->ctx, "Could not read header: %m\n");
531 r = 1;
532 goto CLEANUP;
533 }
534
535 hexdump(db->ctx, &magic, sizeof(magic));
536
537 // Feed magic into the hash
538 r = EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
539 if (r != 1) {
540 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
541 r = 1;
542
543 goto CLEANUP;
544 }
545
546 // Read the header
547 struct loc_database_header_v1 header_v1;
548
549 switch (db->version) {
550 case LOC_DATABASE_VERSION_1:
551 bytes_read = fread(&header_v1, 1, sizeof(header_v1), db->f);
552 if (bytes_read < sizeof(header_v1)) {
553 ERROR(db->ctx, "Could not read header\n");
554 r = 1;
555
556 goto CLEANUP;
557 }
558
559 // Clear signatures
560 memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
561 header_v1.signature1_length = 0;
562 memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
563 header_v1.signature2_length = 0;
564
565 hexdump(db->ctx, &header_v1, sizeof(header_v1));
566
567 // Feed header into the hash
568 r = EVP_DigestVerifyUpdate(mdctx, &header_v1, sizeof(header_v1));
569 if (r != 1) {
570 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
571 r = 1;
572
573 goto CLEANUP;
574 }
575 break;
576
577 default:
578 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
579 db->version);
580 r = -EINVAL;
581 goto CLEANUP;
582 }
583
584 // Walk through the file in chunks of 64kB
585 char buffer[64 * 1024];
586
587 while (!feof(db->f)) {
588 bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
589
590 hexdump(db->ctx, buffer, bytes_read);
591
592 r = EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
593 if (r != 1) {
594 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
595 r = 1;
596
597 goto CLEANUP;
598 }
599 }
600
601 // Check first signature
602 if (db->signature1) {
603 hexdump(db->ctx, db->signature1, db->signature1_length);
604
605 r = EVP_DigestVerifyFinal(mdctx,
606 (unsigned char*)db->signature1, db->signature1_length);
607
608 if (r == 0) {
609 DEBUG(db->ctx, "The first signature is invalid\n");
610 r = 1;
611 } else if (r == 1) {
612 DEBUG(db->ctx, "The first signature is valid\n");
613 r = 0;
614 } else {
615 ERROR(db->ctx, "Error verifying the first signature: %s\n",
616 ERR_error_string(ERR_get_error(), NULL));
617 r = -1;
618 }
619 }
620
621 // Check second signature only when the first one was invalid
622 if (r && db->signature2) {
623 hexdump(db->ctx, db->signature2, db->signature2_length);
624
625 r = EVP_DigestVerifyFinal(mdctx,
626 (unsigned char*)db->signature2, db->signature2_length);
627
628 if (r == 0) {
629 DEBUG(db->ctx, "The second signature is invalid\n");
630 r = 1;
631 } else if (r == 1) {
632 DEBUG(db->ctx, "The second signature is valid\n");
633 r = 0;
634 } else {
635 ERROR(db->ctx, "Error verifying the second signature: %s\n",
636 ERR_error_string(ERR_get_error(), NULL));
637 r = -1;
638 }
639 }
640
641 clock_t end = clock();
642 INFO(db->ctx, "Signature checked in %.4fms\n",
643 (double)(end - start) / CLOCKS_PER_SEC * 1000);
644
645 CLEANUP:
646 // Cleanup
647 EVP_MD_CTX_free(mdctx);
648 EVP_PKEY_free(pkey);
649
650 return r;
651 }
652
653 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
654 return db->created_at;
655 }
656
657 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
658 return loc_stringpool_get(db->pool, db->vendor);
659 }
660
661 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
662 return loc_stringpool_get(db->pool, db->description);
663 }
664
665 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
666 return loc_stringpool_get(db->pool, db->license);
667 }
668
669 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
670 return db->as_count;
671 }
672
673 // Returns the AS at position pos
674 static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
675 if ((size_t)pos >= db->as_count)
676 return -EINVAL;
677
678 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
679
680 int r;
681 switch (db->version) {
682 case LOC_DATABASE_VERSION_1:
683 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, db->as_v1 + pos);
684 break;
685
686 default:
687 return -1;
688 }
689
690 if (r == 0) {
691 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
692 }
693
694 return r;
695 }
696
697 // Performs a binary search to find the AS in the list
698 LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
699 off_t lo = 0;
700 off_t hi = db->as_count - 1;
701
702 #ifdef ENABLE_DEBUG
703 // Save start time
704 clock_t start = clock();
705 #endif
706
707 while (lo <= hi) {
708 off_t i = (lo + hi) / 2;
709
710 // Fetch AS in the middle between lo and hi
711 int r = loc_database_fetch_as(db, as, i);
712 if (r)
713 return r;
714
715 // Check if this is a match
716 uint32_t as_number = loc_as_get_number(*as);
717 if (as_number == number) {
718 #ifdef ENABLE_DEBUG
719 clock_t end = clock();
720
721 // Log how fast this has been
722 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
723 (double)(end - start) / CLOCKS_PER_SEC * 1000);
724 #endif
725
726 return 0;
727 }
728
729 // If it wasn't, we release the AS and
730 // adjust our search pointers
731 loc_as_unref(*as);
732
733 if (as_number < number) {
734 lo = i + 1;
735 } else
736 hi = i - 1;
737 }
738
739 // Nothing found
740 *as = NULL;
741
742 return 1;
743 }
744
745 // Returns the network at position pos
746 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
747 struct in6_addr* address, unsigned int prefix, off_t pos) {
748 if ((size_t)pos >= db->networks_count) {
749 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
750 (intmax_t)pos, (intmax_t)db->networks_count);
751 return -EINVAL;
752 }
753
754
755 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
756
757 int r;
758 switch (db->version) {
759 case LOC_DATABASE_VERSION_1:
760 r = loc_network_new_from_database_v1(db->ctx, network,
761 address, prefix, db->networks_v1 + pos);
762 break;
763
764 default:
765 return -1;
766 }
767
768 if (r == 0)
769 DEBUG(db->ctx, "Got network %s\n", loc_network_str(*network));
770
771 return r;
772 }
773
774 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1* node) {
775 return (node->network != htobe32(0xffffffff));
776 }
777
778 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
779 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
780 const struct loc_database_network_node_v1* node) {
781 off_t network_index = be32toh(node->network);
782
783 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", (intmax_t)(node - db->network_nodes_v1), (intmax_t)network_index);
784
785 // Fetch the network
786 int r = loc_database_fetch_network(db, network,
787 network_address, prefix, network_index);
788 if (r) {
789 ERROR(db->ctx, "Could not fetch network %jd from database\n", (intmax_t)network_index);
790 return r;
791 }
792
793 // Check if the given IP address is inside the network
794 if (!loc_network_matches_address(*network, address)) {
795 DEBUG(db->ctx, "Searched address is not part of the network\n");
796
797 loc_network_unref(*network);
798 *network = NULL;
799 return 1;
800 }
801
802 // A network was found and the IP address matches
803 return 0;
804 }
805
806 // Searches for an exact match along the path
807 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
808 struct loc_network** network, struct in6_addr* network_address,
809 const struct loc_database_network_node_v1* node, unsigned int level) {
810 int r;
811 off_t node_index;
812
813 // Follow the path
814 int bit = loc_address_get_bit(address, level);
815 loc_address_set_bit(network_address, level, bit);
816
817 if (bit == 0)
818 node_index = be32toh(node->zero);
819 else
820 node_index = be32toh(node->one);
821
822 // If the node index is zero, the tree ends here
823 // and we cannot descend any further
824 if (node_index > 0) {
825 // Check boundaries
826 if ((size_t)node_index >= db->network_nodes_count)
827 return -EINVAL;
828
829 // Move on to the next node
830 r = __loc_database_lookup(db, address, network, network_address,
831 db->network_nodes_v1 + node_index, level + 1);
832
833 // End here if a result was found
834 if (r == 0)
835 return r;
836
837 // Raise any errors
838 else if (r < 0)
839 return r;
840
841 DEBUG(db->ctx, "No match found below level %u\n", level);
842 } else {
843 DEBUG(db->ctx, "Tree ended at level %u\n", level);
844 }
845
846 // If this node has a leaf, we will check if it matches
847 if (__loc_database_node_is_leaf(node)) {
848 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
849 if (r <= 0)
850 return r;
851 }
852
853 return 1;
854 }
855
856 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
857 const struct in6_addr* address, struct loc_network** network) {
858 struct in6_addr network_address;
859 memset(&network_address, 0, sizeof(network_address));
860
861 *network = NULL;
862
863 #ifdef ENABLE_DEBUG
864 // Save start time
865 clock_t start = clock();
866 #endif
867
868 int r = __loc_database_lookup(db, address, network, &network_address,
869 db->network_nodes_v1, 0);
870
871 #ifdef ENABLE_DEBUG
872 clock_t end = clock();
873
874 // Log how fast this has been
875 DEBUG(db->ctx, "Executed network search in %.4fms\n",
876 (double)(end - start) / CLOCKS_PER_SEC * 1000);
877 #endif
878
879 return r;
880 }
881
882 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
883 const char* string, struct loc_network** network) {
884 struct in6_addr address;
885
886 int r = loc_address_parse(&address, NULL, string);
887 if (r)
888 return r;
889
890 return loc_database_lookup(db, &address, network);
891 }
892
893 // Returns the country at position pos
894 static int loc_database_fetch_country(struct loc_database* db,
895 struct loc_country** country, off_t pos) {
896 if ((size_t)pos >= db->countries_count)
897 return -EINVAL;
898
899 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
900
901 int r;
902 switch (db->version) {
903 case LOC_DATABASE_VERSION_1:
904 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, db->countries_v1 + pos);
905 break;
906
907 default:
908 return -1;
909 }
910
911 if (r == 0) {
912 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
913 }
914
915 return r;
916 }
917
918 // Performs a binary search to find the country in the list
919 LOC_EXPORT int loc_database_get_country(struct loc_database* db,
920 struct loc_country** country, const char* code) {
921 off_t lo = 0;
922 off_t hi = db->countries_count - 1;
923
924 #ifdef ENABLE_DEBUG
925 // Save start time
926 clock_t start = clock();
927 #endif
928
929 while (lo <= hi) {
930 off_t i = (lo + hi) / 2;
931
932 // Fetch country in the middle between lo and hi
933 int r = loc_database_fetch_country(db, country, i);
934 if (r)
935 return r;
936
937 // Check if this is a match
938 const char* cc = loc_country_get_code(*country);
939 int result = strcmp(code, cc);
940
941 if (result == 0) {
942 #ifdef ENABLE_DEBUG
943 clock_t end = clock();
944
945 // Log how fast this has been
946 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
947 (double)(end - start) / CLOCKS_PER_SEC * 1000);
948 #endif
949
950 return 0;
951 }
952
953 // If it wasn't, we release the country and
954 // adjust our search pointers
955 loc_country_unref(*country);
956
957 if (result > 0) {
958 lo = i + 1;
959 } else
960 hi = i - 1;
961 }
962
963 // Nothing found
964 *country = NULL;
965
966 return 1;
967 }
968
969 // Enumerator
970
971 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
972 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
973
974 // Release all references
975 loc_database_unref(enumerator->db);
976 loc_unref(enumerator->ctx);
977
978 if (enumerator->string)
979 free(enumerator->string);
980
981 if (enumerator->countries)
982 loc_country_list_unref(enumerator->countries);
983
984 if (enumerator->asns)
985 loc_as_list_unref(enumerator->asns);
986
987 // Free network search
988 free(enumerator->networks_visited);
989
990 // Free subnet/bogons stack
991 if (enumerator->stack)
992 loc_network_list_unref(enumerator->stack);
993
994 if (enumerator->subnets)
995 loc_network_list_unref(enumerator->subnets);
996
997 free(enumerator);
998 }
999
1000 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
1001 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
1002 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
1003 if (!e)
1004 return -ENOMEM;
1005
1006 // Reference context
1007 e->ctx = loc_ref(db->ctx);
1008 e->db = loc_database_ref(db);
1009 e->mode = mode;
1010 e->refcount = 1;
1011
1012 // Flatten output?
1013 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
1014
1015 // Initialise graph search
1016 e->network_stack_depth = 1;
1017 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
1018
1019 // Allocate stack
1020 int r = loc_network_list_new(e->ctx, &e->stack);
1021 if (r) {
1022 loc_database_enumerator_free(e);
1023 return r;
1024 }
1025
1026 // Initialize bogon search
1027 loc_address_reset(&e->gap6_start, AF_INET6);
1028 loc_address_reset(&e->gap4_start, AF_INET);
1029
1030 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1031
1032 *enumerator = e;
1033 return 0;
1034 }
1035
1036 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1037 enumerator->refcount++;
1038
1039 return enumerator;
1040 }
1041
1042 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1043 if (!enumerator)
1044 return NULL;
1045
1046 if (--enumerator->refcount > 0)
1047 return enumerator;
1048
1049 loc_database_enumerator_free(enumerator);
1050 return NULL;
1051 }
1052
1053 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1054 enumerator->string = strdup(string);
1055
1056 // Make the string lowercase
1057 for (char *p = enumerator->string; *p; p++)
1058 *p = tolower(*p);
1059
1060 return 0;
1061 }
1062
1063 LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1064 struct loc_database_enumerator* enumerator) {
1065 if (!enumerator->countries)
1066 return NULL;
1067
1068 return loc_country_list_ref(enumerator->countries);
1069 }
1070
1071 LOC_EXPORT int loc_database_enumerator_set_countries(
1072 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1073 if (enumerator->countries)
1074 loc_country_list_unref(enumerator->countries);
1075
1076 enumerator->countries = loc_country_list_ref(countries);
1077
1078 return 0;
1079 }
1080
1081 LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1082 struct loc_database_enumerator* enumerator) {
1083 if (!enumerator->asns)
1084 return NULL;
1085
1086 return loc_as_list_ref(enumerator->asns);
1087 }
1088
1089 LOC_EXPORT int loc_database_enumerator_set_asns(
1090 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1091 if (enumerator->asns)
1092 loc_as_list_unref(enumerator->asns);
1093
1094 enumerator->asns = loc_as_list_ref(asns);
1095
1096 return 0;
1097 }
1098
1099 LOC_EXPORT int loc_database_enumerator_set_flag(
1100 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1101 enumerator->flags |= flag;
1102
1103 return 0;
1104 }
1105
1106 LOC_EXPORT int loc_database_enumerator_set_family(
1107 struct loc_database_enumerator* enumerator, int family) {
1108 enumerator->family = family;
1109
1110 return 0;
1111 }
1112
1113 LOC_EXPORT int loc_database_enumerator_next_as(
1114 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1115 *as = NULL;
1116
1117 // Do not do anything if not in AS mode
1118 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
1119 return 0;
1120
1121 struct loc_database* db = enumerator->db;
1122
1123 while (enumerator->as_index < db->as_count) {
1124 // Fetch the next AS
1125 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
1126 if (r)
1127 return r;
1128
1129 r = loc_as_match_string(*as, enumerator->string);
1130 if (r == 1) {
1131 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
1132 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
1133
1134 return 0;
1135 }
1136
1137 // No match
1138 loc_as_unref(*as);
1139 *as = NULL;
1140 }
1141
1142 // Reset the index
1143 enumerator->as_index = 0;
1144
1145 // We have searched through all of them
1146 return 0;
1147 }
1148
1149 static int loc_database_enumerator_stack_push_node(
1150 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1151 // Do not add empty nodes
1152 if (!offset)
1153 return 0;
1154
1155 // Check if there is any space left on the stack
1156 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1157 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1158 return -1;
1159 }
1160
1161 // Increase stack size
1162 int s = ++e->network_stack_depth;
1163
1164 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
1165
1166 e->network_stack[s].offset = offset;
1167 e->network_stack[s].i = i;
1168 e->network_stack[s].depth = depth;
1169
1170 return 0;
1171 }
1172
1173 static int loc_database_enumerator_match_network(
1174 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1175 // If family is set, it must match
1176 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1177 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
1178 return 0;
1179 }
1180
1181 // Match if no filter criteria is configured
1182 if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
1183 return 1;
1184
1185 // Check if the country code matches
1186 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1187 const char* country_code = loc_network_get_country_code(network);
1188
1189 if (loc_country_list_contains_code(enumerator->countries, country_code)) {
1190 DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
1191 return 1;
1192 }
1193 }
1194
1195 // Check if the ASN matches
1196 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1197 uint32_t asn = loc_network_get_asn(network);
1198
1199 if (loc_as_list_contains_number(enumerator->asns, asn)) {
1200 DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
1201 return 1;
1202 }
1203 }
1204
1205 // Check if flags match
1206 if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
1207 DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
1208 return 1;
1209 }
1210
1211 // Not a match
1212 return 0;
1213 }
1214
1215 static int __loc_database_enumerator_next_network(
1216 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1217 // Return top element from the stack
1218 while (1) {
1219 *network = loc_network_list_pop_first(enumerator->stack);
1220
1221 // Stack is empty
1222 if (!*network)
1223 break;
1224
1225 // Return everything if filter isn't enabled, or only return matches
1226 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1227 return 0;
1228
1229 // Throw away anything that doesn't match
1230 loc_network_unref(*network);
1231 *network = NULL;
1232 }
1233
1234 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1235 enumerator->network_stack_depth);
1236
1237 // Perform DFS
1238 while (enumerator->network_stack_depth > 0) {
1239 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
1240
1241 // Get object from top of the stack
1242 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
1243
1244 // Remove the node from the stack if we have already visited it
1245 if (enumerator->networks_visited[node->offset]) {
1246 enumerator->network_stack_depth--;
1247 continue;
1248 }
1249
1250 // Mark the bits on the path correctly
1251 loc_address_set_bit(&enumerator->network_address,
1252 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1253
1254 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
1255 enumerator->networks_visited[node->offset]++;
1256
1257 // Pop node from top of the stack
1258 struct loc_database_network_node_v1* n =
1259 enumerator->db->network_nodes_v1 + node->offset;
1260
1261 // Add edges to stack
1262 int r = loc_database_enumerator_stack_push_node(enumerator,
1263 be32toh(n->one), 1, node->depth + 1);
1264
1265 if (r)
1266 return r;
1267
1268 r = loc_database_enumerator_stack_push_node(enumerator,
1269 be32toh(n->zero), 0, node->depth + 1);
1270
1271 if (r)
1272 return r;
1273
1274 // Check if this node is a leaf and has a network object
1275 if (__loc_database_node_is_leaf(n)) {
1276 off_t network_index = be32toh(n->network);
1277
1278 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
1279
1280 // Fetch the network object
1281 r = loc_database_fetch_network(enumerator->db, network,
1282 &enumerator->network_address, node->depth, network_index);
1283
1284 // Break on any errors
1285 if (r)
1286 return r;
1287
1288 // Return all networks when the filter is disabled, or check for match
1289 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1290 return 0;
1291
1292 // Does not seem to be a match, so we cleanup and move on
1293 loc_network_unref(*network);
1294 *network = NULL;
1295 }
1296 }
1297
1298 // Reached the end of the search
1299 return 0;
1300 }
1301
1302 static int __loc_database_enumerator_next_network_flattened(
1303 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1304 // Fetch the next network
1305 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1306 if (r)
1307 return r;
1308
1309 // End if we could not read another network
1310 if (!*network)
1311 return 0;
1312
1313 struct loc_network* subnet = NULL;
1314
1315 // Create a list with all subnets
1316 if (!enumerator->subnets) {
1317 r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
1318 if (r)
1319 return r;
1320 }
1321
1322 // Search all subnets from the database
1323 while (1) {
1324 // Fetch the next network in line
1325 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
1326 if (r) {
1327 loc_network_unref(subnet);
1328 loc_network_list_clear(enumerator->subnets);
1329
1330 return r;
1331 }
1332
1333 // End if we did not receive another subnet
1334 if (!subnet)
1335 break;
1336
1337 // Collect all subnets in a list
1338 if (loc_network_is_subnet(*network, subnet)) {
1339 r = loc_network_list_push(enumerator->subnets, subnet);
1340 if (r) {
1341 loc_network_unref(subnet);
1342 loc_network_list_clear(enumerator->subnets);
1343
1344 return r;
1345 }
1346
1347 loc_network_unref(subnet);
1348 continue;
1349 }
1350
1351 // If this is not a subnet, we push it back onto the stack and break
1352 r = loc_network_list_push(enumerator->stack, subnet);
1353 if (r) {
1354 loc_network_unref(subnet);
1355 loc_network_list_clear(enumerator->subnets);
1356
1357 return r;
1358 }
1359
1360 loc_network_unref(subnet);
1361 break;
1362 }
1363
1364 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
1365 loc_network_list_size(enumerator->subnets));
1366
1367 // We can abort here if the network has no subnets
1368 if (loc_network_list_empty(enumerator->subnets)) {
1369 loc_network_list_clear(enumerator->subnets);
1370
1371 return 0;
1372 }
1373
1374 // If the network has any subnets, we will break it into smaller parts
1375 // without the subnets.
1376 struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
1377 if (!excluded) {
1378 loc_network_list_clear(enumerator->subnets);
1379 return 1;
1380 }
1381
1382 // Merge subnets onto the stack
1383 r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
1384 if (r) {
1385 loc_network_list_clear(enumerator->subnets);
1386 loc_network_list_unref(excluded);
1387
1388 return r;
1389 }
1390
1391 // Push excluded list onto the stack
1392 r = loc_network_list_merge(enumerator->stack, excluded);
1393 if (r) {
1394 loc_network_list_clear(enumerator->subnets);
1395 loc_network_list_unref(excluded);
1396
1397 return r;
1398 }
1399
1400 loc_network_list_clear(enumerator->subnets);
1401 loc_network_list_unref(excluded);
1402
1403 // Drop the network and restart the whole process again to pick the next network
1404 loc_network_unref(*network);
1405
1406 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1407 }
1408
1409 /*
1410 This function finds all bogons (i.e. gaps) between the input networks
1411 */
1412 static int __loc_database_enumerator_next_bogon(
1413 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1414 int r;
1415
1416 // Return top element from the stack
1417 while (1) {
1418 *bogon = loc_network_list_pop_first(enumerator->stack);
1419
1420 // Stack is empty
1421 if (!*bogon)
1422 break;
1423
1424 // Return result
1425 return 0;
1426 }
1427
1428 struct loc_network* network = NULL;
1429 struct in6_addr* gap_start = NULL;
1430 struct in6_addr gap_end = IN6ADDR_ANY_INIT;
1431
1432 while (1) {
1433 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1434 if (r)
1435 return r;
1436
1437 // We have read the last network
1438 if (!network)
1439 goto FINISH;
1440
1441 const char* country_code = loc_network_get_country_code(network);
1442
1443 /*
1444 Skip anything that does not have a country code
1445
1446 Even if a network is part of the routing table, and the database provides
1447 an ASN, this does not mean that this is a legitimate announcement.
1448 */
1449 if (country_code && !*country_code) {
1450 loc_network_unref(network);
1451 continue;
1452 }
1453
1454 // Determine the network family
1455 int family = loc_network_address_family(network);
1456
1457 switch (family) {
1458 case AF_INET6:
1459 gap_start = &enumerator->gap6_start;
1460 break;
1461
1462 case AF_INET:
1463 gap_start = &enumerator->gap4_start;
1464 break;
1465
1466 default:
1467 ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
1468 errno = ENOTSUP;
1469 return 1;
1470 }
1471
1472 const struct in6_addr* first_address = loc_network_get_first_address(network);
1473 const struct in6_addr* last_address = loc_network_get_last_address(network);
1474
1475 // Skip if this network is a subnet of a former one
1476 if (loc_address_cmp(gap_start, last_address) >= 0) {
1477 loc_network_unref(network);
1478 continue;
1479 }
1480
1481 // Search where the gap could end
1482 gap_end = *first_address;
1483 loc_address_decrement(&gap_end);
1484
1485 // There is a gap
1486 if (loc_address_cmp(gap_start, &gap_end) <= 0) {
1487 r = loc_network_list_summarize(enumerator->ctx,
1488 gap_start, &gap_end, &enumerator->stack);
1489 if (r) {
1490 loc_network_unref(network);
1491 return r;
1492 }
1493 }
1494
1495 // The gap now starts after this network
1496 *gap_start = *last_address;
1497 loc_address_increment(gap_start);
1498
1499 loc_network_unref(network);
1500
1501 // Try to return something
1502 *bogon = loc_network_list_pop_first(enumerator->stack);
1503 if (*bogon)
1504 break;
1505 }
1506
1507 return 0;
1508
1509 FINISH:
1510
1511 if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
1512 r = loc_address_reset_last(&gap_end, AF_INET6);
1513 if (r)
1514 return r;
1515
1516 if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
1517 r = loc_network_list_summarize(enumerator->ctx,
1518 &enumerator->gap6_start, &gap_end, &enumerator->stack);
1519 if (r)
1520 return r;
1521 }
1522
1523 // Reset start
1524 loc_address_reset(&enumerator->gap6_start, AF_INET6);
1525 }
1526
1527 if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
1528 r = loc_address_reset_last(&gap_end, AF_INET);
1529 if (r)
1530 return r;
1531
1532 if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
1533 r = loc_network_list_summarize(enumerator->ctx,
1534 &enumerator->gap4_start, &gap_end, &enumerator->stack);
1535 if (r)
1536 return r;
1537 }
1538
1539 // Reset start
1540 loc_address_reset(&enumerator->gap4_start, AF_INET);
1541 }
1542
1543 // Try to return something
1544 *bogon = loc_network_list_pop_first(enumerator->stack);
1545
1546 return 0;
1547 }
1548
1549 LOC_EXPORT int loc_database_enumerator_next_network(
1550 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1551 switch (enumerator->mode) {
1552 case LOC_DB_ENUMERATE_NETWORKS:
1553 // Flatten output?
1554 if (enumerator->flatten)
1555 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1556
1557 return __loc_database_enumerator_next_network(enumerator, network, 1);
1558
1559 case LOC_DB_ENUMERATE_BOGONS:
1560 return __loc_database_enumerator_next_bogon(enumerator, network);
1561
1562 default:
1563 return 0;
1564 }
1565 }
1566
1567 LOC_EXPORT int loc_database_enumerator_next_country(
1568 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1569 *country = NULL;
1570
1571 // Do not do anything if not in country mode
1572 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1573 return 0;
1574
1575 struct loc_database* db = enumerator->db;
1576
1577 while (enumerator->country_index < db->countries_count) {
1578 // Fetch the next country
1579 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1580 if (r)
1581 return r;
1582
1583 // We do not filter here, so it always is a match
1584 return 0;
1585 }
1586
1587 // Reset the index
1588 enumerator->country_index = 0;
1589
1590 // We have searched through all of them
1591 return 0;
1592 }