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