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