]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
database: Free filter lists in enumerator
[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 <loc/libloc.h>
40 #include <loc/as.h>
41 #include <loc/as-list.h>
42 #include <loc/compat.h>
43 #include <loc/country.h>
44 #include <loc/country-list.h>
45 #include <loc/database.h>
46 #include <loc/format.h>
47 #include <loc/network.h>
48 #include <loc/private.h>
49 #include <loc/stringpool.h>
50
51 struct loc_database {
52 struct loc_ctx* ctx;
53 int refcount;
54
55 FILE* f;
56
57 enum loc_database_version version;
58 time_t created_at;
59 off_t vendor;
60 off_t description;
61 off_t license;
62
63 // Signatures
64 char* signature1;
65 size_t signature1_length;
66 char* signature2;
67 size_t signature2_length;
68
69 // ASes in the database
70 struct loc_database_as_v1* as_v1;
71 size_t as_count;
72
73 // Network tree
74 struct loc_database_network_node_v1* network_nodes_v1;
75 size_t network_nodes_count;
76
77 // Networks
78 struct loc_database_network_v1* networks_v1;
79 size_t networks_count;
80
81 // Countries
82 struct loc_database_country_v1* countries_v1;
83 size_t countries_count;
84
85 struct loc_stringpool* pool;
86 };
87
88 #define MAX_STACK_DEPTH 256
89
90 struct loc_node_stack {
91 off_t offset;
92 int i; // Is this node 0 or 1?
93 int depth;
94 };
95
96 struct loc_database_enumerator {
97 struct loc_ctx* ctx;
98 struct loc_database* db;
99 enum loc_database_enumerator_mode mode;
100 int refcount;
101
102 // Search string
103 char* string;
104 struct loc_country_list* countries;
105 struct loc_as_list* asns;
106 enum loc_network_flags flags;
107 int family;
108
109 // Flatten output?
110 int flatten;
111
112 // Index of the AS we are looking at
113 unsigned int as_index;
114
115 // Index of the country we are looking at
116 unsigned int country_index;
117
118 // Network state
119 struct in6_addr network_address;
120 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
121 int network_stack_depth;
122 unsigned int* networks_visited;
123
124 // For subnet search
125 struct loc_network_list* stack;
126 };
127
128 static int loc_database_read_magic(struct loc_database* db) {
129 struct loc_database_magic magic;
130
131 // Read from file
132 size_t bytes_read = fread(&magic, 1, sizeof(magic), db->f);
133
134 // Check if we have been able to read enough data
135 if (bytes_read < sizeof(magic)) {
136 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
137 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
138 return -ENOMSG;
139 }
140
141 // Compare magic bytes
142 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
143 DEBUG(db->ctx, "Magic value matches\n");
144
145 // Parse version
146 db->version = magic.version;
147
148 return 0;
149 }
150
151 ERROR(db->ctx, "Unrecognized file type\n");
152
153 // Return an error
154 return 1;
155 }
156
157 static int loc_database_read_as_section_v1(struct loc_database* db,
158 const struct loc_database_header_v1* header) {
159 off_t as_offset = be32toh(header->as_offset);
160 size_t as_length = be32toh(header->as_length);
161
162 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", (intmax_t)as_offset, as_length);
163
164 if (as_length > 0) {
165 db->as_v1 = mmap(NULL, as_length, PROT_READ,
166 MAP_SHARED, fileno(db->f), as_offset);
167
168 if (db->as_v1 == MAP_FAILED)
169 return -errno;
170 }
171
172 db->as_count = as_length / sizeof(*db->as_v1);
173
174 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
175
176 return 0;
177 }
178
179 static int loc_database_read_network_nodes_section_v1(struct loc_database* db,
180 const struct loc_database_header_v1* header) {
181 off_t network_nodes_offset = be32toh(header->network_tree_offset);
182 size_t network_nodes_length = be32toh(header->network_tree_length);
183
184 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
185 (intmax_t)network_nodes_offset, network_nodes_length);
186
187 if (network_nodes_length > 0) {
188 db->network_nodes_v1 = mmap(NULL, network_nodes_length, PROT_READ,
189 MAP_SHARED, fileno(db->f), network_nodes_offset);
190
191 if (db->network_nodes_v1 == MAP_FAILED)
192 return -errno;
193 }
194
195 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v1);
196
197 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
198
199 return 0;
200 }
201
202 static int loc_database_read_networks_section_v1(struct loc_database* db,
203 const struct loc_database_header_v1* header) {
204 off_t networks_offset = be32toh(header->network_data_offset);
205 size_t networks_length = be32toh(header->network_data_length);
206
207 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
208 (intmax_t)networks_offset, networks_length);
209
210 if (networks_length > 0) {
211 db->networks_v1 = mmap(NULL, networks_length, PROT_READ,
212 MAP_SHARED, fileno(db->f), networks_offset);
213
214 if (db->networks_v1 == MAP_FAILED)
215 return -errno;
216 }
217
218 db->networks_count = networks_length / sizeof(*db->networks_v1);
219
220 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
221
222 return 0;
223 }
224
225 static int loc_database_read_countries_section_v1(struct loc_database* db,
226 const struct loc_database_header_v1* header) {
227 off_t countries_offset = be32toh(header->countries_offset);
228 size_t countries_length = be32toh(header->countries_length);
229
230 DEBUG(db->ctx, "Reading countries section from %jd (%zu bytes)\n",
231 (intmax_t)countries_offset, countries_length);
232
233 if (countries_length > 0) {
234 db->countries_v1 = mmap(NULL, countries_length, PROT_READ,
235 MAP_SHARED, fileno(db->f), countries_offset);
236
237 if (db->countries_v1 == MAP_FAILED)
238 return -errno;
239 }
240
241 db->countries_count = countries_length / sizeof(*db->countries_v1);
242
243 INFO(db->ctx, "Read %zu countries from the database\n",
244 db->countries_count);
245
246 return 0;
247 }
248
249 static int loc_database_read_signature(struct loc_database* db,
250 char** dst, char* src, size_t length) {
251 // Check for a plausible signature length
252 if (length > LOC_SIGNATURE_MAX_LENGTH) {
253 ERROR(db->ctx, "Signature too long: %ld\n", length);
254 return -EINVAL;
255 }
256
257 DEBUG(db->ctx, "Reading signature of %ld bytes\n", length);
258
259 // Allocate space
260 *dst = malloc(length);
261 if (!*dst)
262 return -ENOMEM;
263
264 // Copy payload
265 memcpy(*dst, src, length);
266
267 return 0;
268 }
269
270 static int loc_database_read_header_v1(struct loc_database* db) {
271 struct loc_database_header_v1 header;
272 int r;
273
274 // Read from file
275 size_t size = fread(&header, 1, sizeof(header), db->f);
276
277 if (size < sizeof(header)) {
278 ERROR(db->ctx, "Could not read enough data for header\n");
279 return -ENOMSG;
280 }
281
282 // Copy over data
283 db->created_at = be64toh(header.created_at);
284 db->vendor = be32toh(header.vendor);
285 db->description = be32toh(header.description);
286 db->license = be32toh(header.license);
287
288 db->signature1_length = be16toh(header.signature1_length);
289 db->signature2_length = be16toh(header.signature2_length);
290
291 // Read signatures
292 if (db->signature1_length) {
293 r = loc_database_read_signature(db, &db->signature1,
294 header.signature1, db->signature1_length);
295 if (r)
296 return r;
297 }
298
299 if (db->signature2_length) {
300 r = loc_database_read_signature(db, &db->signature2,
301 header.signature2, db->signature2_length);
302 if (r)
303 return r;
304 }
305
306 // Open pool
307 off_t pool_offset = be32toh(header.pool_offset);
308 size_t pool_length = be32toh(header.pool_length);
309
310 r = loc_stringpool_open(db->ctx, &db->pool,
311 db->f, pool_length, pool_offset);
312 if (r)
313 return r;
314
315 // AS section
316 r = loc_database_read_as_section_v1(db, &header);
317 if (r)
318 return r;
319
320 // Network Nodes
321 r = loc_database_read_network_nodes_section_v1(db, &header);
322 if (r)
323 return r;
324
325 // Networks
326 r = loc_database_read_networks_section_v1(db, &header);
327 if (r)
328 return r;
329
330 // countries
331 r = loc_database_read_countries_section_v1(db, &header);
332 if (r)
333 return r;
334
335 return 0;
336 }
337
338 static int loc_database_read_header(struct loc_database* db) {
339 DEBUG(db->ctx, "Database version is %u\n", db->version);
340
341 switch (db->version) {
342 case LOC_DATABASE_VERSION_1:
343 return loc_database_read_header_v1(db);
344
345 default:
346 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
347 return 1;
348 }
349 }
350
351 static int loc_database_read(struct loc_database* db, FILE* f) {
352 clock_t start = clock();
353
354 int fd = fileno(f);
355
356 // Clone file descriptor
357 fd = dup(fd);
358 if (!fd) {
359 ERROR(db->ctx, "Could not duplicate file descriptor\n");
360 return -1;
361 }
362
363 // Reopen the file so that we can keep our own file handle
364 db->f = fdopen(fd, "r");
365 if (!db->f) {
366 ERROR(db->ctx, "Could not re-open database file\n");
367 return -1;
368 }
369
370 // Rewind to the start of the file
371 rewind(db->f);
372
373 // Read magic bytes
374 int r = loc_database_read_magic(db);
375 if (r)
376 return r;
377
378 // Read the header
379 r = loc_database_read_header(db);
380 if (r)
381 return r;
382
383 clock_t end = clock();
384
385 INFO(db->ctx, "Opened database in %.4fms\n",
386 (double)(end - start) / CLOCKS_PER_SEC * 1000);
387
388 return 0;
389 }
390
391 LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
392 // Fail on invalid file handle
393 if (!f)
394 return -EINVAL;
395
396 struct loc_database* db = calloc(1, sizeof(*db));
397 if (!db)
398 return -ENOMEM;
399
400 // Reference context
401 db->ctx = loc_ref(ctx);
402 db->refcount = 1;
403
404 DEBUG(db->ctx, "Database object allocated at %p\n", db);
405
406 int r = loc_database_read(db, f);
407 if (r) {
408 loc_database_unref(db);
409 return r;
410 }
411
412 *database = db;
413
414 return 0;
415 }
416
417 LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
418 db->refcount++;
419
420 return db;
421 }
422
423 static void loc_database_free(struct loc_database* db) {
424 int r;
425
426 DEBUG(db->ctx, "Releasing database %p\n", db);
427
428 // Removing all ASes
429 if (db->as_v1) {
430 r = munmap(db->as_v1, db->as_count * sizeof(*db->as_v1));
431 if (r)
432 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
433 }
434
435 // Remove mapped network sections
436 if (db->networks_v1) {
437 r = munmap(db->networks_v1, db->networks_count * sizeof(*db->networks_v1));
438 if (r)
439 ERROR(db->ctx, "Could not unmap networks section: %s\n", strerror(errno));
440 }
441
442 // Remove mapped network nodes section
443 if (db->network_nodes_v1) {
444 r = munmap(db->network_nodes_v1, db->network_nodes_count * sizeof(*db->network_nodes_v1));
445 if (r)
446 ERROR(db->ctx, "Could not unmap network nodes section: %s\n", strerror(errno));
447 }
448
449 if (db->pool)
450 loc_stringpool_unref(db->pool);
451
452 // Free signature
453 if (db->signature1)
454 free(db->signature1);
455 if (db->signature2)
456 free(db->signature2);
457
458 // Close database file
459 if (db->f)
460 fclose(db->f);
461
462 loc_unref(db->ctx);
463 free(db);
464 }
465
466 LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
467 if (--db->refcount > 0)
468 return NULL;
469
470 loc_database_free(db);
471 return NULL;
472 }
473
474 LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
475 // Cannot do this when no signature is available
476 if (!db->signature1 && !db->signature2) {
477 DEBUG(db->ctx, "No signature available to verify\n");
478 return 1;
479 }
480
481 // Start the stopwatch
482 clock_t start = clock();
483
484 // Load public key
485 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
486 if (!pkey) {
487 char* error = ERR_error_string(ERR_get_error(), NULL);
488 ERROR(db->ctx, "Could not parse public key: %s\n", error);
489
490 return -1;
491 }
492
493 int r = 0;
494
495 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
496
497 // Initialise hash function
498 r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
499 if (r != 1) {
500 ERROR(db->ctx, "Error initializing signature validation: %s\n",
501 ERR_error_string(ERR_get_error(), NULL));
502 r = 1;
503
504 goto CLEANUP;
505 }
506
507 // Reset file to start
508 rewind(db->f);
509
510 // Read magic
511 struct loc_database_magic magic;
512 fread(&magic, 1, sizeof(magic), db->f);
513
514 hexdump(db->ctx, &magic, sizeof(magic));
515
516 // Feed magic into the hash
517 r = EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
518 if (r != 1) {
519 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
520 r = 1;
521
522 goto CLEANUP;
523 }
524
525 // Read the header
526 struct loc_database_header_v1 header_v1;
527 size_t bytes_read;
528
529 switch (db->version) {
530 case LOC_DATABASE_VERSION_1:
531 bytes_read = fread(&header_v1, 1, sizeof(header_v1), db->f);
532 if (bytes_read < sizeof(header_v1)) {
533 ERROR(db->ctx, "Could not read header\n");
534 r = 1;
535
536 goto CLEANUP;
537 }
538
539 // Clear signatures
540 memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
541 header_v1.signature1_length = 0;
542 memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
543 header_v1.signature2_length = 0;
544
545 hexdump(db->ctx, &header_v1, sizeof(header_v1));
546
547 // Feed header into the hash
548 r = EVP_DigestVerifyUpdate(mdctx, &header_v1, sizeof(header_v1));
549 if (r != 1) {
550 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
551 r = 1;
552
553 goto CLEANUP;
554 }
555 break;
556
557 default:
558 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
559 db->version);
560 r = -EINVAL;
561 goto CLEANUP;
562 }
563
564 // Walk through the file in chunks of 64kB
565 char buffer[64 * 1024];
566
567 while (!feof(db->f)) {
568 bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
569
570 hexdump(db->ctx, buffer, bytes_read);
571
572 r = EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
573 if (r != 1) {
574 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
575 r = 1;
576
577 goto CLEANUP;
578 }
579 }
580
581 // Check first signature
582 if (db->signature1) {
583 hexdump(db->ctx, db->signature1, db->signature1_length);
584
585 r = EVP_DigestVerifyFinal(mdctx,
586 (unsigned char*)db->signature1, db->signature1_length);
587
588 if (r == 0) {
589 DEBUG(db->ctx, "The first signature is invalid\n");
590 r = 1;
591 } else if (r == 1) {
592 DEBUG(db->ctx, "The first signature is valid\n");
593 r = 0;
594 } else {
595 ERROR(db->ctx, "Error verifying the first signature: %s\n",
596 ERR_error_string(ERR_get_error(), NULL));
597 r = -1;
598 }
599 }
600
601 // Check second signature only when the first one was invalid
602 if (r && db->signature2) {
603 hexdump(db->ctx, db->signature2, db->signature2_length);
604
605 r = EVP_DigestVerifyFinal(mdctx,
606 (unsigned char*)db->signature2, db->signature2_length);
607
608 if (r == 0) {
609 DEBUG(db->ctx, "The second signature is invalid\n");
610 r = 1;
611 } else if (r == 1) {
612 DEBUG(db->ctx, "The second signature is valid\n");
613 r = 0;
614 } else {
615 ERROR(db->ctx, "Error verifying the second signature: %s\n",
616 ERR_error_string(ERR_get_error(), NULL));
617 r = -1;
618 }
619 }
620
621 clock_t end = clock();
622 DEBUG(db->ctx, "Signature checked in %.4fms\n",
623 (double)(end - start) / CLOCKS_PER_SEC * 1000);
624
625 CLEANUP:
626 // Cleanup
627 EVP_MD_CTX_free(mdctx);
628 EVP_PKEY_free(pkey);
629
630 return r;
631 }
632
633 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
634 return db->created_at;
635 }
636
637 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
638 return loc_stringpool_get(db->pool, db->vendor);
639 }
640
641 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
642 return loc_stringpool_get(db->pool, db->description);
643 }
644
645 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
646 return loc_stringpool_get(db->pool, db->license);
647 }
648
649 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
650 return db->as_count;
651 }
652
653 // Returns the AS at position pos
654 static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
655 if ((size_t)pos >= db->as_count)
656 return -EINVAL;
657
658 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
659
660 int r;
661 switch (db->version) {
662 case LOC_DATABASE_VERSION_1:
663 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, db->as_v1 + pos);
664 break;
665
666 default:
667 return -1;
668 }
669
670 if (r == 0) {
671 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
672 }
673
674 return r;
675 }
676
677 // Performs a binary search to find the AS in the list
678 LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
679 off_t lo = 0;
680 off_t hi = db->as_count - 1;
681
682 // Save start time
683 clock_t start = clock();
684
685 while (lo <= hi) {
686 off_t i = (lo + hi) / 2;
687
688 // Fetch AS in the middle between lo and hi
689 int r = loc_database_fetch_as(db, as, i);
690 if (r)
691 return r;
692
693 // Check if this is a match
694 uint32_t as_number = loc_as_get_number(*as);
695 if (as_number == number) {
696 clock_t end = clock();
697
698 // Log how fast this has been
699 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
700 (double)(end - start) / CLOCKS_PER_SEC * 1000);
701
702 return 0;
703 }
704
705 // If it wasn't, we release the AS and
706 // adjust our search pointers
707 loc_as_unref(*as);
708
709 if (as_number < number) {
710 lo = i + 1;
711 } else
712 hi = i - 1;
713 }
714
715 // Nothing found
716 *as = NULL;
717
718 return 1;
719 }
720
721 // Returns the network at position pos
722 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
723 struct in6_addr* address, unsigned int prefix, off_t pos) {
724 if ((size_t)pos >= db->networks_count) {
725 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
726 (intmax_t)pos, (intmax_t)db->networks_count);
727 return -EINVAL;
728 }
729
730
731 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
732
733 int r;
734 switch (db->version) {
735 case LOC_DATABASE_VERSION_1:
736 r = loc_network_new_from_database_v1(db->ctx, network,
737 address, prefix, db->networks_v1 + pos);
738 break;
739
740 default:
741 return -1;
742 }
743
744 if (r == 0) {
745 char* string = loc_network_str(*network);
746 DEBUG(db->ctx, "Got network %s\n", string);
747 free(string);
748 }
749
750 return r;
751 }
752
753 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1* node) {
754 return (node->network != htobe32(0xffffffff));
755 }
756
757 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
758 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
759 const struct loc_database_network_node_v1* node) {
760 off_t network_index = be32toh(node->network);
761
762 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", (intmax_t)(node - db->network_nodes_v1), (intmax_t)network_index);
763
764 // Fetch the network
765 int r = loc_database_fetch_network(db, network,
766 network_address, prefix, network_index);
767 if (r) {
768 ERROR(db->ctx, "Could not fetch network %jd from database\n", (intmax_t)network_index);
769 return r;
770 }
771
772 // Check if the given IP address is inside the network
773 r = loc_network_match_address(*network, address);
774 if (r) {
775 DEBUG(db->ctx, "Searched address is not part of the network\n");
776
777 loc_network_unref(*network);
778 *network = NULL;
779 return 1;
780 }
781
782 // A network was found and the IP address matches
783 return 0;
784 }
785
786 // Searches for an exact match along the path
787 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
788 struct loc_network** network, struct in6_addr* network_address,
789 const struct loc_database_network_node_v1* node, unsigned int level) {
790 int r;
791 off_t node_index;
792
793 // Follow the path
794 int bit = in6_addr_get_bit(address, level);
795 in6_addr_set_bit(network_address, level, bit);
796
797 if (bit == 0)
798 node_index = be32toh(node->zero);
799 else
800 node_index = be32toh(node->one);
801
802 // If the node index is zero, the tree ends here
803 // and we cannot descend any further
804 if (node_index > 0) {
805 // Check boundaries
806 if ((size_t)node_index >= db->network_nodes_count)
807 return -EINVAL;
808
809 // Move on to the next node
810 r = __loc_database_lookup(db, address, network, network_address,
811 db->network_nodes_v1 + node_index, level + 1);
812
813 // End here if a result was found
814 if (r == 0)
815 return r;
816
817 // Raise any errors
818 else if (r < 0)
819 return r;
820
821 DEBUG(db->ctx, "No match found below level %u\n", level);
822 } else {
823 DEBUG(db->ctx, "Tree ended at level %u\n", level);
824 }
825
826 // If this node has a leaf, we will check if it matches
827 if (__loc_database_node_is_leaf(node)) {
828 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
829 if (r <= 0)
830 return r;
831 }
832
833 return 1;
834 }
835
836 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
837 struct in6_addr* address, struct loc_network** network) {
838 struct in6_addr network_address;
839 memset(&network_address, 0, sizeof(network_address));
840
841 *network = NULL;
842
843 // Save start time
844 clock_t start = clock();
845
846 int r = __loc_database_lookup(db, address, network, &network_address,
847 db->network_nodes_v1, 0);
848
849 clock_t end = clock();
850
851 // Log how fast this has been
852 DEBUG(db->ctx, "Executed network search in %.4fms\n",
853 (double)(end - start) / CLOCKS_PER_SEC * 1000);
854
855 return r;
856 }
857
858 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
859 const char* string, struct loc_network** network) {
860 struct in6_addr address;
861
862 int r = loc_parse_address(db->ctx, string, &address);
863 if (r)
864 return r;
865
866 return loc_database_lookup(db, &address, network);
867 }
868
869 // Returns the country at position pos
870 static int loc_database_fetch_country(struct loc_database* db,
871 struct loc_country** country, off_t pos) {
872 if ((size_t)pos >= db->countries_count)
873 return -EINVAL;
874
875 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
876
877 int r;
878 switch (db->version) {
879 case LOC_DATABASE_VERSION_1:
880 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, db->countries_v1 + pos);
881 break;
882
883 default:
884 return -1;
885 }
886
887 if (r == 0) {
888 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
889 }
890
891 return r;
892 }
893
894 // Performs a binary search to find the country in the list
895 LOC_EXPORT int loc_database_get_country(struct loc_database* db,
896 struct loc_country** country, const char* code) {
897 off_t lo = 0;
898 off_t hi = db->countries_count - 1;
899
900 // Save start time
901 clock_t start = clock();
902
903 while (lo <= hi) {
904 off_t i = (lo + hi) / 2;
905
906 // Fetch country in the middle between lo and hi
907 int r = loc_database_fetch_country(db, country, i);
908 if (r)
909 return r;
910
911 // Check if this is a match
912 const char* cc = loc_country_get_code(*country);
913 int result = strcmp(code, cc);
914
915 if (result == 0) {
916 clock_t end = clock();
917
918 // Log how fast this has been
919 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
920 (double)(end - start) / CLOCKS_PER_SEC * 1000);
921
922 return 0;
923 }
924
925 // If it wasn't, we release the country and
926 // adjust our search pointers
927 loc_country_unref(*country);
928
929 if (result > 0) {
930 lo = i + 1;
931 } else
932 hi = i - 1;
933 }
934
935 // Nothing found
936 *country = NULL;
937
938 return 1;
939 }
940
941 // Enumerator
942
943 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
944 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
945
946 // Release all references
947 loc_database_unref(enumerator->db);
948 loc_unref(enumerator->ctx);
949
950 if (enumerator->string)
951 free(enumerator->string);
952
953 if (enumerator->countries)
954 loc_country_list_unref(enumerator->countries);
955
956 if (enumerator->asns)
957 loc_as_list_unref(enumerator->asns);
958
959 // Free network search
960 free(enumerator->networks_visited);
961
962 // Free subnet stack
963 if (enumerator->stack)
964 loc_network_list_unref(enumerator->stack);
965
966 free(enumerator);
967 }
968
969 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
970 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
971 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
972 if (!e)
973 return -ENOMEM;
974
975 // Reference context
976 e->ctx = loc_ref(db->ctx);
977 e->db = loc_database_ref(db);
978 e->mode = mode;
979 e->refcount = 1;
980
981 // Flatten output?
982 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
983
984 // Initialise graph search
985 e->network_stack_depth = 1;
986 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
987
988 // Allocate stack
989 int r = loc_network_list_new(e->ctx, &e->stack);
990 if (r) {
991 loc_database_enumerator_free(e);
992 return r;
993 }
994
995 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
996
997 *enumerator = e;
998 return 0;
999 }
1000
1001 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1002 enumerator->refcount++;
1003
1004 return enumerator;
1005 }
1006
1007 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1008 if (!enumerator)
1009 return NULL;
1010
1011 if (--enumerator->refcount > 0)
1012 return enumerator;
1013
1014 loc_database_enumerator_free(enumerator);
1015 return NULL;
1016 }
1017
1018 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1019 enumerator->string = strdup(string);
1020
1021 // Make the string lowercase
1022 for (char *p = enumerator->string; *p; p++)
1023 *p = tolower(*p);
1024
1025 return 0;
1026 }
1027
1028 LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1029 struct loc_database_enumerator* enumerator) {
1030 if (!enumerator->countries)
1031 return NULL;
1032
1033 return loc_country_list_ref(enumerator->countries);
1034 }
1035
1036 LOC_EXPORT int loc_database_enumerator_set_countries(
1037 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1038 if (enumerator->countries)
1039 loc_country_list_unref(enumerator->countries);
1040
1041 enumerator->countries = loc_country_list_ref(countries);
1042
1043 return 0;
1044 }
1045
1046 LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1047 struct loc_database_enumerator* enumerator) {
1048 if (!enumerator->asns)
1049 return NULL;
1050
1051 return loc_as_list_ref(enumerator->asns);
1052 }
1053
1054 LOC_EXPORT int loc_database_enumerator_set_asns(
1055 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1056 if (enumerator->asns)
1057 loc_as_list_unref(enumerator->asns);
1058
1059 enumerator->asns = loc_as_list_ref(asns);
1060
1061 return 0;
1062 }
1063
1064 LOC_EXPORT int loc_database_enumerator_set_flag(
1065 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1066 enumerator->flags |= flag;
1067
1068 return 0;
1069 }
1070
1071 LOC_EXPORT int loc_database_enumerator_set_family(
1072 struct loc_database_enumerator* enumerator, int family) {
1073 enumerator->family = family;
1074
1075 return 0;
1076 }
1077
1078 LOC_EXPORT int loc_database_enumerator_next_as(
1079 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1080 *as = NULL;
1081
1082 // Do not do anything if not in AS mode
1083 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
1084 return 0;
1085
1086 struct loc_database* db = enumerator->db;
1087
1088 while (enumerator->as_index < db->as_count) {
1089 // Fetch the next AS
1090 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
1091 if (r)
1092 return r;
1093
1094 r = loc_as_match_string(*as, enumerator->string);
1095 if (r == 1) {
1096 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
1097 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
1098
1099 return 0;
1100 }
1101
1102 // No match
1103 loc_as_unref(*as);
1104 *as = NULL;
1105 }
1106
1107 // Reset the index
1108 enumerator->as_index = 0;
1109
1110 // We have searched through all of them
1111 return 0;
1112 }
1113
1114 static int loc_database_enumerator_stack_push_node(
1115 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1116 // Do not add empty nodes
1117 if (!offset)
1118 return 0;
1119
1120 // Check if there is any space left on the stack
1121 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1122 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1123 return -1;
1124 }
1125
1126 // Increase stack size
1127 int s = ++e->network_stack_depth;
1128
1129 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
1130
1131 e->network_stack[s].offset = offset;
1132 e->network_stack[s].i = i;
1133 e->network_stack[s].depth = depth;
1134
1135 return 0;
1136 }
1137
1138 static int loc_database_enumerator_filter_network(
1139 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1140 // Skip if the family does not match
1141 if (enumerator->family && loc_network_address_family(network) != enumerator->family)
1142 return 1;
1143
1144 // Skip if the country code does not match
1145 if (enumerator->countries) {
1146 if (!loc_country_list_empty(enumerator->countries)) {
1147 const char* country_code = loc_network_get_country_code(network);
1148
1149 if (!loc_country_list_contains_code(enumerator->countries, country_code))
1150 return 1;
1151 }
1152 }
1153
1154 // Skip if the ASN does not match
1155 if (enumerator->asns) {
1156 if (!loc_as_list_empty(enumerator->asns)) {
1157 uint32_t asn = loc_network_get_asn(network);
1158
1159 if (!loc_as_list_contains_number(enumerator->asns, asn))
1160 return 1;
1161 }
1162 }
1163
1164 // Skip if flags do not match
1165 if (enumerator->flags &&
1166 !loc_network_match_flag(network, enumerator->flags))
1167 return 1;
1168
1169 // Do not filter
1170 return 0;
1171 }
1172
1173 static int __loc_database_enumerator_next_network(
1174 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1175 // Return top element from the stack
1176 while (1) {
1177 *network = loc_network_list_pop(enumerator->stack);
1178
1179 // Stack is empty
1180 if (!*network)
1181 break;
1182
1183 // Throw away any networks by filter
1184 if (filter && loc_database_enumerator_filter_network(enumerator, *network)) {
1185 loc_network_unref(*network);
1186 *network = NULL;
1187 continue;
1188 }
1189
1190 // Return result
1191 return 0;
1192 }
1193
1194 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1195 enumerator->network_stack_depth);
1196
1197 // Perform DFS
1198 while (enumerator->network_stack_depth > 0) {
1199 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
1200
1201 // Get object from top of the stack
1202 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
1203
1204 // Remove the node from the stack if we have already visited it
1205 if (enumerator->networks_visited[node->offset]) {
1206 enumerator->network_stack_depth--;
1207 continue;
1208 }
1209
1210 // Mark the bits on the path correctly
1211 in6_addr_set_bit(&enumerator->network_address,
1212 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1213
1214 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
1215 enumerator->networks_visited[node->offset]++;
1216
1217 // Pop node from top of the stack
1218 struct loc_database_network_node_v1* n =
1219 enumerator->db->network_nodes_v1 + node->offset;
1220
1221 // Add edges to stack
1222 int r = loc_database_enumerator_stack_push_node(enumerator,
1223 be32toh(n->one), 1, node->depth + 1);
1224
1225 if (r)
1226 return r;
1227
1228 r = loc_database_enumerator_stack_push_node(enumerator,
1229 be32toh(n->zero), 0, node->depth + 1);
1230
1231 if (r)
1232 return r;
1233
1234 // Check if this node is a leaf and has a network object
1235 if (__loc_database_node_is_leaf(n)) {
1236 off_t network_index = be32toh(n->network);
1237
1238 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
1239
1240 // Fetch the network object
1241 r = loc_database_fetch_network(enumerator->db, network,
1242 &enumerator->network_address, node->depth, network_index);
1243
1244 // Break on any errors
1245 if (r)
1246 return r;
1247
1248 // Return all networks when the filter is disabled
1249 if (!filter)
1250 return 0;
1251
1252 // Check if we are interested in this network
1253 if (loc_database_enumerator_filter_network(enumerator, *network)) {
1254 loc_network_unref(*network);
1255 *network = NULL;
1256
1257 continue;
1258 }
1259
1260 return 0;
1261 }
1262 }
1263
1264 // Reached the end of the search
1265 return 0;
1266 }
1267
1268 static int __loc_database_enumerator_next_network_flattened(
1269 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1270 // Fetch the next network
1271 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1272 if (r)
1273 return r;
1274
1275 // End if we could not read another network
1276 if (!*network)
1277 return 0;
1278
1279 struct loc_network* subnet = NULL;
1280 struct loc_network_list* subnets;
1281
1282 // Create a list with all subnets
1283 r = loc_network_list_new(enumerator->ctx, &subnets);
1284 if (r)
1285 return r;
1286
1287 // Search all subnets from the database
1288 while (1) {
1289 // Fetch the next network in line
1290 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
1291 if (r)
1292 goto END;
1293
1294 // End if we did not receive another subnet
1295 if (!subnet)
1296 break;
1297
1298 // Collect all subnets in a list
1299 if (loc_network_is_subnet(*network, subnet)) {
1300 r = loc_network_list_push(subnets, subnet);
1301 if (r)
1302 goto END;
1303
1304 loc_network_unref(subnet);
1305 continue;
1306 }
1307
1308 // If this is not a subnet, we push it back onto the stack and break
1309 r = loc_network_list_push(enumerator->stack, subnet);
1310 if (r)
1311 goto END;
1312
1313 loc_network_unref(subnet);
1314 break;
1315 }
1316
1317 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n", loc_network_list_size(subnets));
1318
1319 // We can abort here if the network has no subnets
1320 if (loc_network_list_empty(subnets)) {
1321 loc_network_list_unref(subnets);
1322
1323 return 0;
1324 }
1325
1326 // If the network has any subnets, we will break it into smaller parts
1327 // without the subnets.
1328 struct loc_network_list* excluded = loc_network_exclude_list(*network, subnets);
1329 if (!excluded || loc_network_list_empty(excluded)) {
1330 r = 1;
1331 goto END;
1332 }
1333
1334 // Replace network with the first one
1335 loc_network_unref(*network);
1336
1337 *network = loc_network_list_pop_first(excluded);
1338
1339 // Push the rest onto the stack
1340 loc_network_list_reverse(excluded);
1341 loc_network_list_merge(enumerator->stack, excluded);
1342
1343 loc_network_list_unref(excluded);
1344
1345 END:
1346 if (subnet)
1347 loc_network_unref(subnet);
1348
1349 loc_network_list_unref(subnets);
1350
1351 return r;
1352 }
1353
1354 LOC_EXPORT int loc_database_enumerator_next_network(
1355 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1356 // Do not do anything if not in network mode
1357 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
1358 return 0;
1359
1360 // Flatten output?
1361 if (enumerator->flatten)
1362 return __loc_database_enumerator_next_network_flattened(enumerator, network);
1363
1364 return __loc_database_enumerator_next_network(enumerator, network, 1);
1365 }
1366
1367 LOC_EXPORT int loc_database_enumerator_next_country(
1368 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1369 *country = NULL;
1370
1371 // Do not do anything if not in country mode
1372 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1373 return 0;
1374
1375 struct loc_database* db = enumerator->db;
1376
1377 while (enumerator->country_index < db->countries_count) {
1378 // Fetch the next country
1379 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1380 if (r)
1381 return r;
1382
1383 // We do not filter here, so it always is a match
1384 return 0;
1385 }
1386
1387 // Reset the index
1388 enumerator->country_index = 0;
1389
1390 // We have searched through all of them
1391 return 0;
1392 }