]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
Implement listing networks in Python
[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
c182393f 326 loc_stringpool_unref(db->pool);
c34e76f1 327
c182393f
MT
328 loc_unref(db->ctx);
329 free(db);
c34e76f1
MT
330}
331
c182393f
MT
332LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
333 if (--db->refcount > 0)
334 return NULL;
78ace4ed 335
c182393f
MT
336 loc_database_free(db);
337 return NULL;
338}
78ace4ed 339
c182393f
MT
340LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
341 return db->created_at;
342}
78ace4ed 343
c182393f
MT
344LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
345 return loc_stringpool_get(db->pool, db->vendor);
346}
78ace4ed 347
c182393f
MT
348LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
349 return loc_stringpool_get(db->pool, db->description);
350}
78ace4ed 351
4bf49d00
MT
352LOC_EXPORT const char* loc_database_get_license(struct loc_database* db) {
353 return loc_stringpool_get(db->pool, db->license);
354}
355
c182393f
MT
356LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
357 return db->as_count;
78ace4ed
MT
358}
359
c182393f
MT
360// Returns the AS at position pos
361static 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;
2601e83e 364
c182393f 365 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
2601e83e
MT
366
367 int r;
c182393f
MT
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;
2601e83e 372
c182393f
MT
373 default:
374 return -1;
375 }
2601e83e 376
c182393f
MT
377 if (r == 0) {
378 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 379 }
2601e83e 380
c182393f
MT
381 return r;
382}
c34e76f1 383
c182393f
MT
384// Performs a binary search to find the AS in the list
385LOC_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;
c34e76f1 388
8f3e2a06
MT
389 // Save start time
390 clock_t start = clock();
391
c182393f
MT
392 while (lo <= hi) {
393 off_t i = (lo + hi) / 2;
8f5b676a 394
c182393f
MT
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;
a5db3e49 399
c182393f
MT
400 // Check if this is a match
401 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06
MT
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 %.8fs\n", as_number,
407 (double)(end - start) / CLOCKS_PER_SEC);
408
c182393f 409 return 0;
8f3e2a06 410 }
c182393f
MT
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 }
2601e83e 421
c182393f
MT
422 // Nothing found
423 *as = NULL;
2601e83e 424
8f3e2a06 425 return 1;
2601e83e 426}
10778041
MT
427
428// Returns the network at position pos
39a55353
MT
429static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
430 struct in6_addr* address, unsigned int prefix, off_t pos) {
10778041
MT
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:
39a55353
MT
439 r = loc_network_new_from_database_v0(db->ctx, network,
440 address, prefix, db->networks_v0 + pos);
10778041
MT
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}
2a30e4de 455
025ef489 456static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
39a55353 457 return (node->network != htobe32(0xffffffff));
025ef489
MT
458}
459
460static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
39a55353 461 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
2a30e4de 462 const struct loc_database_network_node_v0* node) {
39a55353
MT
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);
2a30e4de
MT
466
467 // Fetch the network
468 int r = loc_database_fetch_network(db, network,
39a55353 469 network_address, prefix, network_index);
e85e2b0b
MT
470 if (r) {
471 ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
2a30e4de 472 return r;
e85e2b0b 473 }
39a55353 474
2a30e4de
MT
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
2a30e4de
MT
489// Searches for an exact match along the path
490static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
491 struct loc_network** network, struct in6_addr* network_address,
f66f15e1 492 const struct loc_database_network_node_v0* node, unsigned int level) {
025ef489 493 int r;
2a30e4de
MT
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
9086d2b1
MT
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;
2a30e4de 511
9086d2b1
MT
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);
2a30e4de 515
9086d2b1
MT
516 // End here if a result was found
517 if (r == 0)
518 return r;
2a30e4de 519
9086d2b1
MT
520 // Raise any errors
521 else if (r < 0)
522 return r;
ec1d9681
MT
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);
9086d2b1 527 }
2a30e4de 528
9086d2b1
MT
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 }
2a30e4de 535
ec1d9681 536 return 1;
2a30e4de
MT
537}
538
539LOC_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 %.8fs\n",
556 (double)(end - start) / CLOCKS_PER_SEC);
557
558 return r;
559}
560
561LOC_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}
7e13db74
MT
571
572// Enumerator
573
ccc7ab4e
MT
574LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
575 struct loc_database* db, enum loc_database_enumerator_mode mode) {
7e13db74
MT
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);
ccc7ab4e 583 e->mode = mode;
7e13db74
MT
584 e->refcount = 1;
585
e3f696c1
MT
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
7e13db74
MT
591 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
592
593 *enumerator = e;
594 return 0;
595}
596
597LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
598 enumerator->refcount++;
599
600 return enumerator;
601}
602
603static 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
d3d8ede6
MT
610 if (enumerator->string)
611 free(enumerator->string);
612
91d89020
MT
613 // Free network search
614 free(enumerator->networks_visited);
615
7e13db74
MT
616 free(enumerator);
617}
618
619LOC_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}
d3d8ede6
MT
629
630LOC_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
35bb3a32
MT
640LOC_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
82910b95
MT
658LOC_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
d3d8ede6 665LOC_EXPORT struct loc_as* loc_database_enumerator_next_as(struct loc_database_enumerator* enumerator) {
ccc7ab4e
MT
666 // Do not do anything if not in AS mode
667 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
668 return NULL;
669
d3d8ede6
MT
670 struct loc_database* db = enumerator->db;
671 struct loc_as* as;
672
673 while (enumerator->as_index < db->as_count) {
674 // Fetch the next AS
675 int r = loc_database_fetch_as(db, &as, enumerator->as_index++);
676 if (r)
677 return NULL;
678
679 r = loc_as_match_string(as, enumerator->string);
273948cf 680 if (r == 1) {
d3d8ede6
MT
681 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
682 loc_as_get_number(as), loc_as_get_name(as), enumerator->string);
683
684 return as;
685 }
686
687 // No match
688 loc_as_unref(as);
689 }
690
691 // Reset the index
692 enumerator->as_index = 0;
693
694 // We have searched through all of them
695 return NULL;
696}
e3f696c1
MT
697
698static int loc_database_enumerator_stack_push_node(
699 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
700 // Do not add empty nodes
701 if (!offset)
702 return 0;
703
704 // Check if there is any space left on the stack
705 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
706 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
707 return -1;
708 }
709
710 // Increase stack size
711 int s = ++e->network_stack_depth;
712
713 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", offset, depth);
714
715 e->network_stack[s].offset = offset;
716 e->network_stack[s].i = i;
717 e->network_stack[s].depth = depth;
718
719 return 0;
720}
721
722static int loc_database_enumerator_network_depth_first_search(
723 struct loc_database_enumerator* e, struct loc_network** network) {
724 // Reset network
725 *network = NULL;
726 int r;
727
728 DEBUG(e->ctx, "Called with a stack of %u nodes\n", e->network_stack_depth);
729
730 // Perform DFS
731 while (e->network_stack_depth > 0) {
732 DEBUG(e->ctx, "Stack depth: %u\n", e->network_stack_depth);
733
734 // Get object from top of the stack
735 struct loc_node_stack* node = &e->network_stack[e->network_stack_depth];
736
737 // Remove the node from the stack if we have already visited it
738 if (e->networks_visited[node->offset]) {
739 e->network_stack_depth--;
740 continue;
741 }
742
74fb733a 743 // Mark the bits on the path correctly
e3f696c1
MT
744 in6_addr_set_bit(&e->network_address,
745 (node->depth > 0) ? node->depth - 1 : 0, node->i);
746
e3f696c1
MT
747 DEBUG(e->ctx, "Looking at node %jd\n", node->offset);
748 e->networks_visited[node->offset]++;
749
750 // Pop node from top of the stack
751 struct loc_database_network_node_v0* n =
752 e->db->network_nodes_v0 + node->offset;
753
754 // Add edges to stack
755 r = loc_database_enumerator_stack_push_node(e,
756 be32toh(n->one), 1, node->depth + 1);
757
758 if (r)
759 return r;
760
761 r = loc_database_enumerator_stack_push_node(e,
762 be32toh(n->zero), 0, node->depth + 1);
763
764 if (r)
765 return r;
766
767 // Check if this node is a leaf and has a network object
768 if (__loc_database_node_is_leaf(n)) {
769 off_t network_index = be32toh(n->network);
770
771 DEBUG(e->ctx, "Node has a network at %jd\n", network_index);
772
773 // Fetch the network object
774 r = loc_database_fetch_network(e->db, network,
775 &e->network_address, node->depth, network_index);
776
777 // Break on any errors
778 if (r)
779 return r;
780
781 // Check if we are interested in this network
782
783 // Skip if the country code does not match
784 if (e->country_code && !loc_network_match_country_code(*network, e->country_code)) {
785 loc_network_unref(*network);
786 continue;
787 }
788
82910b95
MT
789 // Skip if the ASN does not match
790 if (e->asn && !loc_network_match_asn(*network, e->asn)) {
791 loc_network_unref(*network);
792 continue;
793 }
794
e3f696c1
MT
795 return 0;
796 }
797 }
798
799 // Reached the end of the search
fe483cdc
MT
800
801 // Mark all nodes as non-visited
802 for (unsigned int i = 0; i < e->db->network_nodes_count; i++)
803 e->networks_visited[i] = 0;
e3f696c1
MT
804
805 return 0;
806}
807
808LOC_EXPORT struct loc_network* loc_database_enumerator_next_network(
809 struct loc_database_enumerator* enumerator) {
ccc7ab4e
MT
810 // Do not do anything if not in network mode
811 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
812 return NULL;
813
e3f696c1
MT
814 struct loc_network* network = NULL;
815
816 int r = loc_database_enumerator_network_depth_first_search(enumerator, &network);
817 if (r) {
818 return NULL;
819 }
820
821 return network;
822}