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