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