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