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