]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
location-downloader: Verify the database after download
[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
c81205a5
MT
439 // Start the stopwatch
440 clock_t start = clock();
441
726f9984
MT
442 // Load public key
443 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
444 if (!pkey) {
445 char* error = ERR_error_string(ERR_get_error(), NULL);
446 ERROR(db->ctx, "Could not parse public key: %s\n", error);
447
448 return -1;
449 }
450
b1720435
MT
451 int r = 0;
452
453 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
454
b1720435 455 // Initialise hash function
726f9984 456 EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
b1720435
MT
457
458 // Reset file to start
459 rewind(db->f);
460
461 // Read magic
462 struct loc_database_magic magic;
463 fread(&magic, 1, sizeof(magic), db->f);
464
465 // Feed magic into the hash
726f9984 466 EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
b1720435
MT
467
468 // Read the header
469 struct loc_database_header_v0 header_v0;
470
471 switch (db->version) {
472 case 0:
473 fread(&header_v0, 1, sizeof(header_v0), db->f);
474
475 // Clear signature
476 for (unsigned int i = 0; i < sizeof(header_v0.signature); i++) {
477 header_v0.signature[i] = '\0';
478 }
479
480 // Feed header into the hash
726f9984 481 EVP_DigestVerifyUpdate(mdctx, &header_v0, sizeof(header_v0));
b1720435
MT
482 break;
483
484 default:
485 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
486 db->version);
487 r = -EINVAL;
488 goto CLEANUP;
489 }
490
726f9984
MT
491 // Walk through the file in chunks of 64kB
492 char buffer[64 * 1024];
b1720435
MT
493
494 while (!feof(db->f)) {
495 size_t bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
496
726f9984 497 EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
b1720435
MT
498 }
499
726f9984
MT
500 // Finish
501 r = EVP_DigestVerifyFinal(mdctx,
502 (unsigned char*)db->signature, db->signature_length);
503
504 if (r == 0) {
505 DEBUG(db->ctx, "The signature is valid\n");
506 } else if (r == 1) {
507 DEBUG(db->ctx, "The signature is invalid\n");
508 } else {
509 ERROR(db->ctx, "Error verifying the signature: %s\n",
510 ERR_error_string(ERR_get_error(), NULL));
b1720435
MT
511 }
512
c81205a5
MT
513 clock_t end = clock();
514 DEBUG(db->ctx, "Signature checked in %.4fms\n",
515 (double)(end - start) / CLOCKS_PER_SEC * 1000);
516
b1720435
MT
517CLEANUP:
518 // Cleanup
519 EVP_MD_CTX_free(mdctx);
726f9984 520 EVP_PKEY_free(pkey);
b1720435
MT
521
522 return r;
523}
524
c182393f
MT
525LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
526 return db->created_at;
527}
78ace4ed 528
c182393f
MT
529LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
530 return loc_stringpool_get(db->pool, db->vendor);
531}
78ace4ed 532
c182393f
MT
533LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
534 return loc_stringpool_get(db->pool, db->description);
535}
78ace4ed 536
4bf49d00
MT
537LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
538 return loc_stringpool_get(db->pool, db->license);
539}
540
c182393f
MT
541LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
542 return db->as_count;
78ace4ed
MT
543}
544
c182393f
MT
545// Returns the AS at position pos
546static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
547 if ((size_t)pos >= db->as_count)
548 return -EINVAL;
2601e83e 549
5c57de03 550 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
2601e83e
MT
551
552 int r;
c182393f
MT
553 switch (db->version) {
554 case 0:
555 r = loc_as_new_from_database_v0(db->ctx, db->pool, as, db->as_v0 + pos);
556 break;
2601e83e 557
c182393f
MT
558 default:
559 return -1;
560 }
2601e83e 561
c182393f
MT
562 if (r == 0) {
563 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 564 }
2601e83e 565
c182393f
MT
566 return r;
567}
c34e76f1 568
c182393f
MT
569// Performs a binary search to find the AS in the list
570LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
571 off_t lo = 0;
572 off_t hi = db->as_count - 1;
c34e76f1 573
8f3e2a06
MT
574 // Save start time
575 clock_t start = clock();
576
c182393f
MT
577 while (lo <= hi) {
578 off_t i = (lo + hi) / 2;
8f5b676a 579
c182393f
MT
580 // Fetch AS in the middle between lo and hi
581 int r = loc_database_fetch_as(db, as, i);
582 if (r)
583 return r;
a5db3e49 584
c182393f
MT
585 // Check if this is a match
586 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06
MT
587 if (as_number == number) {
588 clock_t end = clock();
589
590 // Log how fast this has been
e16c943b
MT
591 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
592 (double)(end - start) / CLOCKS_PER_SEC * 1000);
8f3e2a06 593
c182393f 594 return 0;
8f3e2a06 595 }
c182393f
MT
596
597 // If it wasn't, we release the AS and
598 // adjust our search pointers
599 loc_as_unref(*as);
600
601 if (as_number < number) {
602 lo = i + 1;
603 } else
604 hi = i - 1;
605 }
2601e83e 606
c182393f
MT
607 // Nothing found
608 *as = NULL;
2601e83e 609
8f3e2a06 610 return 1;
2601e83e 611}
10778041
MT
612
613// Returns the network at position pos
39a55353
MT
614static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
615 struct in6_addr* address, unsigned int prefix, off_t pos) {
9b9e5faf
MT
616 if ((size_t)pos >= db->networks_count) {
617 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
618 (intmax_t)pos, (intmax_t)db->networks_count);
10778041 619 return -EINVAL;
9b9e5faf
MT
620 }
621
10778041 622
5c57de03 623 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
10778041
MT
624
625 int r;
626 switch (db->version) {
627 case 0:
39a55353
MT
628 r = loc_network_new_from_database_v0(db->ctx, network,
629 address, prefix, db->networks_v0 + pos);
10778041
MT
630 break;
631
632 default:
633 return -1;
634 }
635
636 if (r == 0) {
637 char* string = loc_network_str(*network);
638 DEBUG(db->ctx, "Got network %s\n", string);
639 free(string);
640 }
641
642 return r;
643}
2a30e4de 644
025ef489 645static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
39a55353 646 return (node->network != htobe32(0xffffffff));
025ef489
MT
647}
648
649static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
39a55353 650 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
2a30e4de 651 const struct loc_database_network_node_v0* node) {
39a55353
MT
652 off_t network_index = be32toh(node->network);
653
5c57de03 654 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", (intmax_t)(node - db->network_nodes_v0), (intmax_t)network_index);
2a30e4de
MT
655
656 // Fetch the network
657 int r = loc_database_fetch_network(db, network,
39a55353 658 network_address, prefix, network_index);
e85e2b0b 659 if (r) {
5c57de03 660 ERROR(db->ctx, "Could not fetch network %jd from database\n", (intmax_t)network_index);
2a30e4de 661 return r;
e85e2b0b 662 }
39a55353 663
2a30e4de
MT
664 // Check if the given IP address is inside the network
665 r = loc_network_match_address(*network, address);
666 if (r) {
667 DEBUG(db->ctx, "Searched address is not part of the network\n");
668
669 loc_network_unref(*network);
670 *network = NULL;
671 return 1;
672 }
673
674 // A network was found and the IP address matches
675 return 0;
676}
677
2a30e4de
MT
678// Searches for an exact match along the path
679static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
680 struct loc_network** network, struct in6_addr* network_address,
f66f15e1 681 const struct loc_database_network_node_v0* node, unsigned int level) {
025ef489 682 int r;
2a30e4de
MT
683 off_t node_index;
684
685 // Follow the path
686 int bit = in6_addr_get_bit(address, level);
687 in6_addr_set_bit(network_address, level, bit);
688
689 if (bit == 0)
690 node_index = be32toh(node->zero);
691 else
692 node_index = be32toh(node->one);
693
9086d2b1
MT
694 // If the node index is zero, the tree ends here
695 // and we cannot descend any further
696 if (node_index > 0) {
697 // Check boundaries
698 if ((size_t)node_index >= db->network_nodes_count)
699 return -EINVAL;
2a30e4de 700
9086d2b1
MT
701 // Move on to the next node
702 r = __loc_database_lookup(db, address, network, network_address,
703 db->network_nodes_v0 + node_index, level + 1);
2a30e4de 704
9086d2b1
MT
705 // End here if a result was found
706 if (r == 0)
707 return r;
2a30e4de 708
9086d2b1
MT
709 // Raise any errors
710 else if (r < 0)
711 return r;
ec1d9681
MT
712
713 DEBUG(db->ctx, "No match found below level %u\n", level);
714 } else {
715 DEBUG(db->ctx, "Tree ended at level %u\n", level);
9086d2b1 716 }
2a30e4de 717
9086d2b1
MT
718 // If this node has a leaf, we will check if it matches
719 if (__loc_database_node_is_leaf(node)) {
720 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
721 if (r <= 0)
722 return r;
723 }
2a30e4de 724
ec1d9681 725 return 1;
2a30e4de
MT
726}
727
728LOC_EXPORT int loc_database_lookup(struct loc_database* db,
729 struct in6_addr* address, struct loc_network** network) {
730 struct in6_addr network_address;
731 memset(&network_address, 0, sizeof(network_address));
732
733 *network = NULL;
734
735 // Save start time
736 clock_t start = clock();
737
738 int r = __loc_database_lookup(db, address, network, &network_address,
739 db->network_nodes_v0, 0);
740
741 clock_t end = clock();
742
743 // Log how fast this has been
e16c943b
MT
744 DEBUG(db->ctx, "Executed network search in %.4fms\n",
745 (double)(end - start) / CLOCKS_PER_SEC * 1000);
2a30e4de
MT
746
747 return r;
748}
749
750LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
751 const char* string, struct loc_network** network) {
752 struct in6_addr address;
753
754 int r = loc_parse_address(db->ctx, string, &address);
755 if (r)
756 return r;
757
758 return loc_database_lookup(db, &address, network);
759}
7e13db74 760
ec684c1a
MT
761// Returns the country at position pos
762static int loc_database_fetch_country(struct loc_database* db,
763 struct loc_country** country, off_t pos) {
764 if ((size_t)pos >= db->countries_count)
765 return -EINVAL;
766
2e2325a9 767 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
ec684c1a
MT
768
769 int r;
770 switch (db->version) {
771 case 0:
772 r = loc_country_new_from_database_v0(db->ctx, db->pool, country, db->countries_v0 + pos);
773 break;
774
775 default:
776 return -1;
777 }
778
779 if (r == 0) {
780 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
781 }
782
783 return r;
784}
785
786// Performs a binary search to find the country in the list
787LOC_EXPORT int loc_database_get_country(struct loc_database* db,
788 struct loc_country** country, const char* code) {
789 off_t lo = 0;
790 off_t hi = db->countries_count - 1;
791
792 // Save start time
793 clock_t start = clock();
794
795 while (lo <= hi) {
796 off_t i = (lo + hi) / 2;
797
798 // Fetch country in the middle between lo and hi
799 int r = loc_database_fetch_country(db, country, i);
800 if (r)
801 return r;
802
803 // Check if this is a match
804 const char* cc = loc_country_get_code(*country);
805 int result = strcmp(code, cc);
806
807 if (result == 0) {
808 clock_t end = clock();
809
810 // Log how fast this has been
811 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
812 (double)(end - start) / CLOCKS_PER_SEC * 1000);
813
814 return 0;
815 }
816
817 // If it wasn't, we release the country and
818 // adjust our search pointers
819 loc_country_unref(*country);
820
191830da 821 if (result > 0) {
ec684c1a
MT
822 lo = i + 1;
823 } else
824 hi = i - 1;
825 }
826
827 // Nothing found
828 *country = NULL;
829
830 return 1;
831}
832
7e13db74
MT
833// Enumerator
834
ccc7ab4e
MT
835LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
836 struct loc_database* db, enum loc_database_enumerator_mode mode) {
7e13db74
MT
837 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
838 if (!e)
839 return -ENOMEM;
840
841 // Reference context
842 e->ctx = loc_ref(db->ctx);
843 e->db = loc_database_ref(db);
ccc7ab4e 844 e->mode = mode;
7e13db74
MT
845 e->refcount = 1;
846
e3f696c1
MT
847 // Initialise graph search
848 //e->network_stack[++e->network_stack_depth] = 0;
849 e->network_stack_depth = 1;
850 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
851
7e13db74
MT
852 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
853
854 *enumerator = e;
855 return 0;
856}
857
858LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
859 enumerator->refcount++;
860
861 return enumerator;
862}
863
864static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
865 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
866
867 // Release all references
868 loc_database_unref(enumerator->db);
869 loc_unref(enumerator->ctx);
870
d3d8ede6
MT
871 if (enumerator->string)
872 free(enumerator->string);
873
91d89020
MT
874 // Free network search
875 free(enumerator->networks_visited);
876
7e13db74
MT
877 free(enumerator);
878}
879
880LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
881 if (!enumerator)
882 return NULL;
883
884 if (--enumerator->refcount > 0)
885 return enumerator;
886
887 loc_database_enumerator_free(enumerator);
888 return NULL;
889}
d3d8ede6
MT
890
891LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
892 enumerator->string = strdup(string);
893
894 // Make the string lowercase
895 for (char *p = enumerator->string; *p; p++)
896 *p = tolower(*p);
897
898 return 0;
899}
900
35bb3a32
MT
901LOC_EXPORT int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code) {
902 // Set empty country code
903 if (!country_code || !*country_code) {
904 *enumerator->country_code = '\0';
905 return 0;
906 }
907
4ef1761f
MT
908 // Treat A1, A2, A3 as special country codes,
909 // but perform search for flags instead
910 if (strcmp(country_code, "A1") == 0) {
911 return loc_database_enumerator_set_flag(enumerator,
912 LOC_NETWORK_FLAG_ANONYMOUS_PROXY);
913 } else if (strcmp(country_code, "A2") == 0) {
914 return loc_database_enumerator_set_flag(enumerator,
915 LOC_NETWORK_FLAG_SATELLITE_PROVIDER);
916 } else if (strcmp(country_code, "A3") == 0) {
917 return loc_database_enumerator_set_flag(enumerator,
918 LOC_NETWORK_FLAG_ANYCAST);
919 }
920
57146963
MT
921 // Country codes must be two characters
922 if (!loc_country_code_is_valid(country_code))
923 return -EINVAL;
924
35bb3a32
MT
925 for (unsigned int i = 0; i < 3; i++) {
926 enumerator->country_code[i] = country_code[i];
927 }
928
929 return 0;
930}
931
82910b95
MT
932LOC_EXPORT int loc_database_enumerator_set_asn(
933 struct loc_database_enumerator* enumerator, unsigned int asn) {
934 enumerator->asn = asn;
935
936 return 0;
937}
938
9268db5a
MT
939LOC_EXPORT int loc_database_enumerator_set_flag(
940 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
941 enumerator->flags |= flag;
942
943 return 0;
944}
945
15f79e2d
MT
946LOC_EXPORT int loc_database_enumerator_next_as(
947 struct loc_database_enumerator* enumerator, struct loc_as** as) {
948 *as = NULL;
949
ccc7ab4e
MT
950 // Do not do anything if not in AS mode
951 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
15f79e2d 952 return 0;
ccc7ab4e 953
d3d8ede6 954 struct loc_database* db = enumerator->db;
d3d8ede6
MT
955
956 while (enumerator->as_index < db->as_count) {
957 // Fetch the next AS
15f79e2d 958 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
d3d8ede6 959 if (r)
15f79e2d 960 return r;
d3d8ede6 961
15f79e2d 962 r = loc_as_match_string(*as, enumerator->string);
273948cf 963 if (r == 1) {
d3d8ede6 964 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
15f79e2d 965 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
d3d8ede6 966
15f79e2d 967 return 0;
d3d8ede6
MT
968 }
969
970 // No match
15f79e2d 971 loc_as_unref(*as);
74f218f0 972 *as = NULL;
d3d8ede6
MT
973 }
974
975 // Reset the index
976 enumerator->as_index = 0;
977
978 // We have searched through all of them
15f79e2d 979 return 0;
d3d8ede6 980}
e3f696c1
MT
981
982static int loc_database_enumerator_stack_push_node(
983 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
984 // Do not add empty nodes
985 if (!offset)
986 return 0;
987
988 // Check if there is any space left on the stack
989 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
990 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
991 return -1;
992 }
993
994 // Increase stack size
995 int s = ++e->network_stack_depth;
996
2e2325a9 997 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
e3f696c1
MT
998
999 e->network_stack[s].offset = offset;
1000 e->network_stack[s].i = i;
1001 e->network_stack[s].depth = depth;
1002
1003 return 0;
1004}
1005
15f79e2d
MT
1006LOC_EXPORT int loc_database_enumerator_next_network(
1007 struct loc_database_enumerator* enumerator, struct loc_network** network) {
e3f696c1
MT
1008 // Reset network
1009 *network = NULL;
15f79e2d
MT
1010
1011 // Do not do anything if not in network mode
1012 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
1013 return 0;
1014
e3f696c1
MT
1015 int r;
1016
15f79e2d
MT
1017 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1018 enumerator->network_stack_depth);
e3f696c1
MT
1019
1020 // Perform DFS
15f79e2d
MT
1021 while (enumerator->network_stack_depth > 0) {
1022 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
e3f696c1
MT
1023
1024 // Get object from top of the stack
15f79e2d 1025 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
e3f696c1
MT
1026
1027 // Remove the node from the stack if we have already visited it
15f79e2d
MT
1028 if (enumerator->networks_visited[node->offset]) {
1029 enumerator->network_stack_depth--;
e3f696c1
MT
1030 continue;
1031 }
1032
74fb733a 1033 // Mark the bits on the path correctly
15f79e2d 1034 in6_addr_set_bit(&enumerator->network_address,
e3f696c1
MT
1035 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1036
2e2325a9 1037 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
15f79e2d 1038 enumerator->networks_visited[node->offset]++;
e3f696c1
MT
1039
1040 // Pop node from top of the stack
1041 struct loc_database_network_node_v0* n =
15f79e2d 1042 enumerator->db->network_nodes_v0 + node->offset;
e3f696c1
MT
1043
1044 // Add edges to stack
15f79e2d 1045 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1046 be32toh(n->one), 1, node->depth + 1);
1047
1048 if (r)
1049 return r;
1050
15f79e2d 1051 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1052 be32toh(n->zero), 0, node->depth + 1);
1053
1054 if (r)
1055 return r;
1056
1057 // Check if this node is a leaf and has a network object
1058 if (__loc_database_node_is_leaf(n)) {
1059 off_t network_index = be32toh(n->network);
1060
2e2325a9 1061 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
e3f696c1
MT
1062
1063 // Fetch the network object
15f79e2d
MT
1064 r = loc_database_fetch_network(enumerator->db, network,
1065 &enumerator->network_address, node->depth, network_index);
e3f696c1
MT
1066
1067 // Break on any errors
1068 if (r)
1069 return r;
1070
1071 // Check if we are interested in this network
1072
1073 // Skip if the country code does not match
435b621e 1074 if (*enumerator->country_code &&
15f79e2d 1075 !loc_network_match_country_code(*network, enumerator->country_code)) {
e3f696c1 1076 loc_network_unref(*network);
15f79e2d
MT
1077 *network = NULL;
1078
e3f696c1
MT
1079 continue;
1080 }
1081
82910b95 1082 // Skip if the ASN does not match
15f79e2d
MT
1083 if (enumerator->asn &&
1084 !loc_network_match_asn(*network, enumerator->asn)) {
82910b95 1085 loc_network_unref(*network);
15f79e2d
MT
1086 *network = NULL;
1087
82910b95
MT
1088 continue;
1089 }
1090
9268db5a
MT
1091 // Skip if flags do not match
1092 if (enumerator->flags &&
1093 !loc_network_match_flag(*network, enumerator->flags)) {
1094 loc_network_unref(*network);
1095 *network = NULL;
1096 }
1097
e3f696c1
MT
1098 return 0;
1099 }
1100 }
1101
1102 // Reached the end of the search
fe483cdc
MT
1103
1104 // Mark all nodes as non-visited
15f79e2d
MT
1105 for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
1106 enumerator->networks_visited[i] = 0;
e3f696c1
MT
1107
1108 return 0;
1109}