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