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