]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
debian: Ensure changelog distribution is tagged
[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
MT
39#include <libloc/libloc.h>
40#include <libloc/as.h>
41#include <libloc/as-list.h>
42#include <libloc/compat.h>
43#include <libloc/country.h>
44#include <libloc/country-list.h>
45#include <libloc/database.h>
46#include <libloc/format.h>
47#include <libloc/network.h>
48#include <libloc/network-list.h>
49#include <libloc/private.h>
50#include <libloc/stringpool.h>
2601e83e
MT
51
52struct loc_database {
53 struct loc_ctx* ctx;
54 int refcount;
55
b1720435
MT
56 FILE* f;
57
22c7b98b 58 enum loc_database_version version;
96ea74a5 59 time_t created_at;
2601e83e
MT
60 off_t vendor;
61 off_t description;
4bf49d00 62 off_t license;
2601e83e 63
5ce881d4
MT
64 // Signatures
65 char* signature1;
66 size_t signature1_length;
67 char* signature2;
68 size_t signature2_length;
726f9984 69
a5db3e49 70 // ASes in the database
b904896a 71 struct loc_database_as_v1* as_v1;
a5db3e49
MT
72 size_t as_count;
73
f66b7b09 74 // Network tree
b904896a 75 struct loc_database_network_node_v1* network_nodes_v1;
f66b7b09
MT
76 size_t network_nodes_count;
77
a735a563 78 // Networks
b904896a 79 struct loc_database_network_v1* networks_v1;
a735a563
MT
80 size_t networks_count;
81
ec684c1a 82 // Countries
b904896a 83 struct loc_database_country_v1* countries_v1;
ec684c1a
MT
84 size_t countries_count;
85
2601e83e
MT
86 struct loc_stringpool* pool;
87};
88
e3f696c1
MT
89#define MAX_STACK_DEPTH 256
90
91struct loc_node_stack {
92 off_t offset;
93 int i; // Is this node 0 or 1?
94 int depth;
95};
96
7e13db74
MT
97struct loc_database_enumerator {
98 struct loc_ctx* ctx;
99 struct loc_database* db;
ccc7ab4e 100 enum loc_database_enumerator_mode mode;
7e13db74 101 int refcount;
d3d8ede6
MT
102
103 // Search string
104 char* string;
e646a8f3 105 struct loc_country_list* countries;
84a2f0c2 106 struct loc_as_list* asns;
9268db5a 107 enum loc_network_flags flags;
44e5ef71 108 int family;
d3d8ede6 109
681ff05c
MT
110 // Flatten output?
111 int flatten;
112
d3d8ede6
MT
113 // Index of the AS we are looking at
114 unsigned int as_index;
e3f696c1 115
fa9a3663
MT
116 // Index of the country we are looking at
117 unsigned int country_index;
118
e3f696c1
MT
119 // Network state
120 struct in6_addr network_address;
121 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
122 int network_stack_depth;
123 unsigned int* networks_visited;
d87fd7a3 124
8c37d8a7 125 // For subnet search and bogons
d87fd7a3 126 struct loc_network_list* stack;
8c37d8a7 127 struct loc_network* last_network;
7e13db74
MT
128};
129
b1720435 130static int loc_database_read_magic(struct loc_database* db) {
2601e83e
MT
131 struct loc_database_magic magic;
132
133 // Read from file
b1720435 134 size_t bytes_read = fread(&magic, 1, sizeof(magic), db->f);
2601e83e
MT
135
136 // Check if we have been able to read enough data
137 if (bytes_read < sizeof(magic)) {
138 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
139 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
140 return -ENOMSG;
141 }
142
143 // Compare magic bytes
144 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
145 DEBUG(db->ctx, "Magic value matches\n");
146
147 // Parse version
22c7b98b 148 db->version = magic.version;
2601e83e
MT
149
150 return 0;
151 }
152
22c7b98b 153 ERROR(db->ctx, "Unrecognized file type\n");
2601e83e
MT
154
155 // Return an error
156 return 1;
157}
158
b904896a
MT
159static int loc_database_read_as_section_v1(struct loc_database* db,
160 const struct loc_database_header_v1* header) {
edb4ba7c
MT
161 off_t as_offset = be32toh(header->as_offset);
162 size_t as_length = be32toh(header->as_length);
163
5c57de03 164 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", (intmax_t)as_offset, as_length);
a5db3e49 165
c182393f 166 if (as_length > 0) {
b904896a 167 db->as_v1 = mmap(NULL, as_length, PROT_READ,
b1720435 168 MAP_SHARED, fileno(db->f), as_offset);
a5db3e49 169
b904896a 170 if (db->as_v1 == MAP_FAILED)
c182393f 171 return -errno;
a5db3e49
MT
172 }
173
b904896a 174 db->as_count = as_length / sizeof(*db->as_v1);
c182393f 175
a5db3e49
MT
176 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
177
178 return 0;
179}
180
b904896a
MT
181static int loc_database_read_network_nodes_section_v1(struct loc_database* db,
182 const struct loc_database_header_v1* header) {
edb4ba7c
MT
183 off_t network_nodes_offset = be32toh(header->network_tree_offset);
184 size_t network_nodes_length = be32toh(header->network_tree_length);
185
f66b7b09 186 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
5c57de03 187 (intmax_t)network_nodes_offset, network_nodes_length);
f66b7b09
MT
188
189 if (network_nodes_length > 0) {
b904896a 190 db->network_nodes_v1 = mmap(NULL, network_nodes_length, PROT_READ,
b1720435 191 MAP_SHARED, fileno(db->f), network_nodes_offset);
f66b7b09 192
b904896a 193 if (db->network_nodes_v1 == MAP_FAILED)
f66b7b09
MT
194 return -errno;
195 }
196
b904896a 197 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v1);
f66b7b09
MT
198
199 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
200
201 return 0;
202}
203
b904896a
MT
204static int loc_database_read_networks_section_v1(struct loc_database* db,
205 const struct loc_database_header_v1* header) {
a735a563
MT
206 off_t networks_offset = be32toh(header->network_data_offset);
207 size_t networks_length = be32toh(header->network_data_length);
208
209 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
5c57de03 210 (intmax_t)networks_offset, networks_length);
a735a563
MT
211
212 if (networks_length > 0) {
b904896a 213 db->networks_v1 = mmap(NULL, networks_length, PROT_READ,
b1720435 214 MAP_SHARED, fileno(db->f), networks_offset);
a735a563 215
b904896a 216 if (db->networks_v1 == MAP_FAILED)
a735a563
MT
217 return -errno;
218 }
219
b904896a 220 db->networks_count = networks_length / sizeof(*db->networks_v1);
a735a563
MT
221
222 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
223
224 return 0;
225}
226
b904896a
MT
227static int loc_database_read_countries_section_v1(struct loc_database* db,
228 const struct loc_database_header_v1* header) {
ec684c1a
MT
229 off_t countries_offset = be32toh(header->countries_offset);
230 size_t countries_length = be32toh(header->countries_length);
231
232 DEBUG(db->ctx, "Reading countries section from %jd (%zu bytes)\n",
2e2325a9 233 (intmax_t)countries_offset, countries_length);
ec684c1a
MT
234
235 if (countries_length > 0) {
b904896a 236 db->countries_v1 = mmap(NULL, countries_length, PROT_READ,
b1720435 237 MAP_SHARED, fileno(db->f), countries_offset);
ec684c1a 238
b904896a 239 if (db->countries_v1 == MAP_FAILED)
ec684c1a
MT
240 return -errno;
241 }
242
b904896a 243 db->countries_count = countries_length / sizeof(*db->countries_v1);
ec684c1a
MT
244
245 INFO(db->ctx, "Read %zu countries from the database\n",
246 db->countries_count);
247
248 return 0;
249}
250
5ce881d4
MT
251static int loc_database_read_signature(struct loc_database* db,
252 char** dst, char* src, size_t length) {
253 // Check for a plausible signature length
254 if (length > LOC_SIGNATURE_MAX_LENGTH) {
36b9fd75 255 ERROR(db->ctx, "Signature too long: %zu\n", length);
5ce881d4
MT
256 return -EINVAL;
257 }
258
36b9fd75 259 DEBUG(db->ctx, "Reading signature of %zu bytes\n", length);
5ce881d4
MT
260
261 // Allocate space
262 *dst = malloc(length);
263 if (!*dst)
264 return -ENOMEM;
265
266 // Copy payload
267 memcpy(*dst, src, length);
268
269 return 0;
270}
271
b904896a
MT
272static int loc_database_read_header_v1(struct loc_database* db) {
273 struct loc_database_header_v1 header;
5ce881d4 274 int r;
2601e83e
MT
275
276 // Read from file
b1720435 277 size_t size = fread(&header, 1, sizeof(header), db->f);
2601e83e
MT
278
279 if (size < sizeof(header)) {
280 ERROR(db->ctx, "Could not read enough data for header\n");
281 return -ENOMSG;
282 }
283
284 // Copy over data
96ea74a5 285 db->created_at = be64toh(header.created_at);
0676cd80
MT
286 db->vendor = be32toh(header.vendor);
287 db->description = be32toh(header.description);
4bf49d00 288 db->license = be32toh(header.license);
2601e83e 289
c7db968a
MT
290 db->signature1_length = be16toh(header.signature1_length);
291 db->signature2_length = be16toh(header.signature2_length);
726f9984 292
5ce881d4
MT
293 // Read signatures
294 if (db->signature1_length) {
295 r = loc_database_read_signature(db, &db->signature1,
296 header.signature1, db->signature1_length);
297 if (r)
298 return r;
299 }
726f9984 300
5ce881d4
MT
301 if (db->signature2_length) {
302 r = loc_database_read_signature(db, &db->signature2,
303 header.signature2, db->signature2_length);
304 if (r)
305 return r;
726f9984
MT
306 }
307
2601e83e 308 // Open pool
0676cd80
MT
309 off_t pool_offset = be32toh(header.pool_offset);
310 size_t pool_length = be32toh(header.pool_length);
2601e83e 311
5ce881d4 312 r = loc_stringpool_open(db->ctx, &db->pool,
b1720435 313 db->f, pool_length, pool_offset);
2601e83e
MT
314 if (r)
315 return r;
316
a5db3e49 317 // AS section
b904896a 318 r = loc_database_read_as_section_v1(db, &header);
a5db3e49
MT
319 if (r)
320 return r;
321
f66b7b09 322 // Network Nodes
b904896a 323 r = loc_database_read_network_nodes_section_v1(db, &header);
f66b7b09
MT
324 if (r)
325 return r;
326
a735a563 327 // Networks
b904896a 328 r = loc_database_read_networks_section_v1(db, &header);
a735a563
MT
329 if (r)
330 return r;
331
ec684c1a 332 // countries
b904896a 333 r = loc_database_read_countries_section_v1(db, &header);
ec684c1a
MT
334 if (r)
335 return r;
336
2601e83e
MT
337 return 0;
338}
339
b1720435 340static int loc_database_read_header(struct loc_database* db) {
22c7b98b
MT
341 DEBUG(db->ctx, "Database version is %u\n", db->version);
342
2601e83e 343 switch (db->version) {
22c7b98b 344 case LOC_DATABASE_VERSION_1:
b904896a 345 return loc_database_read_header_v1(db);
2601e83e
MT
346
347 default:
348 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
349 return 1;
350 }
351}
352
a7431f1a 353static int loc_database_read(struct loc_database* db, FILE* f) {
02879100
MT
354 clock_t start = clock();
355
b1720435
MT
356 int fd = fileno(f);
357
358 // Clone file descriptor
359 fd = dup(fd);
360 if (!fd) {
361 ERROR(db->ctx, "Could not duplicate file descriptor\n");
362 return -1;
363 }
364
365 // Reopen the file so that we can keep our own file handle
366 db->f = fdopen(fd, "r");
367 if (!db->f) {
368 ERROR(db->ctx, "Could not re-open database file\n");
369 return -1;
370 }
371
372 // Rewind to the start of the file
373 rewind(db->f);
374
02879100 375 // Read magic bytes
b1720435 376 int r = loc_database_read_magic(db);
02879100
MT
377 if (r)
378 return r;
379
380 // Read the header
b1720435 381 r = loc_database_read_header(db);
02879100
MT
382 if (r)
383 return r;
384
385 clock_t end = clock();
386
e16c943b
MT
387 INFO(db->ctx, "Opened database in %.4fms\n",
388 (double)(end - start) / CLOCKS_PER_SEC * 1000);
02879100
MT
389
390 return 0;
391}
392
c182393f 393LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
a7431f1a
MT
394 // Fail on invalid file handle
395 if (!f)
396 return -EINVAL;
397
c182393f
MT
398 struct loc_database* db = calloc(1, sizeof(*db));
399 if (!db)
400 return -ENOMEM;
401
402 // Reference context
403 db->ctx = loc_ref(ctx);
404 db->refcount = 1;
405
406 DEBUG(db->ctx, "Database object allocated at %p\n", db);
407
a7431f1a 408 int r = loc_database_read(db, f);
02879100
MT
409 if (r) {
410 loc_database_unref(db);
2601e83e 411 return r;
02879100 412 }
2601e83e 413
c182393f
MT
414 *database = db;
415
2601e83e 416 return 0;
2601e83e
MT
417}
418
c182393f
MT
419LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
420 db->refcount++;
421
422 return db;
8f5b676a
MT
423}
424
c182393f 425static void loc_database_free(struct loc_database* db) {
f10ebc2d
MT
426 int r;
427
c182393f 428 DEBUG(db->ctx, "Releasing database %p\n", db);
c34e76f1 429
c182393f 430 // Removing all ASes
b904896a
MT
431 if (db->as_v1) {
432 r = munmap(db->as_v1, db->as_count * sizeof(*db->as_v1));
c182393f
MT
433 if (r)
434 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
435 }
c34e76f1 436
f10ebc2d 437 // Remove mapped network sections
b904896a
MT
438 if (db->networks_v1) {
439 r = munmap(db->networks_v1, db->networks_count * sizeof(*db->networks_v1));
f10ebc2d
MT
440 if (r)
441 ERROR(db->ctx, "Could not unmap networks section: %s\n", strerror(errno));
442 }
443
444 // Remove mapped network nodes section
b904896a
MT
445 if (db->network_nodes_v1) {
446 r = munmap(db->network_nodes_v1, db->network_nodes_count * sizeof(*db->network_nodes_v1));
f10ebc2d
MT
447 if (r)
448 ERROR(db->ctx, "Could not unmap network nodes section: %s\n", strerror(errno));
449 }
450
55664c7e
MT
451 // Remove mapped countries section
452 if (db->countries_v1) {
453 r = munmap(db->countries_v1, db->countries_count * sizeof(*db->countries_v1));
454 if (r)
455 ERROR(db->ctx, "Could not unmap countries section: %s\n", strerror(errno));
456 }
457
414d8958
MT
458 if (db->pool)
459 loc_stringpool_unref(db->pool);
c34e76f1 460
726f9984 461 // Free signature
5ce881d4
MT
462 if (db->signature1)
463 free(db->signature1);
464 if (db->signature2)
465 free(db->signature2);
726f9984 466
b1720435
MT
467 // Close database file
468 if (db->f)
469 fclose(db->f);
470
c182393f
MT
471 loc_unref(db->ctx);
472 free(db);
c34e76f1
MT
473}
474
c182393f
MT
475LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
476 if (--db->refcount > 0)
477 return NULL;
78ace4ed 478
c182393f
MT
479 loc_database_free(db);
480 return NULL;
481}
78ace4ed 482
726f9984
MT
483LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
484 // Cannot do this when no signature is available
5ce881d4 485 if (!db->signature1 && !db->signature2) {
726f9984
MT
486 DEBUG(db->ctx, "No signature available to verify\n");
487 return 1;
488 }
489
c81205a5
MT
490 // Start the stopwatch
491 clock_t start = clock();
492
726f9984
MT
493 // Load public key
494 EVP_PKEY* pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
495 if (!pkey) {
496 char* error = ERR_error_string(ERR_get_error(), NULL);
497 ERROR(db->ctx, "Could not parse public key: %s\n", error);
498
499 return -1;
500 }
501
b1720435
MT
502 int r = 0;
503
504 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
505
b1720435 506 // Initialise hash function
e7f4b2ce
MT
507 r = EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey);
508 if (r != 1) {
509 ERROR(db->ctx, "Error initializing signature validation: %s\n",
510 ERR_error_string(ERR_get_error(), NULL));
511 r = 1;
512
513 goto CLEANUP;
514 }
b1720435
MT
515
516 // Reset file to start
517 rewind(db->f);
518
519 // Read magic
520 struct loc_database_magic magic;
521 fread(&magic, 1, sizeof(magic), db->f);
522
a0cff45d
MT
523 hexdump(db->ctx, &magic, sizeof(magic));
524
b1720435 525 // Feed magic into the hash
e7f4b2ce
MT
526 r = EVP_DigestVerifyUpdate(mdctx, &magic, sizeof(magic));
527 if (r != 1) {
528 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
529 r = 1;
530
531 goto CLEANUP;
532 }
b1720435
MT
533
534 // Read the header
b904896a 535 struct loc_database_header_v1 header_v1;
54f0649f 536 size_t bytes_read;
b1720435
MT
537
538 switch (db->version) {
22c7b98b 539 case LOC_DATABASE_VERSION_1:
54f0649f
MT
540 bytes_read = fread(&header_v1, 1, sizeof(header_v1), db->f);
541 if (bytes_read < sizeof(header_v1)) {
542 ERROR(db->ctx, "Could not read header\n");
543 r = 1;
544
545 goto CLEANUP;
546 }
b1720435 547
5ce881d4
MT
548 // Clear signatures
549 memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
550 header_v1.signature1_length = 0;
551 memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
552 header_v1.signature2_length = 0;
b1720435 553
b904896a 554 hexdump(db->ctx, &header_v1, sizeof(header_v1));
a0cff45d 555
b1720435 556 // Feed header into the hash
b904896a 557 r = EVP_DigestVerifyUpdate(mdctx, &header_v1, sizeof(header_v1));
e7f4b2ce
MT
558 if (r != 1) {
559 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
560 r = 1;
561
562 goto CLEANUP;
563 }
b1720435
MT
564 break;
565
566 default:
567 ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
568 db->version);
569 r = -EINVAL;
570 goto CLEANUP;
571 }
572
726f9984
MT
573 // Walk through the file in chunks of 64kB
574 char buffer[64 * 1024];
b1720435
MT
575
576 while (!feof(db->f)) {
54f0649f 577 bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
b1720435 578
a0cff45d
MT
579 hexdump(db->ctx, buffer, bytes_read);
580
e7f4b2ce
MT
581 r = EVP_DigestVerifyUpdate(mdctx, buffer, bytes_read);
582 if (r != 1) {
583 ERROR(db->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
584 r = 1;
585
586 goto CLEANUP;
587 }
b1720435
MT
588 }
589
5ce881d4
MT
590 // Check first signature
591 if (db->signature1) {
592 hexdump(db->ctx, db->signature1, db->signature1_length);
726f9984 593
5ce881d4
MT
594 r = EVP_DigestVerifyFinal(mdctx,
595 (unsigned char*)db->signature1, db->signature1_length);
596
597 if (r == 0) {
598 DEBUG(db->ctx, "The first signature is invalid\n");
599 r = 1;
600 } else if (r == 1) {
601 DEBUG(db->ctx, "The first signature is valid\n");
602 r = 0;
603 } else {
604 ERROR(db->ctx, "Error verifying the first signature: %s\n",
605 ERR_error_string(ERR_get_error(), NULL));
606 r = -1;
607 }
b1720435
MT
608 }
609
5ce881d4
MT
610 // Check second signature only when the first one was invalid
611 if (r && db->signature2) {
612 hexdump(db->ctx, db->signature2, db->signature2_length);
613
614 r = EVP_DigestVerifyFinal(mdctx,
615 (unsigned char*)db->signature2, db->signature2_length);
616
617 if (r == 0) {
618 DEBUG(db->ctx, "The second signature is invalid\n");
619 r = 1;
620 } else if (r == 1) {
621 DEBUG(db->ctx, "The second signature is valid\n");
622 r = 0;
623 } else {
624 ERROR(db->ctx, "Error verifying the second signature: %s\n",
625 ERR_error_string(ERR_get_error(), NULL));
626 r = -1;
627 }
628 }
257626f5 629
c81205a5 630 clock_t end = clock();
6661692f 631 INFO(db->ctx, "Signature checked in %.4fms\n",
c81205a5
MT
632 (double)(end - start) / CLOCKS_PER_SEC * 1000);
633
b1720435
MT
634CLEANUP:
635 // Cleanup
636 EVP_MD_CTX_free(mdctx);
726f9984 637 EVP_PKEY_free(pkey);
b1720435
MT
638
639 return r;
640}
641
c182393f
MT
642LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
643 return db->created_at;
644}
78ace4ed 645
c182393f
MT
646LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
647 return loc_stringpool_get(db->pool, db->vendor);
648}
78ace4ed 649
c182393f
MT
650LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
651 return loc_stringpool_get(db->pool, db->description);
652}
78ace4ed 653
4bf49d00
MT
654LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
655 return loc_stringpool_get(db->pool, db->license);
656}
657
c182393f
MT
658LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
659 return db->as_count;
78ace4ed
MT
660}
661
c182393f
MT
662// Returns the AS at position pos
663static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
664 if ((size_t)pos >= db->as_count)
665 return -EINVAL;
2601e83e 666
5c57de03 667 DEBUG(db->ctx, "Fetching AS at position %jd\n", (intmax_t)pos);
2601e83e
MT
668
669 int r;
c182393f 670 switch (db->version) {
22c7b98b 671 case LOC_DATABASE_VERSION_1:
b904896a 672 r = loc_as_new_from_database_v1(db->ctx, db->pool, as, db->as_v1 + pos);
c182393f 673 break;
2601e83e 674
c182393f
MT
675 default:
676 return -1;
677 }
2601e83e 678
c182393f
MT
679 if (r == 0) {
680 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 681 }
2601e83e 682
c182393f
MT
683 return r;
684}
c34e76f1 685
c182393f
MT
686// Performs a binary search to find the AS in the list
687LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
688 off_t lo = 0;
689 off_t hi = db->as_count - 1;
c34e76f1 690
6661692f 691#ifdef ENABLE_DEBUG
8f3e2a06
MT
692 // Save start time
693 clock_t start = clock();
6661692f 694#endif
8f3e2a06 695
c182393f
MT
696 while (lo <= hi) {
697 off_t i = (lo + hi) / 2;
8f5b676a 698
c182393f
MT
699 // Fetch AS in the middle between lo and hi
700 int r = loc_database_fetch_as(db, as, i);
701 if (r)
702 return r;
a5db3e49 703
c182393f
MT
704 // Check if this is a match
705 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06 706 if (as_number == number) {
6661692f 707#ifdef ENABLE_DEBUG
8f3e2a06
MT
708 clock_t end = clock();
709
710 // Log how fast this has been
e16c943b
MT
711 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
712 (double)(end - start) / CLOCKS_PER_SEC * 1000);
6661692f 713#endif
8f3e2a06 714
c182393f 715 return 0;
8f3e2a06 716 }
c182393f
MT
717
718 // If it wasn't, we release the AS and
719 // adjust our search pointers
720 loc_as_unref(*as);
721
722 if (as_number < number) {
723 lo = i + 1;
724 } else
725 hi = i - 1;
726 }
2601e83e 727
c182393f
MT
728 // Nothing found
729 *as = NULL;
2601e83e 730
8f3e2a06 731 return 1;
2601e83e 732}
10778041
MT
733
734// Returns the network at position pos
39a55353
MT
735static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
736 struct in6_addr* address, unsigned int prefix, off_t pos) {
9b9e5faf
MT
737 if ((size_t)pos >= db->networks_count) {
738 DEBUG(db->ctx, "Network ID out of range: %jd/%jd\n",
739 (intmax_t)pos, (intmax_t)db->networks_count);
10778041 740 return -EINVAL;
9b9e5faf
MT
741 }
742
10778041 743
5c57de03 744 DEBUG(db->ctx, "Fetching network at position %jd\n", (intmax_t)pos);
10778041
MT
745
746 int r;
747 switch (db->version) {
22c7b98b 748 case LOC_DATABASE_VERSION_1:
b904896a
MT
749 r = loc_network_new_from_database_v1(db->ctx, network,
750 address, prefix, db->networks_v1 + pos);
10778041
MT
751 break;
752
753 default:
754 return -1;
755 }
756
6661692f 757#ifdef ENABLE_DEBUG
10778041
MT
758 if (r == 0) {
759 char* string = loc_network_str(*network);
760 DEBUG(db->ctx, "Got network %s\n", string);
761 free(string);
762 }
6661692f 763#endif
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
fc692a58 788 if (!loc_network_match_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
808 int bit = in6_addr_get_bit(address, level);
809 in6_addr_set_bit(network_address, level, bit);
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,
851 struct in6_addr* address, struct loc_network** network) {
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
880 int r = loc_parse_address(db->ctx, string, &address);
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
8c37d8a7
MT
988 if (enumerator->last_network)
989 loc_network_unref(enumerator->last_network);
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
7e13db74
MT
1020 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
1021
1022 *enumerator = e;
1023 return 0;
1024}
1025
1026LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
1027 enumerator->refcount++;
1028
1029 return enumerator;
1030}
1031
7e13db74
MT
1032LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
1033 if (!enumerator)
1034 return NULL;
1035
1036 if (--enumerator->refcount > 0)
1037 return enumerator;
1038
1039 loc_database_enumerator_free(enumerator);
1040 return NULL;
1041}
d3d8ede6
MT
1042
1043LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
1044 enumerator->string = strdup(string);
1045
1046 // Make the string lowercase
1047 for (char *p = enumerator->string; *p; p++)
1048 *p = tolower(*p);
1049
1050 return 0;
1051}
1052
e646a8f3
MT
1053LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
1054 struct loc_database_enumerator* enumerator) {
1055 if (!enumerator->countries)
1056 return NULL;
35bb3a32 1057
e646a8f3
MT
1058 return loc_country_list_ref(enumerator->countries);
1059}
4ef1761f 1060
e646a8f3
MT
1061LOC_EXPORT int loc_database_enumerator_set_countries(
1062 struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
1063 if (enumerator->countries)
1064 loc_country_list_unref(enumerator->countries);
57146963 1065
e646a8f3 1066 enumerator->countries = loc_country_list_ref(countries);
35bb3a32
MT
1067
1068 return 0;
1069}
1070
84a2f0c2
MT
1071LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
1072 struct loc_database_enumerator* enumerator) {
1073 if (!enumerator->asns)
1074 return NULL;
1075
1076 return loc_as_list_ref(enumerator->asns);
1077}
1078
1079LOC_EXPORT int loc_database_enumerator_set_asns(
1080 struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
1081 if (enumerator->asns)
1082 loc_as_list_unref(enumerator->asns);
1083
1084 enumerator->asns = loc_as_list_ref(asns);
82910b95
MT
1085
1086 return 0;
1087}
1088
9268db5a
MT
1089LOC_EXPORT int loc_database_enumerator_set_flag(
1090 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
1091 enumerator->flags |= flag;
1092
1093 return 0;
1094}
1095
44e5ef71
MT
1096LOC_EXPORT int loc_database_enumerator_set_family(
1097 struct loc_database_enumerator* enumerator, int family) {
1098 enumerator->family = family;
1099
1100 return 0;
1101}
1102
15f79e2d
MT
1103LOC_EXPORT int loc_database_enumerator_next_as(
1104 struct loc_database_enumerator* enumerator, struct loc_as** as) {
1105 *as = NULL;
1106
ccc7ab4e
MT
1107 // Do not do anything if not in AS mode
1108 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
15f79e2d 1109 return 0;
ccc7ab4e 1110
d3d8ede6 1111 struct loc_database* db = enumerator->db;
d3d8ede6
MT
1112
1113 while (enumerator->as_index < db->as_count) {
1114 // Fetch the next AS
15f79e2d 1115 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
d3d8ede6 1116 if (r)
15f79e2d 1117 return r;
d3d8ede6 1118
15f79e2d 1119 r = loc_as_match_string(*as, enumerator->string);
273948cf 1120 if (r == 1) {
d3d8ede6 1121 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
15f79e2d 1122 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
d3d8ede6 1123
15f79e2d 1124 return 0;
d3d8ede6
MT
1125 }
1126
1127 // No match
15f79e2d 1128 loc_as_unref(*as);
74f218f0 1129 *as = NULL;
d3d8ede6
MT
1130 }
1131
1132 // Reset the index
1133 enumerator->as_index = 0;
1134
1135 // We have searched through all of them
15f79e2d 1136 return 0;
d3d8ede6 1137}
e3f696c1
MT
1138
1139static int loc_database_enumerator_stack_push_node(
1140 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
1141 // Do not add empty nodes
1142 if (!offset)
1143 return 0;
1144
1145 // Check if there is any space left on the stack
1146 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
1147 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
1148 return -1;
1149 }
1150
1151 // Increase stack size
1152 int s = ++e->network_stack_depth;
1153
2e2325a9 1154 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", (intmax_t)offset, depth);
e3f696c1
MT
1155
1156 e->network_stack[s].offset = offset;
1157 e->network_stack[s].i = i;
1158 e->network_stack[s].depth = depth;
1159
1160 return 0;
1161}
1162
69248038
MT
1163static int loc_database_enumerator_filter_network(
1164 struct loc_database_enumerator* enumerator, struct loc_network* network) {
1165 // Skip if the family does not match
e0e96878
MT
1166 if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
1167 DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
69248038 1168 return 1;
e0e96878 1169 }
69248038
MT
1170
1171 // Skip if the country code does not match
e0e96878
MT
1172 if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
1173 const char* country_code = loc_network_get_country_code(network);
50120b99 1174
e0e96878
MT
1175 if (!loc_country_list_contains_code(enumerator->countries, country_code)) {
1176 DEBUG(enumerator->ctx, "Filtered network %p because of country code not matching\n", network);
1177 return 1;
50120b99
MT
1178 }
1179 }
69248038
MT
1180
1181 // Skip if the ASN does not match
e0e96878
MT
1182 if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
1183 uint32_t asn = loc_network_get_asn(network);
c1a36c94 1184
e0e96878
MT
1185 if (!loc_as_list_contains_number(enumerator->asns, asn)) {
1186 DEBUG(enumerator->ctx, "Filtered network %p because of ASN not matching\n", network);
1187 return 1;
c1a36c94
MT
1188 }
1189 }
69248038
MT
1190
1191 // Skip if flags do not match
e0e96878
MT
1192 if (enumerator->flags && !loc_network_match_flag(network, enumerator->flags)) {
1193 DEBUG(enumerator->ctx, "Filtered network %p because of flags not matching\n", network);
69248038 1194 return 1;
e0e96878 1195 }
69248038
MT
1196
1197 // Do not filter
1198 return 0;
1199}
1200
d87fd7a3
MT
1201static int __loc_database_enumerator_next_network(
1202 struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
1203 // Return top element from the stack
2113e71b 1204 while (1) {
04cbd2bf 1205 *network = loc_network_list_pop_first(enumerator->stack);
2113e71b
MT
1206
1207 // Stack is empty
1208 if (!*network)
1209 break;
1210
1211 // Throw away any networks by filter
1212 if (filter && loc_database_enumerator_filter_network(enumerator, *network)) {
1213 loc_network_unref(*network);
1214 *network = NULL;
1215 continue;
1216 }
1217
1218 // Return result
15f79e2d 1219 return 0;
2113e71b 1220 }
15f79e2d 1221
15f79e2d
MT
1222 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
1223 enumerator->network_stack_depth);
e3f696c1
MT
1224
1225 // Perform DFS
15f79e2d
MT
1226 while (enumerator->network_stack_depth > 0) {
1227 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
e3f696c1
MT
1228
1229 // Get object from top of the stack
15f79e2d 1230 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
e3f696c1
MT
1231
1232 // Remove the node from the stack if we have already visited it
15f79e2d
MT
1233 if (enumerator->networks_visited[node->offset]) {
1234 enumerator->network_stack_depth--;
e3f696c1
MT
1235 continue;
1236 }
1237
74fb733a 1238 // Mark the bits on the path correctly
15f79e2d 1239 in6_addr_set_bit(&enumerator->network_address,
e3f696c1
MT
1240 (node->depth > 0) ? node->depth - 1 : 0, node->i);
1241
2e2325a9 1242 DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
15f79e2d 1243 enumerator->networks_visited[node->offset]++;
e3f696c1
MT
1244
1245 // Pop node from top of the stack
b904896a
MT
1246 struct loc_database_network_node_v1* n =
1247 enumerator->db->network_nodes_v1 + node->offset;
e3f696c1
MT
1248
1249 // Add edges to stack
d87fd7a3 1250 int r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1251 be32toh(n->one), 1, node->depth + 1);
1252
1253 if (r)
1254 return r;
1255
15f79e2d 1256 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
1257 be32toh(n->zero), 0, node->depth + 1);
1258
1259 if (r)
1260 return r;
1261
1262 // Check if this node is a leaf and has a network object
1263 if (__loc_database_node_is_leaf(n)) {
1264 off_t network_index = be32toh(n->network);
1265
2e2325a9 1266 DEBUG(enumerator->ctx, "Node has a network at %jd\n", (intmax_t)network_index);
e3f696c1
MT
1267
1268 // Fetch the network object
15f79e2d
MT
1269 r = loc_database_fetch_network(enumerator->db, network,
1270 &enumerator->network_address, node->depth, network_index);
e3f696c1
MT
1271
1272 // Break on any errors
1273 if (r)
1274 return r;
1275
d87fd7a3
MT
1276 // Return all networks when the filter is disabled
1277 if (!filter)
1278 return 0;
1279
e3f696c1 1280 // Check if we are interested in this network
69248038 1281 if (loc_database_enumerator_filter_network(enumerator, *network)) {
9268db5a
MT
1282 loc_network_unref(*network);
1283 *network = NULL;
864dd22e
MT
1284
1285 continue;
9268db5a
MT
1286 }
1287
e3f696c1
MT
1288 return 0;
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;
1308 struct loc_network_list* subnets;
1309
1310 // Create a list with all subnets
1311 r = loc_network_list_new(enumerator->ctx, &subnets);
1312 if (r)
1313 return r;
1314
1315 // Search all subnets from the database
1316 while (1) {
1317 // Fetch the next network in line
1318 r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
ed0f53df
MT
1319 if (r) {
1320 loc_network_unref(subnet);
1321 loc_network_list_unref(subnets);
1322
1323 return r;
1324 }
d87fd7a3
MT
1325
1326 // End if we did not receive another subnet
1327 if (!subnet)
1328 break;
1329
1330 // Collect all subnets in a list
1331 if (loc_network_is_subnet(*network, subnet)) {
1332 r = loc_network_list_push(subnets, subnet);
ed0f53df
MT
1333 if (r) {
1334 loc_network_unref(subnet);
1335 loc_network_list_unref(subnets);
1336
1337 return r;
1338 }
d87fd7a3
MT
1339
1340 loc_network_unref(subnet);
1341 continue;
1342 }
1343
1344 // If this is not a subnet, we push it back onto the stack and break
1345 r = loc_network_list_push(enumerator->stack, subnet);
ed0f53df
MT
1346 if (r) {
1347 loc_network_unref(subnet);
1348 loc_network_list_unref(subnets);
1349
1350 return r;
1351 }
d87fd7a3
MT
1352
1353 loc_network_unref(subnet);
1354 break;
1355 }
1356
1357 DEBUG(enumerator->ctx, "Found %zu subnet(s)\n", loc_network_list_size(subnets));
1358
1359 // We can abort here if the network has no subnets
1360 if (loc_network_list_empty(subnets)) {
1361 loc_network_list_unref(subnets);
1362
1363 return 0;
1364 }
1365
1366 // If the network has any subnets, we will break it into smaller parts
1367 // without the subnets.
1368 struct loc_network_list* excluded = loc_network_exclude_list(*network, subnets);
ed0f53df
MT
1369 if (!excluded) {
1370 loc_network_list_unref(subnets);
1371 return -1;
1372 }
1373
5dacb45a
MT
1374 // Merge subnets onto the stack
1375 r = loc_network_list_merge(enumerator->stack, subnets);
ed0f53df
MT
1376 if (r) {
1377 loc_network_list_unref(subnets);
1378 loc_network_list_unref(excluded);
1379
1380 return r;
d87fd7a3
MT
1381 }
1382
5dacb45a
MT
1383 // Push excluded list onto the stack
1384 r = loc_network_list_merge(enumerator->stack, excluded);
1385 if (r) {
1386 loc_network_list_unref(subnets);
1387 loc_network_list_unref(excluded);
d87fd7a3 1388
5dacb45a
MT
1389 return r;
1390 }
d87fd7a3 1391
d87fd7a3 1392 loc_network_list_unref(subnets);
5dacb45a
MT
1393 loc_network_list_unref(excluded);
1394
6bd9cd76 1395 // Drop the network and restart the whole process again to pick the next network
5dacb45a 1396 loc_network_unref(*network);
d87fd7a3 1397
6bd9cd76 1398 return __loc_database_enumerator_next_network_flattened(enumerator, network);
d87fd7a3
MT
1399}
1400
8c37d8a7
MT
1401/*
1402 This function finds all bogons (i.e. gaps) between the input networks
1403*/
1404static int __loc_database_enumerator_find_bogons(struct loc_ctx* ctx,
1405 struct loc_network* first, struct loc_network* second, struct loc_network_list* bogons) {
1406 // We do not need to check if first < second because the graph is always ordered
1407
1408 // The last address of the first network + 1 is the start of the gap
1409 struct in6_addr first_address = address_increment(loc_network_get_last_address(first));
1410
1411 // The first address of the second network - 1 is the end of the gap
1412 struct in6_addr last_address = address_decrement(loc_network_get_first_address(second));
1413
1414 // If first address is now greater than last address, there is no gap
1415 if (in6_addr_cmp(&first_address, &last_address) >= 0)
1416 return 0;
1417
1418 return loc_network_list_summarize(ctx, &first_address, &last_address, &bogons);
1419}
1420
1421static int __loc_database_enumerator_next_bogon(
1422 struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
1423 int r;
1424
1425 // Return top element from the stack
1426 while (1) {
1427 *bogon = loc_network_list_pop_first(enumerator->stack);
1428
1429 // Stack is empty
1430 if (!*bogon)
1431 break;
1432
1433 // Return result
1434 return 0;
1435 }
1436
1437 struct loc_network* network = NULL;
1438
1439 while (1) {
1440 r = __loc_database_enumerator_next_network(enumerator, &network, 1);
1441 if (r)
1442 goto ERROR;
1443
1444 if (!network)
1445 break;
1446
1447 if (enumerator->last_network && loc_network_address_family(enumerator->last_network) == loc_network_address_family(network)) {
1448 r = __loc_database_enumerator_find_bogons(enumerator->ctx,
1449 enumerator->last_network, network, enumerator->stack);
1450 if (r) {
1451 loc_network_unref(network);
1452 goto ERROR;
1453 }
1454 }
1455
1456 // Remember network for next iteration
1457 enumerator->last_network = loc_network_ref(network);
1458 loc_network_unref(network);
1459
1460 // Try to return something
1461 *bogon = loc_network_list_pop_first(enumerator->stack);
1462 if (*bogon)
1463 break;
1464 }
1465
1466ERROR:
1467 return r;
1468}
1469
d87fd7a3
MT
1470LOC_EXPORT int loc_database_enumerator_next_network(
1471 struct loc_database_enumerator* enumerator, struct loc_network** network) {
8c37d8a7
MT
1472 switch (enumerator->mode) {
1473 case LOC_DB_ENUMERATE_NETWORKS:
1474 // Flatten output?
1475 if (enumerator->flatten)
1476 return __loc_database_enumerator_next_network_flattened(enumerator, network);
d87fd7a3 1477
8c37d8a7
MT
1478 return __loc_database_enumerator_next_network(enumerator, network, 1);
1479
1480 case LOC_DB_ENUMERATE_BOGONS:
1481 return __loc_database_enumerator_next_bogon(enumerator, network);
d87fd7a3 1482
8c37d8a7
MT
1483 default:
1484 return 0;
1485 }
e3f696c1 1486}
fa9a3663
MT
1487
1488LOC_EXPORT int loc_database_enumerator_next_country(
1489 struct loc_database_enumerator* enumerator, struct loc_country** country) {
1490 *country = NULL;
1491
1492 // Do not do anything if not in country mode
1493 if (enumerator->mode != LOC_DB_ENUMERATE_COUNTRIES)
1494 return 0;
1495
1496 struct loc_database* db = enumerator->db;
1497
1498 while (enumerator->country_index < db->countries_count) {
1499 // Fetch the next country
1500 int r = loc_database_fetch_country(db, country, enumerator->country_index++);
1501 if (r)
1502 return r;
1503
1504 // We do not filter here, so it always is a match
1505 return 0;
1506 }
1507
1508 // Reset the index
1509 enumerator->country_index = 0;
1510
1511 // We have searched through all of them
1512 return 0;
1513}