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