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