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