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