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