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