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