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