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