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