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