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