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