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