]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
test: Fix compiling AS test after header change
[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>
0676cd80 19#include <endian.h>
2601e83e 20#include <errno.h>
10778041 21#include <netinet/in.h>
2601e83e
MT
22#include <stddef.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
c182393f 27#include <sys/mman.h>
2601e83e 28#include <sys/types.h>
96ea74a5 29#include <time.h>
3f35869a 30#include <unistd.h>
2601e83e
MT
31
32#include <loc/libloc.h>
9fc7f001
MT
33#include <loc/as.h>
34#include <loc/database.h>
a5db3e49 35#include <loc/format.h>
10778041 36#include <loc/network.h>
9fc7f001
MT
37#include <loc/private.h>
38#include <loc/stringpool.h>
2601e83e
MT
39
40struct loc_database {
41 struct loc_ctx* ctx;
42 int refcount;
43
44 unsigned int version;
96ea74a5 45 time_t created_at;
2601e83e
MT
46 off_t vendor;
47 off_t description;
4bf49d00 48 off_t license;
2601e83e 49
a5db3e49 50 // ASes in the database
c182393f 51 struct loc_database_as_v0* as_v0;
a5db3e49
MT
52 size_t as_count;
53
f66b7b09
MT
54 // Network tree
55 struct loc_database_network_node_v0* network_nodes_v0;
56 size_t network_nodes_count;
57
a735a563
MT
58 // Networks
59 struct loc_database_network_v0* networks_v0;
60 size_t networks_count;
61
2601e83e
MT
62 struct loc_stringpool* pool;
63};
64
e3f696c1
MT
65#define MAX_STACK_DEPTH 256
66
67struct loc_node_stack {
68 off_t offset;
69 int i; // Is this node 0 or 1?
70 int depth;
71};
72
7e13db74
MT
73struct loc_database_enumerator {
74 struct loc_ctx* ctx;
75 struct loc_database* db;
ccc7ab4e 76 enum loc_database_enumerator_mode mode;
7e13db74 77 int refcount;
d3d8ede6
MT
78
79 // Search string
80 char* string;
35bb3a32 81 char country_code[3];
82910b95 82 uint32_t asn;
d3d8ede6
MT
83
84 // Index of the AS we are looking at
85 unsigned int as_index;
e3f696c1
MT
86
87 // Network state
88 struct in6_addr network_address;
89 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
90 int network_stack_depth;
91 unsigned int* networks_visited;
7e13db74
MT
92};
93
a7431f1a 94static int loc_database_read_magic(struct loc_database* db, FILE* f) {
2601e83e
MT
95 struct loc_database_magic magic;
96
97 // Read from file
a7431f1a 98 size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
2601e83e
MT
99
100 // Check if we have been able to read enough data
101 if (bytes_read < sizeof(magic)) {
102 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
103 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
104 return -ENOMSG;
105 }
106
107 // Compare magic bytes
108 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
109 DEBUG(db->ctx, "Magic value matches\n");
110
111 // Parse version
0676cd80 112 db->version = be16toh(magic.version);
2601e83e
MT
113 DEBUG(db->ctx, "Database version is %u\n", db->version);
114
115 return 0;
116 }
117
118 ERROR(db->ctx, "Database format is not compatible\n");
119
120 // Return an error
121 return 1;
122}
123
a5db3e49 124static int loc_database_read_as_section_v0(struct loc_database* db,
edb4ba7c
MT
125 FILE* f, const struct loc_database_header_v0* header) {
126 off_t as_offset = be32toh(header->as_offset);
127 size_t as_length = be32toh(header->as_length);
128
c182393f 129 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
a5db3e49 130
c182393f
MT
131 if (as_length > 0) {
132 db->as_v0 = mmap(NULL, as_length, PROT_READ,
a7431f1a 133 MAP_SHARED, fileno(f), as_offset);
a5db3e49 134
c182393f
MT
135 if (db->as_v0 == MAP_FAILED)
136 return -errno;
a5db3e49
MT
137 }
138
c182393f
MT
139 db->as_count = as_length / sizeof(*db->as_v0);
140
a5db3e49
MT
141 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
142
143 return 0;
144}
145
f66b7b09 146static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
edb4ba7c
MT
147 FILE* f, const struct loc_database_header_v0* header) {
148 off_t network_nodes_offset = be32toh(header->network_tree_offset);
149 size_t network_nodes_length = be32toh(header->network_tree_length);
150
f66b7b09
MT
151 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
152 network_nodes_offset, network_nodes_length);
153
154 if (network_nodes_length > 0) {
155 db->network_nodes_v0 = mmap(NULL, network_nodes_length, PROT_READ,
156 MAP_SHARED, fileno(f), network_nodes_offset);
157
158 if (db->network_nodes_v0 == MAP_FAILED)
159 return -errno;
160 }
161
162 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v0);
163
164 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
165
166 return 0;
167}
168
a735a563
MT
169static int loc_database_read_networks_section_v0(struct loc_database* db,
170 FILE* f, const struct loc_database_header_v0* header) {
171 off_t networks_offset = be32toh(header->network_data_offset);
172 size_t networks_length = be32toh(header->network_data_length);
173
174 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
175 networks_offset, networks_length);
176
177 if (networks_length > 0) {
178 db->networks_v0 = mmap(NULL, networks_length, PROT_READ,
179 MAP_SHARED, fileno(f), networks_offset);
180
181 if (db->networks_v0 == MAP_FAILED)
182 return -errno;
183 }
184
185 db->networks_count = networks_length / sizeof(*db->networks_v0);
186
187 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
188
189 return 0;
190}
191
a7431f1a 192static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
2601e83e
MT
193 struct loc_database_header_v0 header;
194
195 // Read from file
a7431f1a 196 size_t size = fread(&header, 1, sizeof(header), f);
2601e83e
MT
197
198 if (size < sizeof(header)) {
199 ERROR(db->ctx, "Could not read enough data for header\n");
200 return -ENOMSG;
201 }
202
203 // Copy over data
96ea74a5 204 db->created_at = be64toh(header.created_at);
0676cd80
MT
205 db->vendor = be32toh(header.vendor);
206 db->description = be32toh(header.description);
4bf49d00 207 db->license = be32toh(header.license);
2601e83e
MT
208
209 // Open pool
0676cd80
MT
210 off_t pool_offset = be32toh(header.pool_offset);
211 size_t pool_length = be32toh(header.pool_length);
2601e83e 212
0e974d4b 213 int r = loc_stringpool_open(db->ctx, &db->pool,
a7431f1a 214 f, pool_length, pool_offset);
2601e83e
MT
215 if (r)
216 return r;
217
a5db3e49 218 // AS section
edb4ba7c 219 r = loc_database_read_as_section_v0(db, f, &header);
a5db3e49
MT
220 if (r)
221 return r;
222
f66b7b09 223 // Network Nodes
edb4ba7c 224 r = loc_database_read_network_nodes_section_v0(db, f, &header);
f66b7b09
MT
225 if (r)
226 return r;
227
a735a563
MT
228 // Networks
229 r = loc_database_read_networks_section_v0(db, f, &header);
230 if (r)
231 return r;
232
2601e83e
MT
233 return 0;
234}
235
a7431f1a 236static int loc_database_read_header(struct loc_database* db, FILE* f) {
2601e83e
MT
237 switch (db->version) {
238 case 0:
a7431f1a 239 return loc_database_read_header_v0(db, f);
2601e83e
MT
240
241 default:
242 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
243 return 1;
244 }
245}
246
a7431f1a 247static int loc_database_read(struct loc_database* db, FILE* f) {
02879100
MT
248 clock_t start = clock();
249
250 // Read magic bytes
a7431f1a 251 int r = loc_database_read_magic(db, f);
02879100
MT
252 if (r)
253 return r;
254
255 // Read the header
a7431f1a 256 r = loc_database_read_header(db, f);
02879100
MT
257 if (r)
258 return r;
259
260 clock_t end = clock();
261
262 INFO(db->ctx, "Opened database in %.8fs\n",
263 (double)(end - start) / CLOCKS_PER_SEC);
264
265 return 0;
266}
267
c182393f 268LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
a7431f1a
MT
269 // Fail on invalid file handle
270 if (!f)
271 return -EINVAL;
272
c182393f
MT
273 struct loc_database* db = calloc(1, sizeof(*db));
274 if (!db)
275 return -ENOMEM;
276
277 // Reference context
278 db->ctx = loc_ref(ctx);
279 db->refcount = 1;
280
281 DEBUG(db->ctx, "Database object allocated at %p\n", db);
282
a7431f1a 283 int r = loc_database_read(db, f);
02879100
MT
284 if (r) {
285 loc_database_unref(db);
2601e83e 286 return r;
02879100 287 }
2601e83e 288
c182393f
MT
289 *database = db;
290
2601e83e 291 return 0;
2601e83e
MT
292}
293
c182393f
MT
294LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
295 db->refcount++;
296
297 return db;
8f5b676a
MT
298}
299
c182393f 300static void loc_database_free(struct loc_database* db) {
f10ebc2d
MT
301 int r;
302
c182393f 303 DEBUG(db->ctx, "Releasing database %p\n", db);
c34e76f1 304
c182393f
MT
305 // Removing all ASes
306 if (db->as_v0) {
f10ebc2d 307 r = munmap(db->as_v0, db->as_count * sizeof(*db->as_v0));
c182393f
MT
308 if (r)
309 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
310 }
c34e76f1 311
f10ebc2d
MT
312 // Remove mapped network sections
313 if (db->networks_v0) {
314 r = munmap(db->networks_v0, db->networks_count * sizeof(*db->networks_v0));
315 if (r)
316 ERROR(db->ctx, "Could not unmap networks section: %s\n", strerror(errno));
317 }
318
319 // Remove mapped network nodes section
320 if (db->network_nodes_v0) {
321 r = munmap(db->network_nodes_v0, db->network_nodes_count * sizeof(*db->network_nodes_v0));
322 if (r)
323 ERROR(db->ctx, "Could not unmap network nodes section: %s\n", strerror(errno));
324 }
325
89fe2c23
MT
326 if (db->pool)
327 loc_stringpool_unref(db->pool);
c34e76f1 328
c182393f
MT
329 loc_unref(db->ctx);
330 free(db);
c34e76f1
MT
331}
332
c182393f
MT
333LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
334 if (--db->refcount > 0)
335 return NULL;
78ace4ed 336
c182393f
MT
337 loc_database_free(db);
338 return NULL;
339}
78ace4ed 340
c182393f
MT
341LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
342 return db->created_at;
343}
78ace4ed 344
c182393f
MT
345LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
346 return loc_stringpool_get(db->pool, db->vendor);
347}
78ace4ed 348
c182393f
MT
349LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
350 return loc_stringpool_get(db->pool, db->description);
351}
78ace4ed 352
4bf49d00
MT
353LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
354 return loc_stringpool_get(db->pool, db->license);
355}
356
c182393f
MT
357LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
358 return db->as_count;
78ace4ed
MT
359}
360
c182393f
MT
361// Returns the AS at position pos
362static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
363 if ((size_t)pos >= db->as_count)
364 return -EINVAL;
2601e83e 365
c182393f 366 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
2601e83e
MT
367
368 int r;
c182393f
MT
369 switch (db->version) {
370 case 0:
371 r = loc_as_new_from_database_v0(db->ctx, db->pool, as, db->as_v0 + pos);
372 break;
2601e83e 373
c182393f
MT
374 default:
375 return -1;
376 }
2601e83e 377
c182393f
MT
378 if (r == 0) {
379 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 380 }
2601e83e 381
c182393f
MT
382 return r;
383}
c34e76f1 384
c182393f
MT
385// Performs a binary search to find the AS in the list
386LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
387 off_t lo = 0;
388 off_t hi = db->as_count - 1;
c34e76f1 389
8f3e2a06
MT
390 // Save start time
391 clock_t start = clock();
392
c182393f
MT
393 while (lo <= hi) {
394 off_t i = (lo + hi) / 2;
8f5b676a 395
c182393f
MT
396 // Fetch AS in the middle between lo and hi
397 int r = loc_database_fetch_as(db, as, i);
398 if (r)
399 return r;
a5db3e49 400
c182393f
MT
401 // Check if this is a match
402 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06
MT
403 if (as_number == number) {
404 clock_t end = clock();
405
406 // Log how fast this has been
407 DEBUG(db->ctx, "Found AS%u in %.8fs\n", as_number,
408 (double)(end - start) / CLOCKS_PER_SEC);
409
c182393f 410 return 0;
8f3e2a06 411 }
c182393f
MT
412
413 // If it wasn't, we release the AS and
414 // adjust our search pointers
415 loc_as_unref(*as);
416
417 if (as_number < number) {
418 lo = i + 1;
419 } else
420 hi = i - 1;
421 }
2601e83e 422
c182393f
MT
423 // Nothing found
424 *as = NULL;
2601e83e 425
8f3e2a06 426 return 1;
2601e83e 427}
10778041
MT
428
429// Returns the network at position pos
39a55353
MT
430static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
431 struct in6_addr* address, unsigned int prefix, off_t pos) {
10778041
MT
432 if ((size_t)pos >= db->networks_count)
433 return -EINVAL;
434
435 DEBUG(db->ctx, "Fetching network at position %jd\n", pos);
436
437 int r;
438 switch (db->version) {
439 case 0:
39a55353
MT
440 r = loc_network_new_from_database_v0(db->ctx, network,
441 address, prefix, db->networks_v0 + pos);
10778041
MT
442 break;
443
444 default:
445 return -1;
446 }
447
448 if (r == 0) {
449 char* string = loc_network_str(*network);
450 DEBUG(db->ctx, "Got network %s\n", string);
451 free(string);
452 }
453
454 return r;
455}
2a30e4de 456
025ef489 457static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
39a55353 458 return (node->network != htobe32(0xffffffff));
025ef489
MT
459}
460
461static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
39a55353 462 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
2a30e4de 463 const struct loc_database_network_node_v0* node) {
39a55353
MT
464 off_t network_index = be32toh(node->network);
465
466 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", node - db->network_nodes_v0, network_index);
2a30e4de
MT
467
468 // Fetch the network
469 int r = loc_database_fetch_network(db, network,
39a55353 470 network_address, prefix, network_index);
e85e2b0b
MT
471 if (r) {
472 ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
2a30e4de 473 return r;
e85e2b0b 474 }
39a55353 475
2a30e4de
MT
476 // Check if the given IP address is inside the network
477 r = loc_network_match_address(*network, address);
478 if (r) {
479 DEBUG(db->ctx, "Searched address is not part of the network\n");
480
481 loc_network_unref(*network);
482 *network = NULL;
483 return 1;
484 }
485
486 // A network was found and the IP address matches
487 return 0;
488}
489
2a30e4de
MT
490// Searches for an exact match along the path
491static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
492 struct loc_network** network, struct in6_addr* network_address,
f66f15e1 493 const struct loc_database_network_node_v0* node, unsigned int level) {
025ef489 494 int r;
2a30e4de
MT
495 off_t node_index;
496
497 // Follow the path
498 int bit = in6_addr_get_bit(address, level);
499 in6_addr_set_bit(network_address, level, bit);
500
501 if (bit == 0)
502 node_index = be32toh(node->zero);
503 else
504 node_index = be32toh(node->one);
505
9086d2b1
MT
506 // If the node index is zero, the tree ends here
507 // and we cannot descend any further
508 if (node_index > 0) {
509 // Check boundaries
510 if ((size_t)node_index >= db->network_nodes_count)
511 return -EINVAL;
2a30e4de 512
9086d2b1
MT
513 // Move on to the next node
514 r = __loc_database_lookup(db, address, network, network_address,
515 db->network_nodes_v0 + node_index, level + 1);
2a30e4de 516
9086d2b1
MT
517 // End here if a result was found
518 if (r == 0)
519 return r;
2a30e4de 520
9086d2b1
MT
521 // Raise any errors
522 else if (r < 0)
523 return r;
ec1d9681
MT
524
525 DEBUG(db->ctx, "No match found below level %u\n", level);
526 } else {
527 DEBUG(db->ctx, "Tree ended at level %u\n", level);
9086d2b1 528 }
2a30e4de 529
9086d2b1
MT
530 // If this node has a leaf, we will check if it matches
531 if (__loc_database_node_is_leaf(node)) {
532 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
533 if (r <= 0)
534 return r;
535 }
2a30e4de 536
ec1d9681 537 return 1;
2a30e4de
MT
538}
539
540LOC_EXPORT int loc_database_lookup(struct loc_database* db,
541 struct in6_addr* address, struct loc_network** network) {
542 struct in6_addr network_address;
543 memset(&network_address, 0, sizeof(network_address));
544
545 *network = NULL;
546
547 // Save start time
548 clock_t start = clock();
549
550 int r = __loc_database_lookup(db, address, network, &network_address,
551 db->network_nodes_v0, 0);
552
553 clock_t end = clock();
554
555 // Log how fast this has been
556 DEBUG(db->ctx, "Executed network search in %.8fs\n",
557 (double)(end - start) / CLOCKS_PER_SEC);
558
559 return r;
560}
561
562LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
563 const char* string, struct loc_network** network) {
564 struct in6_addr address;
565
566 int r = loc_parse_address(db->ctx, string, &address);
567 if (r)
568 return r;
569
570 return loc_database_lookup(db, &address, network);
571}
7e13db74
MT
572
573// Enumerator
574
ccc7ab4e
MT
575LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
576 struct loc_database* db, enum loc_database_enumerator_mode mode) {
7e13db74
MT
577 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
578 if (!e)
579 return -ENOMEM;
580
581 // Reference context
582 e->ctx = loc_ref(db->ctx);
583 e->db = loc_database_ref(db);
ccc7ab4e 584 e->mode = mode;
7e13db74
MT
585 e->refcount = 1;
586
e3f696c1
MT
587 // Initialise graph search
588 //e->network_stack[++e->network_stack_depth] = 0;
589 e->network_stack_depth = 1;
590 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
591
7e13db74
MT
592 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
593
594 *enumerator = e;
595 return 0;
596}
597
598LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
599 enumerator->refcount++;
600
601 return enumerator;
602}
603
604static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
605 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
606
607 // Release all references
608 loc_database_unref(enumerator->db);
609 loc_unref(enumerator->ctx);
610
d3d8ede6
MT
611 if (enumerator->string)
612 free(enumerator->string);
613
91d89020
MT
614 // Free network search
615 free(enumerator->networks_visited);
616
7e13db74
MT
617 free(enumerator);
618}
619
620LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
621 if (!enumerator)
622 return NULL;
623
624 if (--enumerator->refcount > 0)
625 return enumerator;
626
627 loc_database_enumerator_free(enumerator);
628 return NULL;
629}
d3d8ede6
MT
630
631LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
632 enumerator->string = strdup(string);
633
634 // Make the string lowercase
635 for (char *p = enumerator->string; *p; p++)
636 *p = tolower(*p);
637
638 return 0;
639}
640
35bb3a32
MT
641LOC_EXPORT int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code) {
642 // Set empty country code
643 if (!country_code || !*country_code) {
644 *enumerator->country_code = '\0';
645 return 0;
646 }
647
648 // Country codes must be two characters
649 if (strlen(country_code) != 2)
650 return -EINVAL;
651
652 for (unsigned int i = 0; i < 3; i++) {
653 enumerator->country_code[i] = country_code[i];
654 }
655
656 return 0;
657}
658
82910b95
MT
659LOC_EXPORT int loc_database_enumerator_set_asn(
660 struct loc_database_enumerator* enumerator, unsigned int asn) {
661 enumerator->asn = asn;
662
663 return 0;
664}
665
15f79e2d
MT
666LOC_EXPORT int loc_database_enumerator_next_as(
667 struct loc_database_enumerator* enumerator, struct loc_as** as) {
668 *as = NULL;
669
ccc7ab4e
MT
670 // Do not do anything if not in AS mode
671 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
15f79e2d 672 return 0;
ccc7ab4e 673
d3d8ede6 674 struct loc_database* db = enumerator->db;
d3d8ede6
MT
675
676 while (enumerator->as_index < db->as_count) {
677 // Fetch the next AS
15f79e2d 678 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
d3d8ede6 679 if (r)
15f79e2d 680 return r;
d3d8ede6 681
15f79e2d 682 r = loc_as_match_string(*as, enumerator->string);
273948cf 683 if (r == 1) {
d3d8ede6 684 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
15f79e2d 685 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
d3d8ede6 686
15f79e2d 687 return 0;
d3d8ede6
MT
688 }
689
690 // No match
15f79e2d 691 loc_as_unref(*as);
74f218f0 692 *as = NULL;
d3d8ede6
MT
693 }
694
695 // Reset the index
696 enumerator->as_index = 0;
697
698 // We have searched through all of them
15f79e2d 699 return 0;
d3d8ede6 700}
e3f696c1
MT
701
702static int loc_database_enumerator_stack_push_node(
703 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
704 // Do not add empty nodes
705 if (!offset)
706 return 0;
707
708 // Check if there is any space left on the stack
709 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
710 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
711 return -1;
712 }
713
714 // Increase stack size
715 int s = ++e->network_stack_depth;
716
717 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", offset, depth);
718
719 e->network_stack[s].offset = offset;
720 e->network_stack[s].i = i;
721 e->network_stack[s].depth = depth;
722
723 return 0;
724}
725
15f79e2d
MT
726LOC_EXPORT int loc_database_enumerator_next_network(
727 struct loc_database_enumerator* enumerator, struct loc_network** network) {
e3f696c1
MT
728 // Reset network
729 *network = NULL;
15f79e2d
MT
730
731 // Do not do anything if not in network mode
732 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
733 return 0;
734
e3f696c1
MT
735 int r;
736
15f79e2d
MT
737 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
738 enumerator->network_stack_depth);
e3f696c1
MT
739
740 // Perform DFS
15f79e2d
MT
741 while (enumerator->network_stack_depth > 0) {
742 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
e3f696c1
MT
743
744 // Get object from top of the stack
15f79e2d 745 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
e3f696c1
MT
746
747 // Remove the node from the stack if we have already visited it
15f79e2d
MT
748 if (enumerator->networks_visited[node->offset]) {
749 enumerator->network_stack_depth--;
e3f696c1
MT
750 continue;
751 }
752
74fb733a 753 // Mark the bits on the path correctly
15f79e2d 754 in6_addr_set_bit(&enumerator->network_address,
e3f696c1
MT
755 (node->depth > 0) ? node->depth - 1 : 0, node->i);
756
15f79e2d
MT
757 DEBUG(enumerator->ctx, "Looking at node %jd\n", node->offset);
758 enumerator->networks_visited[node->offset]++;
e3f696c1
MT
759
760 // Pop node from top of the stack
761 struct loc_database_network_node_v0* n =
15f79e2d 762 enumerator->db->network_nodes_v0 + node->offset;
e3f696c1
MT
763
764 // Add edges to stack
15f79e2d 765 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
766 be32toh(n->one), 1, node->depth + 1);
767
768 if (r)
769 return r;
770
15f79e2d 771 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
772 be32toh(n->zero), 0, node->depth + 1);
773
774 if (r)
775 return r;
776
777 // Check if this node is a leaf and has a network object
778 if (__loc_database_node_is_leaf(n)) {
779 off_t network_index = be32toh(n->network);
780
15f79e2d 781 DEBUG(enumerator->ctx, "Node has a network at %jd\n", network_index);
e3f696c1
MT
782
783 // Fetch the network object
15f79e2d
MT
784 r = loc_database_fetch_network(enumerator->db, network,
785 &enumerator->network_address, node->depth, network_index);
e3f696c1
MT
786
787 // Break on any errors
788 if (r)
789 return r;
790
791 // Check if we are interested in this network
792
793 // Skip if the country code does not match
15f79e2d
MT
794 if (enumerator->country_code &&
795 !loc_network_match_country_code(*network, enumerator->country_code)) {
e3f696c1 796 loc_network_unref(*network);
15f79e2d
MT
797 *network = NULL;
798
e3f696c1
MT
799 continue;
800 }
801
82910b95 802 // Skip if the ASN does not match
15f79e2d
MT
803 if (enumerator->asn &&
804 !loc_network_match_asn(*network, enumerator->asn)) {
82910b95 805 loc_network_unref(*network);
15f79e2d
MT
806 *network = NULL;
807
82910b95
MT
808 continue;
809 }
810
e3f696c1
MT
811 return 0;
812 }
813 }
814
815 // Reached the end of the search
fe483cdc
MT
816
817 // Mark all nodes as non-visited
15f79e2d
MT
818 for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
819 enumerator->networks_visited[i] = 0;
e3f696c1
MT
820
821 return 0;
822}