]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
database: Refactor error handling on create
[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 static void loc_database_free(struct loc_database* db) {
413 DEBUG(db->ctx, "Releasing database %p\n", db);
414
415 // Removing all ASes
416 loc_database_unmap_objects(db, &db->as_objects);
417
418 // Remove mapped network sections
419 loc_database_unmap_objects(db, &db->network_objects);
420
421 // Remove mapped network nodes section
422 loc_database_unmap_objects(db, &db->network_node_objects);
423
424 // Remove mapped countries section
425 loc_database_unmap_objects(db, &db->country_objects);
426
427 // Free the stringpool
428 if (db->pool)
429 loc_stringpool_unref(db->pool);
430
431 // Free signature
432 if (db->signature1)
433 free(db->signature1);
434 if (db->signature2)
435 free(db->signature2);
436
437 // Close database file
438 if (db->f)
439 fclose(db->f);
440
441 loc_unref(db->ctx);
442 free(db);
443 }
444
445 LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
446 struct loc_database* db = NULL;
447 int r;
448
449 // Fail on invalid file handle
450 if (!f) {
451 errno = EINVAL;
452 return 1;
453 }
454
455 // Allocate the database object
456 db = calloc(1, sizeof(*db));
457 if (!db)
458 goto ERROR;
459
460 // Reference context
461 db->ctx = loc_ref(ctx);
462 db->refcount = 1;
463
464 DEBUG(db->ctx, "Database object allocated at %p\n", db);
465
466 // Try to open the database
467 r = loc_database_read(db, f);
468 if (r)
469 goto ERROR;
470
471 *database = db;
472 return 0;
473
474 ERROR:
475 if (db)
476 loc_database_free(db);
477
478 return r;
479 }
480
481 LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
482 db->refcount++;
483
484 return db;
485 }
486
487 LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
488 if (--db->refcount > 0)
489 return NULL;
490
491 loc_database_free(db);
492 return NULL;
493 }
494
495 LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
496 size_t bytes_read = 0;
497
498 // Cannot do this when no signature is available
499 if (!db->signature1 && !db->signature2) {
500 DEBUG(db->ctx, "No signature available to verify\n");
501 return 1;
502 }
503
504 // Start the stopwatch
505 clock_t start = clock();
506
507 // Load public key
508 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
509 if (!pkey) {
510 ERROR(db->ctx, "Could not parse public key: %s\n",
511 ERR_error_string(ERR_get_error(), NULL));
512
513 return -1;
514 }
515
516 int r = 0;
517
518 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
519
520 // Initialise hash function
521 r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
522 if (r != 1) {
523 ERROR(db->ctx, "Error initializing signature validation: %s\n",
524 ERR_error_string(ERR_get_error(), NULL));
525 r = 1;
526
527 goto CLEANUP;
528 }
529
530 // Reset file to start
531 rewind(db->f);
532
533 // Read magic
534 struct loc_database_magic magic;
535 bytes_read = fread(&magic, 1, sizeof(magic), db->f);
536 if (bytes_read < sizeof(magic)) {
537 ERROR(db->ctx, "Could not read header: %m\n");
538 r = 1;
539 goto CLEANUP;
540 }
541
542 hexdump(db->ctx, &magic, sizeof(magic));
543
544 // Feed magic into the hash
545 r = EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
546 if (r != 1) {
547 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
548 r = 1;
549
550 goto CLEANUP;
551 }
552
553 // Read the header
554 struct loc_database_header_v1 header_v1;
555
556 switch (db->version) {
557 case LOC_DATABASE_VERSION_1:
558 bytes_read = fread(&header_v1, 1, sizeof(header_v1), db->f);
559 if (bytes_read < sizeof(header_v1)) {
560 ERROR(db->ctx, "Could not read header\n");
561 r = 1;
562
563 goto CLEANUP;
564 }
565
566 // Clear signatures
567 memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
568 header_v1.signature1_length = 0;
569 memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
570 header_v1.signature2_length = 0;
571
572 hexdump(db->ctx, &header_v1, sizeof(header_v1));
573
574 // Feed header into the hash
575 r = EVP_DigestVerifyUpdate(mdctx, &header_v1, sizeof(header_v1));
576 if (r != 1) {
577 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
578 r = 1;
579
580 goto CLEANUP;
581 }
582 break;
583
584 default:
585 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
586 db->version);
587 r = -EINVAL;
588 goto CLEANUP;
589 }
590
591 // Walk through the file in chunks of 64kB
592 char buffer[64 * 1024];
593
594 while (!feof(db->f)) {
595 bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
596
597 hexdump(db->ctx, buffer, bytes_read);
598
599 r = EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
600 if (r != 1) {
601 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
602 r = 1;
603
604 goto CLEANUP;
605 }
606 }
607
608 // Check first signature
609 if (db->signature1) {
610 hexdump(db->ctx, db->signature1, db->signature1_length);
611
612 r = EVP_DigestVerifyFinal(mdctx,
613 (unsigned char*)db->signature1, db->signature1_length);
614
615 if (r == 0) {
616 DEBUG(db->ctx, "The first signature is invalid\n");
617 r = 1;
618 } else if (r == 1) {
619 DEBUG(db->ctx, "The first signature is valid\n");
620 r = 0;
621 } else {
622 ERROR(db->ctx, "Error verifying the first signature: %s\n",
623 ERR_error_string(ERR_get_error(), NULL));
624 r = -1;
625 }
626 }
627
628 // Check second signature only when the first one was invalid
629 if (r && db->signature2) {
630 hexdump(db->ctx, db->signature2, db->signature2_length);
631
632 r = EVP_DigestVerifyFinal(mdctx,
633 (unsigned char*)db->signature2, db->signature2_length);
634
635 if (r == 0) {
636 DEBUG(db->ctx, "The second signature is invalid\n");
637 r = 1;
638 } else if (r == 1) {
639 DEBUG(db->ctx, "The second signature is valid\n");
640 r = 0;
641 } else {
642 ERROR(db->ctx, "Error verifying the second signature: %s\n",
643 ERR_error_string(ERR_get_error(), NULL));
644 r = -1;
645 }
646 }
647
648 clock_t end = clock();
649 INFO(db->ctx, "Signature checked in %.4fms\n",
650 (double)(end - start) / CLOCKS_PER_SEC * 1000);
651
652 CLEANUP:
653 // Cleanup
654 EVP_MD_CTX_free(mdctx);
655 EVP_PKEY_free(pkey);
656
657 return r;
658 }
659
660 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
661 return db->created_at;
662 }
663
664 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
665 return loc_stringpool_get(db->pool, db->vendor);
666 }
667
668 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
669 return loc_stringpool_get(db->pool, db->description);
670 }
671
672 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
673 return loc_stringpool_get(db->pool, db->license);
674 }
675
676 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
677 return db->as_objects.count;
678 }
679
680 // Returns the AS at position pos
681 static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
682 struct loc_database_as_v1 as_v1;
683 struct loc_database_as_v1* p_v1;
684 int r;
685
686 if ((size_t)pos >= db->as_objects.count) {
687 errno = ERANGE;
688 return 1;
689 }
690
691 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
692
693 switch (db->version) {
694 case LOC_DATABASE_VERSION_1:
695 // Read the object
696 p_v1 = loc_database_read_object(db, &as_v1, &db->as_objects, pos);
697 if (!p_v1)
698 return 1;
699
700 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, p_v1);
701 break;
702
703 default:
704 errno = ENOTSUP;
705 return 1;
706 }
707
708 if (r == 0)
709 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
710
711 return r;
712 }
713
714 // Performs a binary search to find the AS in the list
715 LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
716 off_t lo = 0;
717 off_t hi = db->as_objects.count - 1;
718
719 #ifdef ENABLE_DEBUG
720 // Save start time
721 clock_t start = clock();
722 #endif
723
724 while (lo <= hi) {
725 off_t i = (lo + hi) / 2;
726
727 // Fetch AS in the middle between lo and hi
728 int r = loc_database_fetch_as(db, as, i);
729 if (r)
730 return r;
731
732 // Check if this is a match
733 uint32_t as_number = loc_as_get_number(*as);
734 if (as_number == number) {
735 #ifdef ENABLE_DEBUG
736 clock_t end = clock();
737
738 // Log how fast this has been
739 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
740 (double)(end - start) / CLOCKS_PER_SEC * 1000);
741 #endif
742
743 return 0;
744 }
745
746 // If it wasn't, we release the AS and
747 // adjust our search pointers
748 loc_as_unref(*as);
749
750 if (as_number < number) {
751 lo = i + 1;
752 } else
753 hi = i - 1;
754 }
755
756 // Nothing found
757 *as = NULL;
758
759 return 1;
760 }
761
762 // Returns the network at position pos
763 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
764 struct in6_addr* address, unsigned int prefix, off_t pos) {
765 struct loc_database_network_v1 network_v1;
766 struct loc_database_network_v1* p_v1;
767 int r;
768
769 if ((size_t)pos >= db->network_objects.count) {
770 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
771 (intmax_t)pos, (intmax_t)db->network_objects.count);
772 errno = ERANGE;
773 return 1;
774 }
775
776 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
777
778 switch (db->version) {
779 case LOC_DATABASE_VERSION_1:
780 // Read the object
781 p_v1 = loc_database_read_object(db, &network_v1, &db->network_objects, pos);
782 if (!p_v1)
783 return 1;
784
785 r = loc_network_new_from_database_v1(db->ctx, network, address, prefix, p_v1);
786 break;
787
788 default:
789 errno = ENOTSUP;
790 return 1;
791 }
792
793 if (r == 0)
794 DEBUG(db->ctx, "Got network %s\n", loc_network_str(*network));
795
796 return r;
797 }
798
799 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1* node) {
800 return (node->network != htobe32(0xffffffff));
801 }
802
803 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
804 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
805 const struct loc_database_network_node_v1* node) {
806 off_t network_index = be32toh(node->network);
807
808 DEBUG(db->ctx, "Handling leaf node at %jd\n", (intmax_t)network_index);
809
810 // Fetch the network
811 int r = loc_database_fetch_network(db, network, network_address, prefix, network_index);
812 if (r) {
813 ERROR(db->ctx, "Could not fetch network %jd from database: %m\n",
814 (intmax_t)network_index);
815 return r;
816 }
817
818 // Check if the given IP address is inside the network
819 if (!loc_network_matches_address(*network, address)) {
820 DEBUG(db->ctx, "Searched address is not part of the network\n");
821
822 loc_network_unref(*network);
823 *network = NULL;
824 return 1;
825 }
826
827 // A network was found and the IP address matches
828 return 0;
829 }
830
831 // Searches for an exact match along the path
832 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
833 struct loc_network** network, struct in6_addr* network_address,
834 off_t node_index, unsigned int level) {
835 struct loc_database_network_node_v1 node_v1;
836 struct loc_database_network_node_v1* p_v1;
837
838 int r;
839
840 // Fetch the next node
841 p_v1 = loc_database_read_object(db, &node_v1, &db->network_node_objects, node_index);
842 if (!p_v1)
843 return 1;
844
845 // Follow the path
846 int bit = loc_address_get_bit(address, level);
847 loc_address_set_bit(network_address, level, bit);
848
849 if (bit == 0)
850 node_index = be32toh(p_v1->zero);
851 else
852 node_index = be32toh(p_v1->one);
853
854 // If the node index is zero, the tree ends here
855 // and we cannot descend any further
856 if (node_index > 0) {
857 // Check boundaries
858 if ((size_t)node_index >= db->network_node_objects.count) {
859 errno = ERANGE;
860 return 1;
861 }
862
863 // Move on to the next node
864 r = __loc_database_lookup(db, address, network, network_address, node_index, level + 1);
865
866 // End here if a result was found
867 if (r == 0)
868 return r;
869
870 // Raise any errors
871 else if (r < 0)
872 return r;
873
874 DEBUG(db->ctx, "No match found below level %u\n", level);
875 } else {
876 DEBUG(db->ctx, "Tree ended at level %u\n", level);
877 }
878
879 // If this node has a leaf, we will check if it matches
880 if (__loc_database_node_is_leaf(p_v1)) {
881 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, p_v1);
882 if (r <= 0)
883 return r;
884 }
885
886 return 1;
887 }
888
889 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
890 const struct in6_addr* address, struct loc_network** network) {
891 struct in6_addr network_address;
892 memset(&network_address, 0, sizeof(network_address));
893
894 *network = NULL;
895
896 #ifdef ENABLE_DEBUG
897 // Save start time
898 clock_t start = clock();
899 #endif
900
901 int r = __loc_database_lookup(db, address, network, &network_address, 0, 0);
902
903 #ifdef ENABLE_DEBUG
904 clock_t end = clock();
905
906 // Log how fast this has been
907 DEBUG(db->ctx, "Executed network search in %.4fms\n",
908 (double)(end - start) / CLOCKS_PER_SEC * 1000);
909 #endif
910
911 return r;
912 }
913
914 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
915 const char* string, struct loc_network** network) {
916 struct in6_addr address;
917
918 int r = loc_address_parse(&address, NULL, string);
919 if (r)
920 return r;
921
922 return loc_database_lookup(db, &address, network);
923 }
924
925 // Returns the country at position pos
926 static int loc_database_fetch_country(struct loc_database* db,
927 struct loc_country** country, off_t pos) {
928 struct loc_database_country_v1 country_v1;
929 struct loc_database_country_v1* p_v1;
930 int r;
931
932 // Check if the country is within range
933 if ((size_t)pos >= db->country_objects.count) {
934 errno = ERANGE;
935 return 1;
936 }
937
938 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
939
940 switch (db->version) {
941 case LOC_DATABASE_VERSION_1:
942 // Read the object
943 p_v1 = loc_database_read_object(db, &country_v1, &db->country_objects, pos);
944 if (!p_v1)
945 return 1;
946
947 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, p_v1);
948 break;
949
950 default:
951 errno = ENOTSUP;
952 return 1;
953 }
954
955 if (r == 0)
956 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
957
958 return r;
959 }
960
961 // Performs a binary search to find the country in the list
962 LOC_EXPORT int loc_database_get_country(struct loc_database* db,
963 struct loc_country** country, const char* code) {
964 off_t lo = 0;
965 off_t hi = db->country_objects.count - 1;
966
967 // Check if the country code is valid
968 if (!loc_country_code_is_valid(code)) {
969 errno = EINVAL;
970 return 1;
971 }
972
973 #ifdef ENABLE_DEBUG
974 // Save start time
975 clock_t start = clock();
976 #endif
977
978 while (lo <= hi) {
979 off_t i = (lo + hi) / 2;
980
981 // Fetch country in the middle between lo and hi
982 int r = loc_database_fetch_country(db, country, i);
983 if (r)
984 return r;
985
986 // Check if this is a match
987 const char* cc = loc_country_get_code(*country);
988 int result = strcmp(code, cc);
989
990 if (result == 0) {
991 #ifdef ENABLE_DEBUG
992 clock_t end = clock();
993
994 // Log how fast this has been
995 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
996 (double)(end - start) / CLOCKS_PER_SEC * 1000);
997 #endif
998
999 return 0;
1000 }
1001
1002 // If it wasn't, we release the country and
1003 // adjust our search pointers
1004 loc_country_unref(*country);
1005
1006 if (result > 0) {
1007 lo = i + 1;
1008 } else
1009 hi = i - 1;
1010 }
1011
1012 // Nothing found
1013 *country = NULL;
1014
1015 return 0;
1016 }
1017
1018 // Enumerator
1019
1020 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
1021 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
1022
1023 // Release all references
1024 loc_database_unref(enumerator->db);
1025 loc_unref(enumerator->ctx);
1026
1027 if (enumerator->string)
1028 free(enumerator->string);
1029
1030 if (enumerator->countries)
1031 loc_country_list_unref(enumerator->countries);
1032
1033 if (enumerator->asns)
1034 loc_as_list_unref(enumerator->asns);
1035
1036 // Free network search
1037 if (enumerator->networks_visited)
1038 free(enumerator->networks_visited);
1039
1040 // Free subnet/bogons stack
1041 if (enumerator->stack)
1042 loc_network_list_unref(enumerator->stack);
1043
1044 if (enumerator->subnets)
1045 loc_network_list_unref(enumerator->subnets);
1046
1047 free(enumerator);
1048 }
1049
1050 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
1051 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
1052 int r;
1053
1054 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
1055 if (!e) {
1056 return -ENOMEM;
1057 }
1058
1059 // Reference context
1060 e->ctx = loc_ref(db->ctx);
1061 e->db = loc_database_ref(db);
1062 e->mode = mode;
1063 e->refcount = 1;
1064
1065 // Flatten output?
1066 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
1067
1068 // Initialise graph search
1069 e->network_stack_depth = 1;
1070 e->networks_visited = calloc(db->network_node_objects.count, sizeof(*e->networks_visited));
1071 printf("COUNT = %zu, P = %p\n", db->network_node_objects.count, e->networks_visited);
1072 if (!e->networks_visited) {
1073 ERROR(db->ctx, "Could not allocated visited networks: %m\n");
1074 r = 1;
1075 goto ERROR;
1076 }
1077
1078 // Allocate stack
1079 r = loc_network_list_new(e->ctx, &e->stack);
1080 if (r)
1081 goto ERROR;
1082
1083 // Initialize bogon search
1084 loc_address_reset(&e->gap6_start, AF_INET6);
1085 loc_address_reset(&e->gap4_start, AF_INET);
1086
1087 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1088
1089 *enumerator = e;
1090 return 0;
1091
1092 ERROR:
1093 if (e)
1094 loc_database_enumerator_free(e);
1095
1096 return r;
1097 }
1098
1099 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1100 enumerator->refcount++;
1101
1102 return enumerator;
1103 }
1104
1105 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1106 if (!enumerator)
1107 return NULL;
1108
1109 if (--enumerator->refcount > 0)
1110 return enumerator;
1111
1112 loc_database_enumerator_free(enumerator);
1113 return NULL;
1114 }
1115
1116 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1117 enumerator->string = strdup(string);
1118
1119 // Make the string lowercase
1120 for (char *p = enumerator->string; *p; p++)
1121 *p = tolower(*p);
1122
1123 return 0;
1124 }
1125
1126 LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1127 struct loc_database_enumerator* enumerator) {
1128 if (!enumerator->countries)
1129 return NULL;
1130
1131 return loc_country_list_ref(enumerator->countries);
1132 }
1133
1134 LOC_EXPORT int loc_database_enumerator_set_countries(
1135 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1136 if (enumerator->countries)
1137 loc_country_list_unref(enumerator->countries);
1138
1139 enumerator->countries = loc_country_list_ref(countries);
1140
1141 return 0;
1142 }
1143
1144 LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1145 struct loc_database_enumerator* enumerator) {
1146 if (!enumerator->asns)
1147 return NULL;
1148
1149 return loc_as_list_ref(enumerator->asns);
1150 }
1151
1152 LOC_EXPORT int loc_database_enumerator_set_asns(
1153 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1154 if (enumerator->asns)
1155 loc_as_list_unref(enumerator->asns);
1156
1157 enumerator->asns = loc_as_list_ref(asns);
1158
1159 return 0;
1160 }
1161
1162 LOC_EXPORT int loc_database_enumerator_set_flag(
1163 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1164 enumerator->flags |= flag;
1165
1166 return 0;
1167 }
1168
1169 LOC_EXPORT int loc_database_enumerator_set_family(
1170 struct loc_database_enumerator* enumerator, int family) {
1171 enumerator->family = family;
1172
1173 return 0;
1174 }
1175
1176 LOC_EXPORT int loc_database_enumerator_next_as(
1177 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1178 *as = NULL;
1179
1180 // Do not do anything if not in AS mode
1181 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
1182 return 0;
1183
1184 struct loc_database* db = enumerator->db;
1185
1186 while (enumerator->as_index < db->as_objects.count) {
1187 // Fetch the next AS
1188 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
1189 if (r)
1190 return r;
1191
1192 r = loc_as_match_string(*as, enumerator->string);
1193 if (r == 1) {
1194 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
1195 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
1196
1197 return 0;
1198 }
1199
1200 // No match
1201 loc_as_unref(*as);
1202 *as = NULL;
1203 }
1204
1205 // Reset the index
1206 enumerator->as_index = 0;
1207
1208 // We have searched through all of them
1209 return 0;
1210 }
1211
1212 static int loc_database_enumerator_stack_push_node(
1213 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1214 // Do not add empty nodes
1215 if (!offset)
1216 return 0;
1217
1218 // Check if there is any space left on the stack
1219 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1220 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1221 return 1;
1222 }
1223
1224 // Check if the node is in range
1225 if (offset >= (off_t)e->db->network_node_objects.count) {
1226 ERROR(e->ctx, "Trying to add invalid node with offset %jd/%zu\n",
1227 offset, e->db->network_node_objects.count);
1228 errno = ERANGE;
1229 return 1;
1230 }
1231
1232 // Increase stack size
1233 int s = ++e->network_stack_depth;
1234
1235 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
1236
1237 e->network_stack[s].offset = offset;
1238 e->network_stack[s].i = i;
1239 e->network_stack[s].depth = depth;
1240
1241 return 0;
1242 }
1243
1244 static int loc_database_enumerator_match_network(
1245 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1246 // If family is set, it must match
1247 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1248 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
1249 return 0;
1250 }
1251
1252 // Match if no filter criteria is configured
1253 if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
1254 return 1;
1255
1256 // Check if the country code matches
1257 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1258 const char* country_code = loc_network_get_country_code(network);
1259
1260 if (loc_country_list_contains_code(enumerator->countries, country_code)) {
1261 DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
1262 return 1;
1263 }
1264 }
1265
1266 // Check if the ASN matches
1267 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1268 uint32_t asn = loc_network_get_asn(network);
1269
1270 if (loc_as_list_contains_number(enumerator->asns, asn)) {
1271 DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
1272 return 1;
1273 }
1274 }
1275
1276 // Check if flags match
1277 if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
1278 DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
1279 return 1;
1280 }
1281
1282 // Not a match
1283 return 0;
1284 }
1285
1286 static int __loc_database_enumerator_next_network(
1287 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1288 struct loc_database_network_node_v1 node_v1;
1289
1290 // Return top element from the stack
1291 while (1) {
1292 *network = loc_network_list_pop_first(enumerator->stack);
1293
1294 // Stack is empty
1295 if (!*network)
1296 break;
1297
1298 // Return everything if filter isn't enabled, or only return matches
1299 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1300 return 0;
1301
1302 // Throw away anything that doesn't match
1303 loc_network_unref(*network);
1304 *network = NULL;
1305 }
1306
1307 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1308 enumerator->network_stack_depth);
1309
1310 // Perform DFS
1311 while (enumerator->network_stack_depth > 0) {
1312 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
1313
1314 // Get object from top of the stack
1315 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
1316
1317 DEBUG(enumerator->ctx, " Got node: %jd\n", node->offset);
1318
1319 // Remove the node from the stack if we have already visited it
1320 if (enumerator->networks_visited[node->offset]) {
1321 enumerator->network_stack_depth--;
1322 continue;
1323 }
1324
1325 // Mark the bits on the path correctly
1326 loc_address_set_bit(&enumerator->network_address,
1327 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1328
1329 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
1330 enumerator->networks_visited[node->offset]++;
1331
1332 // Pop node from top of the stack
1333 struct loc_database_network_node_v1* n = loc_database_read_object(enumerator->db,
1334 &node_v1, &enumerator->db->network_node_objects, node->offset);
1335 if (!n)
1336 return 1;
1337
1338 // Add edges to stack
1339 int r = loc_database_enumerator_stack_push_node(enumerator,
1340 be32toh(n->one), 1, node->depth + 1);
1341 if (r)
1342 return r;
1343
1344 r = loc_database_enumerator_stack_push_node(enumerator,
1345 be32toh(n->zero), 0, node->depth + 1);
1346 if (r)
1347 return r;
1348
1349 // Check if this node is a leaf and has a network object
1350 if (__loc_database_node_is_leaf(n)) {
1351 off_t network_index = be32toh(n->network);
1352
1353 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
1354
1355 // Fetch the network object
1356 r = loc_database_fetch_network(enumerator->db, network,
1357 &enumerator->network_address, node->depth, network_index);
1358
1359 // Break on any errors
1360 if (r)
1361 return r;
1362
1363 // Return all networks when the filter is disabled, or check for match
1364 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1365 return 0;
1366
1367 // Does not seem to be a match, so we cleanup and move on
1368 loc_network_unref(*network);
1369 *network = NULL;
1370 }
1371 }
1372
1373 // Reached the end of the search
1374 return 0;
1375 }
1376
1377 static int __loc_database_enumerator_next_network_flattened(
1378 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1379 // Fetch the next network
1380 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1381 if (r)
1382 return r;
1383
1384 // End if we could not read another network
1385 if (!*network)
1386 return 0;
1387
1388 struct loc_network* subnet = NULL;
1389
1390 // Create a list with all subnets
1391 if (!enumerator->subnets) {
1392 r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
1393 if (r)
1394 return r;
1395 }
1396
1397 // Search all subnets from the database
1398 while (1) {
1399 // Fetch the next network in line
1400 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
1401 if (r) {
1402 loc_network_unref(subnet);
1403 loc_network_list_clear(enumerator->subnets);
1404
1405 return r;
1406 }
1407
1408 // End if we did not receive another subnet
1409 if (!subnet)
1410 break;
1411
1412 // Collect all subnets in a list
1413 if (loc_network_is_subnet(*network, subnet)) {
1414 r = loc_network_list_push(enumerator->subnets, subnet);
1415 if (r) {
1416 loc_network_unref(subnet);
1417 loc_network_list_clear(enumerator->subnets);
1418
1419 return r;
1420 }
1421
1422 loc_network_unref(subnet);
1423 continue;
1424 }
1425
1426 // If this is not a subnet, we push it back onto the stack and break
1427 r = loc_network_list_push(enumerator->stack, subnet);
1428 if (r) {
1429 loc_network_unref(subnet);
1430 loc_network_list_clear(enumerator->subnets);
1431
1432 return r;
1433 }
1434
1435 loc_network_unref(subnet);
1436 break;
1437 }
1438
1439 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
1440 loc_network_list_size(enumerator->subnets));
1441
1442 // We can abort here if the network has no subnets
1443 if (loc_network_list_empty(enumerator->subnets)) {
1444 loc_network_list_clear(enumerator->subnets);
1445
1446 return 0;
1447 }
1448
1449 // If the network has any subnets, we will break it into smaller parts
1450 // without the subnets.
1451 struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
1452 if (!excluded) {
1453 loc_network_list_clear(enumerator->subnets);
1454 return 1;
1455 }
1456
1457 // Merge subnets onto the stack
1458 r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
1459 if (r) {
1460 loc_network_list_clear(enumerator->subnets);
1461 loc_network_list_unref(excluded);
1462
1463 return r;
1464 }
1465
1466 // Push excluded list onto the stack
1467 r = loc_network_list_merge(enumerator->stack, excluded);
1468 if (r) {
1469 loc_network_list_clear(enumerator->subnets);
1470 loc_network_list_unref(excluded);
1471
1472 return r;
1473 }
1474
1475 loc_network_list_clear(enumerator->subnets);
1476 loc_network_list_unref(excluded);
1477
1478 // Drop the network and restart the whole process again to pick the next network
1479 loc_network_unref(*network);
1480
1481 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1482 }
1483
1484 /*
1485 This function finds all bogons (i.e. gaps) between the input networks
1486 */
1487 static int __loc_database_enumerator_next_bogon(
1488 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1489 int r;
1490
1491 // Return top element from the stack
1492 while (1) {
1493 *bogon = loc_network_list_pop_first(enumerator->stack);
1494
1495 // Stack is empty
1496 if (!*bogon)
1497 break;
1498
1499 // Return result
1500 return 0;
1501 }
1502
1503 struct loc_network* network = NULL;
1504 struct in6_addr* gap_start = NULL;
1505 struct in6_addr gap_end = IN6ADDR_ANY_INIT;
1506
1507 while (1) {
1508 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1509 if (r)
1510 return r;
1511
1512 // We have read the last network
1513 if (!network)
1514 goto FINISH;
1515
1516 const char* country_code = loc_network_get_country_code(network);
1517
1518 /*
1519 Skip anything that does not have a country code
1520
1521 Even if a network is part of the routing table, and the database provides
1522 an ASN, this does not mean that this is a legitimate announcement.
1523 */
1524 if (country_code && !*country_code) {
1525 loc_network_unref(network);
1526 continue;
1527 }
1528
1529 // Determine the network family
1530 int family = loc_network_address_family(network);
1531
1532 switch (family) {
1533 case AF_INET6:
1534 gap_start = &enumerator->gap6_start;
1535 break;
1536
1537 case AF_INET:
1538 gap_start = &enumerator->gap4_start;
1539 break;
1540
1541 default:
1542 ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
1543 errno = ENOTSUP;
1544 return 1;
1545 }
1546
1547 const struct in6_addr* first_address = loc_network_get_first_address(network);
1548 const struct in6_addr* last_address = loc_network_get_last_address(network);
1549
1550 // Skip if this network is a subnet of a former one
1551 if (loc_address_cmp(gap_start, last_address) >= 0) {
1552 loc_network_unref(network);
1553 continue;
1554 }
1555
1556 // Search where the gap could end
1557 gap_end = *first_address;
1558 loc_address_decrement(&gap_end);
1559
1560 // There is a gap
1561 if (loc_address_cmp(gap_start, &gap_end) <= 0) {
1562 r = loc_network_list_summarize(enumerator->ctx,
1563 gap_start, &gap_end, &enumerator->stack);
1564 if (r) {
1565 loc_network_unref(network);
1566 return r;
1567 }
1568 }
1569
1570 // The gap now starts after this network
1571 *gap_start = *last_address;
1572 loc_address_increment(gap_start);
1573
1574 loc_network_unref(network);
1575
1576 // Try to return something
1577 *bogon = loc_network_list_pop_first(enumerator->stack);
1578 if (*bogon)
1579 break;
1580 }
1581
1582 return 0;
1583
1584 FINISH:
1585
1586 if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
1587 r = loc_address_reset_last(&gap_end, AF_INET6);
1588 if (r)
1589 return r;
1590
1591 if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
1592 r = loc_network_list_summarize(enumerator->ctx,
1593 &enumerator->gap6_start, &gap_end, &enumerator->stack);
1594 if (r)
1595 return r;
1596 }
1597
1598 // Reset start
1599 loc_address_reset(&enumerator->gap6_start, AF_INET6);
1600 }
1601
1602 if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
1603 r = loc_address_reset_last(&gap_end, AF_INET);
1604 if (r)
1605 return r;
1606
1607 if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
1608 r = loc_network_list_summarize(enumerator->ctx,
1609 &enumerator->gap4_start, &gap_end, &enumerator->stack);
1610 if (r)
1611 return r;
1612 }
1613
1614 // Reset start
1615 loc_address_reset(&enumerator->gap4_start, AF_INET);
1616 }
1617
1618 // Try to return something
1619 *bogon = loc_network_list_pop_first(enumerator->stack);
1620
1621 return 0;
1622 }
1623
1624 LOC_EXPORT int loc_database_enumerator_next_network(
1625 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1626 switch (enumerator->mode) {
1627 case LOC_DB_ENUMERATE_NETWORKS:
1628 // Flatten output?
1629 if (enumerator->flatten)
1630 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1631
1632 return __loc_database_enumerator_next_network(enumerator, network, 1);
1633
1634 case LOC_DB_ENUMERATE_BOGONS:
1635 return __loc_database_enumerator_next_bogon(enumerator, network);
1636
1637 default:
1638 return 0;
1639 }
1640 }
1641
1642 LOC_EXPORT int loc_database_enumerator_next_country(
1643 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1644 *country = NULL;
1645
1646 // Do not do anything if not in country mode
1647 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1648 return 0;
1649
1650 struct loc_database* db = enumerator->db;
1651
1652 while (enumerator->country_index < db->country_objects.count) {
1653 // Fetch the next country
1654 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1655 if (r)
1656 return r;
1657
1658 // We do not filter here, so it always is a match
1659 return 0;
1660 }
1661
1662 // Reset the index
1663 enumerator->country_index = 0;
1664
1665 // We have searched through all of them
1666 return 0;
1667 }