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