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