]> git.ipfire.org Git - location/libloc.git/blob - src/database.c
lua: Fix raising an exception if no network was found
[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 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 off_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 size_t offset = p - db->data;
153
154 // Return if everything is within the boundary
155 if (offset <= 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 = %jd, length = %zu)\n", p, offset, length);
160 DEBUG(db->ctx, " data = %p (length = %zu)\n", db->data, db->length);
161 DEBUG(db->ctx, " end = %p\n", db->data + db->length);
162 DEBUG(db->ctx, " overflow of %zu byte(s)\n", 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 %zu 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) {
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 %d\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
911 // End here if a result was found
912 if (r == 0)
913 return r;
914
915 // Raise any errors
916 else if (r < 0)
917 return r;
918
919 DEBUG(db->ctx, "No match found below level %u\n", level);
920 } else {
921 DEBUG(db->ctx, "Tree ended at level %u\n", level);
922 }
923
924 // If this node has a leaf, we will check if it matches
925 if (__loc_database_node_is_leaf(node_v1)) {
926 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node_v1);
927 if (r < 0)
928 return r;
929 }
930
931 // Return no error - even if nothing was found
932 return 0;
933 }
934
935 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
936 const struct in6_addr* address, struct loc_network** network) {
937 struct in6_addr network_address;
938 memset(&network_address, 0, sizeof(network_address));
939
940 *network = NULL;
941
942 #ifdef ENABLE_DEBUG
943 // Save start time
944 clock_t start = clock();
945 #endif
946
947 int r = __loc_database_lookup(db, address, network, &network_address, 0, 0);
948
949 #ifdef ENABLE_DEBUG
950 clock_t end = clock();
951
952 // Log how fast this has been
953 DEBUG(db->ctx, "Executed network search in %.4fms\n",
954 (double)(end - start) / CLOCKS_PER_SEC * 1000);
955 #endif
956
957 return r;
958 }
959
960 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
961 const char* string, struct loc_network** network) {
962 struct in6_addr address;
963
964 int r = loc_address_parse(&address, NULL, string);
965 if (r)
966 return r;
967
968 return loc_database_lookup(db, &address, network);
969 }
970
971 // Returns the country at position pos
972 static int loc_database_fetch_country(struct loc_database* db,
973 struct loc_country** country, off_t pos) {
974 struct loc_database_country_v1* country_v1 = NULL;
975 int r;
976
977 // Check if the country is within range
978 if ((size_t)pos >= db->country_objects.count) {
979 errno = ERANGE;
980 return 1;
981 }
982
983 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
984
985 switch (db->version) {
986 case LOC_DATABASE_VERSION_1:
987 // Read the object
988 country_v1 = (struct loc_database_country_v1*)loc_database_object(db,
989 &db->country_objects, sizeof(*country_v1), pos);
990 if (!country_v1)
991 return 1;
992
993 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, country_v1);
994 break;
995
996 default:
997 errno = ENOTSUP;
998 return 1;
999 }
1000
1001 if (r == 0)
1002 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
1003
1004 return r;
1005 }
1006
1007 // Performs a binary search to find the country in the list
1008 LOC_EXPORT int loc_database_get_country(struct loc_database* db,
1009 struct loc_country** country, const char* code) {
1010 off_t lo = 0;
1011 off_t hi = db->country_objects.count - 1;
1012
1013 // Check if the country code is valid
1014 if (!loc_country_code_is_valid(code)) {
1015 errno = EINVAL;
1016 return 1;
1017 }
1018
1019 #ifdef ENABLE_DEBUG
1020 // Save start time
1021 clock_t start = clock();
1022 #endif
1023
1024 while (lo <= hi) {
1025 off_t i = (lo + hi) / 2;
1026
1027 // Fetch country in the middle between lo and hi
1028 int r = loc_database_fetch_country(db, country, i);
1029 if (r)
1030 return r;
1031
1032 // Check if this is a match
1033 const char* cc = loc_country_get_code(*country);
1034 int result = strcmp(code, cc);
1035
1036 if (result == 0) {
1037 #ifdef ENABLE_DEBUG
1038 clock_t end = clock();
1039
1040 // Log how fast this has been
1041 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
1042 (double)(end - start) / CLOCKS_PER_SEC * 1000);
1043 #endif
1044
1045 return 0;
1046 }
1047
1048 // If it wasn't, we release the country and
1049 // adjust our search pointers
1050 loc_country_unref(*country);
1051
1052 if (result > 0) {
1053 lo = i + 1;
1054 } else
1055 hi = i - 1;
1056 }
1057
1058 // Nothing found
1059 *country = NULL;
1060
1061 return 0;
1062 }
1063
1064 // Enumerator
1065
1066 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
1067 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
1068
1069 // Release all references
1070 loc_database_unref(enumerator->db);
1071 loc_unref(enumerator->ctx);
1072
1073 if (enumerator->string)
1074 free(enumerator->string);
1075
1076 if (enumerator->countries)
1077 loc_country_list_unref(enumerator->countries);
1078
1079 if (enumerator->asns)
1080 loc_as_list_unref(enumerator->asns);
1081
1082 // Free network search
1083 if (enumerator->networks_visited)
1084 free(enumerator->networks_visited);
1085
1086 // Free subnet/bogons stack
1087 if (enumerator->stack)
1088 loc_network_list_unref(enumerator->stack);
1089
1090 if (enumerator->subnets)
1091 loc_network_list_unref(enumerator->subnets);
1092
1093 free(enumerator);
1094 }
1095
1096 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
1097 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
1098 int r;
1099
1100 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
1101 if (!e) {
1102 return -ENOMEM;
1103 }
1104
1105 // Reference context
1106 e->ctx = loc_ref(db->ctx);
1107 e->db = loc_database_ref(db);
1108 e->mode = mode;
1109 e->refcount = 1;
1110
1111 // Flatten output?
1112 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
1113
1114 // Initialise graph search
1115 e->network_stack_depth = 1;
1116 e->networks_visited = calloc(db->network_node_objects.count, sizeof(*e->networks_visited));
1117 if (!e->networks_visited) {
1118 ERROR(db->ctx, "Could not allocated visited networks: %m\n");
1119 r = 1;
1120 goto ERROR;
1121 }
1122
1123 // Allocate stack
1124 r = loc_network_list_new(e->ctx, &e->stack);
1125 if (r)
1126 goto ERROR;
1127
1128 // Initialize bogon search
1129 loc_address_reset(&e->gap6_start, AF_INET6);
1130 loc_address_reset(&e->gap4_start, AF_INET);
1131
1132 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1133
1134 *enumerator = e;
1135 return 0;
1136
1137 ERROR:
1138 if (e)
1139 loc_database_enumerator_free(e);
1140
1141 return r;
1142 }
1143
1144 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1145 enumerator->refcount++;
1146
1147 return enumerator;
1148 }
1149
1150 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1151 if (!enumerator)
1152 return NULL;
1153
1154 if (--enumerator->refcount > 0)
1155 return enumerator;
1156
1157 loc_database_enumerator_free(enumerator);
1158 return NULL;
1159 }
1160
1161 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1162 enumerator->string = strdup(string);
1163
1164 // Make the string lowercase
1165 for (char *p = enumerator->string; *p; p++)
1166 *p = tolower(*p);
1167
1168 return 0;
1169 }
1170
1171 LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1172 struct loc_database_enumerator* enumerator) {
1173 if (!enumerator->countries)
1174 return NULL;
1175
1176 return loc_country_list_ref(enumerator->countries);
1177 }
1178
1179 LOC_EXPORT int loc_database_enumerator_set_countries(
1180 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1181 if (enumerator->countries)
1182 loc_country_list_unref(enumerator->countries);
1183
1184 enumerator->countries = loc_country_list_ref(countries);
1185
1186 return 0;
1187 }
1188
1189 LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1190 struct loc_database_enumerator* enumerator) {
1191 if (!enumerator->asns)
1192 return NULL;
1193
1194 return loc_as_list_ref(enumerator->asns);
1195 }
1196
1197 LOC_EXPORT int loc_database_enumerator_set_asns(
1198 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1199 if (enumerator->asns)
1200 loc_as_list_unref(enumerator->asns);
1201
1202 enumerator->asns = loc_as_list_ref(asns);
1203
1204 return 0;
1205 }
1206
1207 LOC_EXPORT int loc_database_enumerator_set_flag(
1208 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1209 enumerator->flags |= flag;
1210
1211 return 0;
1212 }
1213
1214 LOC_EXPORT int loc_database_enumerator_set_family(
1215 struct loc_database_enumerator* enumerator, int family) {
1216 enumerator->family = family;
1217
1218 return 0;
1219 }
1220
1221 LOC_EXPORT int loc_database_enumerator_next_as(
1222 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1223 *as = NULL;
1224
1225 // Do not do anything if not in AS mode
1226 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
1227 return 0;
1228
1229 struct loc_database* db = enumerator->db;
1230
1231 while (enumerator->as_index < db->as_objects.count) {
1232 // Fetch the next AS
1233 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
1234 if (r)
1235 return r;
1236
1237 r = loc_as_match_string(*as, enumerator->string);
1238 if (r == 1) {
1239 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
1240 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
1241
1242 return 0;
1243 }
1244
1245 // No match
1246 loc_as_unref(*as);
1247 *as = NULL;
1248 }
1249
1250 // Reset the index
1251 enumerator->as_index = 0;
1252
1253 // We have searched through all of them
1254 return 0;
1255 }
1256
1257 static int loc_database_enumerator_stack_push_node(
1258 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1259 // Do not add empty nodes
1260 if (!offset)
1261 return 0;
1262
1263 // Check if there is any space left on the stack
1264 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1265 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1266 return 1;
1267 }
1268
1269 // Check if the node is in range
1270 if (offset >= (off_t)e->db->network_node_objects.count) {
1271 ERROR(e->ctx, "Trying to add invalid node with offset %jd/%zu\n",
1272 offset, e->db->network_node_objects.count);
1273 errno = ERANGE;
1274 return 1;
1275 }
1276
1277 // Increase stack size
1278 int s = ++e->network_stack_depth;
1279
1280 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
1281
1282 e->network_stack[s].offset = offset;
1283 e->network_stack[s].i = i;
1284 e->network_stack[s].depth = depth;
1285
1286 return 0;
1287 }
1288
1289 static int loc_database_enumerator_match_network(
1290 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1291 // If family is set, it must match
1292 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1293 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
1294 return 0;
1295 }
1296
1297 // Match if no filter criteria is configured
1298 if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
1299 return 1;
1300
1301 // Check if the country code matches
1302 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1303 const char* country_code = loc_network_get_country_code(network);
1304
1305 if (loc_country_list_contains_code(enumerator->countries, country_code)) {
1306 DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
1307 return 1;
1308 }
1309 }
1310
1311 // Check if the ASN matches
1312 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1313 uint32_t asn = loc_network_get_asn(network);
1314
1315 if (loc_as_list_contains_number(enumerator->asns, asn)) {
1316 DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
1317 return 1;
1318 }
1319 }
1320
1321 // Check if flags match
1322 if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
1323 DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
1324 return 1;
1325 }
1326
1327 // Not a match
1328 return 0;
1329 }
1330
1331 static int __loc_database_enumerator_next_network(
1332 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1333 // Return top element from the stack
1334 while (1) {
1335 *network = loc_network_list_pop_first(enumerator->stack);
1336
1337 // Stack is empty
1338 if (!*network)
1339 break;
1340
1341 // Return everything if filter isn't enabled, or only return matches
1342 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1343 return 0;
1344
1345 // Throw away anything that doesn't match
1346 loc_network_unref(*network);
1347 *network = NULL;
1348 }
1349
1350 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1351 enumerator->network_stack_depth);
1352
1353 // Perform DFS
1354 while (enumerator->network_stack_depth > 0) {
1355 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
1356
1357 // Get object from top of the stack
1358 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
1359
1360 DEBUG(enumerator->ctx, " Got node: %jd\n", node->offset);
1361
1362 // Remove the node from the stack if we have already visited it
1363 if (enumerator->networks_visited[node->offset]) {
1364 enumerator->network_stack_depth--;
1365 continue;
1366 }
1367
1368 // Mark the bits on the path correctly
1369 loc_address_set_bit(&enumerator->network_address,
1370 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1371
1372 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
1373 enumerator->networks_visited[node->offset]++;
1374
1375 // Pop node from top of the stack
1376 struct loc_database_network_node_v1* n =
1377 (struct loc_database_network_node_v1*)loc_database_object(enumerator->db,
1378 &enumerator->db->network_node_objects, sizeof(*n), node->offset);
1379 if (!n)
1380 return 1;
1381
1382 // Add edges to stack
1383 int r = loc_database_enumerator_stack_push_node(enumerator,
1384 be32toh(n->one), 1, node->depth + 1);
1385 if (r)
1386 return r;
1387
1388 r = loc_database_enumerator_stack_push_node(enumerator,
1389 be32toh(n->zero), 0, node->depth + 1);
1390 if (r)
1391 return r;
1392
1393 // Check if this node is a leaf and has a network object
1394 if (__loc_database_node_is_leaf(n)) {
1395 off_t network_index = be32toh(n->network);
1396
1397 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
1398
1399 // Fetch the network object
1400 r = loc_database_fetch_network(enumerator->db, network,
1401 &enumerator->network_address, node->depth, network_index);
1402
1403 // Break on any errors
1404 if (r)
1405 return r;
1406
1407 // Return all networks when the filter is disabled, or check for match
1408 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1409 return 0;
1410
1411 // Does not seem to be a match, so we cleanup and move on
1412 loc_network_unref(*network);
1413 *network = NULL;
1414 }
1415 }
1416
1417 // Reached the end of the search
1418 return 0;
1419 }
1420
1421 static int __loc_database_enumerator_next_network_flattened(
1422 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1423 // Fetch the next network
1424 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1425 if (r)
1426 return r;
1427
1428 // End if we could not read another network
1429 if (!*network)
1430 return 0;
1431
1432 struct loc_network* subnet = NULL;
1433
1434 // Create a list with all subnets
1435 if (!enumerator->subnets) {
1436 r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
1437 if (r)
1438 return r;
1439 }
1440
1441 // Search all subnets from the database
1442 while (1) {
1443 // Fetch the next network in line
1444 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
1445 if (r) {
1446 loc_network_unref(subnet);
1447 loc_network_list_clear(enumerator->subnets);
1448
1449 return r;
1450 }
1451
1452 // End if we did not receive another subnet
1453 if (!subnet)
1454 break;
1455
1456 // Collect all subnets in a list
1457 if (loc_network_is_subnet(*network, subnet)) {
1458 r = loc_network_list_push(enumerator->subnets, subnet);
1459 if (r) {
1460 loc_network_unref(subnet);
1461 loc_network_list_clear(enumerator->subnets);
1462
1463 return r;
1464 }
1465
1466 loc_network_unref(subnet);
1467 continue;
1468 }
1469
1470 // If this is not a subnet, we push it back onto the stack and break
1471 r = loc_network_list_push(enumerator->stack, subnet);
1472 if (r) {
1473 loc_network_unref(subnet);
1474 loc_network_list_clear(enumerator->subnets);
1475
1476 return r;
1477 }
1478
1479 loc_network_unref(subnet);
1480 break;
1481 }
1482
1483 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
1484 loc_network_list_size(enumerator->subnets));
1485
1486 // We can abort here if the network has no subnets
1487 if (loc_network_list_empty(enumerator->subnets)) {
1488 loc_network_list_clear(enumerator->subnets);
1489
1490 return 0;
1491 }
1492
1493 // If the network has any subnets, we will break it into smaller parts
1494 // without the subnets.
1495 struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
1496 if (!excluded) {
1497 loc_network_list_clear(enumerator->subnets);
1498 return 1;
1499 }
1500
1501 // Merge subnets onto the stack
1502 r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
1503 if (r) {
1504 loc_network_list_clear(enumerator->subnets);
1505 loc_network_list_unref(excluded);
1506
1507 return r;
1508 }
1509
1510 // Push excluded list onto the stack
1511 r = loc_network_list_merge(enumerator->stack, excluded);
1512 if (r) {
1513 loc_network_list_clear(enumerator->subnets);
1514 loc_network_list_unref(excluded);
1515
1516 return r;
1517 }
1518
1519 loc_network_list_clear(enumerator->subnets);
1520 loc_network_list_unref(excluded);
1521
1522 // Drop the network and restart the whole process again to pick the next network
1523 loc_network_unref(*network);
1524
1525 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1526 }
1527
1528 /*
1529 This function finds all bogons (i.e. gaps) between the input networks
1530 */
1531 static int __loc_database_enumerator_next_bogon(
1532 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1533 int r;
1534
1535 // Return top element from the stack
1536 while (1) {
1537 *bogon = loc_network_list_pop_first(enumerator->stack);
1538
1539 // Stack is empty
1540 if (!*bogon)
1541 break;
1542
1543 // Return result
1544 return 0;
1545 }
1546
1547 struct loc_network* network = NULL;
1548 struct in6_addr* gap_start = NULL;
1549 struct in6_addr gap_end = IN6ADDR_ANY_INIT;
1550
1551 while (1) {
1552 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1553 if (r)
1554 return r;
1555
1556 // We have read the last network
1557 if (!network)
1558 goto FINISH;
1559
1560 const char* country_code = loc_network_get_country_code(network);
1561
1562 /*
1563 Skip anything that does not have a country code
1564
1565 Even if a network is part of the routing table, and the database provides
1566 an ASN, this does not mean that this is a legitimate announcement.
1567 */
1568 if (country_code && !*country_code) {
1569 loc_network_unref(network);
1570 continue;
1571 }
1572
1573 // Determine the network family
1574 int family = loc_network_address_family(network);
1575
1576 switch (family) {
1577 case AF_INET6:
1578 gap_start = &enumerator->gap6_start;
1579 break;
1580
1581 case AF_INET:
1582 gap_start = &enumerator->gap4_start;
1583 break;
1584
1585 default:
1586 ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
1587 errno = ENOTSUP;
1588 return 1;
1589 }
1590
1591 const struct in6_addr* first_address = loc_network_get_first_address(network);
1592 const struct in6_addr* last_address = loc_network_get_last_address(network);
1593
1594 // Skip if this network is a subnet of a former one
1595 if (loc_address_cmp(gap_start, last_address) >= 0) {
1596 loc_network_unref(network);
1597 continue;
1598 }
1599
1600 // Search where the gap could end
1601 gap_end = *first_address;
1602 loc_address_decrement(&gap_end);
1603
1604 // There is a gap
1605 if (loc_address_cmp(gap_start, &gap_end) <= 0) {
1606 r = loc_network_list_summarize(enumerator->ctx,
1607 gap_start, &gap_end, &enumerator->stack);
1608 if (r) {
1609 loc_network_unref(network);
1610 return r;
1611 }
1612 }
1613
1614 // The gap now starts after this network
1615 *gap_start = *last_address;
1616 loc_address_increment(gap_start);
1617
1618 loc_network_unref(network);
1619
1620 // Try to return something
1621 *bogon = loc_network_list_pop_first(enumerator->stack);
1622 if (*bogon)
1623 break;
1624 }
1625
1626 return 0;
1627
1628 FINISH:
1629
1630 if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
1631 r = loc_address_reset_last(&gap_end, AF_INET6);
1632 if (r)
1633 return r;
1634
1635 if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
1636 r = loc_network_list_summarize(enumerator->ctx,
1637 &enumerator->gap6_start, &gap_end, &enumerator->stack);
1638 if (r)
1639 return r;
1640 }
1641
1642 // Reset start
1643 loc_address_reset(&enumerator->gap6_start, AF_INET6);
1644 }
1645
1646 if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
1647 r = loc_address_reset_last(&gap_end, AF_INET);
1648 if (r)
1649 return r;
1650
1651 if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
1652 r = loc_network_list_summarize(enumerator->ctx,
1653 &enumerator->gap4_start, &gap_end, &enumerator->stack);
1654 if (r)
1655 return r;
1656 }
1657
1658 // Reset start
1659 loc_address_reset(&enumerator->gap4_start, AF_INET);
1660 }
1661
1662 // Try to return something
1663 *bogon = loc_network_list_pop_first(enumerator->stack);
1664
1665 return 0;
1666 }
1667
1668 LOC_EXPORT int loc_database_enumerator_next_network(
1669 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1670 switch (enumerator->mode) {
1671 case LOC_DB_ENUMERATE_NETWORKS:
1672 // Flatten output?
1673 if (enumerator->flatten)
1674 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1675
1676 return __loc_database_enumerator_next_network(enumerator, network, 1);
1677
1678 case LOC_DB_ENUMERATE_BOGONS:
1679 return __loc_database_enumerator_next_bogon(enumerator, network);
1680
1681 default:
1682 return 0;
1683 }
1684 }
1685
1686 LOC_EXPORT int loc_database_enumerator_next_country(
1687 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1688 *country = NULL;
1689
1690 // Do not do anything if not in country mode
1691 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1692 return 0;
1693
1694 struct loc_database* db = enumerator->db;
1695
1696 while (enumerator->country_index < db->country_objects.count) {
1697 // Fetch the next country
1698 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1699 if (r)
1700 return r;
1701
1702 // We do not filter here, so it always is a match
1703 return 0;
1704 }
1705
1706 // Reset the index
1707 enumerator->country_index = 0;
1708
1709 // We have searched through all of them
1710 return 0;
1711 }