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