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