]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
database: Improve error reporting when the magic cannot be read
[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
c12a1385 39#include <libloc/libloc.h>
0b1fef38 40#include <libloc/address.h>
c12a1385
MT
41#include <libloc/as.h>
42#include <libloc/as-list.h>
43#include <libloc/compat.h>
44#include <libloc/country.h>
45#include <libloc/country-list.h>
46#include <libloc/database.h>
47#include <libloc/format.h>
48#include <libloc/network.h>
49#include <libloc/network-list.h>
50#include <libloc/private.h>
51#include <libloc/stringpool.h>
2601e83e
MT
52
53struct loc_database {
54 struct loc_ctx* ctx;
55 int refcount;
56
b1720435
MT
57 FILE* f;
58
22c7b98b 59 enum loc_database_version version;
96ea74a5 60 time_t created_at;
2601e83e
MT
61 off_t vendor;
62 off_t description;
4bf49d00 63 off_t license;
2601e83e 64
5ce881d4
MT
65 // Signatures
66 char* signature1;
67 size_t signature1_length;
68 char* signature2;
69 size_t signature2_length;
726f9984 70
a5db3e49 71 // ASes in the database
b904896a 72 struct loc_database_as_v1* as_v1;
a5db3e49
MT
73 size_t as_count;
74
f66b7b09 75 // Network tree
b904896a 76 struct loc_database_network_node_v1* network_nodes_v1;
f66b7b09
MT
77 size_t network_nodes_count;
78
a735a563 79 // Networks
b904896a 80 struct loc_database_network_v1* networks_v1;
a735a563
MT
81 size_t networks_count;
82
ec684c1a 83 // Countries
b904896a 84 struct loc_database_country_v1* countries_v1;
ec684c1a
MT
85 size_t countries_count;
86
2601e83e
MT
87 struct loc_stringpool* pool;
88};
89
e3f696c1
MT
90#define MAX_STACK_DEPTH 256
91
92struct loc_node_stack {
93 off_t offset;
94 int i; // Is this node 0 or 1?
95 int depth;
96};
97
7e13db74
MT
98struct loc_database_enumerator {
99 struct loc_ctx* ctx;
100 struct loc_database* db;
ccc7ab4e 101 enum loc_database_enumerator_mode mode;
7e13db74 102 int refcount;
d3d8ede6
MT
103
104 // Search string
105 char* string;
e646a8f3 106 struct loc_country_list* countries;
84a2f0c2 107 struct loc_as_list* asns;
9268db5a 108 enum loc_network_flags flags;
44e5ef71 109 int family;
d3d8ede6 110
681ff05c
MT
111 // Flatten output?
112 int flatten;
113
d3d8ede6
MT
114 // Index of the AS we are looking at
115 unsigned int as_index;
e3f696c1 116
fa9a3663
MT
117 // Index of the country we are looking at
118 unsigned int country_index;
119
e3f696c1
MT
120 // Network state
121 struct in6_addr network_address;
122 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
123 int network_stack_depth;
124 unsigned int* networks_visited;
d87fd7a3 125
8c37d8a7 126 // For subnet search and bogons
d87fd7a3 127 struct loc_network_list* stack;
065b3496 128 struct loc_network_list* subnets;
9306c68d
MT
129
130 // For bogons
131 struct in6_addr gap6_start;
132 struct in6_addr gap4_start;
7e13db74
MT
133};
134
b1720435 135static int loc_database_read_magic(struct loc_database* db) {
2601e83e
MT
136 struct loc_database_magic magic;
137
138 // Read from file
b1720435 139 size_t bytes_read = fread(&magic, 1, sizeof(magic), db->f);
2601e83e
MT
140
141 // Check if we have been able to read enough data
142 if (bytes_read < sizeof(magic)) {
143 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
144 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
808d6ac8 145 goto ERROR;
2601e83e
MT
146 }
147
148 // Compare magic bytes
149 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
150 DEBUG(db->ctx, "Magic value matches\n");
151
152 // Parse version
22c7b98b 153 db->version = magic.version;
2601e83e
MT
154
155 return 0;
156 }
157
808d6ac8 158ERROR:
22c7b98b 159 ERROR(db->ctx, "Unrecognized file type\n");
808d6ac8 160 errno = ENOMSG;
2601e83e
MT
161
162 // Return an error
163 return 1;
164}
165
b904896a
MT
166static int loc_database_read_as_section_v1(struct loc_database* db,
167 const struct loc_database_header_v1* header) {
edb4ba7c
MT
168 off_t as_offset = be32toh(header->as_offset);
169 size_t as_length = be32toh(header->as_length);
170
5c57de03 171 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", (intmax_t)as_offset, as_length);
a5db3e49 172
c182393f 173 if (as_length > 0) {
b904896a 174 db->as_v1 = mmap(NULL, as_length, PROT_READ,
bee8c9b3 175 MAP_PRIVATE, fileno(db->f), as_offset);
a5db3e49 176
47dfa832
MT
177 if (db->as_v1 == MAP_FAILED) {
178 ERROR(db->ctx, "Could not mmap() AS section: %m\n");
198e382c 179 return 1;
47dfa832 180 }
a5db3e49
MT
181 }
182
b904896a 183 db->as_count = as_length / sizeof(*db->as_v1);
c182393f 184
a5db3e49
MT
185 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
186
187 return 0;
188}
189
b904896a
MT
190static int loc_database_read_network_nodes_section_v1(struct loc_database* db,
191 const struct loc_database_header_v1* header) {
edb4ba7c
MT
192 off_t network_nodes_offset = be32toh(header->network_tree_offset);
193 size_t network_nodes_length = be32toh(header->network_tree_length);
194
f66b7b09 195 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
5c57de03 196 (intmax_t)network_nodes_offset, network_nodes_length);
f66b7b09
MT
197
198 if (network_nodes_length > 0) {
b904896a 199 db->network_nodes_v1 = mmap(NULL, network_nodes_length, PROT_READ,
bee8c9b3 200 MAP_PRIVATE, fileno(db->f), network_nodes_offset);
f66b7b09 201
47dfa832
MT
202 if (db->network_nodes_v1 == MAP_FAILED) {
203 ERROR(db->ctx, "Could not mmap() network nodes section: %m\n");
198e382c 204 return 1;
47dfa832 205 }
f66b7b09
MT
206 }
207
b904896a 208 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v1);
f66b7b09
MT
209
210 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
211
212 return 0;
213}
214
b904896a
MT
215static int loc_database_read_networks_section_v1(struct loc_database* db,
216 const struct loc_database_header_v1* header) {
a735a563
MT
217 off_t networks_offset = be32toh(header->network_data_offset);
218 size_t networks_length = be32toh(header->network_data_length);
219
220 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
5c57de03 221 (intmax_t)networks_offset, networks_length);
a735a563
MT
222
223 if (networks_length > 0) {
b904896a 224 db->networks_v1 = mmap(NULL, networks_length, PROT_READ,
bee8c9b3 225 MAP_PRIVATE, fileno(db->f), networks_offset);
a735a563 226
47dfa832
MT
227 if (db->networks_v1 == MAP_FAILED) {
228 ERROR(db->ctx, "Could not mmap() networks section: %m\n");
198e382c 229 return 1;
47dfa832 230 }
a735a563
MT
231 }
232
b904896a 233 db->networks_count = networks_length / sizeof(*db->networks_v1);
a735a563
MT
234
235 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
236
237 return 0;
238}
239
b904896a
MT
240static int loc_database_read_countries_section_v1(struct loc_database* db,
241 const struct loc_database_header_v1* header) {
ec684c1a
MT
242 off_t countries_offset = be32toh(header->countries_offset);
243 size_t countries_length = be32toh(header->countries_length);
244
245 DEBUG(db->ctx, "Reading countries section from %jd (%zu bytes)\n",
2e2325a9 246 (intmax_t)countries_offset, countries_length);
ec684c1a
MT
247
248 if (countries_length > 0) {
b904896a 249 db->countries_v1 = mmap(NULL, countries_length, PROT_READ,
bee8c9b3 250 MAP_PRIVATE, fileno(db->f), countries_offset);
ec684c1a 251
47dfa832
MT
252 if (db->countries_v1 == MAP_FAILED) {
253 ERROR(db->ctx, "Could not mmap() countries section: %m\n");
198e382c 254 return 1;
47dfa832 255 }
ec684c1a
MT
256 }
257
b904896a 258 db->countries_count = countries_length / sizeof(*db->countries_v1);
ec684c1a
MT
259
260 INFO(db->ctx, "Read %zu countries from the database\n",
261 db->countries_count);
262
263 return 0;
264}
265
5ce881d4
MT
266static int loc_database_read_signature(struct loc_database* db,
267 char** dst, char* src, size_t length) {
268 // Check for a plausible signature length
269 if (length > LOC_SIGNATURE_MAX_LENGTH) {
36b9fd75 270 ERROR(db->ctx, "Signature too long: %zu\n", length);
5ce881d4
MT
271 return -EINVAL;
272 }
273
36b9fd75 274 DEBUG(db->ctx, "Reading signature of %zu bytes\n", length);
5ce881d4
MT
275
276 // Allocate space
277 *dst = malloc(length);
278 if (!*dst)
279 return -ENOMEM;
280
281 // Copy payload
282 memcpy(*dst, src, length);
283
284 return 0;
285}
286
b904896a
MT
287static int loc_database_read_header_v1(struct loc_database* db) {
288 struct loc_database_header_v1 header;
5ce881d4 289 int r;
2601e83e
MT
290
291 // Read from file
b1720435 292 size_t size = fread(&header, 1, sizeof(header), db->f);
2601e83e
MT
293
294 if (size < sizeof(header)) {
295 ERROR(db->ctx, "Could not read enough data for header\n");
296 return -ENOMSG;
297 }
298
299 // Copy over data
96ea74a5 300 db->created_at = be64toh(header.created_at);
0676cd80
MT
301 db->vendor = be32toh(header.vendor);
302 db->description = be32toh(header.description);
4bf49d00 303 db->license = be32toh(header.license);
2601e83e 304
c7db968a
MT
305 db->signature1_length = be16toh(header.signature1_length);
306 db->signature2_length = be16toh(header.signature2_length);
726f9984 307
5ce881d4
MT
308 // Read signatures
309 if (db->signature1_length) {
310 r = loc_database_read_signature(db, &db->signature1,
311 header.signature1, db->signature1_length);
312 if (r)
313 return r;
314 }
726f9984 315
5ce881d4
MT
316 if (db->signature2_length) {
317 r = loc_database_read_signature(db, &db->signature2,
318 header.signature2, db->signature2_length);
319 if (r)
320 return r;
726f9984
MT
321 }
322
2601e83e 323 // Open pool
0676cd80
MT
324 off_t pool_offset = be32toh(header.pool_offset);
325 size_t pool_length = be32toh(header.pool_length);
2601e83e 326
5ce881d4 327 r = loc_stringpool_open(db->ctx, &db->pool,
b1720435 328 db->f, pool_length, pool_offset);
2601e83e
MT
329 if (r)
330 return r;
331
a5db3e49 332 // AS section
b904896a 333 r = loc_database_read_as_section_v1(db, &header);
a5db3e49
MT
334 if (r)
335 return r;
336
f66b7b09 337 // Network Nodes
b904896a 338 r = loc_database_read_network_nodes_section_v1(db, &header);
f66b7b09
MT
339 if (r)
340 return r;
341
a735a563 342 // Networks
b904896a 343 r = loc_database_read_networks_section_v1(db, &header);
a735a563
MT
344 if (r)
345 return r;
346
ec684c1a 347 // countries
b904896a 348 r = loc_database_read_countries_section_v1(db, &header);
ec684c1a
MT
349 if (r)
350 return r;
351
2601e83e
MT
352 return 0;
353}
354
b1720435 355static int loc_database_read_header(struct loc_database* db) {
22c7b98b
MT
356 DEBUG(db->ctx, "Database version is %u\n", db->version);
357
2601e83e 358 switch (db->version) {
22c7b98b 359 case LOC_DATABASE_VERSION_1:
b904896a 360 return loc_database_read_header_v1(db);
2601e83e
MT
361
362 default:
363 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
364 return 1;
365 }
366}
367
a7431f1a 368static int loc_database_read(struct loc_database* db, FILE* f) {
02879100
MT
369 clock_t start = clock();
370
b1720435
MT
371 int fd = fileno(f);
372
373 // Clone file descriptor
374 fd = dup(fd);
375 if (!fd) {
376 ERROR(db->ctx, "Could not duplicate file descriptor\n");
377 return -1;
378 }
379
380 // Reopen the file so that we can keep our own file handle
381 db->f = fdopen(fd, "r");
382 if (!db->f) {
383 ERROR(db->ctx, "Could not re-open database file\n");
384 return -1;
385 }
386
387 // Rewind to the start of the file
388 rewind(db->f);
389
02879100 390 // Read magic bytes
b1720435 391 int r = loc_database_read_magic(db);
02879100
MT
392 if (r)
393 return r;
394
395 // Read the header
b1720435 396 r = loc_database_read_header(db);
02879100
MT
397 if (r)
398 return r;
399
400 clock_t end = clock();
401
e16c943b
MT
402 INFO(db->ctx, "Opened database in %.4fms\n",
403 (double)(end - start) / CLOCKS_PER_SEC * 1000);
02879100
MT
404
405 return 0;
406}
407
c182393f 408LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
a7431f1a
MT
409 // Fail on invalid file handle
410 if (!f)
411 return -EINVAL;
412
c182393f
MT
413 struct loc_database* db = calloc(1, sizeof(*db));
414 if (!db)
415 return -ENOMEM;
416
417 // Reference context
418 db->ctx = loc_ref(ctx);
419 db->refcount = 1;
420
421 DEBUG(db->ctx, "Database object allocated at %p\n", db);
422
a7431f1a 423 int r = loc_database_read(db, f);
02879100
MT
424 if (r) {
425 loc_database_unref(db);
2601e83e 426 return r;
02879100 427 }
2601e83e 428
c182393f
MT
429 *database = db;
430
2601e83e 431 return 0;
2601e83e
MT
432}
433
c182393f
MT
434LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
435 db->refcount++;
436
437 return db;
8f5b676a
MT
438}
439
c182393f 440static void loc_database_free(struct loc_database* db) {
f10ebc2d
MT
441 int r;
442
c182393f 443 DEBUG(db->ctx, "Releasing database %p\n", db);
c34e76f1 444
c182393f 445 // Removing all ASes
06b88d93 446 if (db->as_v1 && db->as_v1 != MAP_FAILED) {
b904896a 447 r = munmap(db->as_v1, db->as_count * sizeof(*db->as_v1));
c182393f 448 if (r)
6cd02f1d 449 ERROR(db->ctx, "Could not unmap AS section: %m\n");
c182393f 450 }
c34e76f1 451
f10ebc2d 452 // Remove mapped network sections
06b88d93 453 if (db->networks_v1 && db->networks_v1 != MAP_FAILED) {
b904896a 454 r = munmap(db->networks_v1, db->networks_count * sizeof(*db->networks_v1));
f10ebc2d 455 if (r)
6cd02f1d 456 ERROR(db->ctx, "Could not unmap networks section: %m\n");
f10ebc2d
MT
457 }
458
459 // Remove mapped network nodes section
06b88d93 460 if (db->network_nodes_v1 && db->network_nodes_v1 != MAP_FAILED) {
b904896a 461 r = munmap(db->network_nodes_v1, db->network_nodes_count * sizeof(*db->network_nodes_v1));
f10ebc2d 462 if (r)
6cd02f1d 463 ERROR(db->ctx, "Could not unmap network nodes section: %m\n");
f10ebc2d
MT
464 }
465
55664c7e 466 // Remove mapped countries section
06b88d93 467 if (db->countries_v1 && db->countries_v1 != MAP_FAILED) {
55664c7e
MT
468 r = munmap(db->countries_v1, db->countries_count * sizeof(*db->countries_v1));
469 if (r)
6cd02f1d 470 ERROR(db->ctx, "Could not unmap countries section: %m\n");
55664c7e
MT
471 }
472
414d8958
MT
473 if (db->pool)
474 loc_stringpool_unref(db->pool);
c34e76f1 475
726f9984 476 // Free signature
5ce881d4
MT
477 if (db->signature1)
478 free(db->signature1);
479 if (db->signature2)
480 free(db->signature2);
726f9984 481
b1720435
MT
482 // Close database file
483 if (db->f)
484 fclose(db->f);
485
c182393f
MT
486 loc_unref(db->ctx);
487 free(db);
c34e76f1
MT
488}
489
c182393f
MT
490LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
491 if (--db->refcount > 0)
492 return NULL;
78ace4ed 493
c182393f
MT
494 loc_database_free(db);
495 return NULL;
496}
78ace4ed 497
726f9984 498LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
98b2876e
MT
499 size_t bytes_read = 0;
500
726f9984 501 // Cannot do this when no signature is available
5ce881d4 502 if (!db->signature1 && !db->signature2) {
726f9984
MT
503 DEBUG(db->ctx, "No signature available to verify\n");
504 return 1;
505 }
506
c81205a5
MT
507 // Start the stopwatch
508 clock_t start = clock();
509
726f9984
MT
510 // Load public key
511 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
512 if (!pkey) {
6cd02f1d
MT
513 ERROR(db->ctx, "Could not parse public key: %s\n",
514 ERR_error_string(ERR_get_error(), NULL));
726f9984
MT
515
516 return -1;
517 }
518
b1720435
MT
519 int r = 0;
520
521 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
522
b1720435 523 // Initialise hash function
e7f4b2ce
MT
524 r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
525 if (r != 1) {
526 ERROR(db->ctx, "Error initializing signature validation: %s\n",
527 ERR_error_string(ERR_get_error(), NULL));
528 r = 1;
529
530 goto CLEANUP;
531 }
b1720435
MT
532
533 // Reset file to start
534 rewind(db->f);
535
536 // Read magic
537 struct loc_database_magic magic;
98b2876e
MT
538 bytes_read = fread(&magic, 1, sizeof(magic), db->f);
539 if (bytes_read < sizeof(magic)) {
540 ERROR(db->ctx, "Could not read header: %m\n");
541 r = 1;
542 goto CLEANUP;
543 }
b1720435 544
a0cff45d
MT
545 hexdump(db->ctx, &magic, sizeof(magic));
546
b1720435 547 // Feed magic into the hash
e7f4b2ce
MT
548 r = EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
549 if (r != 1) {
550 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
551 r = 1;
552
553 goto CLEANUP;
554 }
b1720435
MT
555
556 // Read the header
b904896a 557 struct loc_database_header_v1 header_v1;
b1720435
MT
558
559 switch (db->version) {
22c7b98b 560 case LOC_DATABASE_VERSION_1:
54f0649f
MT
561 bytes_read = fread(&header_v1, 1, sizeof(header_v1), db->f);
562 if (bytes_read < sizeof(header_v1)) {
563 ERROR(db->ctx, "Could not read header\n");
564 r = 1;
565
566 goto CLEANUP;
567 }
b1720435 568
5ce881d4
MT
569 // Clear signatures
570 memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
571 header_v1.signature1_length = 0;
572 memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
573 header_v1.signature2_length = 0;
b1720435 574
b904896a 575 hexdump(db->ctx, &header_v1, sizeof(header_v1));
a0cff45d 576
b1720435 577 // Feed header into the hash
b904896a 578 r = EVP_DigestVerifyUpdate(mdctx, &header_v1, sizeof(header_v1));
e7f4b2ce
MT
579 if (r != 1) {
580 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
581 r = 1;
582
583 goto CLEANUP;
584 }
b1720435
MT
585 break;
586
587 default:
588 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
589 db->version);
590 r = -EINVAL;
591 goto CLEANUP;
592 }
593
726f9984
MT
594 // Walk through the file in chunks of 64kB
595 char buffer[64 * 1024];
b1720435
MT
596
597 while (!feof(db->f)) {
54f0649f 598 bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
b1720435 599
a0cff45d
MT
600 hexdump(db->ctx, buffer, bytes_read);
601
e7f4b2ce
MT
602 r = EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
603 if (r != 1) {
604 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
605 r = 1;
606
607 goto CLEANUP;
608 }
b1720435
MT
609 }
610
5ce881d4
MT
611 // Check first signature
612 if (db->signature1) {
613 hexdump(db->ctx, db->signature1, db->signature1_length);
726f9984 614
5ce881d4
MT
615 r = EVP_DigestVerifyFinal(mdctx,
616 (unsigned char*)db->signature1, db->signature1_length);
617
618 if (r == 0) {
619 DEBUG(db->ctx, "The first signature is invalid\n");
620 r = 1;
621 } else if (r == 1) {
622 DEBUG(db->ctx, "The first signature is valid\n");
623 r = 0;
624 } else {
625 ERROR(db->ctx, "Error verifying the first signature: %s\n",
626 ERR_error_string(ERR_get_error(), NULL));
627 r = -1;
628 }
b1720435
MT
629 }
630
5ce881d4
MT
631 // Check second signature only when the first one was invalid
632 if (r && db->signature2) {
633 hexdump(db->ctx, db->signature2, db->signature2_length);
634
635 r = EVP_DigestVerifyFinal(mdctx,
636 (unsigned char*)db->signature2, db->signature2_length);
637
638 if (r == 0) {
639 DEBUG(db->ctx, "The second signature is invalid\n");
640 r = 1;
641 } else if (r == 1) {
642 DEBUG(db->ctx, "The second signature is valid\n");
643 r = 0;
644 } else {
645 ERROR(db->ctx, "Error verifying the second signature: %s\n",
646 ERR_error_string(ERR_get_error(), NULL));
647 r = -1;
648 }
649 }
257626f5 650
c81205a5 651 clock_t end = clock();
6661692f 652 INFO(db->ctx, "Signature checked in %.4fms\n",
c81205a5
MT
653 (double)(end - start) / CLOCKS_PER_SEC * 1000);
654
b1720435
MT
655CLEANUP:
656 // Cleanup
657 EVP_MD_CTX_free(mdctx);
726f9984 658 EVP_PKEY_free(pkey);
b1720435
MT
659
660 return r;
661}
662
c182393f
MT
663LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
664 return db->created_at;
665}
78ace4ed 666
c182393f
MT
667LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
668 return loc_stringpool_get(db->pool, db->vendor);
669}
78ace4ed 670
c182393f
MT
671LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
672 return loc_stringpool_get(db->pool, db->description);
673}
78ace4ed 674
4bf49d00
MT
675LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
676 return loc_stringpool_get(db->pool, db->license);
677}
678
c182393f
MT
679LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
680 return db->as_count;
78ace4ed
MT
681}
682
c182393f
MT
683// Returns the AS at position pos
684static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
685 if ((size_t)pos >= db->as_count)
686 return -EINVAL;
2601e83e 687
5c57de03 688 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
2601e83e
MT
689
690 int r;
c182393f 691 switch (db->version) {
22c7b98b 692 case LOC_DATABASE_VERSION_1:
b904896a 693 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, db->as_v1 + pos);
c182393f 694 break;
2601e83e 695
c182393f
MT
696 default:
697 return -1;
698 }
2601e83e 699
c182393f
MT
700 if (r == 0) {
701 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 702 }
2601e83e 703
c182393f
MT
704 return r;
705}
c34e76f1 706
c182393f
MT
707// Performs a binary search to find the AS in the list
708LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
709 off_t lo = 0;
710 off_t hi = db->as_count - 1;
c34e76f1 711
6661692f 712#ifdef ENABLE_DEBUG
8f3e2a06
MT
713 // Save start time
714 clock_t start = clock();
6661692f 715#endif
8f3e2a06 716
c182393f
MT
717 while (lo <= hi) {
718 off_t i = (lo + hi) / 2;
8f5b676a 719
c182393f
MT
720 // Fetch AS in the middle between lo and hi
721 int r = loc_database_fetch_as(db, as, i);
722 if (r)
723 return r;
a5db3e49 724
c182393f
MT
725 // Check if this is a match
726 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06 727 if (as_number == number) {
6661692f 728#ifdef ENABLE_DEBUG
8f3e2a06
MT
729 clock_t end = clock();
730
731 // Log how fast this has been
e16c943b
MT
732 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
733 (double)(end - start) / CLOCKS_PER_SEC * 1000);
6661692f 734#endif
8f3e2a06 735
c182393f 736 return 0;
8f3e2a06 737 }
c182393f
MT
738
739 // If it wasn't, we release the AS and
740 // adjust our search pointers
741 loc_as_unref(*as);
742
743 if (as_number < number) {
744 lo = i + 1;
745 } else
746 hi = i - 1;
747 }
2601e83e 748
c182393f
MT
749 // Nothing found
750 *as = NULL;
2601e83e 751
8f3e2a06 752 return 1;
2601e83e 753}
10778041
MT
754
755// Returns the network at position pos
39a55353
MT
756static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
757 struct in6_addr* address, unsigned int prefix, off_t pos) {
9b9e5faf
MT
758 if ((size_t)pos >= db->networks_count) {
759 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
760 (intmax_t)pos, (intmax_t)db->networks_count);
10778041 761 return -EINVAL;
9b9e5faf
MT
762 }
763
10778041 764
5c57de03 765 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
10778041
MT
766
767 int r;
768 switch (db->version) {
22c7b98b 769 case LOC_DATABASE_VERSION_1:
b904896a
MT
770 r = loc_network_new_from_database_v1(db->ctx, network,
771 address, prefix, db->networks_v1 + pos);
10778041
MT
772 break;
773
774 default:
775 return -1;
776 }
777
0a0a289a
MT
778 if (r == 0)
779 DEBUG(db->ctx, "Got network %s\n", loc_network_str(*network));
10778041
MT
780
781 return r;
782}
2a30e4de 783
b904896a 784static int __loc_database_node_is_leaf(const struct loc_database_network_node_v1* node) {
39a55353 785 return (node->network != htobe32(0xffffffff));
025ef489
MT
786}
787
788static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
39a55353 789 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
b904896a 790 const struct loc_database_network_node_v1* node) {
39a55353
MT
791 off_t network_index = be32toh(node->network);
792
b904896a 793 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", (intmax_t)(node - db->network_nodes_v1), (intmax_t)network_index);
2a30e4de
MT
794
795 // Fetch the network
796 int r = loc_database_fetch_network(db, network,
39a55353 797 network_address, prefix, network_index);
e85e2b0b 798 if (r) {
5c57de03 799 ERROR(db->ctx, "Could not fetch network %jd from database\n", (intmax_t)network_index);
2a30e4de 800 return r;
e85e2b0b 801 }
39a55353 802
2a30e4de 803 // Check if the given IP address is inside the network
0258d3c9 804 if (!loc_network_matches_address(*network, address)) {
2a30e4de
MT
805 DEBUG(db->ctx, "Searched address is not part of the network\n");
806
807 loc_network_unref(*network);
808 *network = NULL;
809 return 1;
810 }
811
812 // A network was found and the IP address matches
813 return 0;
814}
815
2a30e4de
MT
816// Searches for an exact match along the path
817static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
818 struct loc_network** network, struct in6_addr* network_address,
b904896a 819 const struct loc_database_network_node_v1* node, unsigned int level) {
025ef489 820 int r;
2a30e4de
MT
821 off_t node_index;
822
823 // Follow the path
d698ca09
MT
824 int bit = loc_address_get_bit(address, level);
825 loc_address_set_bit(network_address, level, bit);
2a30e4de
MT
826
827 if (bit == 0)
828 node_index = be32toh(node->zero);
829 else
830 node_index = be32toh(node->one);
831
9086d2b1
MT
832 // If the node index is zero, the tree ends here
833 // and we cannot descend any further
834 if (node_index > 0) {
835 // Check boundaries
836 if ((size_t)node_index >= db->network_nodes_count)
837 return -EINVAL;
2a30e4de 838
9086d2b1
MT
839 // Move on to the next node
840 r = __loc_database_lookup(db, address, network, network_address,
b904896a 841 db->network_nodes_v1 + node_index, level + 1);
2a30e4de 842
9086d2b1
MT
843 // End here if a result was found
844 if (r == 0)
845 return r;
2a30e4de 846
9086d2b1
MT
847 // Raise any errors
848 else if (r < 0)
849 return r;
ec1d9681
MT
850
851 DEBUG(db->ctx, "No match found below level %u\n", level);
852 } else {
853 DEBUG(db->ctx, "Tree ended at level %u\n", level);
9086d2b1 854 }
2a30e4de 855
9086d2b1
MT
856 // If this node has a leaf, we will check if it matches
857 if (__loc_database_node_is_leaf(node)) {
858 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
859 if (r <= 0)
860 return r;
861 }
2a30e4de 862
ec1d9681 863 return 1;
2a30e4de
MT
864}
865
866LOC_EXPORT int loc_database_lookup(struct loc_database* db,
a178efea 867 const struct in6_addr* address, struct loc_network** network) {
2a30e4de
MT
868 struct in6_addr network_address;
869 memset(&network_address, 0, sizeof(network_address));
870
871 *network = NULL;
872
6661692f 873#ifdef ENABLE_DEBUG
2a30e4de
MT
874 // Save start time
875 clock_t start = clock();
6661692f 876#endif
2a30e4de
MT
877
878 int r = __loc_database_lookup(db, address, network, &network_address,
b904896a 879 db->network_nodes_v1, 0);
2a30e4de 880
6661692f 881#ifdef ENABLE_DEBUG
2a30e4de
MT
882 clock_t end = clock();
883
884 // Log how fast this has been
e16c943b
MT
885 DEBUG(db->ctx, "Executed network search in %.4fms\n",
886 (double)(end - start) / CLOCKS_PER_SEC * 1000);
6661692f 887#endif
2a30e4de
MT
888
889 return r;
890}
891
892LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
893 const char* string, struct loc_network** network) {
894 struct in6_addr address;
895
95b6a8e4 896 int r = loc_address_parse(&address, NULL, string);
2a30e4de
MT
897 if (r)
898 return r;
899
900 return loc_database_lookup(db, &address, network);
901}
7e13db74 902
ec684c1a
MT
903// Returns the country at position pos
904static int loc_database_fetch_country(struct loc_database* db,
905 struct loc_country** country, off_t pos) {
906 if ((size_t)pos >= db->countries_count)
907 return -EINVAL;
908
2e2325a9 909 DEBUG(db->ctx, "Fetching country at position %jd\n", (intmax_t)pos);
ec684c1a
MT
910
911 int r;
912 switch (db->version) {
22c7b98b 913 case LOC_DATABASE_VERSION_1:
b904896a 914 r = loc_country_new_from_database_v1(db->ctx, db->pool, country, db->countries_v1 + pos);
ec684c1a
MT
915 break;
916
917 default:
918 return -1;
919 }
920
921 if (r == 0) {
922 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
923 }
924
925 return r;
926}
927
928// Performs a binary search to find the country in the list
929LOC_EXPORT int loc_database_get_country(struct loc_database* db,
930 struct loc_country** country, const char* code) {
931 off_t lo = 0;
932 off_t hi = db->countries_count - 1;
933
6661692f 934#ifdef ENABLE_DEBUG
ec684c1a
MT
935 // Save start time
936 clock_t start = clock();
6661692f 937#endif
ec684c1a
MT
938
939 while (lo <= hi) {
940 off_t i = (lo + hi) / 2;
941
942 // Fetch country in the middle between lo and hi
943 int r = loc_database_fetch_country(db, country, i);
944 if (r)
945 return r;
946
947 // Check if this is a match
948 const char* cc = loc_country_get_code(*country);
949 int result = strcmp(code, cc);
950
951 if (result == 0) {
6661692f 952#ifdef ENABLE_DEBUG
ec684c1a
MT
953 clock_t end = clock();
954
955 // Log how fast this has been
956 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
957 (double)(end - start) / CLOCKS_PER_SEC * 1000);
6661692f 958#endif
ec684c1a
MT
959
960 return 0;
961 }
962
963 // If it wasn't, we release the country and
964 // adjust our search pointers
965 loc_country_unref(*country);
966
191830da 967 if (result > 0) {
ec684c1a
MT
968 lo = i + 1;
969 } else
970 hi = i - 1;
971 }
972
973 // Nothing found
974 *country = NULL;
975
976 return 1;
977}
978
7e13db74
MT
979// Enumerator
980
d87fd7a3
MT
981static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
982 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
983
984 // Release all references
985 loc_database_unref(enumerator->db);
986 loc_unref(enumerator->ctx);
987
988 if (enumerator->string)
989 free(enumerator->string);
990
5470d06c
MT
991 if (enumerator->countries)
992 loc_country_list_unref(enumerator->countries);
993
994 if (enumerator->asns)
995 loc_as_list_unref(enumerator->asns);
996
d87fd7a3
MT
997 // Free network search
998 free(enumerator->networks_visited);
999
8c37d8a7 1000 // Free subnet/bogons stack
d87fd7a3
MT
1001 if (enumerator->stack)
1002 loc_network_list_unref(enumerator->stack);
1003
065b3496
MT
1004 if (enumerator->subnets)
1005 loc_network_list_unref(enumerator->subnets);
1006
d87fd7a3
MT
1007 free(enumerator);
1008}
1009
ccc7ab4e 1010LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
681ff05c 1011 struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
7e13db74
MT
1012 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
1013 if (!e)
1014 return -ENOMEM;
1015
1016 // Reference context
1017 e->ctx = loc_ref(db->ctx);
1018 e->db = loc_database_ref(db);
ccc7ab4e 1019 e->mode = mode;
7e13db74
MT
1020 e->refcount = 1;
1021
681ff05c
MT
1022 // Flatten output?
1023 e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
1024
e3f696c1 1025 // Initialise graph search
e3f696c1
MT
1026 e->network_stack_depth = 1;
1027 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
1028
d87fd7a3
MT
1029 // Allocate stack
1030 int r = loc_network_list_new(e->ctx, &e->stack);
1031 if (r) {
1032 loc_database_enumerator_free(e);
1033 return r;
1034 }
1035
9306c68d
MT
1036 // Initialize bogon search
1037 loc_address_reset(&e->gap6_start, AF_INET6);
1038 loc_address_reset(&e->gap4_start, AF_INET);
1039
7e13db74
MT
1040 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1041
1042 *enumerator = e;
1043 return 0;
1044}
1045
1046LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1047 enumerator->refcount++;
1048
1049 return enumerator;
1050}
1051
7e13db74
MT
1052LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1053 if (!enumerator)
1054 return NULL;
1055
1056 if (--enumerator->refcount > 0)
1057 return enumerator;
1058
1059 loc_database_enumerator_free(enumerator);
1060 return NULL;
1061}
d3d8ede6
MT
1062
1063LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1064 enumerator->string = strdup(string);
1065
1066 // Make the string lowercase
1067 for (char *p = enumerator->string; *p; p++)
1068 *p = tolower(*p);
1069
1070 return 0;
1071}
1072
e646a8f3
MT
1073LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1074 struct loc_database_enumerator* enumerator) {
1075 if (!enumerator->countries)
1076 return NULL;
35bb3a32 1077
e646a8f3
MT
1078 return loc_country_list_ref(enumerator->countries);
1079}
4ef1761f 1080
e646a8f3
MT
1081LOC_EXPORT int loc_database_enumerator_set_countries(
1082 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1083 if (enumerator->countries)
1084 loc_country_list_unref(enumerator->countries);
57146963 1085
e646a8f3 1086 enumerator->countries = loc_country_list_ref(countries);
35bb3a32
MT
1087
1088 return 0;
1089}
1090
84a2f0c2
MT
1091LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1092 struct loc_database_enumerator* enumerator) {
1093 if (!enumerator->asns)
1094 return NULL;
1095
1096 return loc_as_list_ref(enumerator->asns);
1097}
1098
1099LOC_EXPORT int loc_database_enumerator_set_asns(
1100 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1101 if (enumerator->asns)
1102 loc_as_list_unref(enumerator->asns);
1103
1104 enumerator->asns = loc_as_list_ref(asns);
82910b95
MT
1105
1106 return 0;
1107}
1108
9268db5a
MT
1109LOC_EXPORT int loc_database_enumerator_set_flag(
1110 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1111 enumerator->flags |= flag;
1112
1113 return 0;
1114}
1115
44e5ef71
MT
1116LOC_EXPORT int loc_database_enumerator_set_family(
1117 struct loc_database_enumerator* enumerator, int family) {
1118 enumerator->family = family;
1119
1120 return 0;
1121}
1122
15f79e2d
MT
1123LOC_EXPORT int loc_database_enumerator_next_as(
1124 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1125 *as = NULL;
1126
ccc7ab4e
MT
1127 // Do not do anything if not in AS mode
1128 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
15f79e2d 1129 return 0;
ccc7ab4e 1130
d3d8ede6 1131 struct loc_database* db = enumerator->db;
d3d8ede6
MT
1132
1133 while (enumerator->as_index < db->as_count) {
1134 // Fetch the next AS
15f79e2d 1135 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
d3d8ede6 1136 if (r)
15f79e2d 1137 return r;
d3d8ede6 1138
15f79e2d 1139 r = loc_as_match_string(*as, enumerator->string);
273948cf 1140 if (r == 1) {
d3d8ede6 1141 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
15f79e2d 1142 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
d3d8ede6 1143
15f79e2d 1144 return 0;
d3d8ede6
MT
1145 }
1146
1147 // No match
15f79e2d 1148 loc_as_unref(*as);
74f218f0 1149 *as = NULL;
d3d8ede6
MT
1150 }
1151
1152 // Reset the index
1153 enumerator->as_index = 0;
1154
1155 // We have searched through all of them
15f79e2d 1156 return 0;
d3d8ede6 1157}
e3f696c1
MT
1158
1159static int loc_database_enumerator_stack_push_node(
1160 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1161 // Do not add empty nodes
1162 if (!offset)
1163 return 0;
1164
1165 // Check if there is any space left on the stack
1166 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1167 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1168 return -1;
1169 }
1170
1171 // Increase stack size
1172 int s = ++e->network_stack_depth;
1173
2e2325a9 1174 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
e3f696c1
MT
1175
1176 e->network_stack[s].offset = offset;
1177 e->network_stack[s].i = i;
1178 e->network_stack[s].depth = depth;
1179
1180 return 0;
1181}
1182
964633d9 1183static int loc_database_enumerator_match_network(
69248038 1184 struct loc_database_enumerator* enumerator, struct loc_network* network) {
964633d9 1185 // If family is set, it must match
e0e96878
MT
1186 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1187 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
964633d9 1188 return 0;
e0e96878 1189 }
69248038 1190
098d871b
MT
1191 // Match if no filter criteria is configured
1192 if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
1193 return 1;
1194
964633d9 1195 // Check if the country code matches
e0e96878
MT
1196 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1197 const char* country_code = loc_network_get_country_code(network);
50120b99 1198
964633d9
MT
1199 if (loc_country_list_contains_code(enumerator->countries, country_code)) {
1200 DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
e0e96878 1201 return 1;
50120b99
MT
1202 }
1203 }
69248038 1204
964633d9 1205 // Check if the ASN matches
e0e96878
MT
1206 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1207 uint32_t asn = loc_network_get_asn(network);
c1a36c94 1208
964633d9
MT
1209 if (loc_as_list_contains_number(enumerator->asns, asn)) {
1210 DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
e0e96878 1211 return 1;
c1a36c94
MT
1212 }
1213 }
69248038 1214
964633d9
MT
1215 // Check if flags match
1216 if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
1217 DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
69248038 1218 return 1;
e0e96878 1219 }
69248038 1220
964633d9 1221 // Not a match
69248038
MT
1222 return 0;
1223}
1224
d87fd7a3
MT
1225static int __loc_database_enumerator_next_network(
1226 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1227 // Return top element from the stack
2113e71b 1228 while (1) {
04cbd2bf 1229 *network = loc_network_list_pop_first(enumerator->stack);
2113e71b
MT
1230
1231 // Stack is empty
1232 if (!*network)
1233 break;
1234
964633d9
MT
1235 // Return everything if filter isn't enabled, or only return matches
1236 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
1237 return 0;
2113e71b 1238
964633d9
MT
1239 // Throw away anything that doesn't match
1240 loc_network_unref(*network);
1241 *network = NULL;
2113e71b 1242 }
15f79e2d 1243
15f79e2d
MT
1244 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1245 enumerator->network_stack_depth);
e3f696c1
MT
1246
1247 // Perform DFS
15f79e2d
MT
1248 while (enumerator->network_stack_depth > 0) {
1249 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
e3f696c1
MT
1250
1251 // Get object from top of the stack
15f79e2d 1252 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
e3f696c1
MT
1253
1254 // Remove the node from the stack if we have already visited it
15f79e2d
MT
1255 if (enumerator->networks_visited[node->offset]) {
1256 enumerator->network_stack_depth--;
e3f696c1
MT
1257 continue;
1258 }
1259
74fb733a 1260 // Mark the bits on the path correctly
d698ca09 1261 loc_address_set_bit(&enumerator->network_address,
e3f696c1
MT
1262 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1263
2e2325a9 1264 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
15f79e2d 1265 enumerator->networks_visited[node->offset]++;
e3f696c1
MT
1266
1267 // Pop node from top of the stack
b904896a
MT
1268 struct loc_database_network_node_v1* n =
1269 enumerator->db->network_nodes_v1 + node->offset;
e3f696c1
MT
1270
1271 // Add edges to stack
d87fd7a3 1272 int r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1273 be32toh(n->one), 1, node->depth + 1);
1274
1275 if (r)
1276 return r;
1277
15f79e2d 1278 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1279 be32toh(n->zero), 0, node->depth + 1);
1280
1281 if (r)
1282 return r;
1283
1284 // Check if this node is a leaf and has a network object
1285 if (__loc_database_node_is_leaf(n)) {
1286 off_t network_index = be32toh(n->network);
1287
2e2325a9 1288 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
e3f696c1
MT
1289
1290 // Fetch the network object
15f79e2d
MT
1291 r = loc_database_fetch_network(enumerator->db, network,
1292 &enumerator->network_address, node->depth, network_index);
e3f696c1
MT
1293
1294 // Break on any errors
1295 if (r)
1296 return r;
1297
964633d9
MT
1298 // Return all networks when the filter is disabled, or check for match
1299 if (!filter || loc_database_enumerator_match_network(enumerator, *network))
d87fd7a3
MT
1300 return 0;
1301
964633d9
MT
1302 // Does not seem to be a match, so we cleanup and move on
1303 loc_network_unref(*network);
1304 *network = NULL;
e3f696c1
MT
1305 }
1306 }
1307
1308 // Reached the end of the search
d87fd7a3
MT
1309 return 0;
1310}
fe483cdc 1311
d87fd7a3
MT
1312static int __loc_database_enumerator_next_network_flattened(
1313 struct loc_database_enumerator* enumerator, struct loc_network** network) {
1314 // Fetch the next network
1315 int r = __loc_database_enumerator_next_network(enumerator, network, 1);
1316 if (r)
1317 return r;
e3f696c1 1318
d87fd7a3
MT
1319 // End if we could not read another network
1320 if (!*network)
1321 return 0;
1322
1323 struct loc_network* subnet = NULL;
d87fd7a3
MT
1324
1325 // Create a list with all subnets
065b3496
MT
1326 if (!enumerator->subnets) {
1327 r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
1328 if (r)
1329 return r;
1330 }
d87fd7a3
MT
1331
1332 // Search all subnets from the database
1333 while (1) {
1334 // Fetch the next network in line
1335 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
ed0f53df
MT
1336 if (r) {
1337 loc_network_unref(subnet);
065b3496 1338 loc_network_list_clear(enumerator->subnets);
ed0f53df
MT
1339
1340 return r;
1341 }
d87fd7a3
MT
1342
1343 // End if we did not receive another subnet
1344 if (!subnet)
1345 break;
1346
1347 // Collect all subnets in a list
1348 if (loc_network_is_subnet(*network, subnet)) {
065b3496 1349 r = loc_network_list_push(enumerator->subnets, subnet);
ed0f53df
MT
1350 if (r) {
1351 loc_network_unref(subnet);
065b3496 1352 loc_network_list_clear(enumerator->subnets);
ed0f53df
MT
1353
1354 return r;
1355 }
d87fd7a3
MT
1356
1357 loc_network_unref(subnet);
1358 continue;
1359 }
1360
1361 // If this is not a subnet, we push it back onto the stack and break
1362 r = loc_network_list_push(enumerator->stack, subnet);
ed0f53df
MT
1363 if (r) {
1364 loc_network_unref(subnet);
065b3496 1365 loc_network_list_clear(enumerator->subnets);
ed0f53df
MT
1366
1367 return r;
1368 }
d87fd7a3
MT
1369
1370 loc_network_unref(subnet);
1371 break;
1372 }
1373
065b3496
MT
1374 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
1375 loc_network_list_size(enumerator->subnets));
d87fd7a3
MT
1376
1377 // We can abort here if the network has no subnets
065b3496
MT
1378 if (loc_network_list_empty(enumerator->subnets)) {
1379 loc_network_list_clear(enumerator->subnets);
d87fd7a3
MT
1380
1381 return 0;
1382 }
1383
1384 // If the network has any subnets, we will break it into smaller parts
1385 // without the subnets.
065b3496 1386 struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
ed0f53df 1387 if (!excluded) {
065b3496 1388 loc_network_list_clear(enumerator->subnets);
725718e1 1389 return 1;
ed0f53df
MT
1390 }
1391
5dacb45a 1392 // Merge subnets onto the stack
065b3496 1393 r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
ed0f53df 1394 if (r) {
065b3496 1395 loc_network_list_clear(enumerator->subnets);
ed0f53df
MT
1396 loc_network_list_unref(excluded);
1397
1398 return r;
d87fd7a3
MT
1399 }
1400
5dacb45a
MT
1401 // Push excluded list onto the stack
1402 r = loc_network_list_merge(enumerator->stack, excluded);
1403 if (r) {
065b3496 1404 loc_network_list_clear(enumerator->subnets);
5dacb45a 1405 loc_network_list_unref(excluded);
d87fd7a3 1406
5dacb45a
MT
1407 return r;
1408 }
d87fd7a3 1409
065b3496 1410 loc_network_list_clear(enumerator->subnets);
5dacb45a
MT
1411 loc_network_list_unref(excluded);
1412
6bd9cd76 1413 // Drop the network and restart the whole process again to pick the next network
5dacb45a 1414 loc_network_unref(*network);
d87fd7a3 1415
6bd9cd76 1416 return __loc_database_enumerator_next_network_flattened(enumerator, network);
d87fd7a3
MT
1417}
1418
8c37d8a7
MT
1419/*
1420 This function finds all bogons (i.e. gaps) between the input networks
1421*/
8c37d8a7
MT
1422static int __loc_database_enumerator_next_bogon(
1423 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1424 int r;
1425
1426 // Return top element from the stack
1427 while (1) {
1428 *bogon = loc_network_list_pop_first(enumerator->stack);
1429
1430 // Stack is empty
1431 if (!*bogon)
1432 break;
1433
1434 // Return result
1435 return 0;
1436 }
1437
1438 struct loc_network* network = NULL;
9306c68d
MT
1439 struct in6_addr* gap_start = NULL;
1440 struct in6_addr gap_end = IN6ADDR_ANY_INIT;
8c37d8a7
MT
1441
1442 while (1) {
1443 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1444 if (r)
9306c68d 1445 return r;
8c37d8a7 1446
9306c68d 1447 // We have read the last network
8c37d8a7 1448 if (!network)
9306c68d
MT
1449 goto FINISH;
1450
5a01e11e
MT
1451 const char* country_code = loc_network_get_country_code(network);
1452
1453 /*
1454 Skip anything that does not have a country code
1455
1456 Even if a network is part of the routing table, and the database provides
1457 an ASN, this does not mean that this is a legitimate announcement.
1458 */
1459 if (country_code && !*country_code) {
1460 loc_network_unref(network);
1461 continue;
1462 }
1463
9306c68d
MT
1464 // Determine the network family
1465 int family = loc_network_address_family(network);
1466
1467 switch (family) {
1468 case AF_INET6:
1469 gap_start = &enumerator->gap6_start;
1470 break;
1471
1472 case AF_INET:
1473 gap_start = &enumerator->gap4_start;
1474 break;
1475
1476 default:
1477 ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
1478 errno = ENOTSUP;
1479 return 1;
1480 }
1481
8649c654
MT
1482 const struct in6_addr* first_address = loc_network_get_first_address(network);
1483 const struct in6_addr* last_address = loc_network_get_last_address(network);
1484
1485 // Skip if this network is a subnet of a former one
1486 if (loc_address_cmp(gap_start, last_address) >= 0) {
1487 loc_network_unref(network);
1488 continue;
1489 }
1490
9306c68d 1491 // Search where the gap could end
8649c654 1492 gap_end = *first_address;
5b72642c 1493 loc_address_decrement(&gap_end);
8c37d8a7 1494
9306c68d 1495 // There is a gap
c833e45e 1496 if (loc_address_cmp(gap_start, &gap_end) <= 0) {
9306c68d
MT
1497 r = loc_network_list_summarize(enumerator->ctx,
1498 gap_start, &gap_end, &enumerator->stack);
8c37d8a7
MT
1499 if (r) {
1500 loc_network_unref(network);
9306c68d 1501 return r;
8c37d8a7
MT
1502 }
1503 }
1504
9306c68d 1505 // The gap now starts after this network
8649c654 1506 *gap_start = *last_address;
5b72642c 1507 loc_address_increment(gap_start);
9306c68d 1508
8c37d8a7
MT
1509 loc_network_unref(network);
1510
1511 // Try to return something
1512 *bogon = loc_network_list_pop_first(enumerator->stack);
1513 if (*bogon)
1514 break;
1515 }
1516
9306c68d
MT
1517 return 0;
1518
1519FINISH:
1520
1521 if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
1522 r = loc_address_reset_last(&gap_end, AF_INET6);
1523 if (r)
1524 return r;
1525
c833e45e 1526 if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
9306c68d
MT
1527 r = loc_network_list_summarize(enumerator->ctx,
1528 &enumerator->gap6_start, &gap_end, &enumerator->stack);
1529 if (r)
1530 return r;
1531 }
c0b1f64f
MT
1532
1533 // Reset start
1534 loc_address_reset(&enumerator->gap6_start, AF_INET6);
9306c68d
MT
1535 }
1536
1537 if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
1538 r = loc_address_reset_last(&gap_end, AF_INET);
1539 if (r)
1540 return r;
1541
c833e45e 1542 if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
9306c68d
MT
1543 r = loc_network_list_summarize(enumerator->ctx,
1544 &enumerator->gap4_start, &gap_end, &enumerator->stack);
1545 if (r)
1546 return r;
1547 }
c0b1f64f
MT
1548
1549 // Reset start
1550 loc_address_reset(&enumerator->gap4_start, AF_INET);
9306c68d
MT
1551 }
1552
1553 // Try to return something
1554 *bogon = loc_network_list_pop_first(enumerator->stack);
1555
1556 return 0;
8c37d8a7
MT
1557}
1558
d87fd7a3
MT
1559LOC_EXPORT int loc_database_enumerator_next_network(
1560 struct loc_database_enumerator* enumerator, struct loc_network** network) {
8c37d8a7
MT
1561 switch (enumerator->mode) {
1562 case LOC_DB_ENUMERATE_NETWORKS:
1563 // Flatten output?
1564 if (enumerator->flatten)
1565 return __loc_database_enumerator_next_network_flattened(enumerator, network);
d87fd7a3 1566
8c37d8a7
MT
1567 return __loc_database_enumerator_next_network(enumerator, network, 1);
1568
1569 case LOC_DB_ENUMERATE_BOGONS:
1570 return __loc_database_enumerator_next_bogon(enumerator, network);
d87fd7a3 1571
8c37d8a7
MT
1572 default:
1573 return 0;
1574 }
e3f696c1 1575}
fa9a3663
MT
1576
1577LOC_EXPORT int loc_database_enumerator_next_country(
1578 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1579 *country = NULL;
1580
1581 // Do not do anything if not in country mode
1582 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1583 return 0;
1584
1585 struct loc_database* db = enumerator->db;
1586
1587 while (enumerator->country_index < db->countries_count) {
1588 // Fetch the next country
1589 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1590 if (r)
1591 return r;
1592
1593 // We do not filter here, so it always is a match
1594 return 0;
1595 }
1596
1597 // Reset the index
1598 enumerator->country_index = 0;
1599
1600 // We have searched through all of them
1601 return 0;
1602}