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