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