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