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