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