]> git.ipfire.org Git - people/ms/libloc.git/blob - src/database.c
228188440d4c2d5551e5f4d791f55253c934699c
[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
84 // Index of the AS we are looking at
85 unsigned int as_index;
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;
92 };
93
94 static int loc_database_read_magic(struct loc_database* db, FILE* f) {
95 struct loc_database_magic magic;
96
97 // Read from file
98 size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
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
112 db->version = be16toh(magic.version);
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
124 static int loc_database_read_as_section_v0(struct loc_database* db,
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
129 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
130
131 if (as_length > 0) {
132 db->as_v0 = mmap(NULL, as_length, PROT_READ,
133 MAP_SHARED, fileno(f), as_offset);
134
135 if (db->as_v0 == MAP_FAILED)
136 return -errno;
137 }
138
139 db->as_count = as_length / sizeof(*db->as_v0);
140
141 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
142
143 return 0;
144 }
145
146 static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
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
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
169 static 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
192 static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
193 struct loc_database_header_v0 header;
194
195 // Read from file
196 size_t size = fread(&header, 1, sizeof(header), f);
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
204 db->created_at = be64toh(header.created_at);
205 db->vendor = be32toh(header.vendor);
206 db->description = be32toh(header.description);
207 db->license = be32toh(header.license);
208
209 // Open pool
210 off_t pool_offset = be32toh(header.pool_offset);
211 size_t pool_length = be32toh(header.pool_length);
212
213 int r = loc_stringpool_open(db->ctx, &db->pool,
214 f, pool_length, pool_offset);
215 if (r)
216 return r;
217
218 // AS section
219 r = loc_database_read_as_section_v0(db, f, &header);
220 if (r)
221 return r;
222
223 // Network Nodes
224 r = loc_database_read_network_nodes_section_v0(db, f, &header);
225 if (r)
226 return r;
227
228 // Networks
229 r = loc_database_read_networks_section_v0(db, f, &header);
230 if (r)
231 return r;
232
233 return 0;
234 }
235
236 static int loc_database_read_header(struct loc_database* db, FILE* f) {
237 switch (db->version) {
238 case 0:
239 return loc_database_read_header_v0(db, f);
240
241 default:
242 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
243 return 1;
244 }
245 }
246
247 static int loc_database_read(struct loc_database* db, FILE* f) {
248 clock_t start = clock();
249
250 // Read magic bytes
251 int r = loc_database_read_magic(db, f);
252 if (r)
253 return r;
254
255 // Read the header
256 r = loc_database_read_header(db, f);
257 if (r)
258 return r;
259
260 clock_t end = clock();
261
262 INFO(db->ctx, "Opened database in %.4fms\n",
263 (double)(end - start) / CLOCKS_PER_SEC * 1000);
264
265 return 0;
266 }
267
268 LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
269 // Fail on invalid file handle
270 if (!f)
271 return -EINVAL;
272
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
283 int r = loc_database_read(db, f);
284 if (r) {
285 loc_database_unref(db);
286 return r;
287 }
288
289 *database = db;
290
291 return 0;
292 }
293
294 LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
295 db->refcount++;
296
297 return db;
298 }
299
300 static void loc_database_free(struct loc_database* db) {
301 int r;
302
303 DEBUG(db->ctx, "Releasing database %p\n", db);
304
305 // Removing all ASes
306 if (db->as_v0) {
307 r = munmap(db->as_v0, db->as_count * sizeof(*db->as_v0));
308 if (r)
309 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
310 }
311
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
326 loc_stringpool_unref(db->pool);
327
328 loc_unref(db->ctx);
329 free(db);
330 }
331
332 LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
333 if (--db->refcount > 0)
334 return NULL;
335
336 loc_database_free(db);
337 return NULL;
338 }
339
340 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
341 return db->created_at;
342 }
343
344 LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
345 return loc_stringpool_get(db->pool, db->vendor);
346 }
347
348 LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
349 return loc_stringpool_get(db->pool, db->description);
350 }
351
352 LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
353 return loc_stringpool_get(db->pool, db->license);
354 }
355
356 LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
357 return db->as_count;
358 }
359
360 // Returns the AS at position pos
361 static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
362 if ((size_t)pos >= db->as_count)
363 return -EINVAL;
364
365 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
366
367 int r;
368 switch (db->version) {
369 case 0:
370 r = loc_as_new_from_database_v0(db->ctx, db->pool, as, db->as_v0 + pos);
371 break;
372
373 default:
374 return -1;
375 }
376
377 if (r == 0) {
378 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
379 }
380
381 return r;
382 }
383
384 // Performs a binary search to find the AS in the list
385 LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
386 off_t lo = 0;
387 off_t hi = db->as_count - 1;
388
389 // Save start time
390 clock_t start = clock();
391
392 while (lo <= hi) {
393 off_t i = (lo + hi) / 2;
394
395 // Fetch AS in the middle between lo and hi
396 int r = loc_database_fetch_as(db, as, i);
397 if (r)
398 return r;
399
400 // Check if this is a match
401 uint32_t as_number = loc_as_get_number(*as);
402 if (as_number == number) {
403 clock_t end = clock();
404
405 // Log how fast this has been
406 DEBUG(db->ctx, "Found AS%u in %.4fms\n", as_number,
407 (double)(end - start) / CLOCKS_PER_SEC * 1000);
408
409 return 0;
410 }
411
412 // If it wasn't, we release the AS and
413 // adjust our search pointers
414 loc_as_unref(*as);
415
416 if (as_number < number) {
417 lo = i + 1;
418 } else
419 hi = i - 1;
420 }
421
422 // Nothing found
423 *as = NULL;
424
425 return 1;
426 }
427
428 // Returns the network at position pos
429 static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
430 struct in6_addr* address, unsigned int prefix, off_t pos) {
431 if ((size_t)pos >= db->networks_count)
432 return -EINVAL;
433
434 DEBUG(db->ctx, "Fetching network at position %jd\n", pos);
435
436 int r;
437 switch (db->version) {
438 case 0:
439 r = loc_network_new_from_database_v0(db->ctx, network,
440 address, prefix, db->networks_v0 + pos);
441 break;
442
443 default:
444 return -1;
445 }
446
447 if (r == 0) {
448 char* string = loc_network_str(*network);
449 DEBUG(db->ctx, "Got network %s\n", string);
450 free(string);
451 }
452
453 return r;
454 }
455
456 static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
457 return (node->network != htobe32(0xffffffff));
458 }
459
460 static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
461 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
462 const struct loc_database_network_node_v0* node) {
463 off_t network_index = be32toh(node->network);
464
465 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", node - db->network_nodes_v0, network_index);
466
467 // Fetch the network
468 int r = loc_database_fetch_network(db, network,
469 network_address, prefix, network_index);
470 if (r) {
471 ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
472 return r;
473 }
474
475 // Check if the given IP address is inside the network
476 r = loc_network_match_address(*network, address);
477 if (r) {
478 DEBUG(db->ctx, "Searched address is not part of the network\n");
479
480 loc_network_unref(*network);
481 *network = NULL;
482 return 1;
483 }
484
485 // A network was found and the IP address matches
486 return 0;
487 }
488
489 // Searches for an exact match along the path
490 static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
491 struct loc_network** network, struct in6_addr* network_address,
492 const struct loc_database_network_node_v0* node, unsigned int level) {
493 int r;
494 off_t node_index;
495
496 // Follow the path
497 int bit = in6_addr_get_bit(address, level);
498 in6_addr_set_bit(network_address, level, bit);
499
500 if (bit == 0)
501 node_index = be32toh(node->zero);
502 else
503 node_index = be32toh(node->one);
504
505 // If the node index is zero, the tree ends here
506 // and we cannot descend any further
507 if (node_index > 0) {
508 // Check boundaries
509 if ((size_t)node_index >= db->network_nodes_count)
510 return -EINVAL;
511
512 // Move on to the next node
513 r = __loc_database_lookup(db, address, network, network_address,
514 db->network_nodes_v0 + node_index, level + 1);
515
516 // End here if a result was found
517 if (r == 0)
518 return r;
519
520 // Raise any errors
521 else if (r < 0)
522 return r;
523
524 DEBUG(db->ctx, "No match found below level %u\n", level);
525 } else {
526 DEBUG(db->ctx, "Tree ended at level %u\n", level);
527 }
528
529 // If this node has a leaf, we will check if it matches
530 if (__loc_database_node_is_leaf(node)) {
531 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
532 if (r <= 0)
533 return r;
534 }
535
536 return 1;
537 }
538
539 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
540 struct in6_addr* address, struct loc_network** network) {
541 struct in6_addr network_address;
542 memset(&network_address, 0, sizeof(network_address));
543
544 *network = NULL;
545
546 // Save start time
547 clock_t start = clock();
548
549 int r = __loc_database_lookup(db, address, network, &network_address,
550 db->network_nodes_v0, 0);
551
552 clock_t end = clock();
553
554 // Log how fast this has been
555 DEBUG(db->ctx, "Executed network search in %.4fms\n",
556 (double)(end - start) / CLOCKS_PER_SEC * 1000);
557
558 return r;
559 }
560
561 LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
562 const char* string, struct loc_network** network) {
563 struct in6_addr address;
564
565 int r = loc_parse_address(db->ctx, string, &address);
566 if (r)
567 return r;
568
569 return loc_database_lookup(db, &address, network);
570 }
571
572 // Enumerator
573
574 LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
575 struct loc_database* db, enum loc_database_enumerator_mode mode) {
576 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
577 if (!e)
578 return -ENOMEM;
579
580 // Reference context
581 e->ctx = loc_ref(db->ctx);
582 e->db = loc_database_ref(db);
583 e->mode = mode;
584 e->refcount = 1;
585
586 // Initialise graph search
587 //e->network_stack[++e->network_stack_depth] = 0;
588 e->network_stack_depth = 1;
589 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
590
591 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
592
593 *enumerator = e;
594 return 0;
595 }
596
597 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
598 enumerator->refcount++;
599
600 return enumerator;
601 }
602
603 static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
604 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
605
606 // Release all references
607 loc_database_unref(enumerator->db);
608 loc_unref(enumerator->ctx);
609
610 if (enumerator->string)
611 free(enumerator->string);
612
613 // Free network search
614 free(enumerator->networks_visited);
615
616 free(enumerator);
617 }
618
619 LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
620 if (!enumerator)
621 return NULL;
622
623 if (--enumerator->refcount > 0)
624 return enumerator;
625
626 loc_database_enumerator_free(enumerator);
627 return NULL;
628 }
629
630 LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
631 enumerator->string = strdup(string);
632
633 // Make the string lowercase
634 for (char *p = enumerator->string; *p; p++)
635 *p = tolower(*p);
636
637 return 0;
638 }
639
640 LOC_EXPORT int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code) {
641 // Set empty country code
642 if (!country_code || !*country_code) {
643 *enumerator->country_code = '\0';
644 return 0;
645 }
646
647 // Country codes must be two characters
648 if (strlen(country_code) != 2)
649 return -EINVAL;
650
651 for (unsigned int i = 0; i < 3; i++) {
652 enumerator->country_code[i] = country_code[i];
653 }
654
655 return 0;
656 }
657
658 LOC_EXPORT int loc_database_enumerator_set_asn(
659 struct loc_database_enumerator* enumerator, unsigned int asn) {
660 enumerator->asn = asn;
661
662 return 0;
663 }
664
665 LOC_EXPORT int loc_database_enumerator_next_as(
666 struct loc_database_enumerator* enumerator, struct loc_as** as) {
667 *as = NULL;
668
669 // Do not do anything if not in AS mode
670 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
671 return 0;
672
673 struct loc_database* db = enumerator->db;
674
675 while (enumerator->as_index < db->as_count) {
676 // Fetch the next AS
677 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
678 if (r)
679 return r;
680
681 r = loc_as_match_string(*as, enumerator->string);
682 if (r == 1) {
683 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
684 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
685
686 return 0;
687 }
688
689 // No match
690 loc_as_unref(*as);
691 *as = NULL;
692 }
693
694 // Reset the index
695 enumerator->as_index = 0;
696
697 // We have searched through all of them
698 return 0;
699 }
700
701 static int loc_database_enumerator_stack_push_node(
702 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
703 // Do not add empty nodes
704 if (!offset)
705 return 0;
706
707 // Check if there is any space left on the stack
708 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
709 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
710 return -1;
711 }
712
713 // Increase stack size
714 int s = ++e->network_stack_depth;
715
716 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", offset, depth);
717
718 e->network_stack[s].offset = offset;
719 e->network_stack[s].i = i;
720 e->network_stack[s].depth = depth;
721
722 return 0;
723 }
724
725 LOC_EXPORT int loc_database_enumerator_next_network(
726 struct loc_database_enumerator* enumerator, struct loc_network** network) {
727 // Reset network
728 *network = NULL;
729
730 // Do not do anything if not in network mode
731 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
732 return 0;
733
734 int r;
735
736 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
737 enumerator->network_stack_depth);
738
739 // Perform DFS
740 while (enumerator->network_stack_depth > 0) {
741 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
742
743 // Get object from top of the stack
744 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
745
746 // Remove the node from the stack if we have already visited it
747 if (enumerator->networks_visited[node->offset]) {
748 enumerator->network_stack_depth--;
749 continue;
750 }
751
752 // Mark the bits on the path correctly
753 in6_addr_set_bit(&enumerator->network_address,
754 (node->depth > 0) ? node->depth - 1 : 0, node->i);
755
756 DEBUG(enumerator->ctx, "Looking at node %jd\n", node->offset);
757 enumerator->networks_visited[node->offset]++;
758
759 // Pop node from top of the stack
760 struct loc_database_network_node_v0* n =
761 enumerator->db->network_nodes_v0 + node->offset;
762
763 // Add edges to stack
764 r = loc_database_enumerator_stack_push_node(enumerator,
765 be32toh(n->one), 1, node->depth + 1);
766
767 if (r)
768 return r;
769
770 r = loc_database_enumerator_stack_push_node(enumerator,
771 be32toh(n->zero), 0, node->depth + 1);
772
773 if (r)
774 return r;
775
776 // Check if this node is a leaf and has a network object
777 if (__loc_database_node_is_leaf(n)) {
778 off_t network_index = be32toh(n->network);
779
780 DEBUG(enumerator->ctx, "Node has a network at %jd\n", network_index);
781
782 // Fetch the network object
783 r = loc_database_fetch_network(enumerator->db, network,
784 &enumerator->network_address, node->depth, network_index);
785
786 // Break on any errors
787 if (r)
788 return r;
789
790 // Check if we are interested in this network
791
792 // Skip if the country code does not match
793 if (enumerator->country_code &&
794 !loc_network_match_country_code(*network, enumerator->country_code)) {
795 loc_network_unref(*network);
796 *network = NULL;
797
798 continue;
799 }
800
801 // Skip if the ASN does not match
802 if (enumerator->asn &&
803 !loc_network_match_asn(*network, enumerator->asn)) {
804 loc_network_unref(*network);
805 *network = NULL;
806
807 continue;
808 }
809
810 return 0;
811 }
812 }
813
814 // Reached the end of the search
815
816 // Mark all nodes as non-visited
817 for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
818 enumerator->networks_visited[i] = 0;
819
820 return 0;
821 }