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