]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
database: Initialize r on create
[people/ms/libloc.git] / src / database.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15 */
16
17 #include <arpa/inet.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <netinet/in.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #ifdef HAVE_ENDIAN_H
32 # include <endian.h>
33 #endif
34
35 #include <openssl/err.h>
36 #include <openssl/evp.h>
37 #include <openssl/pem.h>
38
39 #include <libloc/libloc.h>
40 #include <libloc/address.h>
41 #include <libloc/as.h>
42 #include <libloc/as-list.h>
43 #include <libloc/compat.h>
44 #include <libloc/country.h>
45 #include <libloc/country-list.h>
46 #include <libloc/database.h>
47 #include <libloc/format.h>
48 #include <libloc/network.h>
49 #include <libloc/network-list.h>
50 #include <libloc/private.h>
51 #include <libloc/stringpool.h>
52
53 struct loc_database_objects {
54 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 // Check first signature
645 if (db->signature1.data) {
646 hexdump(db->ctx, db->signature1.data, db->signature1.length);
647
648 r = EVP_DigestVerifyFinal(mdctx,
649 (unsigned char*)db->signature1.data, db->signature1.length);
650
651 if (r == 0) {
652 DEBUG(db->ctx, "The first signature is invalid\n");
653 r = 1;
654 } else if (r == 1) {
655 DEBUG(db->ctx, "The first signature is valid\n");
656 r = 0;
657 } else {
658 ERROR(db->ctx, "Error verifying the first signature: %s\n",
659 ERR_error_string(ERR_get_error(), NULL));
660 r = -1;
661 }
662 }
663
664 // Check second signature only when the first one was invalid
665 if (r && db->signature2.data) {
666 hexdump(db->ctx, db->signature2.data, db->signature2.length);
667
668 r = EVP_DigestVerifyFinal(mdctx,
669 (unsigned char*)db->signature2.data, db->signature2.length);
670
671 if (r == 0) {
672 DEBUG(db->ctx, "The second signature is invalid\n");
673 r = 1;
674 } else if (r == 1) {
675 DEBUG(db->ctx, "The second signature is valid\n");
676 r = 0;
677 } else {
678 ERROR(db->ctx, "Error verifying the second signature: %s\n",
679 ERR_error_string(ERR_get_error(), NULL));
680 r = -1;
681 }
682 }
683
684 clock_t end = clock();
685 INFO(db->ctx, "Signature checked in %.4fms\n",
686 (double)(end - start) / CLOCKS_PER_SEC * 1000);
687
688 CLEANUP:
689 // Cleanup
690 EVP_MD_CTX_free(mdctx);
691 EVP_PKEY_free(pkey);
692
693 return r;
694 }
695
696 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
697 return db->created_at;
698 }
699
700 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
701 return loc_stringpool_get(db->pool, db->vendor);
702 }
703
704 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
705 return loc_stringpool_get(db->pool, db->description);
706 }
707
708 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
709 return loc_stringpool_get(db->pool, db->license);
710 }
711
712 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
713 return db->as_objects.count;
714 }
715
716 // Returns the AS at position pos
717 static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
718 struct loc_database_as_v1* as_v1 = NULL;
719 int r;
720
721 if ((size_t)pos >= db->as_objects.count) {
722 errno = ERANGE;
723 return 1;
724 }
725
726 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
727
728 switch (db->version) {
729 case LOC_DATABASE_VERSION_1:
730 // Find the object
731 as_v1 = (struct loc_database_as_v1*)loc_database_object(db,
732 &db->as_objects, sizeof(*as_v1), pos);
733 if (!as_v1)
734 return 1;
735
736 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, as_v1);
737 break;
738
739 default:
740 errno = ENOTSUP;
741 return 1;
742 }
743
744 if (r == 0)
745 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
746
747 return r;
748 }
749
750 // Performs a binary search to find the AS in the list
751 LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
752 off_t lo = 0;
753 off_t hi = db->as_objects.count - 1;
754
755 #ifdef ENABLE_DEBUG
756 // Save start time
757 clock_t start = clock();
758 #endif
759
760 while (lo <= hi) {
761 off_t i = (lo + hi) / 2;
762
763 // Fetch AS in the middle between lo and hi
764 int r = loc_database_fetch_as(db, as, i);
765 if (r)
766 return r;
767
768 // Check if this is a match
769 uint32_t as_number = loc_as_get_number(*as);
770 if (as_number == number) {
771 #ifdef ENABLE_DEBUG
772 clock_t end = clock();
773
774 // Log how fast this has been
775 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
776 (double)(end - start) / CLOCKS_PER_SEC * 1000);
777 #endif
778
779 return 0;
780 }
781
782 // If it wasn't, we release the AS and
783 // adjust our search pointers
784 loc_as_unref(*as);
785
786 if (as_number < number) {
787 lo = i + 1;
788 } else
789 hi = i - 1;
790 }
791
792 // Nothing found
793 *as = NULL;
794
795 return 1;
796 }
797
798 // Returns the network at position pos
799 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
800 struct in6_addr* address, unsigned int prefix, off_t pos) {
801 struct loc_database_network_v1* network_v1 = NULL;
802 int r;
803
804 if ((size_t)pos >= db->network_objects.count) {
805 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
806 (intmax_t)pos, (intmax_t)db->network_objects.count);
807 errno = ERANGE;
808 return 1;
809 }
810
811 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
812
813 switch (db->version) {
814 case LOC_DATABASE_VERSION_1:
815 // Read the object
816 network_v1 = (struct loc_database_network_v1*)loc_database_object(db,
817 &db->network_objects, sizeof(*network_v1), pos);
818 if (!network_v1)
819 return 1;
820
821 r = loc_network_new_from_database_v1(db->ctx, network, address, prefix, network_v1);
822 break;
823
824 default:
825 errno = ENOTSUP;
826 return 1;
827 }
828
829 if (r == 0)
830 DEBUG(db->ctx, "Got network %s\n", loc_network_str(*network));
831
832 return r;
833 }
834
835 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1* node) {
836 return (node->network != htobe32(0xffffffff));
837 }
838
839 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
840 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
841 const struct loc_database_network_node_v1* node) {
842 off_t network_index = be32toh(node->network);
843
844 DEBUG(db->ctx, "Handling leaf node at %jd\n", (intmax_t)network_index);
845
846 // Fetch the network
847 int r = loc_database_fetch_network(db, network, network_address, prefix, network_index);
848 if (r) {
849 ERROR(db->ctx, "Could not fetch network %jd from database: %m\n",
850 (intmax_t)network_index);
851 return r;
852 }
853
854 // Check if the given IP address is inside the network
855 if (!loc_network_matches_address(*network, address)) {
856 DEBUG(db->ctx, "Searched address is not part of the network\n");
857
858 loc_network_unref(*network);
859 *network = NULL;
860 return 1;
861 }
862
863 // A network was found and the IP address matches
864 return 0;
865 }
866
867 // Searches for an exact match along the path
868 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
869 struct loc_network** network, struct in6_addr* network_address,
870 off_t node_index, unsigned int level) {
871 struct loc_database_network_node_v1* node_v1 = NULL;
872
873 int r;
874
875 // Fetch the next node
876 node_v1 = (struct loc_database_network_node_v1*)loc_database_object(db,
877 &db->network_node_objects, sizeof(*node_v1), node_index);
878 if (!node_v1)
879 return 1;
880
881 // Follow the path
882 int bit = loc_address_get_bit(address, level);
883 loc_address_set_bit(network_address, level, bit);
884
885 if (bit == 0)
886 node_index = be32toh(node_v1->zero);
887 else
888 node_index = be32toh(node_v1->one);
889
890 // If the node index is zero, the tree ends here
891 // and we cannot descend any further
892 if (node_index > 0) {
893 // Check boundaries
894 if ((size_t)node_index >= db->network_node_objects.count) {
895 errno = ERANGE;
896 return 1;
897 }
898
899 // Move on to the next node
900 r = __loc_database_lookup(db, address, network, network_address, node_index, level + 1);
901
902 // End here if a result was found
903 if (r == 0)
904 return r;
905
906 // Raise any errors
907 else if (r < 0)
908 return r;
909
910 DEBUG(db->ctx, "No match found below level %u\n", level);
911 } else {
912 DEBUG(db->ctx, "Tree ended at level %u\n", level);
913 }
914
915 // If this node has a leaf, we will check if it matches
916 if (__loc_database_node_is_leaf(node_v1)) {
917 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node_v1);
918 if (r <= 0)
919 return r;
920 }
921
922 return 1;
923 }
924
925 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
926 const struct in6_addr* address, struct loc_network** network) {
927 struct in6_addr network_address;
928 memset(&network_address, 0, sizeof(network_address));
929
930 *network = NULL;
931
932 #ifdef ENABLE_DEBUG
933 // Save start time
934 clock_t start = clock();
935 #endif
936
937 int r = __loc_database_lookup(db, address, network, &network_address, 0, 0);
938
939 #ifdef ENABLE_DEBUG
940 clock_t end = clock();
941
942 // Log how fast this has been
943 DEBUG(db->ctx, "Executed network search in %.4fms\n",
944 (double)(end - start) / CLOCKS_PER_SEC * 1000);
945 #endif
946
947 return r;
948 }
949
950 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
951 const char* string, struct loc_network** network) {
952 struct in6_addr address;
953
954 int r = loc_address_parse(&address, NULL, string);
955 if (r)
956 return r;
957
958 return loc_database_lookup(db, &address, network);
959 }
960
961 // Returns the country at position pos
962 static int loc_database_fetch_country(struct loc_database* db,
963 struct loc_country** country, off_t pos) {
964 struct loc_database_country_v1* country_v1 = NULL;
965 int r;
966
967 // Check if the country is within range
968 if ((size_t)pos >= db->country_objects.count) {
969 errno = ERANGE;
970 return 1;
971 }
972
973 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
974
975 switch (db->version) {
976 case LOC_DATABASE_VERSION_1:
977 // Read the object
978 country_v1 = (struct loc_database_country_v1*)loc_database_object(db,
979 &db->country_objects, sizeof(*country_v1), pos);
980 if (!country_v1)
981 return 1;
982
983 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, country_v1);
984 break;
985
986 default:
987 errno = ENOTSUP;
988 return 1;
989 }
990
991 if (r == 0)
992 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
993
994 return r;
995 }
996
997 // Performs a binary search to find the country in the list
998 LOC_EXPORT int loc_database_get_country(struct loc_database* db,
999 struct loc_country** country, const char* code) {
1000 off_t lo = 0;
1001 off_t hi = db->country_objects.count - 1;
1002
1003 // Check if the country code is valid
1004 if (!loc_country_code_is_valid(code)) {
1005 errno = EINVAL;
1006 return 1;
1007 }
1008
1009 #ifdef ENABLE_DEBUG
1010 // Save start time
1011 clock_t start = clock();
1012 #endif
1013
1014 while (lo <= hi) {
1015 off_t i = (lo + hi) / 2;
1016
1017 // Fetch country in the middle between lo and hi
1018 int r = loc_database_fetch_country(db, country, i);
1019 if (r)
1020 return r;
1021
1022 // Check if this is a match
1023 const char* cc = loc_country_get_code(*country);
1024 int result = strcmp(code, cc);
1025
1026 if (result == 0) {
1027 #ifdef ENABLE_DEBUG
1028 clock_t end = clock();
1029
1030 // Log how fast this has been
1031 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
1032 (double)(end - start) / CLOCKS_PER_SEC * 1000);
1033 #endif
1034
1035 return 0;
1036 }
1037
1038 // If it wasn't, we release the country and
1039 // adjust our search pointers
1040 loc_country_unref(*country);
1041
1042 if (result > 0) {
1043 lo = i + 1;
1044 } else
1045 hi = i - 1;
1046 }
1047
1048 // Nothing found
1049 *country = NULL;
1050
1051 return 0;
1052 }
1053
1054 // Enumerator
1055
1056 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
1057 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
1058
1059 // Release all references
1060 loc_database_unref(enumerator->db);
1061 loc_unref(enumerator->ctx);
1062
1063 if (enumerator->string)
1064 free(enumerator->string);
1065
1066 if (enumerator->countries)
1067 loc_country_list_unref(enumerator->countries);
1068
1069 if (enumerator->asns)
1070 loc_as_list_unref(enumerator->asns);
1071
1072 // Free network search
1073 if (enumerator->networks_visited)
1074 free(enumerator->networks_visited);
1075
1076 // Free subnet/bogons stack
1077 if (enumerator->stack)
1078 loc_network_list_unref(enumerator->stack);
1079
1080 if (enumerator->subnets)
1081 loc_network_list_unref(enumerator->subnets);
1082
1083 free(enumerator);
1084 }
1085
1086 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
1087 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
1088 int r;
1089
1090 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
1091 if (!e) {
1092 return -ENOMEM;
1093 }
1094
1095 // Reference context
1096 e->ctx = loc_ref(db->ctx);
1097 e->db = loc_database_ref(db);
1098 e->mode = mode;
1099 e->refcount = 1;
1100
1101 // Flatten output?
1102 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
1103
1104 // Initialise graph search
1105 e->network_stack_depth = 1;
1106 e->networks_visited = calloc(db->network_node_objects.count, sizeof(*e->networks_visited));
1107 if (!e->networks_visited) {
1108 ERROR(db->ctx, "Could not allocated visited networks: %m\n");
1109 r = 1;
1110 goto ERROR;
1111 }
1112
1113 // Allocate stack
1114 r = loc_network_list_new(e->ctx, &e->stack);
1115 if (r)
1116 goto ERROR;
1117
1118 // Initialize bogon search
1119 loc_address_reset(&e->gap6_start, AF_INET6);
1120 loc_address_reset(&e->gap4_start, AF_INET);
1121
1122 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1123
1124 *enumerator = e;
1125 return 0;
1126
1127 ERROR:
1128 if (e)
1129 loc_database_enumerator_free(e);
1130
1131 return r;
1132 }
1133
1134 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1135 enumerator->refcount++;
1136
1137 return enumerator;
1138 }
1139
1140 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1141 if (!enumerator)
1142 return NULL;
1143
1144 if (--enumerator->refcount > 0)
1145 return enumerator;
1146
1147 loc_database_enumerator_free(enumerator);
1148 return NULL;
1149 }
1150
1151 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1152 enumerator->string = strdup(string);
1153
1154 // Make the string lowercase
1155 for (char *p = enumerator->string; *p; p++)
1156 *p = tolower(*p);
1157
1158 return 0;
1159 }
1160
1161 LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1162 struct loc_database_enumerator* enumerator) {
1163 if (!enumerator->countries)
1164 return NULL;
1165
1166 return loc_country_list_ref(enumerator->countries);
1167 }
1168
1169 LOC_EXPORT int loc_database_enumerator_set_countries(
1170 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1171 if (enumerator->countries)
1172 loc_country_list_unref(enumerator->countries);
1173
1174 enumerator->countries = loc_country_list_ref(countries);
1175
1176 return 0;
1177 }
1178
1179 LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1180 struct loc_database_enumerator* enumerator) {
1181 if (!enumerator->asns)
1182 return NULL;
1183
1184 return loc_as_list_ref(enumerator->asns);
1185 }
1186
1187 LOC_EXPORT int loc_database_enumerator_set_asns(
1188 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1189 if (enumerator->asns)
1190 loc_as_list_unref(enumerator->asns);
1191
1192 enumerator->asns = loc_as_list_ref(asns);
1193
1194 return 0;
1195 }
1196
1197 LOC_EXPORT int loc_database_enumerator_set_flag(
1198 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1199 enumerator->flags |= flag;
1200
1201 return 0;
1202 }
1203
1204 LOC_EXPORT int loc_database_enumerator_set_family(
1205 struct loc_database_enumerator* enumerator, int family) {
1206 enumerator->family = family;
1207
1208 return 0;
1209 }
1210
1211 LOC_EXPORT int loc_database_enumerator_next_as(
1212 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1213 *as = NULL;
1214
1215 // Do not do anything if not in AS mode
1216 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
1217 return 0;
1218
1219 struct loc_database* db = enumerator->db;
1220
1221 while (enumerator->as_index < db->as_objects.count) {
1222 // Fetch the next AS
1223 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
1224 if (r)
1225 return r;
1226
1227 r = loc_as_match_string(*as, enumerator->string);
1228 if (r == 1) {
1229 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
1230 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
1231
1232 return 0;
1233 }
1234
1235 // No match
1236 loc_as_unref(*as);
1237 *as = NULL;
1238 }
1239
1240 // Reset the index
1241 enumerator->as_index = 0;
1242
1243 // We have searched through all of them
1244 return 0;
1245 }
1246
1247 static int loc_database_enumerator_stack_push_node(
1248 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1249 // Do not add empty nodes
1250 if (!offset)
1251 return 0;
1252
1253 // Check if there is any space left on the stack
1254 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1255 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1256 return 1;
1257 }
1258
1259 // Check if the node is in range
1260 if (offset >= (off_t)e->db->network_node_objects.count) {
1261 ERROR(e->ctx, "Trying to add invalid node with offset %jd/%zu\n",
1262 offset, e->db->network_node_objects.count);
1263 errno = ERANGE;
1264 return 1;
1265 }
1266
1267 // Increase stack size
1268 int s = ++e->network_stack_depth;
1269
1270 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
1271
1272 e->network_stack[s].offset = offset;
1273 e->network_stack[s].i = i;
1274 e->network_stack[s].depth = depth;
1275
1276 return 0;
1277 }
1278
1279 static int loc_database_enumerator_match_network(
1280 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1281 // If family is set, it must match
1282 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1283 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
1284 return 0;
1285 }
1286
1287 // Match if no filter criteria is configured
1288 if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
1289 return 1;
1290
1291 // Check if the country code matches
1292 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1293 const char* country_code = loc_network_get_country_code(network);
1294
1295 if (loc_country_list_contains_code(enumerator->countries, country_code)) {
1296 DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
1297 return 1;
1298 }
1299 }
1300
1301 // Check if the ASN matches
1302 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1303 uint32_t asn = loc_network_get_asn(network);
1304
1305 if (loc_as_list_contains_number(enumerator->asns, asn)) {
1306 DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
1307 return 1;
1308 }
1309 }
1310
1311 // Check if flags match
1312 if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
1313 DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
1314 return 1;
1315 }
1316
1317 // Not a match
1318 return 0;
1319 }
1320
1321 static int __loc_database_enumerator_next_network(
1322 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1323 // Return top element from the stack
1324 while (1) {
1325 *network = loc_network_list_pop_first(enumerator->stack);
1326
1327 // Stack is empty
1328 if (!*network)
1329 break;
1330
1331 // Return everything if filter isn't enabled, or only return matches
1332 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1333 return 0;
1334
1335 // Throw away anything that doesn't match
1336 loc_network_unref(*network);
1337 *network = NULL;
1338 }
1339
1340 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1341 enumerator->network_stack_depth);
1342
1343 // Perform DFS
1344 while (enumerator->network_stack_depth > 0) {
1345 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
1346
1347 // Get object from top of the stack
1348 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
1349
1350 DEBUG(enumerator->ctx, " Got node: %jd\n", node->offset);
1351
1352 // Remove the node from the stack if we have already visited it
1353 if (enumerator->networks_visited[node->offset]) {
1354 enumerator->network_stack_depth--;
1355 continue;
1356 }
1357
1358 // Mark the bits on the path correctly
1359 loc_address_set_bit(&enumerator->network_address,
1360 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1361
1362 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
1363 enumerator->networks_visited[node->offset]++;
1364
1365 // Pop node from top of the stack
1366 struct loc_database_network_node_v1* n =
1367 (struct loc_database_network_node_v1*)loc_database_object(enumerator->db,
1368 &enumerator->db->network_node_objects, sizeof(*n), node->offset);
1369 if (!n)
1370 return 1;
1371
1372 // Add edges to stack
1373 int r = loc_database_enumerator_stack_push_node(enumerator,
1374 be32toh(n->one), 1, node->depth + 1);
1375 if (r)
1376 return r;
1377
1378 r = loc_database_enumerator_stack_push_node(enumerator,
1379 be32toh(n->zero), 0, node->depth + 1);
1380 if (r)
1381 return r;
1382
1383 // Check if this node is a leaf and has a network object
1384 if (__loc_database_node_is_leaf(n)) {
1385 off_t network_index = be32toh(n->network);
1386
1387 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
1388
1389 // Fetch the network object
1390 r = loc_database_fetch_network(enumerator->db, network,
1391 &enumerator->network_address, node->depth, network_index);
1392
1393 // Break on any errors
1394 if (r)
1395 return r;
1396
1397 // Return all networks when the filter is disabled, or check for match
1398 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1399 return 0;
1400
1401 // Does not seem to be a match, so we cleanup and move on
1402 loc_network_unref(*network);
1403 *network = NULL;
1404 }
1405 }
1406
1407 // Reached the end of the search
1408 return 0;
1409 }
1410
1411 static int __loc_database_enumerator_next_network_flattened(
1412 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1413 // Fetch the next network
1414 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1415 if (r)
1416 return r;
1417
1418 // End if we could not read another network
1419 if (!*network)
1420 return 0;
1421
1422 struct loc_network* subnet = NULL;
1423
1424 // Create a list with all subnets
1425 if (!enumerator->subnets) {
1426 r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
1427 if (r)
1428 return r;
1429 }
1430
1431 // Search all subnets from the database
1432 while (1) {
1433 // Fetch the next network in line
1434 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
1435 if (r) {
1436 loc_network_unref(subnet);
1437 loc_network_list_clear(enumerator->subnets);
1438
1439 return r;
1440 }
1441
1442 // End if we did not receive another subnet
1443 if (!subnet)
1444 break;
1445
1446 // Collect all subnets in a list
1447 if (loc_network_is_subnet(*network, subnet)) {
1448 r = loc_network_list_push(enumerator->subnets, subnet);
1449 if (r) {
1450 loc_network_unref(subnet);
1451 loc_network_list_clear(enumerator->subnets);
1452
1453 return r;
1454 }
1455
1456 loc_network_unref(subnet);
1457 continue;
1458 }
1459
1460 // If this is not a subnet, we push it back onto the stack and break
1461 r = loc_network_list_push(enumerator->stack, subnet);
1462 if (r) {
1463 loc_network_unref(subnet);
1464 loc_network_list_clear(enumerator->subnets);
1465
1466 return r;
1467 }
1468
1469 loc_network_unref(subnet);
1470 break;
1471 }
1472
1473 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
1474 loc_network_list_size(enumerator->subnets));
1475
1476 // We can abort here if the network has no subnets
1477 if (loc_network_list_empty(enumerator->subnets)) {
1478 loc_network_list_clear(enumerator->subnets);
1479
1480 return 0;
1481 }
1482
1483 // If the network has any subnets, we will break it into smaller parts
1484 // without the subnets.
1485 struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
1486 if (!excluded) {
1487 loc_network_list_clear(enumerator->subnets);
1488 return 1;
1489 }
1490
1491 // Merge subnets onto the stack
1492 r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
1493 if (r) {
1494 loc_network_list_clear(enumerator->subnets);
1495 loc_network_list_unref(excluded);
1496
1497 return r;
1498 }
1499
1500 // Push excluded list onto the stack
1501 r = loc_network_list_merge(enumerator->stack, excluded);
1502 if (r) {
1503 loc_network_list_clear(enumerator->subnets);
1504 loc_network_list_unref(excluded);
1505
1506 return r;
1507 }
1508
1509 loc_network_list_clear(enumerator->subnets);
1510 loc_network_list_unref(excluded);
1511
1512 // Drop the network and restart the whole process again to pick the next network
1513 loc_network_unref(*network);
1514
1515 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1516 }
1517
1518 /*
1519 This function finds all bogons (i.e. gaps) between the input networks
1520 */
1521 static int __loc_database_enumerator_next_bogon(
1522 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1523 int r;
1524
1525 // Return top element from the stack
1526 while (1) {
1527 *bogon = loc_network_list_pop_first(enumerator->stack);
1528
1529 // Stack is empty
1530 if (!*bogon)
1531 break;
1532
1533 // Return result
1534 return 0;
1535 }
1536
1537 struct loc_network* network = NULL;
1538 struct in6_addr* gap_start = NULL;
1539 struct in6_addr gap_end = IN6ADDR_ANY_INIT;
1540
1541 while (1) {
1542 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1543 if (r)
1544 return r;
1545
1546 // We have read the last network
1547 if (!network)
1548 goto FINISH;
1549
1550 const char* country_code = loc_network_get_country_code(network);
1551
1552 /*
1553 Skip anything that does not have a country code
1554
1555 Even if a network is part of the routing table, and the database provides
1556 an ASN, this does not mean that this is a legitimate announcement.
1557 */
1558 if (country_code && !*country_code) {
1559 loc_network_unref(network);
1560 continue;
1561 }
1562
1563 // Determine the network family
1564 int family = loc_network_address_family(network);
1565
1566 switch (family) {
1567 case AF_INET6:
1568 gap_start = &enumerator->gap6_start;
1569 break;
1570
1571 case AF_INET:
1572 gap_start = &enumerator->gap4_start;
1573 break;
1574
1575 default:
1576 ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
1577 errno = ENOTSUP;
1578 return 1;
1579 }
1580
1581 const struct in6_addr* first_address = loc_network_get_first_address(network);
1582 const struct in6_addr* last_address = loc_network_get_last_address(network);
1583
1584 // Skip if this network is a subnet of a former one
1585 if (loc_address_cmp(gap_start, last_address) >= 0) {
1586 loc_network_unref(network);
1587 continue;
1588 }
1589
1590 // Search where the gap could end
1591 gap_end = *first_address;
1592 loc_address_decrement(&gap_end);
1593
1594 // There is a gap
1595 if (loc_address_cmp(gap_start, &gap_end) <= 0) {
1596 r = loc_network_list_summarize(enumerator->ctx,
1597 gap_start, &gap_end, &enumerator->stack);
1598 if (r) {
1599 loc_network_unref(network);
1600 return r;
1601 }
1602 }
1603
1604 // The gap now starts after this network
1605 *gap_start = *last_address;
1606 loc_address_increment(gap_start);
1607
1608 loc_network_unref(network);
1609
1610 // Try to return something
1611 *bogon = loc_network_list_pop_first(enumerator->stack);
1612 if (*bogon)
1613 break;
1614 }
1615
1616 return 0;
1617
1618 FINISH:
1619
1620 if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
1621 r = loc_address_reset_last(&gap_end, AF_INET6);
1622 if (r)
1623 return r;
1624
1625 if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
1626 r = loc_network_list_summarize(enumerator->ctx,
1627 &enumerator->gap6_start, &gap_end, &enumerator->stack);
1628 if (r)
1629 return r;
1630 }
1631
1632 // Reset start
1633 loc_address_reset(&enumerator->gap6_start, AF_INET6);
1634 }
1635
1636 if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
1637 r = loc_address_reset_last(&gap_end, AF_INET);
1638 if (r)
1639 return r;
1640
1641 if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
1642 r = loc_network_list_summarize(enumerator->ctx,
1643 &enumerator->gap4_start, &gap_end, &enumerator->stack);
1644 if (r)
1645 return r;
1646 }
1647
1648 // Reset start
1649 loc_address_reset(&enumerator->gap4_start, AF_INET);
1650 }
1651
1652 // Try to return something
1653 *bogon = loc_network_list_pop_first(enumerator->stack);
1654
1655 return 0;
1656 }
1657
1658 LOC_EXPORT int loc_database_enumerator_next_network(
1659 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1660 switch (enumerator->mode) {
1661 case LOC_DB_ENUMERATE_NETWORKS:
1662 // Flatten output?
1663 if (enumerator->flatten)
1664 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1665
1666 return __loc_database_enumerator_next_network(enumerator, network, 1);
1667
1668 case LOC_DB_ENUMERATE_BOGONS:
1669 return __loc_database_enumerator_next_bogon(enumerator, network);
1670
1671 default:
1672 return 0;
1673 }
1674 }
1675
1676 LOC_EXPORT int loc_database_enumerator_next_country(
1677 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1678 *country = NULL;
1679
1680 // Do not do anything if not in country mode
1681 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1682 return 0;
1683
1684 struct loc_database* db = enumerator->db;
1685
1686 while (enumerator->country_index < db->country_objects.count) {
1687 // Fetch the next country
1688 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1689 if (r)
1690 return r;
1691
1692 // We do not filter here, so it always is a match
1693 return 0;
1694 }
1695
1696 // Reset the index
1697 enumerator->country_index = 0;
1698
1699 // We have searched through all of them
1700 return 0;
1701 }