]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
f293ac1af568a17976d7a37c22ef65aadbdb7a66
[people/ms/libloc.git] / src / database.c
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
17 #include <arpa/inet.h>
18 #include <ctype.h>
19 #include <endian.h>
20 #include <errno.h>
21 #include <netinet/in.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #include <unistd.h>
31
32 #include <loc/libloc.h>
33 #include <loc/as.h>
34 #include <loc/database.h>
35 #include <loc/format.h>
36 #include <loc/network.h>
37 #include <loc/private.h>
38 #include <loc/stringpool.h>
39
40 struct loc_database {
41 struct loc_ctx* ctx;
42 int refcount;
43
44 unsigned int version;
45 time_t created_at;
46 off_t vendor;
47 off_t description;
48 off_t license;
49
50 // ASes in the database
51 struct loc_database_as_v0* as_v0;
52 size_t as_count;
53
54 // Network tree
55 struct loc_database_network_node_v0* network_nodes_v0;
56 size_t network_nodes_count;
57
58 // Networks
59 struct loc_database_network_v0* networks_v0;
60 size_t networks_count;
61
62 struct loc_stringpool* pool;
63 };
64
65 #define MAX_STACK_DEPTH 256
66
67 struct loc_node_stack {
68 off_t offset;
69 int i; // Is this node 0 or 1?
70 int depth;
71 };
72
73 struct loc_database_enumerator {
74 struct loc_ctx* ctx;
75 struct loc_database* db;
76 enum loc_database_enumerator_mode mode;
77 int refcount;
78
79 // Search string
80 char* string;
81 char country_code[3];
82 uint32_t asn;
83 enum loc_network_flags flags;
84
85 // Index of the AS we are looking at
86 unsigned int as_index;
87
88 // Network state
89 struct in6_addr network_address;
90 struct loc_node_stack network_stack[MAX_STACK_DEPTH];
91 int network_stack_depth;
92 unsigned int* networks_visited;
93 };
94
95 static int loc_database_read_magic(struct loc_database* db, FILE* f) {
96 struct loc_database_magic magic;
97
98 // Read from file
99 size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
100
101 // Check if we have been able to read enough data
102 if (bytes_read < sizeof(magic)) {
103 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
104 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
105 return -ENOMSG;
106 }
107
108 // Compare magic bytes
109 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
110 DEBUG(db->ctx, "Magic value matches\n");
111
112 // Parse version
113 db->version = be16toh(magic.version);
114 DEBUG(db->ctx, "Database version is %u\n", db->version);
115
116 return 0;
117 }
118
119 ERROR(db->ctx, "Database format is not compatible\n");
120
121 // Return an error
122 return 1;
123 }
124
125 static int loc_database_read_as_section_v0(struct loc_database* db,
126 FILE* f, const struct loc_database_header_v0* header) {
127 off_t as_offset = be32toh(header->as_offset);
128 size_t as_length = be32toh(header->as_length);
129
130 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
131
132 if (as_length > 0) {
133 db->as_v0 = mmap(NULL, as_length, PROT_READ,
134 MAP_SHARED, fileno(f), as_offset);
135
136 if (db->as_v0 == MAP_FAILED)
137 return -errno;
138 }
139
140 db->as_count = as_length / sizeof(*db->as_v0);
141
142 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
143
144 return 0;
145 }
146
147 static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
148 FILE* f, const struct loc_database_header_v0* header) {
149 off_t network_nodes_offset = be32toh(header->network_tree_offset);
150 size_t network_nodes_length = be32toh(header->network_tree_length);
151
152 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
153 network_nodes_offset, network_nodes_length);
154
155 if (network_nodes_length > 0) {
156 db->network_nodes_v0 = mmap(NULL, network_nodes_length, PROT_READ,
157 MAP_SHARED, fileno(f), network_nodes_offset);
158
159 if (db->network_nodes_v0 == MAP_FAILED)
160 return -errno;
161 }
162
163 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v0);
164
165 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
166
167 return 0;
168 }
169
170 static int loc_database_read_networks_section_v0(struct loc_database* db,
171 FILE* f, const struct loc_database_header_v0* header) {
172 off_t networks_offset = be32toh(header->network_data_offset);
173 size_t networks_length = be32toh(header->network_data_length);
174
175 DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
176 networks_offset, networks_length);
177
178 if (networks_length > 0) {
179 db->networks_v0 = mmap(NULL, networks_length, PROT_READ,
180 MAP_SHARED, fileno(f), networks_offset);
181
182 if (db->networks_v0 == MAP_FAILED)
183 return -errno;
184 }
185
186 db->networks_count = networks_length / sizeof(*db->networks_v0);
187
188 INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
189
190 return 0;
191 }
192
193 static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
194 struct loc_database_header_v0 header;
195
196 // Read from file
197 size_t size = fread(&header, 1, sizeof(header), f);
198
199 if (size < sizeof(header)) {
200 ERROR(db->ctx, "Could not read enough data for header\n");
201 return -ENOMSG;
202 }
203
204 // Copy over data
205 db->created_at = be64toh(header.created_at);
206 db->vendor = be32toh(header.vendor);
207 db->description = be32toh(header.description);
208 db->license = be32toh(header.license);
209
210 // Open pool
211 off_t pool_offset = be32toh(header.pool_offset);
212 size_t pool_length = be32toh(header.pool_length);
213
214 int r = loc_stringpool_open(db->ctx, &db->pool,
215 f, pool_length, pool_offset);
216 if (r)
217 return r;
218
219 // AS section
220 r = loc_database_read_as_section_v0(db, f, &header);
221 if (r)
222 return r;
223
224 // Network Nodes
225 r = loc_database_read_network_nodes_section_v0(db, f, &header);
226 if (r)
227 return r;
228
229 // Networks
230 r = loc_database_read_networks_section_v0(db, f, &header);
231 if (r)
232 return r;
233
234 return 0;
235 }
236
237 static int loc_database_read_header(struct loc_database* db, FILE* f) {
238 switch (db->version) {
239 case 0:
240 return loc_database_read_header_v0(db, f);
241
242 default:
243 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
244 return 1;
245 }
246 }
247
248 static int loc_database_read(struct loc_database* db, FILE* f) {
249 clock_t start = clock();
250
251 // Read magic bytes
252 int r = loc_database_read_magic(db, f);
253 if (r)
254 return r;
255
256 // Read the header
257 r = loc_database_read_header(db, f);
258 if (r)
259 return r;
260
261 clock_t end = clock();
262
263 INFO(db->ctx, "Opened database in %.4fms\n",
264 (double)(end - start) / CLOCKS_PER_SEC * 1000);
265
266 return 0;
267 }
268
269 LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
270 // Fail on invalid file handle
271 if (!f)
272 return -EINVAL;
273
274 struct loc_database* db = calloc(1, sizeof(*db));
275 if (!db)
276 return -ENOMEM;
277
278 // Reference context
279 db->ctx = loc_ref(ctx);
280 db->refcount = 1;
281
282 DEBUG(db->ctx, "Database object allocated at %p\n", db);
283
284 int r = loc_database_read(db, f);
285 if (r) {
286 loc_database_unref(db);
287 return r;
288 }
289
290 *database = db;
291
292 return 0;
293 }
294
295 LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
296 db->refcount++;
297
298 return db;
299 }
300
301 static void loc_database_free(struct loc_database* db) {
302 int r;
303
304 DEBUG(db->ctx, "Releasing database %p\n", db);
305
306 // Removing all ASes
307 if (db->as_v0) {
308 r = munmap(db->as_v0, db->as_count * sizeof(*db->as_v0));
309 if (r)
310 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
311 }
312
313 // Remove mapped network sections
314 if (db->networks_v0) {
315 r = munmap(db->networks_v0, db->networks_count * sizeof(*db->networks_v0));
316 if (r)
317 ERROR(db->ctx, "Could not unmap networks section: %s\n", strerror(errno));
318 }
319
320 // Remove mapped network nodes section
321 if (db->network_nodes_v0) {
322 r = munmap(db->network_nodes_v0, db->network_nodes_count * sizeof(*db->network_nodes_v0));
323 if (r)
324 ERROR(db->ctx, "Could not unmap network nodes section: %s\n", strerror(errno));
325 }
326
327 loc_stringpool_unref(db->pool);
328
329 loc_unref(db->ctx);
330 free(db);
331 }
332
333 LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
334 if (--db->refcount > 0)
335 return NULL;
336
337 loc_database_free(db);
338 return NULL;
339 }
340
341 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
342 return db->created_at;
343 }
344
345 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
346 return loc_stringpool_get(db->pool, db->vendor);
347 }
348
349 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
350 return loc_stringpool_get(db->pool, db->description);
351 }
352
353 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
354 return loc_stringpool_get(db->pool, db->license);
355 }
356
357 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
358 return db->as_count;
359 }
360
361 // Returns the AS at position pos
362 static 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;
365
366 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
367
368 int r;
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;
373
374 default:
375 return -1;
376 }
377
378 if (r == 0) {
379 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
380 }
381
382 return r;
383 }
384
385 // Performs a binary search to find the AS in the list
386 LOC_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;
389
390 // Save start time
391 clock_t start = clock();
392
393 while (lo <= hi) {
394 off_t i = (lo + hi) / 2;
395
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;
400
401 // Check if this is a match
402 uint32_t as_number = loc_as_get_number(*as);
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 %.4fms\n", as_number,
408 (double)(end - start) / CLOCKS_PER_SEC * 1000);
409
410 return 0;
411 }
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 }
422
423 // Nothing found
424 *as = NULL;
425
426 return 1;
427 }
428
429 // Returns the network at position pos
430 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
431 struct in6_addr* address, unsigned int prefix, off_t pos) {
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:
440 r = loc_network_new_from_database_v0(db->ctx, network,
441 address, prefix, db->networks_v0 + pos);
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 }
456
457 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
458 return (node->network != htobe32(0xffffffff));
459 }
460
461 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
462 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
463 const struct loc_database_network_node_v0* node) {
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);
467
468 // Fetch the network
469 int r = loc_database_fetch_network(db, network,
470 network_address, prefix, network_index);
471 if (r) {
472 ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
473 return r;
474 }
475
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
490 // Searches for an exact match along the path
491 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
492 struct loc_network** network, struct in6_addr* network_address,
493 const struct loc_database_network_node_v0* node, unsigned int level) {
494 int r;
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
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;
512
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);
516
517 // End here if a result was found
518 if (r == 0)
519 return r;
520
521 // Raise any errors
522 else if (r < 0)
523 return r;
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);
528 }
529
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 }
536
537 return 1;
538 }
539
540 LOC_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 %.4fms\n",
557 (double)(end - start) / CLOCKS_PER_SEC * 1000);
558
559 return r;
560 }
561
562 LOC_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 }
572
573 // Enumerator
574
575 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
576 struct loc_database* db, enum loc_database_enumerator_mode mode) {
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);
584 e->mode = mode;
585 e->refcount = 1;
586
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
592 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
593
594 *enumerator = e;
595 return 0;
596 }
597
598 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
599 enumerator->refcount++;
600
601 return enumerator;
602 }
603
604 static 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
611 if (enumerator->string)
612 free(enumerator->string);
613
614 // Free network search
615 free(enumerator->networks_visited);
616
617 free(enumerator);
618 }
619
620 LOC_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 }
630
631 LOC_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
641 LOC_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 // Treat A1, A2, A3 as special country codes,
653 // but perform search for flags instead
654 if (strcmp(country_code, "A1") == 0) {
655 return loc_database_enumerator_set_flag(enumerator,
656 LOC_NETWORK_FLAG_ANONYMOUS_PROXY);
657 } else if (strcmp(country_code, "A2") == 0) {
658 return loc_database_enumerator_set_flag(enumerator,
659 LOC_NETWORK_FLAG_SATELLITE_PROVIDER);
660 } else if (strcmp(country_code, "A3") == 0) {
661 return loc_database_enumerator_set_flag(enumerator,
662 LOC_NETWORK_FLAG_ANYCAST);
663 }
664
665 for (unsigned int i = 0; i < 3; i++) {
666 enumerator->country_code[i] = country_code[i];
667 }
668
669 return 0;
670 }
671
672 LOC_EXPORT int loc_database_enumerator_set_asn(
673 struct loc_database_enumerator* enumerator, unsigned int asn) {
674 enumerator->asn = asn;
675
676 return 0;
677 }
678
679 LOC_EXPORT int loc_database_enumerator_set_flag(
680 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
681 enumerator->flags |= flag;
682
683 return 0;
684 }
685
686 LOC_EXPORT int loc_database_enumerator_next_as(
687 struct loc_database_enumerator* enumerator, struct loc_as** as) {
688 *as = NULL;
689
690 // Do not do anything if not in AS mode
691 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
692 return 0;
693
694 struct loc_database* db = enumerator->db;
695
696 while (enumerator->as_index < db->as_count) {
697 // Fetch the next AS
698 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
699 if (r)
700 return r;
701
702 r = loc_as_match_string(*as, enumerator->string);
703 if (r == 1) {
704 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
705 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
706
707 return 0;
708 }
709
710 // No match
711 loc_as_unref(*as);
712 *as = NULL;
713 }
714
715 // Reset the index
716 enumerator->as_index = 0;
717
718 // We have searched through all of them
719 return 0;
720 }
721
722 static int loc_database_enumerator_stack_push_node(
723 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
724 // Do not add empty nodes
725 if (!offset)
726 return 0;
727
728 // Check if there is any space left on the stack
729 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
730 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
731 return -1;
732 }
733
734 // Increase stack size
735 int s = ++e->network_stack_depth;
736
737 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", offset, depth);
738
739 e->network_stack[s].offset = offset;
740 e->network_stack[s].i = i;
741 e->network_stack[s].depth = depth;
742
743 return 0;
744 }
745
746 LOC_EXPORT int loc_database_enumerator_next_network(
747 struct loc_database_enumerator* enumerator, struct loc_network** network) {
748 // Reset network
749 *network = NULL;
750
751 // Do not do anything if not in network mode
752 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
753 return 0;
754
755 int r;
756
757 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
758 enumerator->network_stack_depth);
759
760 // Perform DFS
761 while (enumerator->network_stack_depth > 0) {
762 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
763
764 // Get object from top of the stack
765 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
766
767 // Remove the node from the stack if we have already visited it
768 if (enumerator->networks_visited[node->offset]) {
769 enumerator->network_stack_depth--;
770 continue;
771 }
772
773 // Mark the bits on the path correctly
774 in6_addr_set_bit(&enumerator->network_address,
775 (node->depth > 0) ? node->depth - 1 : 0, node->i);
776
777 DEBUG(enumerator->ctx, "Looking at node %jd\n", node->offset);
778 enumerator->networks_visited[node->offset]++;
779
780 // Pop node from top of the stack
781 struct loc_database_network_node_v0* n =
782 enumerator->db->network_nodes_v0 + node->offset;
783
784 // Add edges to stack
785 r = loc_database_enumerator_stack_push_node(enumerator,
786 be32toh(n->one), 1, node->depth + 1);
787
788 if (r)
789 return r;
790
791 r = loc_database_enumerator_stack_push_node(enumerator,
792 be32toh(n->zero), 0, node->depth + 1);
793
794 if (r)
795 return r;
796
797 // Check if this node is a leaf and has a network object
798 if (__loc_database_node_is_leaf(n)) {
799 off_t network_index = be32toh(n->network);
800
801 DEBUG(enumerator->ctx, "Node has a network at %jd\n", network_index);
802
803 // Fetch the network object
804 r = loc_database_fetch_network(enumerator->db, network,
805 &enumerator->network_address, node->depth, network_index);
806
807 // Break on any errors
808 if (r)
809 return r;
810
811 // Check if we are interested in this network
812
813 // Skip if the country code does not match
814 if (enumerator->country_code &&
815 !loc_network_match_country_code(*network, enumerator->country_code)) {
816 loc_network_unref(*network);
817 *network = NULL;
818
819 continue;
820 }
821
822 // Skip if the ASN does not match
823 if (enumerator->asn &&
824 !loc_network_match_asn(*network, enumerator->asn)) {
825 loc_network_unref(*network);
826 *network = NULL;
827
828 continue;
829 }
830
831 // Skip if flags do not match
832 if (enumerator->flags &&
833 !loc_network_match_flag(*network, enumerator->flags)) {
834 loc_network_unref(*network);
835 *network = NULL;
836 }
837
838 return 0;
839 }
840 }
841
842 // Reached the end of the search
843
844 // Mark all nodes as non-visited
845 for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
846 enumerator->networks_visited[i] = 0;
847
848 return 0;
849 }